@arkade-os/sdk 0.4.6 → 0.4.8
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/cjs/identity/seedIdentity.js +2 -2
- package/dist/cjs/index.js +9 -2
- package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +140 -7
- package/dist/cjs/wallet/serviceWorker/wallet.js +264 -34
- package/dist/cjs/wallet/vtxo-manager.js +62 -45
- package/dist/cjs/wallet/wallet.js +94 -34
- package/dist/cjs/worker/errors.js +17 -0
- package/dist/cjs/worker/messageBus.js +7 -2
- package/dist/esm/identity/seedIdentity.js +2 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +136 -6
- package/dist/esm/wallet/serviceWorker/wallet.js +264 -34
- package/dist/esm/wallet/vtxo-manager.js +62 -45
- package/dist/esm/wallet/wallet.js +94 -34
- package/dist/esm/worker/errors.js +12 -0
- package/dist/esm/worker/messageBus.js +7 -2
- package/dist/types/identity/seedIdentity.d.ts +5 -2
- package/dist/types/index.d.ts +5 -4
- package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +80 -3
- package/dist/types/wallet/serviceWorker/wallet.d.ts +8 -0
- package/dist/types/wallet/vtxo-manager.d.ts +24 -2
- package/dist/types/wallet/wallet.d.ts +10 -0
- package/dist/types/worker/errors.d.ts +6 -0
- package/package.json +1 -1
|
@@ -105,7 +105,7 @@ class SeedIdentity {
|
|
|
105
105
|
static fromSeed(seed, opts) {
|
|
106
106
|
const descriptor = hasDescriptor(opts)
|
|
107
107
|
? opts.descriptor
|
|
108
|
-
: buildDescriptor(seed, opts.isMainnet);
|
|
108
|
+
: buildDescriptor(seed, opts.isMainnet ?? true);
|
|
109
109
|
return new SeedIdentity(seed, descriptor);
|
|
110
110
|
}
|
|
111
111
|
async xOnlyPublicKey() {
|
|
@@ -194,7 +194,7 @@ class MnemonicIdentity extends SeedIdentity {
|
|
|
194
194
|
const seed = (0, bip39_1.mnemonicToSeedSync)(phrase, passphrase);
|
|
195
195
|
const descriptor = hasDescriptor(opts)
|
|
196
196
|
? opts.descriptor
|
|
197
|
-
: buildDescriptor(seed, opts.isMainnet);
|
|
197
|
+
: buildDescriptor(seed, opts.isMainnet ?? true);
|
|
198
198
|
return new MnemonicIdentity(seed, descriptor);
|
|
199
199
|
}
|
|
200
200
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -36,8 +36,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
};
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.
|
|
40
|
-
exports.
|
|
39
|
+
exports.VtxoTaprootTree = exports.VtxoTreeExpiry = exports.CosignerPublicKey = exports.getArkPsbtFields = exports.setArkPsbtField = exports.ArkPsbtFieldKeyType = exports.ArkPsbtFieldKey = exports.TapTreeCoder = exports.CLTVMultisigTapscript = exports.ConditionMultisigTapscript = exports.ConditionCSVMultisigTapscript = exports.CSVMultisigTapscript = exports.MultisigTapscript = exports.decodeTapscript = exports.ServiceWorkerReadonlyWallet = exports.ServiceWorkerWallet = exports.ServiceWorkerTimeoutError = exports.MessageBusNotInitializedError = exports.DelegatorNotConfiguredError = exports.ReadonlyWalletError = exports.WalletNotInitializedError = exports.WalletMessageHandler = exports.MessageBus = exports.setupServiceWorker = exports.SettlementEventType = exports.ChainTxType = exports.IndexerTxType = exports.TxType = exports.VHTLC = exports.VtxoScript = exports.DelegateVtxo = exports.DefaultVtxo = exports.ArkAddress = exports.RestIndexerProvider = exports.RestArkProvider = exports.EsploraProvider = exports.ESPLORA_URL = exports.RestDelegatorProvider = exports.DelegatorManagerImpl = exports.VtxoManager = exports.Ramps = exports.OnchainWallet = exports.ReadonlyDescriptorIdentity = exports.MnemonicIdentity = exports.SeedIdentity = exports.ReadonlySingleKey = exports.SingleKey = exports.ReadonlyWallet = exports.Wallet = exports.asset = void 0;
|
|
40
|
+
exports.contractFromArkContractWithAddress = exports.contractFromArkContract = exports.decodeArkContract = exports.encodeArkContract = exports.VHTLCContractHandler = exports.DelegateContractHandler = exports.DefaultContractHandler = exports.contractHandlers = exports.ContractWatcher = exports.ContractManager = exports.getSequence = exports.isExpired = exports.isSubdust = exports.isSpendable = exports.isRecoverable = exports.buildForfeitTx = exports.validateConnectorsTxGraph = exports.validateVtxoTxGraph = exports.Batch = exports.maybeArkError = exports.ArkError = exports.Transaction = exports.Unroll = exports.P2A = exports.TxTree = exports.BIP322 = exports.Intent = exports.ContractRepositoryImpl = exports.WalletRepositoryImpl = exports.rollbackMigration = exports.getMigrationStatus = exports.requiresMigration = exports.migrateWalletRepository = exports.MIGRATION_KEY = exports.InMemoryContractRepository = exports.InMemoryWalletRepository = exports.IndexedDBContractRepository = exports.IndexedDBWalletRepository = exports.openDatabase = exports.closeDatabase = exports.networks = exports.ArkNote = exports.isValidArkAddress = exports.isVtxoExpiringSoon = exports.combineTapscriptSigs = exports.hasBoardingTxExpired = exports.waitForIncomingFunds = exports.verifyTapscriptSignatures = exports.buildOffchainTx = exports.ConditionWitness = void 0;
|
|
41
|
+
exports.isArkContract = void 0;
|
|
41
42
|
const transaction_1 = require("./utils/transaction");
|
|
42
43
|
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return transaction_1.Transaction; } });
|
|
43
44
|
const singleKey_1 = require("./identity/singleKey");
|
|
@@ -175,3 +176,9 @@ Object.defineProperty(exports, "closeDatabase", { enumerable: true, get: functio
|
|
|
175
176
|
Object.defineProperty(exports, "openDatabase", { enumerable: true, get: function () { return manager_1.openDatabase; } });
|
|
176
177
|
const wallet_message_handler_1 = require("./wallet/serviceWorker/wallet-message-handler");
|
|
177
178
|
Object.defineProperty(exports, "WalletMessageHandler", { enumerable: true, get: function () { return wallet_message_handler_1.WalletMessageHandler; } });
|
|
179
|
+
Object.defineProperty(exports, "WalletNotInitializedError", { enumerable: true, get: function () { return wallet_message_handler_1.WalletNotInitializedError; } });
|
|
180
|
+
Object.defineProperty(exports, "ReadonlyWalletError", { enumerable: true, get: function () { return wallet_message_handler_1.ReadonlyWalletError; } });
|
|
181
|
+
Object.defineProperty(exports, "DelegatorNotConfiguredError", { enumerable: true, get: function () { return wallet_message_handler_1.DelegatorNotConfiguredError; } });
|
|
182
|
+
const errors_2 = require("./worker/errors");
|
|
183
|
+
Object.defineProperty(exports, "MessageBusNotInitializedError", { enumerable: true, get: function () { return errors_2.MessageBusNotInitializedError; } });
|
|
184
|
+
Object.defineProperty(exports, "ServiceWorkerTimeoutError", { enumerable: true, get: function () { return errors_2.ServiceWorkerTimeoutError; } });
|
|
@@ -1,9 +1,30 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.WalletMessageHandler = exports.DEFAULT_MESSAGE_TAG = void 0;
|
|
3
|
+
exports.WalletMessageHandler = exports.DEFAULT_MESSAGE_TAG = exports.DelegatorNotConfiguredError = exports.ReadonlyWalletError = exports.WalletNotInitializedError = void 0;
|
|
4
4
|
const indexer_1 = require("../../providers/indexer");
|
|
5
5
|
const index_1 = require("../index");
|
|
6
6
|
const utils_1 = require("../utils");
|
|
7
|
+
class WalletNotInitializedError extends Error {
|
|
8
|
+
constructor() {
|
|
9
|
+
super("Wallet handler not initialized");
|
|
10
|
+
this.name = "WalletNotInitializedError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.WalletNotInitializedError = WalletNotInitializedError;
|
|
14
|
+
class ReadonlyWalletError extends Error {
|
|
15
|
+
constructor() {
|
|
16
|
+
super("Read-only wallet: operation requires signing");
|
|
17
|
+
this.name = "ReadonlyWalletError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.ReadonlyWalletError = ReadonlyWalletError;
|
|
21
|
+
class DelegatorNotConfiguredError extends Error {
|
|
22
|
+
constructor() {
|
|
23
|
+
super("Delegator not configured");
|
|
24
|
+
this.name = "DelegatorNotConfiguredError";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.DelegatorNotConfiguredError = DelegatorNotConfiguredError;
|
|
7
28
|
exports.DEFAULT_MESSAGE_TAG = "WALLET_UPDATER";
|
|
8
29
|
class WalletMessageHandler {
|
|
9
30
|
/**
|
|
@@ -24,7 +45,31 @@ class WalletMessageHandler {
|
|
|
24
45
|
this.walletRepository = repositories.walletRepository;
|
|
25
46
|
}
|
|
26
47
|
async stop() {
|
|
27
|
-
|
|
48
|
+
if (this.incomingFundsSubscription) {
|
|
49
|
+
this.incomingFundsSubscription();
|
|
50
|
+
this.incomingFundsSubscription = undefined;
|
|
51
|
+
}
|
|
52
|
+
if (this.contractEventsSubscription) {
|
|
53
|
+
this.contractEventsSubscription();
|
|
54
|
+
this.contractEventsSubscription = undefined;
|
|
55
|
+
}
|
|
56
|
+
// Dispose the wallet to stop VtxoManager background tasks
|
|
57
|
+
// (auto-renewal, boarding UTXO polling) and ContractWatcher.
|
|
58
|
+
try {
|
|
59
|
+
if (this.wallet) {
|
|
60
|
+
await this.wallet.dispose();
|
|
61
|
+
}
|
|
62
|
+
else if (this.readonlyWallet) {
|
|
63
|
+
await this.readonlyWallet.dispose();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (_) {
|
|
67
|
+
// best-effort teardown
|
|
68
|
+
}
|
|
69
|
+
this.wallet = undefined;
|
|
70
|
+
this.readonlyWallet = undefined;
|
|
71
|
+
this.arkProvider = undefined;
|
|
72
|
+
this.indexerProvider = undefined;
|
|
28
73
|
}
|
|
29
74
|
async tick(_now) {
|
|
30
75
|
const results = await Promise.allSettled(this.onNextTick.map((fn) => fn()));
|
|
@@ -47,7 +92,7 @@ class WalletMessageHandler {
|
|
|
47
92
|
}
|
|
48
93
|
requireWallet() {
|
|
49
94
|
if (!this.wallet) {
|
|
50
|
-
throw new
|
|
95
|
+
throw new ReadonlyWalletError();
|
|
51
96
|
}
|
|
52
97
|
return this.wallet;
|
|
53
98
|
}
|
|
@@ -69,7 +114,7 @@ class WalletMessageHandler {
|
|
|
69
114
|
if (!this.readonlyWallet) {
|
|
70
115
|
return this.tagged({
|
|
71
116
|
id,
|
|
72
|
-
error: new
|
|
117
|
+
error: new WalletNotInitializedError(),
|
|
73
118
|
});
|
|
74
119
|
}
|
|
75
120
|
try {
|
|
@@ -297,7 +342,7 @@ class WalletMessageHandler {
|
|
|
297
342
|
const wallet = this.requireWallet();
|
|
298
343
|
const delegatorManager = await wallet.getDelegatorManager();
|
|
299
344
|
if (!delegatorManager) {
|
|
300
|
-
throw new
|
|
345
|
+
throw new DelegatorNotConfiguredError();
|
|
301
346
|
}
|
|
302
347
|
const info = await delegatorManager.getDelegateInfo();
|
|
303
348
|
return this.tagged({
|
|
@@ -306,6 +351,83 @@ class WalletMessageHandler {
|
|
|
306
351
|
payload: { info },
|
|
307
352
|
});
|
|
308
353
|
}
|
|
354
|
+
case "RECOVER_VTXOS": {
|
|
355
|
+
const wallet = this.requireWallet();
|
|
356
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
357
|
+
const txid = await vtxoManager.recoverVtxos((e) => {
|
|
358
|
+
this.scheduleForNextTick(() => this.tagged({
|
|
359
|
+
id,
|
|
360
|
+
type: "RECOVER_VTXOS_EVENT",
|
|
361
|
+
payload: e,
|
|
362
|
+
}));
|
|
363
|
+
});
|
|
364
|
+
return this.tagged({
|
|
365
|
+
id,
|
|
366
|
+
type: "RECOVER_VTXOS_SUCCESS",
|
|
367
|
+
payload: { txid },
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
case "GET_RECOVERABLE_BALANCE": {
|
|
371
|
+
const wallet = this.requireWallet();
|
|
372
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
373
|
+
const balance = await vtxoManager.getRecoverableBalance();
|
|
374
|
+
return this.tagged({
|
|
375
|
+
id,
|
|
376
|
+
type: "RECOVERABLE_BALANCE",
|
|
377
|
+
payload: {
|
|
378
|
+
recoverable: balance.recoverable.toString(),
|
|
379
|
+
subdust: balance.subdust.toString(),
|
|
380
|
+
includesSubdust: balance.includesSubdust,
|
|
381
|
+
vtxoCount: balance.vtxoCount,
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
case "GET_EXPIRING_VTXOS": {
|
|
386
|
+
const wallet = this.requireWallet();
|
|
387
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
388
|
+
const vtxos = await vtxoManager.getExpiringVtxos(message.payload.thresholdMs);
|
|
389
|
+
return this.tagged({
|
|
390
|
+
id,
|
|
391
|
+
type: "EXPIRING_VTXOS",
|
|
392
|
+
payload: { vtxos },
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
case "RENEW_VTXOS": {
|
|
396
|
+
const wallet = this.requireWallet();
|
|
397
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
398
|
+
const txid = await vtxoManager.renewVtxos((e) => {
|
|
399
|
+
this.scheduleForNextTick(() => this.tagged({
|
|
400
|
+
id,
|
|
401
|
+
type: "RENEW_VTXOS_EVENT",
|
|
402
|
+
payload: e,
|
|
403
|
+
}));
|
|
404
|
+
});
|
|
405
|
+
return this.tagged({
|
|
406
|
+
id,
|
|
407
|
+
type: "RENEW_VTXOS_SUCCESS",
|
|
408
|
+
payload: { txid },
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
case "GET_EXPIRED_BOARDING_UTXOS": {
|
|
412
|
+
const wallet = this.requireWallet();
|
|
413
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
414
|
+
const utxos = await vtxoManager.getExpiredBoardingUtxos();
|
|
415
|
+
return this.tagged({
|
|
416
|
+
id,
|
|
417
|
+
type: "EXPIRED_BOARDING_UTXOS",
|
|
418
|
+
payload: { utxos },
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
case "SWEEP_EXPIRED_BOARDING_UTXOS": {
|
|
422
|
+
const wallet = this.requireWallet();
|
|
423
|
+
const vtxoManager = await wallet.getVtxoManager();
|
|
424
|
+
const txid = await vtxoManager.sweepExpiredBoardingUtxos();
|
|
425
|
+
return this.tagged({
|
|
426
|
+
id,
|
|
427
|
+
type: "SWEEP_EXPIRED_BOARDING_UTXOS_SUCCESS",
|
|
428
|
+
payload: { txid },
|
|
429
|
+
});
|
|
430
|
+
}
|
|
309
431
|
default:
|
|
310
432
|
console.error("Unknown message type", message);
|
|
311
433
|
throw new Error("Unknown message");
|
|
@@ -481,6 +603,17 @@ class WalletMessageHandler {
|
|
|
481
603
|
}
|
|
482
604
|
});
|
|
483
605
|
await this.ensureContractEventBroadcasting();
|
|
606
|
+
// Eagerly start the VtxoManager so its background tasks (auto-renewal,
|
|
607
|
+
// boarding UTXO polling/sweep) run inside the service worker without
|
|
608
|
+
// waiting for a client to send a vtxo-manager message first.
|
|
609
|
+
if (this.wallet) {
|
|
610
|
+
try {
|
|
611
|
+
await this.wallet.getVtxoManager();
|
|
612
|
+
}
|
|
613
|
+
catch (error) {
|
|
614
|
+
console.error("Error starting VtxoManager:", error);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
484
617
|
}
|
|
485
618
|
async handleSettle(message) {
|
|
486
619
|
const wallet = this.requireWallet();
|
|
@@ -523,7 +656,7 @@ class WalletMessageHandler {
|
|
|
523
656
|
const wallet = this.requireWallet();
|
|
524
657
|
const delegatorManager = await wallet.getDelegatorManager();
|
|
525
658
|
if (!delegatorManager) {
|
|
526
|
-
throw new
|
|
659
|
+
throw new DelegatorNotConfiguredError();
|
|
527
660
|
}
|
|
528
661
|
const { vtxoOutpoints, destination, delegateAt } = message.payload;
|
|
529
662
|
const allVtxos = await wallet.getVtxos();
|
|
@@ -550,7 +683,7 @@ class WalletMessageHandler {
|
|
|
550
683
|
}
|
|
551
684
|
async handleGetVtxos(message) {
|
|
552
685
|
if (!this.readonlyWallet) {
|
|
553
|
-
throw new
|
|
686
|
+
throw new WalletNotInitializedError();
|
|
554
687
|
}
|
|
555
688
|
const vtxos = await this.getSpendableVtxos();
|
|
556
689
|
const dustAmount = this.readonlyWallet.dustAmount;
|
|
@@ -6,6 +6,37 @@ const utils_1 = require("../../worker/browser/utils");
|
|
|
6
6
|
const repositories_1 = require("../../repositories");
|
|
7
7
|
const wallet_message_handler_1 = require("./wallet-message-handler");
|
|
8
8
|
const utils_2 = require("../utils");
|
|
9
|
+
const errors_1 = require("../../worker/errors");
|
|
10
|
+
// Check by error name instead of instanceof because postMessage uses the
|
|
11
|
+
// structured clone algorithm which strips the prototype chain — the page
|
|
12
|
+
// receives a plain Error, not the original MessageBusNotInitializedError.
|
|
13
|
+
function isMessageBusNotInitializedError(error) {
|
|
14
|
+
return (error instanceof Error && error.name === "MessageBusNotInitializedError");
|
|
15
|
+
}
|
|
16
|
+
const DEDUPABLE_REQUEST_TYPES = new Set([
|
|
17
|
+
"GET_ADDRESS",
|
|
18
|
+
"GET_BALANCE",
|
|
19
|
+
"GET_BOARDING_ADDRESS",
|
|
20
|
+
"GET_BOARDING_UTXOS",
|
|
21
|
+
"GET_STATUS",
|
|
22
|
+
"GET_TRANSACTION_HISTORY",
|
|
23
|
+
"IS_CONTRACT_MANAGER_WATCHING",
|
|
24
|
+
"GET_DELEGATE_INFO",
|
|
25
|
+
"GET_RECOVERABLE_BALANCE",
|
|
26
|
+
"GET_EXPIRED_BOARDING_UTXOS",
|
|
27
|
+
"GET_VTXOS",
|
|
28
|
+
"GET_CONTRACTS",
|
|
29
|
+
"GET_CONTRACTS_WITH_VTXOS",
|
|
30
|
+
"GET_SPENDABLE_PATHS",
|
|
31
|
+
"GET_ALL_SPENDING_PATHS",
|
|
32
|
+
"GET_ASSET_DETAILS",
|
|
33
|
+
"GET_EXPIRING_VTXOS",
|
|
34
|
+
"RELOAD_WALLET",
|
|
35
|
+
]);
|
|
36
|
+
function getRequestDedupKey(request) {
|
|
37
|
+
const { id, tag, ...rest } = request;
|
|
38
|
+
return JSON.stringify(rest);
|
|
39
|
+
}
|
|
9
40
|
const isPrivateKeyIdentity = (identity) => {
|
|
10
41
|
return typeof identity.toHex === "function";
|
|
11
42
|
};
|
|
@@ -82,7 +113,7 @@ const initializeMessageBus = (serviceWorker, config, timeoutMs = 2000) => {
|
|
|
82
113
|
};
|
|
83
114
|
const timeoutId = setTimeout(() => {
|
|
84
115
|
cleanup();
|
|
85
|
-
reject(new
|
|
116
|
+
reject(new errors_1.ServiceWorkerTimeoutError("MessageBus timed out"));
|
|
86
117
|
}, timeoutMs);
|
|
87
118
|
navigator.serviceWorker.addEventListener("message", onMessage);
|
|
88
119
|
serviceWorker.postMessage(initCmd);
|
|
@@ -98,6 +129,8 @@ class ServiceWorkerReadonlyWallet {
|
|
|
98
129
|
this.initConfig = null;
|
|
99
130
|
this.initWalletPayload = null;
|
|
100
131
|
this.reinitPromise = null;
|
|
132
|
+
this.pingPromise = null;
|
|
133
|
+
this.inflightRequests = new Map();
|
|
101
134
|
this.identity = identity;
|
|
102
135
|
this.walletRepository = walletRepository;
|
|
103
136
|
this.contractRepository = contractRepository;
|
|
@@ -191,7 +224,7 @@ class ServiceWorkerReadonlyWallet {
|
|
|
191
224
|
};
|
|
192
225
|
const timeoutId = setTimeout(() => {
|
|
193
226
|
cleanup();
|
|
194
|
-
reject(new
|
|
227
|
+
reject(new errors_1.ServiceWorkerTimeoutError(`Service worker message timed out (${request.type})`));
|
|
195
228
|
}, 30000);
|
|
196
229
|
const messageHandler = (event) => {
|
|
197
230
|
const response = event.data;
|
|
@@ -210,18 +243,137 @@ class ServiceWorkerReadonlyWallet {
|
|
|
210
243
|
this.serviceWorker.postMessage(request);
|
|
211
244
|
});
|
|
212
245
|
}
|
|
246
|
+
// Like sendMessageDirect but supports streaming responses: intermediate
|
|
247
|
+
// messages are forwarded via onEvent while the promise resolves on the
|
|
248
|
+
// first response for which isComplete returns true. The timeout resets
|
|
249
|
+
// on every intermediate event so long-running but progressing operations
|
|
250
|
+
// don't time out prematurely.
|
|
251
|
+
sendMessageStreaming(request, onEvent, isComplete) {
|
|
252
|
+
return new Promise((resolve, reject) => {
|
|
253
|
+
const resetTimeout = () => {
|
|
254
|
+
clearTimeout(timeoutId);
|
|
255
|
+
timeoutId = setTimeout(() => {
|
|
256
|
+
cleanup();
|
|
257
|
+
reject(new errors_1.ServiceWorkerTimeoutError(`Service worker message timed out (${request.type})`));
|
|
258
|
+
}, 30000);
|
|
259
|
+
};
|
|
260
|
+
const cleanup = () => {
|
|
261
|
+
clearTimeout(timeoutId);
|
|
262
|
+
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
263
|
+
};
|
|
264
|
+
let timeoutId;
|
|
265
|
+
resetTimeout();
|
|
266
|
+
const messageHandler = (event) => {
|
|
267
|
+
const response = event.data;
|
|
268
|
+
if (request.id !== response.id)
|
|
269
|
+
return;
|
|
270
|
+
if (response.error) {
|
|
271
|
+
cleanup();
|
|
272
|
+
reject(response.error);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
if (isComplete(response)) {
|
|
276
|
+
cleanup();
|
|
277
|
+
resolve(response);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
resetTimeout();
|
|
281
|
+
onEvent(response);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
navigator.serviceWorker.addEventListener("message", messageHandler);
|
|
285
|
+
this.serviceWorker.postMessage(request);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async sendMessage(request) {
|
|
289
|
+
if (!DEDUPABLE_REQUEST_TYPES.has(request.type)) {
|
|
290
|
+
return this.sendMessageWithRetry(request);
|
|
291
|
+
}
|
|
292
|
+
const key = getRequestDedupKey(request);
|
|
293
|
+
const existing = this.inflightRequests.get(key);
|
|
294
|
+
if (existing)
|
|
295
|
+
return existing;
|
|
296
|
+
const promise = this.sendMessageWithRetry(request).finally(() => {
|
|
297
|
+
this.inflightRequests.delete(key);
|
|
298
|
+
});
|
|
299
|
+
this.inflightRequests.set(key, promise);
|
|
300
|
+
return promise;
|
|
301
|
+
}
|
|
302
|
+
pingServiceWorker() {
|
|
303
|
+
if (this.pingPromise)
|
|
304
|
+
return this.pingPromise;
|
|
305
|
+
this.pingPromise = new Promise((resolve, reject) => {
|
|
306
|
+
const pingId = (0, utils_2.getRandomId)();
|
|
307
|
+
const cleanup = () => {
|
|
308
|
+
clearTimeout(timeoutId);
|
|
309
|
+
navigator.serviceWorker.removeEventListener("message", onMessage);
|
|
310
|
+
};
|
|
311
|
+
const timeoutId = setTimeout(() => {
|
|
312
|
+
cleanup();
|
|
313
|
+
reject(new errors_1.ServiceWorkerTimeoutError("Service worker ping timed out"));
|
|
314
|
+
}, 2000);
|
|
315
|
+
const onMessage = (event) => {
|
|
316
|
+
if (event.data?.id === pingId && event.data?.tag === "PONG") {
|
|
317
|
+
cleanup();
|
|
318
|
+
resolve();
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
navigator.serviceWorker.addEventListener("message", onMessage);
|
|
322
|
+
this.serviceWorker.postMessage({
|
|
323
|
+
id: pingId,
|
|
324
|
+
tag: "PING",
|
|
325
|
+
});
|
|
326
|
+
}).finally(() => {
|
|
327
|
+
this.pingPromise = null;
|
|
328
|
+
});
|
|
329
|
+
return this.pingPromise;
|
|
330
|
+
}
|
|
213
331
|
// send a message, retrying up to 2 times if the service worker was
|
|
214
332
|
// killed and restarted by the OS (mobile browsers do this aggressively)
|
|
215
|
-
async
|
|
333
|
+
async sendMessageWithRetry(request) {
|
|
334
|
+
// Skip the preflight ping during the initial INIT_WALLET call:
|
|
335
|
+
// create() hasn't set initConfig yet, so reinitialize() would throw.
|
|
336
|
+
if (this.initConfig) {
|
|
337
|
+
try {
|
|
338
|
+
await this.pingServiceWorker();
|
|
339
|
+
}
|
|
340
|
+
catch {
|
|
341
|
+
await this.reinitialize();
|
|
342
|
+
}
|
|
343
|
+
}
|
|
216
344
|
const maxRetries = 2;
|
|
217
345
|
for (let attempt = 0;; attempt++) {
|
|
218
346
|
try {
|
|
219
347
|
return await this.sendMessageDirect(request);
|
|
220
348
|
}
|
|
221
349
|
catch (error) {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
350
|
+
if (!isMessageBusNotInitializedError(error) ||
|
|
351
|
+
attempt >= maxRetries) {
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
await this.reinitialize();
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
// Like sendMessage but for streaming responses — retries with
|
|
359
|
+
// reinitialize when the service worker has been killed/restarted.
|
|
360
|
+
async sendMessageWithEvents(request, onEvent, isComplete) {
|
|
361
|
+
if (this.initConfig) {
|
|
362
|
+
try {
|
|
363
|
+
await this.pingServiceWorker();
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
await this.reinitialize();
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
const maxRetries = 2;
|
|
370
|
+
for (let attempt = 0;; attempt++) {
|
|
371
|
+
try {
|
|
372
|
+
return await this.sendMessageStreaming(request, onEvent, isComplete);
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
if (!isMessageBusNotInitializedError(error) ||
|
|
376
|
+
attempt >= maxRetries) {
|
|
225
377
|
throw error;
|
|
226
378
|
}
|
|
227
379
|
await this.reinitialize();
|
|
@@ -675,34 +827,8 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
675
827
|
payload: { params },
|
|
676
828
|
};
|
|
677
829
|
try {
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
const response = event.data;
|
|
681
|
-
if (response.id !== message.id) {
|
|
682
|
-
return;
|
|
683
|
-
}
|
|
684
|
-
if (response.error) {
|
|
685
|
-
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
686
|
-
reject(response.error);
|
|
687
|
-
return;
|
|
688
|
-
}
|
|
689
|
-
switch (response.type) {
|
|
690
|
-
case "SETTLE_EVENT":
|
|
691
|
-
if (callback) {
|
|
692
|
-
callback(response.payload);
|
|
693
|
-
}
|
|
694
|
-
break;
|
|
695
|
-
case "SETTLE_SUCCESS":
|
|
696
|
-
navigator.serviceWorker.removeEventListener("message", messageHandler);
|
|
697
|
-
resolve(response.payload.txid);
|
|
698
|
-
break;
|
|
699
|
-
default:
|
|
700
|
-
console.error(`Unexpected response type for SETTLE request: ${response.type}`);
|
|
701
|
-
}
|
|
702
|
-
};
|
|
703
|
-
navigator.serviceWorker.addEventListener("message", messageHandler);
|
|
704
|
-
this.serviceWorker.postMessage(message);
|
|
705
|
-
});
|
|
830
|
+
const response = await this.sendMessageWithEvents(message, (resp) => callback?.(resp.payload), (resp) => resp.type === "SETTLE_SUCCESS");
|
|
831
|
+
return response.payload.txid;
|
|
706
832
|
}
|
|
707
833
|
catch (error) {
|
|
708
834
|
throw new Error(`Settlement failed: ${error}`);
|
|
@@ -776,5 +902,109 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
776
902
|
};
|
|
777
903
|
return manager;
|
|
778
904
|
}
|
|
905
|
+
async getVtxoManager() {
|
|
906
|
+
const wallet = this;
|
|
907
|
+
const messageTag = this.messageTag;
|
|
908
|
+
const manager = {
|
|
909
|
+
async recoverVtxos(eventCallback) {
|
|
910
|
+
const message = {
|
|
911
|
+
tag: messageTag,
|
|
912
|
+
type: "RECOVER_VTXOS",
|
|
913
|
+
id: (0, utils_2.getRandomId)(),
|
|
914
|
+
};
|
|
915
|
+
try {
|
|
916
|
+
const response = await wallet.sendMessageWithEvents(message, (resp) => eventCallback?.(resp.payload), (resp) => resp.type === "RECOVER_VTXOS_SUCCESS");
|
|
917
|
+
return response.payload.txid;
|
|
918
|
+
}
|
|
919
|
+
catch (e) {
|
|
920
|
+
throw new Error(`Failed to recover vtxos: ${e}`);
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
async getRecoverableBalance() {
|
|
924
|
+
const message = {
|
|
925
|
+
tag: messageTag,
|
|
926
|
+
type: "GET_RECOVERABLE_BALANCE",
|
|
927
|
+
id: (0, utils_2.getRandomId)(),
|
|
928
|
+
};
|
|
929
|
+
try {
|
|
930
|
+
const response = await wallet.sendMessage(message);
|
|
931
|
+
const payload = response
|
|
932
|
+
.payload;
|
|
933
|
+
return {
|
|
934
|
+
recoverable: BigInt(payload.recoverable),
|
|
935
|
+
subdust: BigInt(payload.subdust),
|
|
936
|
+
includesSubdust: payload.includesSubdust,
|
|
937
|
+
vtxoCount: payload.vtxoCount,
|
|
938
|
+
};
|
|
939
|
+
}
|
|
940
|
+
catch (e) {
|
|
941
|
+
throw new Error(`Failed to get recoverable balance: ${e}`);
|
|
942
|
+
}
|
|
943
|
+
},
|
|
944
|
+
async getExpiringVtxos(thresholdMs) {
|
|
945
|
+
const message = {
|
|
946
|
+
tag: messageTag,
|
|
947
|
+
type: "GET_EXPIRING_VTXOS",
|
|
948
|
+
id: (0, utils_2.getRandomId)(),
|
|
949
|
+
payload: { thresholdMs },
|
|
950
|
+
};
|
|
951
|
+
try {
|
|
952
|
+
const response = await wallet.sendMessage(message);
|
|
953
|
+
return response.payload.vtxos;
|
|
954
|
+
}
|
|
955
|
+
catch (e) {
|
|
956
|
+
throw new Error(`Failed to get expiring vtxos: ${e}`);
|
|
957
|
+
}
|
|
958
|
+
},
|
|
959
|
+
async renewVtxos(eventCallback) {
|
|
960
|
+
const message = {
|
|
961
|
+
tag: messageTag,
|
|
962
|
+
type: "RENEW_VTXOS",
|
|
963
|
+
id: (0, utils_2.getRandomId)(),
|
|
964
|
+
};
|
|
965
|
+
try {
|
|
966
|
+
const response = await wallet.sendMessageWithEvents(message, (resp) => eventCallback?.(resp.payload), (resp) => resp.type === "RENEW_VTXOS_SUCCESS");
|
|
967
|
+
return response.payload.txid;
|
|
968
|
+
}
|
|
969
|
+
catch (e) {
|
|
970
|
+
throw new Error(`Failed to renew vtxos: ${e}`);
|
|
971
|
+
}
|
|
972
|
+
},
|
|
973
|
+
async getExpiredBoardingUtxos() {
|
|
974
|
+
const message = {
|
|
975
|
+
tag: messageTag,
|
|
976
|
+
type: "GET_EXPIRED_BOARDING_UTXOS",
|
|
977
|
+
id: (0, utils_2.getRandomId)(),
|
|
978
|
+
};
|
|
979
|
+
try {
|
|
980
|
+
const response = await wallet.sendMessage(message);
|
|
981
|
+
return response.payload
|
|
982
|
+
.utxos;
|
|
983
|
+
}
|
|
984
|
+
catch (e) {
|
|
985
|
+
throw new Error(`Failed to get expired boarding utxos: ${e}`);
|
|
986
|
+
}
|
|
987
|
+
},
|
|
988
|
+
async sweepExpiredBoardingUtxos() {
|
|
989
|
+
const message = {
|
|
990
|
+
tag: messageTag,
|
|
991
|
+
type: "SWEEP_EXPIRED_BOARDING_UTXOS",
|
|
992
|
+
id: (0, utils_2.getRandomId)(),
|
|
993
|
+
};
|
|
994
|
+
try {
|
|
995
|
+
const response = await wallet.sendMessage(message);
|
|
996
|
+
return response
|
|
997
|
+
.payload.txid;
|
|
998
|
+
}
|
|
999
|
+
catch (e) {
|
|
1000
|
+
throw new Error(`Failed to sweep expired boarding utxos: ${e}`);
|
|
1001
|
+
}
|
|
1002
|
+
},
|
|
1003
|
+
async dispose() {
|
|
1004
|
+
return;
|
|
1005
|
+
},
|
|
1006
|
+
};
|
|
1007
|
+
return manager;
|
|
1008
|
+
}
|
|
779
1009
|
}
|
|
780
1010
|
exports.ServiceWorkerWallet = ServiceWorkerWallet;
|