mainnet-js 3.1.7 → 4.0.0-next.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.
Files changed (83) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/{mainnet-3.1.7.js → mainnet-4.0.0-next.2.js} +66 -166
  3. package/dist/module/cache/walletCache.d.ts +16 -6
  4. package/dist/module/cache/walletCache.d.ts.map +1 -1
  5. package/dist/module/cache/walletCache.js +92 -34
  6. package/dist/module/cache/walletCache.js.map +1 -1
  7. package/dist/module/mine/mine.d.ts.map +1 -1
  8. package/dist/module/mine/mine.js +14 -19
  9. package/dist/module/mine/mine.js.map +1 -1
  10. package/dist/module/network/Connection.d.ts +1 -12
  11. package/dist/module/network/Connection.d.ts.map +1 -1
  12. package/dist/module/network/Connection.js +12 -33
  13. package/dist/module/network/Connection.js.map +1 -1
  14. package/dist/module/network/ElectrumNetworkProvider.d.ts +4 -7
  15. package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
  16. package/dist/module/network/ElectrumNetworkProvider.js +43 -70
  17. package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
  18. package/dist/module/network/configuration.d.ts +2 -4
  19. package/dist/module/network/configuration.d.ts.map +1 -1
  20. package/dist/module/network/configuration.js +25 -50
  21. package/dist/module/network/configuration.js.map +1 -1
  22. package/dist/module/network/constant.d.ts +7 -7
  23. package/dist/module/network/constant.d.ts.map +1 -1
  24. package/dist/module/network/constant.js +20 -24
  25. package/dist/module/network/constant.js.map +1 -1
  26. package/dist/module/network/default.d.ts +3 -2
  27. package/dist/module/network/default.d.ts.map +1 -1
  28. package/dist/module/network/default.js +19 -51
  29. package/dist/module/network/default.js.map +1 -1
  30. package/dist/module/network/index.d.ts +2 -2
  31. package/dist/module/network/index.d.ts.map +1 -1
  32. package/dist/module/network/index.js +2 -2
  33. package/dist/module/network/index.js.map +1 -1
  34. package/dist/module/network/interface.d.ts +0 -6
  35. package/dist/module/network/interface.d.ts.map +1 -1
  36. package/dist/module/wallet/Base.d.ts.map +1 -1
  37. package/dist/module/wallet/Base.js +33 -12
  38. package/dist/module/wallet/Base.js.map +1 -1
  39. package/dist/module/wallet/HDWallet.d.ts.map +1 -1
  40. package/dist/module/wallet/HDWallet.js +16 -35
  41. package/dist/module/wallet/HDWallet.js.map +1 -1
  42. package/dist/module/wallet/Watch.d.ts +22 -1
  43. package/dist/module/wallet/Watch.d.ts.map +1 -1
  44. package/dist/module/wallet/Watch.js +137 -6
  45. package/dist/module/wallet/Watch.js.map +1 -1
  46. package/dist/module/wallet/Wif.d.ts +1 -1
  47. package/dist/module/wallet/Wif.d.ts.map +1 -1
  48. package/dist/module/wallet/Wif.js +0 -4
  49. package/dist/module/wallet/Wif.js.map +1 -1
  50. package/dist/tsconfig.tsbuildinfo +1 -1
  51. package/package.json +5 -3
  52. package/src/cache/walletCache.ts +122 -57
  53. package/src/mine/mine.ts +18 -22
  54. package/src/network/Connection.test.ts +8 -7
  55. package/src/network/Connection.ts +16 -42
  56. package/src/network/ElectrumNetworkProvider.ts +54 -88
  57. package/src/network/Rpc.test.ts +6 -5
  58. package/src/network/configuration.test.ts +23 -25
  59. package/src/network/configuration.ts +29 -55
  60. package/src/network/constant.ts +20 -25
  61. package/src/network/default.ts +25 -87
  62. package/src/network/electrum.test.ts +47 -11
  63. package/src/network/index.ts +7 -2
  64. package/src/network/interface.ts +0 -7
  65. package/src/network/subscription.test.ts +268 -0
  66. package/src/wallet/Base.ts +39 -17
  67. package/src/wallet/Cashtokens.test.headless.js +6 -0
  68. package/src/wallet/Cashtokens.test.ts +13 -0
  69. package/src/wallet/HDWallet.test.ts +4 -4
  70. package/src/wallet/HDWallet.ts +19 -35
  71. package/src/wallet/WalletCache.test.ts +6 -2
  72. package/src/wallet/Watch.ts +199 -7
  73. package/src/wallet/Wif.test.ts +6 -0
  74. package/src/wallet/Wif.ts +2 -9
  75. package/tsconfig.browser.json +10 -1
  76. package/tsconfig.json +12 -1
  77. package/webpack.config.cjs +1 -0
  78. package/dist/module/network/util.d.ts +0 -3
  79. package/dist/module/network/util.d.ts.map +0 -1
  80. package/dist/module/network/util.js +0 -27
  81. package/dist/module/network/util.js.map +0 -1
  82. package/src/network/util.test.ts +0 -24
  83. package/src/network/util.ts +0 -30
