mainnet-js 3.0.1 → 3.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.
Files changed (38) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/{mainnet-3.0.1.js → mainnet-3.1.1.js} +5 -5
  3. package/dist/module/history/getHistory.js +10 -10
  4. package/dist/module/history/getHistory.js.map +1 -1
  5. package/dist/module/network/ElectrumNetworkProvider.d.ts +7 -3
  6. package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
  7. package/dist/module/network/ElectrumNetworkProvider.js +51 -25
  8. package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
  9. package/dist/module/network/NetworkProvider.d.ts +14 -6
  10. package/dist/module/network/NetworkProvider.d.ts.map +1 -1
  11. package/dist/module/network/index.d.ts +1 -1
  12. package/dist/module/network/index.d.ts.map +1 -1
  13. package/dist/module/network/interface.d.ts +4 -3
  14. package/dist/module/network/interface.d.ts.map +1 -1
  15. package/dist/module/wallet/Base.d.ts.map +1 -1
  16. package/dist/module/wallet/Base.js +1 -2
  17. package/dist/module/wallet/Base.js.map +1 -1
  18. package/dist/module/wallet/HDWallet.d.ts +8 -0
  19. package/dist/module/wallet/HDWallet.d.ts.map +1 -1
  20. package/dist/module/wallet/HDWallet.js +28 -0
  21. package/dist/module/wallet/HDWallet.js.map +1 -1
  22. package/dist/module/wallet/Util.d.ts +5 -3
  23. package/dist/module/wallet/Util.d.ts.map +1 -1
  24. package/dist/module/wallet/Util.js +28 -22
  25. package/dist/module/wallet/Util.js.map +1 -1
  26. package/dist/tsconfig.tsbuildinfo +1 -1
  27. package/package.json +1 -1
  28. package/src/history/getHistory.ts +15 -15
  29. package/src/network/ElectrumNetworkProvider.ts +105 -39
  30. package/src/network/NetworkProvider.ts +44 -6
  31. package/src/network/index.ts +5 -1
  32. package/src/network/interface.ts +8 -3
  33. package/src/wallet/Base.ts +1 -4
  34. package/src/wallet/HDWallet.test.ts +239 -0
  35. package/src/wallet/HDWallet.ts +36 -0
  36. package/src/wallet/Util.test.ts +56 -6
  37. package/src/wallet/Util.ts +67 -46
  38. package/src/wallet/Wif.test.ts +3 -9
@@ -14,7 +14,12 @@ import {
14
14
  HeaderI,
15
15
  } from "../interface.js";
16
16
  import { Network } from "../interface.js";
17
- import { ElectrumRawTransaction, ElectrumUtxo } from "./interface.js";
17
+ import {
18
+ ElectrumRawTransaction,
19
+ ElectrumRawTransactionVinWithValues,
20
+ ElectrumRawTransactionWithInputValues,
21
+ ElectrumUtxo,
22
+ } from "./interface.js";
18
23
 
19
24
  import { CancelFn } from "../wallet/interface.js";
20
25
  import { getTransactionHash } from "../util/transaction.js";
@@ -25,6 +30,9 @@ import { IndexedDbCache } from "../cache/IndexedDbCache.js";
25
30
  import { WebStorageCache } from "../cache/WebStorageCache.js";
26
31
  import { MemoryCache } from "../cache/MemoryCache.js";
27
32
 
