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.
- package/dist/index.html +1 -1
- package/dist/{mainnet-3.1.7.js → mainnet-4.0.0-next.2.js} +66 -166
- package/dist/module/cache/walletCache.d.ts +16 -6
- package/dist/module/cache/walletCache.d.ts.map +1 -1
- package/dist/module/cache/walletCache.js +92 -34
- package/dist/module/cache/walletCache.js.map +1 -1
- package/dist/module/mine/mine.d.ts.map +1 -1
- package/dist/module/mine/mine.js +14 -19
- package/dist/module/mine/mine.js.map +1 -1
- package/dist/module/network/Connection.d.ts +1 -12
- package/dist/module/network/Connection.d.ts.map +1 -1
- package/dist/module/network/Connection.js +12 -33
- package/dist/module/network/Connection.js.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.d.ts +4 -7
- package/dist/module/network/ElectrumNetworkProvider.d.ts.map +1 -1
- package/dist/module/network/ElectrumNetworkProvider.js +43 -70
- package/dist/module/network/ElectrumNetworkProvider.js.map +1 -1
- package/dist/module/network/configuration.d.ts +2 -4
- package/dist/module/network/configuration.d.ts.map +1 -1
- package/dist/module/network/configuration.js +25 -50
- package/dist/module/network/configuration.js.map +1 -1
- package/dist/module/network/constant.d.ts +7 -7
- package/dist/module/network/constant.d.ts.map +1 -1
- package/dist/module/network/constant.js +20 -24
- package/dist/module/network/constant.js.map +1 -1
- package/dist/module/network/default.d.ts +3 -2
- package/dist/module/network/default.d.ts.map +1 -1
- package/dist/module/network/default.js +19 -51
- package/dist/module/network/default.js.map +1 -1
- package/dist/module/network/index.d.ts +2 -2
- package/dist/module/network/index.d.ts.map +1 -1
- package/dist/module/network/index.js +2 -2
- package/dist/module/network/index.js.map +1 -1
- package/dist/module/network/interface.d.ts +0 -6
- package/dist/module/network/interface.d.ts.map +1 -1
- package/dist/module/wallet/Base.d.ts.map +1 -1
- package/dist/module/wallet/Base.js +33 -12
- package/dist/module/wallet/Base.js.map +1 -1
- package/dist/module/wallet/HDWallet.d.ts.map +1 -1
- package/dist/module/wallet/HDWallet.js +16 -35
- package/dist/module/wallet/HDWallet.js.map +1 -1
- package/dist/module/wallet/Watch.d.ts +22 -1
- package/dist/module/wallet/Watch.d.ts.map +1 -1
- package/dist/module/wallet/Watch.js +137 -6
- package/dist/module/wallet/Watch.js.map +1 -1
- package/dist/module/wallet/Wif.d.ts +1 -1
- package/dist/module/wallet/Wif.d.ts.map +1 -1
- package/dist/module/wallet/Wif.js +0 -4
- package/dist/module/wallet/Wif.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/cache/walletCache.ts +122 -57
- package/src/mine/mine.ts +18 -22
- package/src/network/Connection.test.ts +8 -7
- package/src/network/Connection.ts +16 -42
- package/src/network/ElectrumNetworkProvider.ts +54 -88
- package/src/network/Rpc.test.ts +6 -5
- package/src/network/configuration.test.ts +23 -25
- package/src/network/configuration.ts +29 -55
- package/src/network/constant.ts +20 -25
- package/src/network/default.ts +25 -87
- package/src/network/electrum.test.ts +47 -11
- package/src/network/index.ts +7 -2
- package/src/network/interface.ts +0 -7
- package/src/network/subscription.test.ts +268 -0
- package/src/wallet/Base.ts +39 -17
- package/src/wallet/Cashtokens.test.headless.js +6 -0
- package/src/wallet/Cashtokens.test.ts +13 -0
- package/src/wallet/HDWallet.test.ts +4 -4
- package/src/wallet/HDWallet.ts +19 -35
- package/src/wallet/WalletCache.test.ts +6 -2
- package/src/wallet/Watch.ts +199 -7
- package/src/wallet/Wif.test.ts +6 -0
- package/src/wallet/Wif.ts +2 -9
- package/tsconfig.browser.json +10 -1
- package/tsconfig.json +12 -1
- package/webpack.config.cjs +1 -0
- package/dist/module/network/util.d.ts +0 -3
- package/dist/module/network/util.d.ts.map +0 -1
- package/dist/module/network/util.js +0 -27
- package/dist/module/network/util.js.map +0 -1
- package/src/network/util.test.ts +0 -24
- 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
|
+
});
|
package/src/wallet/Base.ts
CHANGED
|
@@ -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
|
|
705
|
-
encodedTransaction,
|
|
716
|
+
const awaitPropagation =
|
|
706
717
|
options?.awaitTransactionPropagation === undefined ||
|
|
707
|
-
|
|
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
|
|
782
|
-
encodedTransaction,
|
|
801
|
+
const awaitPropagation =
|
|
783
802
|
options?.awaitTransactionPropagation === undefined ||
|
|
784
|
-
|
|
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
|
-
|
|
1113
|
-
|
|
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
|
|
package/src/wallet/HDWallet.ts
CHANGED
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
sha256,
|
|
17
17
|
utf8ToBin,
|
|
18
18
|
} from "@bitauth/libauth";
|
|
19
|
-
import {
|
|
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
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
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
|
|
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)).
|
|
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
|
});
|