@@ -0,0 +1,268 @@
1
+ import { webSocket } from "@rpckit/websocket/electrum-cash";
2
+ import { createProvider, setGlobalProvider, removeGlobalProvider } from ".";
3
+ import { Network } from "../interface";
4
+ import type { Transport, Unsubscribe } from "@rpckit/core";
5
+ import type { ElectrumCashSchema } from "@rpckit/core/electrum-cash";
6
+ import { Wallet } from "../wallet/Wif";
7
+
8
+ describe("Provider subscription: blockchain.transaction.subscribe", () => {
9
+ let provider: Awaited<ReturnType<typeof createProvider>>;
10
+
11
+ beforeAll(async () => {
12
+ provider = await createProvider(Network.MAINNET);
13
+ });
14
+
15
+ afterAll(async () => {
16
+ await provider.disconnect();
17
+ });
18
+
19
+ test("Should receive initial confirmation count for confirmed transaction", async () => {
20
+ // A well-known confirmed BCH transaction
21
+ const txHash =
22
+ "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251";
23
+
24
+ const received: Array<[string, number | null]> = [];
25
+ const cancel = await provider.subscribeToTransaction(txHash, (data) => {
26
+ received.push(data);
27
+ });
28
+
29
+ // Wait a short time for the initial result to arrive
30
+ await new Promise((r) => setTimeout(r, 100));
31
+
32
+ expect(received.length).toBeGreaterThanOrEqual(1);
33
+ // Initial result should be [txHash, confirmations]
34
+ expect(received[0][0]).toBe(txHash);
35
+ expect(received[0][1]).toBeGreaterThan(0); // Confirmed transaction has > 0 confirmations
36
+
37
+ await cancel();
38
+ });
39
+
40
+ test("Should receive null for unconfirmed/unknown transaction", async () => {
41
+ // A made-up transaction hash that doesn't exist
42
+ const fakeTxHash =
43
+ "0000000000000000000000000000000000000000000000000000000000000000";
44
+
45
+ const received: Array<[string, number | null]> = [];
46
+ const cancel = await provider.subscribeToTransaction(fakeTxHash, (data) => {
47
+ received.push(data);
48
+ });
49
+
50
+ await new Promise((r) => setTimeout(r, 100));
51
+
52
+ expect(received.length).toBeGreaterThanOrEqual(1);
53
+ // Unknown transaction should return [txHash, null]
54
+ expect(received[0][0]).toBe(fakeTxHash);
55
+ expect(received[0][1]).toBeNull();
56
+
57
+ await cancel();
58
+ });
59
+ });
60
+
61
+ describe("Provider subscription: blockchain.address.subscribe", () => {
62
+ let provider: Awaited<ReturnType<typeof createProvider>>;
63
+
64
+ beforeAll(async () => {
65
+ provider = await createProvider(Network.MAINNET);
66
+ });
67
+
68
+ afterAll(async () => {
69
+ await provider.disconnect();
70
+ });
71
+
72
+ test("Should receive initial status for address with history", async () => {
73
+ // The Bitcoin genesis address (has transaction history)
74
+ const address = "bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy";
75
+
76
+ const received: Array<[string, string | null]> = [];
77
+ const cancel = await provider.subscribeToAddress(address, (data) => {
78
+ received.push(data);
79
+ });
80
+
81
+ await new Promise((r) => setTimeout(r, 100));
82
+
83
+ expect(received.length).toBeGreaterThanOrEqual(1);
84
+ // Initial result should be [address, status_hash]
85
+ expect(received[0][0]).toBe(address);
86
+ expect(received[0][1]).not.toBeNull(); // Address with history has a status hash
87
+ expect(typeof received[0][1]).toBe("string");
88
+
89
+ await cancel();
90
+ });
91
+
92
+ test("Should receive null status for unused address", async () => {
93
+ // A valid but never-used address (generated from random pubkey hash)
94
+ // Using a standard P2PKH address with unlikely-to-be-used hash
95
+ const unusedAddress =
96
+ "bitcoincash:qr95sy3j9xwd2ap32xkykttr4cvcu7as4y0qverfuy";
97
+
98
+ const received: Array<[string, string | null]> = [];
99
+ const cancel = await provider.subscribeToAddress(unusedAddress, (data) => {
100
+ received.push(data);
101
+ });
102
+
103
+ await new Promise((r) => setTimeout(r, 100));
104
+
105
+ expect(received.length).toBeGreaterThanOrEqual(1);
106
+ // Unused address should have null status
107
+ expect(received[0][0]).toBe(unusedAddress);
108
+ expect(received[0][1]).toBeNull();
109
+
110
+ await cancel();
111
+ });
112
+ });
113
+
114
+ describe("Provider subscription: blockchain.headers.subscribe", () => {
115
+ let provider: Awaited<ReturnType<typeof createProvider>>;
116
+
117
+ beforeAll(async () => {
118
+ provider = await createProvider(Network.MAINNET);
119
+ });
120
+
121
+ afterAll(async () => {
122
+ await provider.disconnect();
123
+ });
124
+
125
+ test("Should receive initial block header", async () => {
126
+ const received: Array<{ height: number; hex: string }> = [];
127
+ const cancel = await provider.subscribeToHeaders((header) => {
128
+ received.push(header);
129
+ });
130
+
131
+ await new Promise((r) => setTimeout(r, 100));
132
+
133
+ expect(received.length).toBeGreaterThanOrEqual(1);
134
+ // Header should have height and hex
135
+ expect(received[0].height).toBeGreaterThan(800000);
136
+ expect(typeof received[0].hex).toBe("string");
137
+ expect(received[0].hex.length).toBeGreaterThan(0);
138
+
139
+ await cancel();
140
+ });
141
+ });
142
+
143
+ describe("Raw transport subscription format", () => {
144
+ let transport: Transport<ElectrumCashSchema>;
145
+
146
+ beforeAll(async () => {
147
+ transport = webSocket("wss://fulcrum.pat.mn:50004");
148
+ await transport.connect();
149
+ });
150
+
151
+ afterAll(async () => {
152
+ await transport.close();
153
+ });
154
+
155
+ test("blockchain.transaction.subscribe returns [txHash, confirmations] format", async () => {
156
+ const txHash =
157
+ "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251";
158
+
159
+ const received: unknown[] = [];
160
+ const unsub: Unsubscribe = await transport.subscribe(
161
+ "blockchain.transaction.subscribe",
162
+ txHash,
163
+ (data) => {
164
+ received.push(data);
165
+ }
166
+ );
167
+
168
+ await new Promise((r) => setTimeout(r, 100));
169
+
170
+ expect(received.length).toBeGreaterThanOrEqual(1);
171
+ // With transformInitialResult, format should be [txHash, confirmations]
172
+ const data = received[0] as [string, number | null];
173
+ expect(Array.isArray(data)).toBe(true);
174
+ expect(data[0]).toBe(txHash);
175
+ expect(typeof data[1]).toBe("number");
176
+
177
+ await unsub();
178
+ });
179
+
180
+ test("blockchain.address.subscribe returns [address, status] format", async () => {
181
+ const address = "bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy";
182
+
183
+ const received: unknown[] = [];
184
+ const unsub: Unsubscribe = await transport.subscribe(
185
+ "blockchain.address.subscribe",
186
+ address,
187
+ (data) => {
188
+ received.push(data);
189
+ }
190
+ );
191
+
192
+ await new Promise((r) => setTimeout(r, 100));
193
+
194
+ expect(received.length).toBeGreaterThanOrEqual(1);
195
+ // With transformInitialResult, format should be [address, status]
196
+ const data = received[0] as [string, string | null];
197
+ expect(Array.isArray(data)).toBe(true);
198
+ expect(data[0]).toBe(address);
199
+ expect(typeof data[1]).toBe("string");
200
+
201
+ await unsub();
202
+ });
203
+
204
+ test("blockchain.headers.subscribe returns [header] format", async () => {
205
+ const received: unknown[] = [];
206
+ const unsub: Unsubscribe = await transport.subscribe(
207
+ "blockchain.headers.subscribe",
208
+ (data) => {
209
+ received.push(data);
210
+ }
211
+ );
212
+
213
+ await new Promise((r) => setTimeout(r, 100));
214
+
215
+ expect(received.length).toBeGreaterThanOrEqual(1);
216
+ // With transformInitialResult (no params), format should be [header]
217
+ const data = received[0] as [{ height: number; hex: string }];
218
+ expect(Array.isArray(data)).toBe(true);
219
+ expect(data[0].height).toBeGreaterThan(800000);
220
+ expect(typeof data[0].hex).toBe("string");
221
+
222
+ await unsub();
223
+ });
224
+ });
225
+
226
+ describe("Wallet waitForTransaction with specific txHash", () => {
227
+ let wallet: Wallet;
228
+ let provider: Awaited<ReturnType<typeof createProvider>>;
229
+
230
+ beforeAll(async () => {
231
+ // Create provider for mainnet and set it globally
232
+ provider = await createProvider(Network.MAINNET);
233
+ await provider.connect();
234
+ setGlobalProvider(Network.MAINNET, provider);
235
+
236
+ // Create a mainnet wallet using the global provider
237
+ wallet = await Wallet.watchOnly(
238
+ "bitcoincash:qp3wjpa3tjlj042z2wv7hahsldgwhwy0rq9sywjpyy"
239
+ );
240
+ });
241
+
242
+ afterAll(async () => {
243
+ removeGlobalProvider(Network.MAINNET);
244
+ await provider?.disconnect();
245
+ });
246
+
247
+ test("Should resolve immediately for confirmed transaction", async () => {
248
+ // A well-known confirmed BCH transaction
249
+ const txHash =
250
+ "4db095f34d632a4daf942142c291f1f2abb5ba2e1ccac919d85bdc2f671fb251";
251
+
252
+ // waitForTransaction with a specific txHash should resolve when confirmations > 0
253
+ const result = await Promise.race([
254
+ wallet.waitForTransaction({
255
+ txHash,
256
+ getTransactionInfo: true,
257
+ getBalance: false,
258
+ }),
259
+ new Promise((_, reject) =>
260
+ setTimeout(() => reject(new Error("Timeout")), 5000)
261
+ ),
262
+ ]);
263
+
264
+ expect(result).toBeDefined();
265
+ expect((result as any).transactionInfo).toBeDefined();
266
+ expect((result as any).transactionInfo.hash).toBe(txHash);
267
+ });
268
+ });
@@ -486,6 +486,12 @@ export class BaseWallet implements WalletI {
486
486
  // waits for address balance to be greater than or equal to the target value
487
487
  // this call halts the execution
488
488
  public async waitForBalance(value: bigint): Promise<bigint> {
489
+ // Check if condition is already met before watching
490
+ const currentBalance = await this.getBalance();
491
+ if (currentBalance >= value) {
492
+ return currentBalance;
493
+ }
494
+
489
495
  return new Promise(async (resolve) => {
490
496
  let watchCancel: CancelFn;
491
497
  watchCancel = await this.watchBalance(async (balance: bigint) => {
@@ -515,6 +521,12 @@ export class BaseWallet implements WalletI {
515
521
  category: string,
516
522
  amount: bigint
517
523
  ): Promise<bigint> {
524
+ // Check if condition is already met before watching
525
+ const currentBalance = await this.getTokenBalance(category);
526
+ if (currentBalance >= amount) {
527
+ return currentBalance;
528
+ }
529
+
518
530
  return new Promise(async (resolve) => {
519
531
  let watchCancel: CancelFn;
520
532
  watchCancel = await this.watchTokenBalance(
@@ -701,11 +713,19 @@ export class BaseWallet implements WalletI {
701
713
  resp.categories = categories;
702
714
 
703
715
  if (options?.buildUnsigned !== true) {
704
- const txId = await this.submitTransaction(
705
- encodedTransaction,
716
+ const awaitPropagation =
706
717
  options?.awaitTransactionPropagation === undefined ||
707
- options?.awaitTransactionPropagation === true
708
- );
718
+ options?.awaitTransactionPropagation === true;
719
+
720
+ let updatePromise: Promise<void>;
721
+ if (awaitPropagation) {
722
+ updatePromise = this.waitForUpdate({ timeout: 2500 });
723
+ }
724
+
725
+ const [_, txId] = await Promise.all([
726
+ updatePromise!,
727
+ this.submitTransaction(encodedTransaction, awaitPropagation),
728
+ ]);
709
729
 
710
730
  resp.txId = txId;
711
731
  resp.explorerUrl = this.explorerUrl(resp.txId);
@@ -778,11 +798,19 @@ export class BaseWallet implements WalletI {
778
798
  resp.categories = categories;
779
799
 
780
800
  if (options?.buildUnsigned !== true) {
781
- const txId = await this.submitTransaction(
782
- encodedTransaction,
801
+ const awaitPropagation =
783
802
  options?.awaitTransactionPropagation === undefined ||
784
- options?.awaitTransactionPropagation === true
785
- );
803
+ options?.awaitTransactionPropagation === true;
804
+
805
+ let updatePromise: Promise<void>;
806
+ if (awaitPropagation) {
807
+ updatePromise = this.waitForUpdate({ timeout: 2500 });
808
+ }
809
+
810
+ const [_, txId] = await Promise.all([
811
+ updatePromise!,
812
+ this.submitTransaction(encodedTransaction, awaitPropagation),
813
+ ]);
786
814
 
787
815
  resp.txId = txId;
788
816
  resp.explorerUrl = this.explorerUrl(resp.txId);
@@ -1107,15 +1135,9 @@ export class BaseWallet implements WalletI {
1107
1135
 
1108
1136
  // waiting for any address transaction
1109
1137
  let watchCancel: CancelFn;
1110
- let initialResponseSeen = false;
1111
1138
  watchCancel = await this.watchStatus(async (_status) => {
1112
- if (initialResponseSeen) {
1113
- await watchCancel?.();
1114
- resolve(makeResponse());
1115
- return;
1116
- }
1117
-
1118
- initialResponseSeen = true;
1139
+ await watchCancel?.();
1140
+ resolve(makeResponse());
1119
1141
  });
1120
1142
  });
1121
1143
  }
@@ -1416,7 +1438,7 @@ export class BaseWallet implements WalletI {
1416
1438
  */
1417
1439
  public async getTokenBalance(category: string): Promise<bigint> {
1418
1440
  const utxos = (await this.getTokenUtxos(category)).filter(
1419
- (val) => val.token?.amount
1441
+ (val) => val.token?.amount && val.token?.category === category
1420
1442
  );
1421
1443
  return sumTokenAmounts(utxos, category);
1422
1444
  }
@@ -601,6 +601,8 @@ describe(`Wallet should function in the browser`, () => {
601
601
  sourceOutputs
602
602
  );
603
603
  await aliceWallet.submitTransaction(signed);
604
+ await aliceWallet.waitForUpdate();
605
+ await aliceWatchWallet.waitForUpdate();
604
606
 
605
607
  category = categories[0];
606
608
 
@@ -653,6 +655,8 @@ describe(`Wallet should function in the browser`, () => {
653
655
  sourceOutputs
654
656
  );
655
657
  await aliceWallet.submitTransaction(signed);
658
+ await aliceWallet.waitForUpdate();
659
+ await aliceWatchWallet.waitForUpdate();
656
660
 
657
661
  expect(await aliceWallet.getNftTokenBalance(category)).toBe(2);
658
662
  const tokenUtxos = await aliceWallet.getTokenUtxos(category);
@@ -711,6 +715,8 @@ describe(`Wallet should function in the browser`, () => {
711
715
  sourceOutputs
712
716
  );
713
717
  await aliceWallet.submitTransaction(signed);
718
+ await aliceWallet.waitForUpdate();
719
+
714
720
  expect(await aliceWallet.getNftTokenBalance(category)).toBe(2);
715
721
  const tokenUtxos = await aliceWallet.getTokenUtxos(category);
716
722
  expect(tokenUtxos.length).toBe(2);
@@ -934,6 +934,7 @@ describe(`Test cashtokens`, () => {
934
934
 
935
935
  // prepare inputs for two token geneses
936
936
  await alice.send({ cashaddr: bob.cashaddr!, value: 10000n });
937
+ await bob.waitForUpdate();
937
938
 
938
939
  const genesisResponse = await bob.tokenGenesis({
939
940
  nft: {
@@ -1221,7 +1222,13 @@ describe(`Test cashtokens`, () => {
1221
1222
  unsignedTransaction!,
1222
1223
  sourceOutputs!
1223
1224
  );
1225
+ const statusChanged = aliceWallet.waitForUpdate({ timeout: 10000 });
1226
+ const watchStatusChanged = aliceWatchWallet.waitForUpdate({
1227
+ timeout: 10000,
1228
+ });
1224
1229
  await aliceWallet.submitTransaction(signed);
1230
+ await statusChanged;
1231
+ await watchStatusChanged;
1225
1232
 
1226
1233
  category = categories![0];
1227
1234
 
@@ -1269,7 +1276,13 @@ describe(`Test cashtokens`, () => {
1269
1276
  unsignedTransaction!,
1270
1277
  sourceOutputs!
1271
1278
  );
1279
+ const statusChanged2 = aliceWallet.waitForUpdate({ timeout: 10000 });
1280
+ const watchStatusChanged2 = aliceWatchWallet.waitForUpdate({
1281
+ timeout: 10000,
1282
+ });
1272
1283
  await aliceWallet.submitTransaction(signed);
1284
+ await statusChanged2;
1285
+ await watchStatusChanged2;
1273
1286
 
1274
1287
  expect(await aliceWallet.getNftTokenBalance(category)).toBe(2);
1275
1288
  const tokenUtxos = await aliceWallet.getTokenUtxos(category);
@@ -389,7 +389,7 @@ describe("HDWallet", () => {
389
389
  hdWallet.getChangeAddress(100);
390
390
 
391
391
  // persist cache
392
- await hdWallet.walletCache.persist();
392
+ await hdWallet.walletCache.persist(true);
393
393
 
394
394
  // check cache data is there in other instance
395
395
  const otherWallet = await RegTestHDWallet.fromId(hdWallet.toDbString());
@@ -458,7 +458,7 @@ describe("HDWallet", () => {
458
458
  ).toBe(1);
459
459
 
460
460
  // persist cache
461
- await hdWallet.walletCache.persist();
461
+ await hdWallet.walletCache.persist(true);
462
462
 
463
463
  // check cache data is there in other instance
464
464
  const otherWallet = await RegTestHDWallet.fromId(hdWallet.toDbString());
@@ -507,7 +507,7 @@ describe("HDWallet", () => {
507
507
  ).toBe(1);
508
508
 
509
509
  // persist cache
510
- await hdWallet.walletCache.persist();
510
+ await hdWallet.walletCache.persist(true);
511
511
 
512
512
  // check cache data is there in other instance
513
513
  const otherWallet = await RegTestHDWallet.fromId(hdWallet.toDbString());
@@ -620,7 +620,7 @@ describe("HDWallet", () => {
620
620
  expect(rawHistory.length).toBe(2);
621
621
 
622
622
  // Persist and reload - cache should be preserved
623
- await hdWallet.walletCache.persist();
623
+ await hdWallet.walletCache.persist(true);
624
624
  const otherWallet = await RegTestHDWallet.fromId(hdWallet.toDbString());
625
625
  await otherWallet.watchPromise;
626
626
 
@@ -16,7 +16,7 @@ import {
16
16
  sha256,
17
17
  utf8ToBin,
18
18
  } from "@bitauth/libauth";
19
- import { PersistentWalletCache, WalletCacheI } from "../cache/index.js";
19
+ import { HDWalletCache, WalletCacheI } from "../cache/index.js";
20
20
  import { Config } from "../config.js";
21
21
  import { NetworkType, prefixFromNetworkMap, UnitEnum } from "../enum.js";
22
22
  import { getHistory } from "../history/getHistory.js";
@@ -240,7 +240,7 @@ export class HDWallet extends BaseWallet {
240
240
  )
241
241
  );
242
242
  // @ts-ignore
243
- this.walletCache = new PersistentWalletCache(
243
+ this.walletCache = new HDWalletCache(
244
244
  this.walletId,
245
245
  this.xPrivNode ?? this.xPubNode,
246
246
  this.networkPrefix
@@ -529,41 +529,25 @@ export class HDWallet extends BaseWallet {
529
529
 
530
530
  if (isSatisfied()) return;
531
531
 
532
- return new Promise(async (resolve) => {
533
- let timer: ReturnType<typeof setTimeout>;
534
- let resolved = false;
535
- let watchCancel: CancelFn;
536
-
537
- const done = async () => {
538
- if (resolved) return;
539
- resolved = true;
540
- clearTimeout(timer);
541
- await watchCancel?.();
542
- resolve();
543
- };
544
-
545
- const resetTimer = () => {
546
- clearTimeout(timer);
547
- timer = setTimeout(done, timeout);
548
- };
549
-
550
- // Uses default 100ms debounce — callback only fires after 100ms of
551
- // quiet, meaning the cascade has settled before we check the condition.
552
- watchCancel = await this.watchStatus(async () => {
553
- if (isSatisfied()) {
554
- await done();
555
- } else {
556
- resetTimer(); // cascade settled but target not met — wait for more activity
557
- }
532
+ // Single watcher that resolves a rotating promise on each status change
533
+ let statusResolve: () => void;
534
+ const cancel = await this.watchStatus(() => {
535
+ statusResolve?.();
536
+ });
537
+
538
+ // Race status changes against idle timeout; reset timeout on each change
539
+ while (!isSatisfied()) {
540
+ const statusChange = new Promise<"status">((resolve) => {
541
+ statusResolve = () => resolve("status");
558
542
  });
543
+ const idle = new Promise<"idle">((resolve) =>
544
+ setTimeout(resolve, timeout, "idle")
545
+ );
559
546
 
560
- // Re-check + start initial idle timer after subscription
561
- if (isSatisfied()) {
562
- await done();
563
- } else {
564
- resetTimer();
565
- }
566
- });
547
+ if ((await Promise.race([statusChange, idle])) === "idle") break;
548
+ }
549
+
550
+ await cancel();
567
551
  }
568
552
 
569
553
  /**
@@ -35,11 +35,15 @@ describe("Wallet cache initialization", () => {
35
35
  await wallet.stop();
36
36
  });
37
37
 
38
- test("Watch wallet should have empty walletCache", async () => {
38
+ test("Watch wallet should have walletCache without private key", async () => {
39
39
  const wallet = await RegTestWatchWallet.watchOnly(
40
40
  "bchreg:qpttdv3qg2usm4nm7talhxhl05mlhms3ys43u76rn0"
41
41
  );
42
42
  expect(wallet.walletCache).toBeDefined();
43
- expect(wallet.walletCache!.get(wallet.cashaddr)).toBeUndefined();
43
+ expect(wallet.walletCache!.get(wallet.cashaddr)).toBeDefined();
44
+ expect(
45
+ wallet.walletCache!.get(wallet.cashaddr)!.privateKey
46
+ ).toBeUndefined();
47
+ await wallet.stop();
44
48
  });
45
49
  });