33
+ /** Internal type for cached verbose transactions. fetchHeight is stripped before returning. */
34
+ type CachedRawTransaction = ElectrumRawTransaction & { fetchHeight: number };
35
+
28
36
  export default class ElectrumNetworkProvider implements NetworkProvider {
29
37
  public electrum: ElectrumClient<ElectrumClientEvents>;
30
38
  public subscriptions: number = 0;
@@ -135,7 +143,7 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
135
143
  if (hashes.length === 0) return new Map();
136
144
 
137
145
  const results = new Map<string, string>();
138
- const keys = hashes.map((h) => `tx-${this.network}-${h}-false-false`);
146
+ const keys = hashes.map((hash) => `txraw-${this.network}-${hash}`);
139
147
 
140
148
  // batch cache read
141
149
  let cached: Map<string, string | null> | undefined;
@@ -168,7 +176,7 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
168
176
  // batch cache write
169
177
  if (this.cache) {
170
178
  const entries: [string, string][] = fetched.map(([hash, tx]) => [
171
- `tx-${this.network}-${hash}-false-false`,
179
+ `txraw-${this.network}-${hash}`,
172
180
  tx,
173
181
  ]);
174
182
  await this.cache.setItems(entries);
@@ -186,7 +194,9 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
186
194
  if (heights.length === 0) return new Map();
187
195
 
188
196
  const results = new Map<number, HeaderI>();
189
- const keys = heights.map((h) => `header-${this.network}-${h}-true`);
197
+ const keys = heights.map(
198
+ (height) => `header-${this.network}-${height}-true`
199
+ );
190
200
 
191
201
  // batch cache read
192
202
  let cached: Map<string, string | null> | undefined;
@@ -251,56 +261,84 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
251
261
  return this.currentHeight;
252
262
  }
253
263
 
264
+ async getRawTransaction(
265
+ txHash: string,
266
+ verbose: true,
267
+ loadInputValues: true
268
+ ): Promise<ElectrumRawTransactionWithInputValues>;
269
+ async getRawTransaction(
270
+ txHash: string,
271
+ verbose: true,
272
+ loadInputValues?: false
273
+ ): Promise<ElectrumRawTransaction>;
274
+ async getRawTransaction(
275
+ txHash: string,
276
+ verbose?: false,
277
+ loadInputValues?: false
278
+ ): Promise<string>;
254
279
  async getRawTransaction(
255
280
  txHash: string,
256
281
  verbose: boolean = false,
257
282
  loadInputValues: boolean = false
258
- ): Promise<string> {
259
- const key = `tx-${this.network}-${txHash}-${verbose}-${loadInputValues}`;
283
+ ): Promise<
284
+ string | ElectrumRawTransaction | ElectrumRawTransactionWithInputValues
285
+ > {
286
+ const nonVerboseKey = `txraw-${this.network}-${txHash}`;
287
+ const verboseKey = `tx-${this.network}-${txHash}`;
288
+ const key = verbose ? verboseKey : nonVerboseKey;
260
289
 
261
290
  if (this.cache) {
262
291
  const cached = await this.cache.getItem(key);
263
292
  if (cached) {
264
- return verbose ? JSON.parse(cached) : cached;
293
+ if (!verbose) {
294
+ return cached;
295
+ }
296
+
297
+ const cachedTx = JSON.parse(cached) as CachedRawTransaction;
298
+ if (cachedTx.confirmations > 0) {
299
+ const currentHeight = await this.getBlockHeight();
300
+ cachedTx.confirmations += currentHeight - cachedTx.fetchHeight;
301
+ }
302
+ const { fetchHeight: _, ...transaction } = cachedTx;
303
+
304
+ if (loadInputValues) {
305
+ return this.enrichWithInputValues(transaction);
306
+ }
307
+
308
+ return transaction;
265
309
  }
266
310
  }
267
311
 
268
312
  try {
269
- const transaction = (await this.performRequest(
313
+ const result = await this.performRequest(
270
314
  "blockchain.transaction.get",
271
315
  txHash,
272
316
  verbose
273
- )) as ElectrumRawTransaction;
317
+ );
318
+
319
+ if (!verbose) {
320
+ const hex = result as string;
321
+ if (this.cache) {
322
+ await this.cache.setItem(key, hex);
323
+ }
324
+ return hex;
325
+ }
326
+
327
+ const cachedTx = result as CachedRawTransaction;
328
+ cachedTx.confirmations ??= 0;
329
+ cachedTx.fetchHeight = await this.getBlockHeight();
274
330
 
275
331
  if (this.cache) {
276
- await this.cache.setItem(
277
- key,
278
- verbose
279
- ? JSON.stringify(transaction)
280
- : (transaction as unknown as string)
281
- );
332
+ await this.cache.setItem(key, JSON.stringify(cachedTx));
282
333
  }
283
334
 
284
- if (verbose && loadInputValues) {
285
- // get unique transaction hashes
286
- const hashes = [...new Set(transaction.vin.map((val) => val.txid))];
287
- const transactions = await Promise.all(
288
- hashes.map((hash) => this.getRawTransactionObject(hash, false))
289
- );
290
- const transactionMap = new Map<string, ElectrumRawTransaction>();
291
- transactions.forEach((val) => transactionMap.set(val.hash, val));
292
-
293
- transaction.vin.forEach((input) => {
294
- const output = transactionMap
295
- .get(input.txid)!
296
- .vout.find((val) => val.n === input.vout)!;
297
- input.address = output.scriptPubKey.addresses[0];
298
- input.value = output.value;
299
- input.tokenData = output.tokenData;
300
- });
335
+ const { fetchHeight: _, ...transaction } = cachedTx;
336
+
337
+ if (loadInputValues) {
338
+ return this.enrichWithInputValues(transaction);
301
339
  }
302
340
 
303
- return transaction as any;
341
+ return transaction;
304
342
  } catch (error: any) {
305
343
  if (
306
344
  (error.message as string).indexOf(
@@ -314,16 +352,44 @@ export default class ElectrumNetworkProvider implements NetworkProvider {
314
352
  }
315
353
  }
316
354
 
355
+ private async enrichWithInputValues(
356
+ transaction: ElectrumRawTransaction
357
+ ): Promise<ElectrumRawTransactionWithInputValues> {
358
+ const hashes = [...new Set(transaction.vin.map((val) => val.txid))];
359
+ const transactions = await Promise.all(
360
+ hashes.map((hash) => this.getRawTransactionObject(hash, false))
361
+ );
362
+ const transactionMap = new Map<string, ElectrumRawTransaction>();
363
+ transactions.forEach((val) => transactionMap.set(val.hash, val));
364
+
365
+ const enrichedVin: ElectrumRawTransactionVinWithValues[] =
366
+ transaction.vin.map((input) => {
367
+ const output = transactionMap
368
+ .get(input.txid)!
369
+ .vout.find((val) => val.n === input.vout)!;
370
+ return { ...input, ...output };
371
+ });
372
+
373
+ return { ...transaction, vin: enrichedVin };
374
+ }
375
+
317
376
  // gets the decoded transaction in human readable form
377
+ async getRawTransactionObject(
378
+ txHash: string,
379
+ loadInputValues: true
380
+ ): Promise<ElectrumRawTransactionWithInputValues>;
381
+ async getRawTransactionObject(
382
+ txHash: string,
383
+ loadInputValues?: false
384
+ ): Promise<ElectrumRawTransaction>;
318
385
  async getRawTransactionObject(
319
386
  txHash: string,
320
387
  loadInputValues: boolean = false
321
- ): Promise<ElectrumRawTransaction> {
322
- return (await this.getRawTransaction(
323
- txHash,
324
- true,
325
- loadInputValues
326
- )) as unknown as ElectrumRawTransaction;
388
+ ): Promise<ElectrumRawTransaction | ElectrumRawTransactionWithInputValues> {
389
+ if (loadInputValues) {
390
+ return this.getRawTransaction(txHash, true, true);
391
+ }
392
+ return this.getRawTransaction(txHash, true);
327
393
  }
328
394
 
329
395
  async sendRawTransaction(
@@ -1,4 +1,8 @@
1
1
  import { TxI, Utxo, Network, HexHeaderI, HeaderI } from "../interface.js";
2
+ import {
3
+ ElectrumRawTransaction,
4
+ ElectrumRawTransactionWithInputValues,
5
+ } from "./interface.js";
2
6
  import { CancelFn } from "../wallet/interface.js";
3
7
 
4
8
  export default interface NetworkProvider {
@@ -38,13 +42,35 @@ export default interface NetworkProvider {
38
42
  getRelayFee(): Promise<number>;
39
43
 
40
44
  /**
41
- * Retrieve the Hex transaction details for a given transaction ID.
45
+ * Retrieve transaction details for a given transaction ID.
42
46
  * @param txHash Hex of transaction hash.
43
- * @param verbose Whether a verbose coin-specific response is required.
47
+ * @param verbose When true, returns a decoded transaction object instead of hex.
48
+ * @param loadInputValues When true (requires verbose), each vin is enriched with value, address and tokenData from the parent output.
44
49
  * @throws {Error} If the transaction does not exist
45
- * @returns The full hex transaction for the provided transaction ID.
50
+ * @returns The hex string (non-verbose) or decoded transaction object (verbose).
46
51
  */
47
- getRawTransaction(txHash: string): Promise<string>;
52
+ getRawTransaction(
53
+ txHash: string,
54
+ verbose: true,
55
+ loadInputValues: true
56
+ ): Promise<ElectrumRawTransactionWithInputValues>;
57
+ getRawTransaction(
58
+ txHash: string,
59
+ verbose: true,
60
+ loadInputValues?: false
61
+ ): Promise<ElectrumRawTransaction>;
62
+ getRawTransaction(
63
+ txHash: string,
64
+ verbose?: false,
65
+ loadInputValues?: false
66
+ ): Promise<string>;
67
+ getRawTransaction(
68
+ txHash: string,
69
+ verbose?: boolean,
70
+ loadInputValues?: boolean
71
+ ): Promise<
72
+ string | ElectrumRawTransaction | ElectrumRawTransactionWithInputValues
73
+ >;
48
74
 
49
75
  /**
50
76
  * Batch retrieve raw transactions by their hashes.
@@ -63,10 +89,22 @@ export default interface NetworkProvider {
63
89
  /**
64
90
  * Retrieve a verbose coin-specific response transaction details for a given transaction ID.
65
91
  * @param txHash Hex of transaction hash.
92
+ * @param loadInputValues When true, each vin is enriched with value, address and tokenData from the parent output.
66
93
  * @throws {Error} If the transaction does not exist
67
- * @returns The full hex transaction for the provided transaction ID.
94
+ * @returns The decoded transaction object.
68
95
  */
69
- getRawTransactionObject(txHash: string): Promise<any>;
96
+ getRawTransactionObject(
97
+ txHash: string,
98
+ loadInputValues: true
99
+ ): Promise<ElectrumRawTransactionWithInputValues>;
100
+ getRawTransactionObject(
101
+ txHash: string,
102
+ loadInputValues?: false
103
+ ): Promise<ElectrumRawTransaction>;
104
+ getRawTransactionObject(
105
+ txHash: string,
106
+ loadInputValues?: boolean
107
+ ): Promise<ElectrumRawTransaction | ElectrumRawTransactionWithInputValues>;
70
108
 
71
109
  /**
72
110
  * Broadcast a raw hex transaction to the Bitcoin Cash network.
@@ -7,4 +7,8 @@ export {
7
7
  } from "./Connection.js";
8
8
  export { default as ElectrumNetworkProvider } from "./ElectrumNetworkProvider.js";
9
9
  export { default as NetworkProvider } from "./NetworkProvider.js";
10
- export { ElectrumRawTransaction } from "./interface.js";
10
+ export {
11
+ ElectrumRawTransaction,
12
+ ElectrumRawTransactionWithInputValues,
13
+ ElectrumRawTransactionVinWithValues,
14
+ } from "./interface.js";
@@ -54,9 +54,14 @@ export interface ElectrumRawTransactionVin {
54
54
  sequence: number;
55
55
  txid: string;
56
56
  vout: number;
57
- value?: number; // optional extention by mainnet.cash
58
- address?: string; // optional extension by mainnet.cash
59
- tokenData?: ElectrumTokenData; // optional extension by mainnet.cash
57
+ }
58
+
59
+ export type ElectrumRawTransactionVinWithValues = ElectrumRawTransactionVin &
60
+ ElectrumRawTransactionVout;
61
+
62
+ export interface ElectrumRawTransactionWithInputValues
63
+ extends Omit<ElectrumRawTransaction, "vin"> {
64
+ vin: ElectrumRawTransactionVinWithValues[];
60
65
  }
61
66
 
62
67
  export interface ElectrumRawTransactionVout {
@@ -571,10 +571,7 @@ export class BaseWallet implements WalletI {
571
571
  ): Promise<CancelFn> {
572
572
  return this.watchTransactions(
573
573
  async (transaction: ElectrumRawTransaction) => {
574
- if (
575
- transaction.vin.some((val) => val.tokenData) ||
576
- transaction.vout.some((val) => val.tokenData)
577
- ) {
574
+ if (transaction.vout.some((val) => val.tokenData)) {
578
575
  callback(transaction);
579
576
  }
580
577
  }