@arkade-os/sdk 0.4.0-next.6 → 0.4.0-next.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/wallet/serviceWorker/wallet.js +70 -4
- package/dist/cjs/wallet/wallet.js +12 -27
- package/dist/cjs/worker/browser/utils.js +42 -27
- package/dist/esm/wallet/serviceWorker/wallet.js +70 -4
- package/dist/esm/wallet/wallet.js +12 -27
- package/dist/esm/worker/browser/utils.js +41 -26
- package/dist/types/wallet/asset.d.ts +3 -3
- package/dist/types/wallet/index.d.ts +1 -1
- package/dist/types/wallet/serviceWorker/wallet.d.ts +21 -1
- package/dist/types/wallet/wallet.d.ts +8 -5
- package/dist/types/worker/browser/utils.d.ts +8 -2
- package/package.json +1 -1
|
@@ -95,6 +95,9 @@ class ServiceWorkerReadonlyWallet {
|
|
|
95
95
|
constructor(serviceWorker, identity, walletRepository, contractRepository, messageTag) {
|
|
96
96
|
this.serviceWorker = serviceWorker;
|
|
97
97
|
this.messageTag = messageTag;
|
|
98
|
+
this.initConfig = null;
|
|
99
|
+
this.initWalletPayload = null;
|
|
100
|
+
this.reinitPromise = null;
|
|
98
101
|
this.identity = identity;
|
|
99
102
|
this.walletRepository = walletRepository;
|
|
100
103
|
this.contractRepository = contractRepository;
|
|
@@ -135,6 +138,16 @@ class ServiceWorkerReadonlyWallet {
|
|
|
135
138
|
payload: initConfig,
|
|
136
139
|
};
|
|
137
140
|
await wallet.sendMessage(initMessage);
|
|
141
|
+
wallet.initConfig = {
|
|
142
|
+
wallet: initConfig.key,
|
|
143
|
+
arkServer: {
|
|
144
|
+
url: initConfig.arkServerUrl,
|
|
145
|
+
publicKey: initConfig.arkServerPublicKey,
|
|
146
|
+
},
|
|
147
|
+
delegatorUrl: initConfig.delegatorUrl,
|
|
148
|
+
};
|
|
149
|
+
wallet.initWalletPayload = initConfig;
|
|
150
|
+
wallet.messageBusTimeoutMs = options.messageBusTimeoutMs;
|
|
138
151
|
return wallet;
|
|
139
152
|
}
|
|
140
153
|
/**
|
|
@@ -160,15 +173,17 @@ class ServiceWorkerReadonlyWallet {
|
|
|
160
173
|
*/
|
|
161
174
|
static async setup(options) {
|
|
162
175
|
// Register and setup the service worker
|
|
163
|
-
const serviceWorker = await (0, utils_1.setupServiceWorker)(
|
|
176
|
+
const serviceWorker = await (0, utils_1.setupServiceWorker)({
|
|
177
|
+
path: options.serviceWorkerPath,
|
|
178
|
+
activationTimeoutMs: options.serviceWorkerActivationTimeoutMs,
|
|
179
|
+
});
|
|
164
180
|
// Use the existing create method
|
|
165
181
|
return await ServiceWorkerReadonlyWallet.create({
|
|
166
182
|
...options,
|
|
167
183
|
serviceWorker,
|
|
168
184
|
});
|
|
169
185
|
}
|
|
170
|
-
|
|
171
|
-
async sendMessage(request) {
|
|
186
|
+
sendMessageDirect(request) {
|
|
172
187
|
return new Promise((resolve, reject) => {
|
|
173
188
|
const cleanup = () => {
|
|
174
189
|
clearTimeout(timeoutId);
|
|
@@ -195,6 +210,44 @@ class ServiceWorkerReadonlyWallet {
|
|
|
195
210
|
this.serviceWorker.postMessage(request);
|
|
196
211
|
});
|
|
197
212
|
}
|
|
213
|
+
// send a message, retrying up to 2 times if the service worker was
|
|
214
|
+
// killed and restarted by the OS (mobile browsers do this aggressively)
|
|
215
|
+
async sendMessage(request) {
|
|
216
|
+
const maxRetries = 2;
|
|
217
|
+
for (let attempt = 0;; attempt++) {
|
|
218
|
+
try {
|
|
219
|
+
return await this.sendMessageDirect(request);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
const isNotInitialized = typeof error?.message === "string" &&
|
|
223
|
+
error.message.includes("MessageBus not initialized");
|
|
224
|
+
if (!isNotInitialized || attempt >= maxRetries) {
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
await this.reinitialize();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
async reinitialize() {
|
|
232
|
+
if (this.reinitPromise)
|
|
233
|
+
return this.reinitPromise;
|
|
234
|
+
this.reinitPromise = (async () => {
|
|
235
|
+
if (!this.initConfig || !this.initWalletPayload) {
|
|
236
|
+
throw new Error("Cannot re-initialize: missing configuration");
|
|
237
|
+
}
|
|
238
|
+
await initializeMessageBus(this.serviceWorker, this.initConfig, this.messageBusTimeoutMs);
|
|
239
|
+
const initMessage = {
|
|
240
|
+
tag: this.messageTag,
|
|
241
|
+
type: "INIT_WALLET",
|
|
242
|
+
id: (0, utils_2.getRandomId)(),
|
|
243
|
+
payload: this.initWalletPayload,
|
|
244
|
+
};
|
|
245
|
+
await this.sendMessageDirect(initMessage);
|
|
246
|
+
})().finally(() => {
|
|
247
|
+
this.reinitPromise = null;
|
|
248
|
+
});
|
|
249
|
+
return this.reinitPromise;
|
|
250
|
+
}
|
|
198
251
|
async clear() {
|
|
199
252
|
const message = {
|
|
200
253
|
id: (0, utils_2.getRandomId)(),
|
|
@@ -554,6 +607,16 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
554
607
|
};
|
|
555
608
|
// Initialize the service worker
|
|
556
609
|
await wallet.sendMessage(initMessage);
|
|
610
|
+
wallet.initConfig = {
|
|
611
|
+
wallet: initConfig.key,
|
|
612
|
+
arkServer: {
|
|
613
|
+
url: initConfig.arkServerUrl,
|
|
614
|
+
publicKey: initConfig.arkServerPublicKey,
|
|
615
|
+
},
|
|
616
|
+
delegatorUrl: initConfig.delegatorUrl,
|
|
617
|
+
};
|
|
618
|
+
wallet.initWalletPayload = initConfig;
|
|
619
|
+
wallet.messageBusTimeoutMs = options.messageBusTimeoutMs;
|
|
557
620
|
return wallet;
|
|
558
621
|
}
|
|
559
622
|
/**
|
|
@@ -579,7 +642,10 @@ class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
579
642
|
*/
|
|
580
643
|
static async setup(options) {
|
|
581
644
|
// Register and setup the service worker
|
|
582
|
-
const serviceWorker = await (0, utils_1.setupServiceWorker)(
|
|
645
|
+
const serviceWorker = await (0, utils_1.setupServiceWorker)({
|
|
646
|
+
path: options.serviceWorkerPath,
|
|
647
|
+
activationTimeoutMs: options.serviceWorkerActivationTimeoutMs,
|
|
648
|
+
});
|
|
583
649
|
// Use the existing create method
|
|
584
650
|
return ServiceWorkerWallet.create({
|
|
585
651
|
...options,
|
|
@@ -273,21 +273,6 @@ class ReadonlyWallet {
|
|
|
273
273
|
await this.walletRepository.saveVtxos(address, allExtended);
|
|
274
274
|
return allExtended;
|
|
275
275
|
}
|
|
276
|
-
async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
|
|
277
|
-
const scripts = [base_1.hex.encode(this.offchainTapscript.pkScript)];
|
|
278
|
-
const response = await this.indexerProvider.getVtxos({ scripts });
|
|
279
|
-
const allVtxos = response.vtxos;
|
|
280
|
-
let vtxos = allVtxos.filter(_1.isSpendable);
|
|
281
|
-
// all recoverable vtxos are spendable by definition
|
|
282
|
-
if (!filter.withRecoverable) {
|
|
283
|
-
vtxos = vtxos.filter((vtxo) => !(0, _1.isRecoverable)(vtxo) && !(0, _1.isExpired)(vtxo));
|
|
284
|
-
}
|
|
285
|
-
if (filter.withUnrolled) {
|
|
286
|
-
const spentVtxos = allVtxos.filter((vtxo) => !(0, _1.isSpendable)(vtxo));
|
|
287
|
-
vtxos.push(...spentVtxos.filter((vtxo) => vtxo.isUnrolled));
|
|
288
|
-
}
|
|
289
|
-
return vtxos;
|
|
290
|
-
}
|
|
291
276
|
async getTransactionHistory() {
|
|
292
277
|
const scripts = await this.getWalletScripts();
|
|
293
278
|
const response = await this.indexerProvider.getVtxos({ scripts });
|
|
@@ -738,6 +723,10 @@ class Wallet extends ReadonlyWallet {
|
|
|
738
723
|
async getDelegatorManager() {
|
|
739
724
|
return this._delegatorManager;
|
|
740
725
|
}
|
|
726
|
+
/**
|
|
727
|
+
* @deprecated Use `send`
|
|
728
|
+
* @param params
|
|
729
|
+
*/
|
|
741
730
|
async sendBitcoin(params) {
|
|
742
731
|
if (params.amount <= 0) {
|
|
743
732
|
throw new Error("Amount must be positive");
|
|
@@ -971,7 +960,7 @@ class Wallet extends ReadonlyWallet {
|
|
|
971
960
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
972
961
|
// the signed forfeits transactions to submit
|
|
973
962
|
const signedForfeits = [];
|
|
974
|
-
const vtxos = await this.
|
|
963
|
+
const vtxos = await this.getVtxos();
|
|
975
964
|
let settlementPsbt = btc_signer_1.Transaction.fromPSBT(base_1.base64.decode(event.commitmentTx));
|
|
976
965
|
let hasBoardingUtxos = false;
|
|
977
966
|
let connectorIndex = 0;
|
|
@@ -1290,7 +1279,7 @@ class Wallet extends ReadonlyWallet {
|
|
|
1290
1279
|
const recipients = (0, utils_1.validateRecipients)(args, Number(this.dustAmount));
|
|
1291
1280
|
const address = await this.getAddress();
|
|
1292
1281
|
const outputAddress = address_1.ArkAddress.decode(address);
|
|
1293
|
-
const virtualCoins = await this.
|
|
1282
|
+
const virtualCoins = await this.getVtxos({
|
|
1294
1283
|
withRecoverable: false,
|
|
1295
1284
|
});
|
|
1296
1285
|
// keep track of asset changes
|
|
@@ -1425,16 +1414,12 @@ class Wallet extends ReadonlyWallet {
|
|
|
1425
1414
|
* @returns The ark transaction id and server-signed checkpoint PSBTs (for bookkeeping)
|
|
1426
1415
|
*/
|
|
1427
1416
|
async buildAndSubmitOffchainTx(inputs, outputs) {
|
|
1428
|
-
const
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
...input,
|
|
1435
|
-
tapLeafScript,
|
|
1436
|
-
tapTree,
|
|
1437
|
-
})), outputs, this.serverUnrollScript);
|
|
1417
|
+
const offchainTx = (0, arkTransaction_1.buildOffchainTx)(inputs.map((input) => {
|
|
1418
|
+
return {
|
|
1419
|
+
...input,
|
|
1420
|
+
tapLeafScript: input.forfeitTapLeafScript,
|
|
1421
|
+
};
|
|
1422
|
+
}), outputs, this.serverUnrollScript);
|
|
1438
1423
|
const signedVirtualTx = await this.identity.sign(offchainTx.arkTx);
|
|
1439
1424
|
const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(base_1.base64.encode(signedVirtualTx.toPSBT()), offchainTx.checkpoints.map((c) => base_1.base64.encode(c.toPSBT())));
|
|
1440
1425
|
const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
|
|
@@ -1,22 +1,53 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_DB_NAME = void 0;
|
|
3
|
+
exports.DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = exports.DEFAULT_DB_NAME = void 0;
|
|
4
4
|
exports.setupServiceWorker = setupServiceWorker;
|
|
5
5
|
exports.DEFAULT_DB_NAME = "arkade-service-worker";
|
|
6
|
+
exports.DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 10000;
|
|
7
|
+
function normalizeOptions(pathOrOptions) {
|
|
8
|
+
if (typeof pathOrOptions === "string") {
|
|
9
|
+
return {
|
|
10
|
+
path: pathOrOptions,
|
|
11
|
+
activationTimeoutMs: exports.DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS,
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return {
|
|
15
|
+
path: pathOrOptions.path,
|
|
16
|
+
activationTimeoutMs: pathOrOptions.activationTimeoutMs ??
|
|
17
|
+
exports.DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function waitForServiceWorkerReady(timeoutMs) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
const timeoutId = setTimeout(() => {
|
|
23
|
+
reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
|
|
24
|
+
}, timeoutMs);
|
|
25
|
+
navigator.serviceWorker.ready
|
|
26
|
+
.then((registration) => {
|
|
27
|
+
clearTimeout(timeoutId);
|
|
28
|
+
resolve(registration);
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
clearTimeout(timeoutId);
|
|
32
|
+
reject(error);
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
6
36
|
/**
|
|
7
37
|
* setupServiceWorker sets up the service worker.
|
|
8
|
-
* @param
|
|
38
|
+
* @param pathOrOptions - the path to the service worker script or setup options
|
|
9
39
|
* @throws if service workers are not supported or activation fails
|
|
10
40
|
* @example
|
|
11
41
|
* ```typescript
|
|
12
42
|
* const worker = await setupServiceWorker("/service-worker.js");
|
|
13
43
|
* ```
|
|
14
44
|
*/
|
|
15
|
-
async function setupServiceWorker(
|
|
45
|
+
async function setupServiceWorker(pathOrOptions) {
|
|
16
46
|
// check if service workers are supported
|
|
17
47
|
if (!("serviceWorker" in navigator)) {
|
|
18
48
|
throw new Error("Service workers are not supported in this browser");
|
|
19
49
|
}
|
|
50
|
+
const { path, activationTimeoutMs } = normalizeOptions(pathOrOptions);
|
|
20
51
|
// register service worker
|
|
21
52
|
const registration = await navigator.serviceWorker.register(path);
|
|
22
53
|
// force update to ensure the service worker is active
|
|
@@ -25,28 +56,12 @@ async function setupServiceWorker(path) {
|
|
|
25
56
|
if (!serviceWorker) {
|
|
26
57
|
throw new Error("Failed to get service worker instance");
|
|
27
58
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const onError = () => {
|
|
37
|
-
cleanup();
|
|
38
|
-
reject(new Error("Service worker failed to activate"));
|
|
39
|
-
};
|
|
40
|
-
const timeout = setTimeout(() => {
|
|
41
|
-
cleanup();
|
|
42
|
-
reject(new Error("Service worker activation timed out"));
|
|
43
|
-
}, 10000);
|
|
44
|
-
const cleanup = () => {
|
|
45
|
-
navigator.serviceWorker.removeEventListener("activate", onActivate);
|
|
46
|
-
navigator.serviceWorker.removeEventListener("error", onError);
|
|
47
|
-
clearTimeout(timeout);
|
|
48
|
-
};
|
|
49
|
-
navigator.serviceWorker.addEventListener("activate", onActivate);
|
|
50
|
-
navigator.serviceWorker.addEventListener("error", onError);
|
|
51
|
-
});
|
|
59
|
+
if (serviceWorker.state === "activated") {
|
|
60
|
+
return serviceWorker;
|
|
61
|
+
}
|
|
62
|
+
const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
|
|
63
|
+
if (!readyRegistration.active) {
|
|
64
|
+
throw new Error("Service worker registration is ready but has no active worker");
|
|
65
|
+
}
|
|
66
|
+
return readyRegistration.active;
|
|
52
67
|
}
|
|
@@ -92,6 +92,9 @@ export class ServiceWorkerReadonlyWallet {
|
|
|
92
92
|
constructor(serviceWorker, identity, walletRepository, contractRepository, messageTag) {
|
|
93
93
|
this.serviceWorker = serviceWorker;
|
|
94
94
|
this.messageTag = messageTag;
|
|
95
|
+
this.initConfig = null;
|
|
96
|
+
this.initWalletPayload = null;
|
|
97
|
+
this.reinitPromise = null;
|
|
95
98
|
this.identity = identity;
|
|
96
99
|
this.walletRepository = walletRepository;
|
|
97
100
|
this.contractRepository = contractRepository;
|
|
@@ -132,6 +135,16 @@ export class ServiceWorkerReadonlyWallet {
|
|
|
132
135
|
payload: initConfig,
|
|
133
136
|
};
|
|
134
137
|
await wallet.sendMessage(initMessage);
|
|
138
|
+
wallet.initConfig = {
|
|
139
|
+
wallet: initConfig.key,
|
|
140
|
+
arkServer: {
|
|
141
|
+
url: initConfig.arkServerUrl,
|
|
142
|
+
publicKey: initConfig.arkServerPublicKey,
|
|
143
|
+
},
|
|
144
|
+
delegatorUrl: initConfig.delegatorUrl,
|
|
145
|
+
};
|
|
146
|
+
wallet.initWalletPayload = initConfig;
|
|
147
|
+
wallet.messageBusTimeoutMs = options.messageBusTimeoutMs;
|
|
135
148
|
return wallet;
|
|
136
149
|
}
|
|
137
150
|
/**
|
|
@@ -157,15 +170,17 @@ export class ServiceWorkerReadonlyWallet {
|
|
|
157
170
|
*/
|
|
158
171
|
static async setup(options) {
|
|
159
172
|
// Register and setup the service worker
|
|
160
|
-
const serviceWorker = await setupServiceWorker(
|
|
173
|
+
const serviceWorker = await setupServiceWorker({
|
|
174
|
+
path: options.serviceWorkerPath,
|
|
175
|
+
activationTimeoutMs: options.serviceWorkerActivationTimeoutMs,
|
|
176
|
+
});
|
|
161
177
|
// Use the existing create method
|
|
162
178
|
return await ServiceWorkerReadonlyWallet.create({
|
|
163
179
|
...options,
|
|
164
180
|
serviceWorker,
|
|
165
181
|
});
|
|
166
182
|
}
|
|
167
|
-
|
|
168
|
-
async sendMessage(request) {
|
|
183
|
+
sendMessageDirect(request) {
|
|
169
184
|
return new Promise((resolve, reject) => {
|
|
170
185
|
const cleanup = () => {
|
|
171
186
|
clearTimeout(timeoutId);
|
|
@@ -192,6 +207,44 @@ export class ServiceWorkerReadonlyWallet {
|
|
|
192
207
|
this.serviceWorker.postMessage(request);
|
|
193
208
|
});
|
|
194
209
|
}
|
|
210
|
+
// send a message, retrying up to 2 times if the service worker was
|
|
211
|
+
// killed and restarted by the OS (mobile browsers do this aggressively)
|
|
212
|
+
async sendMessage(request) {
|
|
213
|
+
const maxRetries = 2;
|
|
214
|
+
for (let attempt = 0;; attempt++) {
|
|
215
|
+
try {
|
|
216
|
+
return await this.sendMessageDirect(request);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
const isNotInitialized = typeof error?.message === "string" &&
|
|
220
|
+
error.message.includes("MessageBus not initialized");
|
|
221
|
+
if (!isNotInitialized || attempt >= maxRetries) {
|
|
222
|
+
throw error;
|
|
223
|
+
}
|
|
224
|
+
await this.reinitialize();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
async reinitialize() {
|
|
229
|
+
if (this.reinitPromise)
|
|
230
|
+
return this.reinitPromise;
|
|
231
|
+
this.reinitPromise = (async () => {
|
|
232
|
+
if (!this.initConfig || !this.initWalletPayload) {
|
|
233
|
+
throw new Error("Cannot re-initialize: missing configuration");
|
|
234
|
+
}
|
|
235
|
+
await initializeMessageBus(this.serviceWorker, this.initConfig, this.messageBusTimeoutMs);
|
|
236
|
+
const initMessage = {
|
|
237
|
+
tag: this.messageTag,
|
|
238
|
+
type: "INIT_WALLET",
|
|
239
|
+
id: getRandomId(),
|
|
240
|
+
payload: this.initWalletPayload,
|
|
241
|
+
};
|
|
242
|
+
await this.sendMessageDirect(initMessage);
|
|
243
|
+
})().finally(() => {
|
|
244
|
+
this.reinitPromise = null;
|
|
245
|
+
});
|
|
246
|
+
return this.reinitPromise;
|
|
247
|
+
}
|
|
195
248
|
async clear() {
|
|
196
249
|
const message = {
|
|
197
250
|
id: getRandomId(),
|
|
@@ -550,6 +603,16 @@ export class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
550
603
|
};
|
|
551
604
|
// Initialize the service worker
|
|
552
605
|
await wallet.sendMessage(initMessage);
|
|
606
|
+
wallet.initConfig = {
|
|
607
|
+
wallet: initConfig.key,
|
|
608
|
+
arkServer: {
|
|
609
|
+
url: initConfig.arkServerUrl,
|
|
610
|
+
publicKey: initConfig.arkServerPublicKey,
|
|
611
|
+
},
|
|
612
|
+
delegatorUrl: initConfig.delegatorUrl,
|
|
613
|
+
};
|
|
614
|
+
wallet.initWalletPayload = initConfig;
|
|
615
|
+
wallet.messageBusTimeoutMs = options.messageBusTimeoutMs;
|
|
553
616
|
return wallet;
|
|
554
617
|
}
|
|
555
618
|
/**
|
|
@@ -575,7 +638,10 @@ export class ServiceWorkerWallet extends ServiceWorkerReadonlyWallet {
|
|
|
575
638
|
*/
|
|
576
639
|
static async setup(options) {
|
|
577
640
|
// Register and setup the service worker
|
|
578
|
-
const serviceWorker = await setupServiceWorker(
|
|
641
|
+
const serviceWorker = await setupServiceWorker({
|
|
642
|
+
path: options.serviceWorkerPath,
|
|
643
|
+
activationTimeoutMs: options.serviceWorkerActivationTimeoutMs,
|
|
644
|
+
});
|
|
579
645
|
// Use the existing create method
|
|
580
646
|
return ServiceWorkerWallet.create({
|
|
581
647
|
...options,
|
|
@@ -268,21 +268,6 @@ export class ReadonlyWallet {
|
|
|
268
268
|
await this.walletRepository.saveVtxos(address, allExtended);
|
|
269
269
|
return allExtended;
|
|
270
270
|
}
|
|
271
|
-
async getVirtualCoins(filter = { withRecoverable: true, withUnrolled: false }) {
|
|
272
|
-
const scripts = [hex.encode(this.offchainTapscript.pkScript)];
|
|
273
|
-
const response = await this.indexerProvider.getVtxos({ scripts });
|
|
274
|
-
const allVtxos = response.vtxos;
|
|
275
|
-
let vtxos = allVtxos.filter(isSpendable);
|
|
276
|
-
// all recoverable vtxos are spendable by definition
|
|
277
|
-
if (!filter.withRecoverable) {
|
|
278
|
-
vtxos = vtxos.filter((vtxo) => !isRecoverable(vtxo) && !isExpired(vtxo));
|
|
279
|
-
}
|
|
280
|
-
if (filter.withUnrolled) {
|
|
281
|
-
const spentVtxos = allVtxos.filter((vtxo) => !isSpendable(vtxo));
|
|
282
|
-
vtxos.push(...spentVtxos.filter((vtxo) => vtxo.isUnrolled));
|
|
283
|
-
}
|
|
284
|
-
return vtxos;
|
|
285
|
-
}
|
|
286
271
|
async getTransactionHistory() {
|
|
287
272
|
const scripts = await this.getWalletScripts();
|
|
288
273
|
const response = await this.indexerProvider.getVtxos({ scripts });
|
|
@@ -732,6 +717,10 @@ export class Wallet extends ReadonlyWallet {
|
|
|
732
717
|
async getDelegatorManager() {
|
|
733
718
|
return this._delegatorManager;
|
|
734
719
|
}
|
|
720
|
+
/**
|
|
721
|
+
* @deprecated Use `send`
|
|
722
|
+
* @param params
|
|
723
|
+
*/
|
|
735
724
|
async sendBitcoin(params) {
|
|
736
725
|
if (params.amount <= 0) {
|
|
737
726
|
throw new Error("Amount must be positive");
|
|
@@ -965,7 +954,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
965
954
|
async handleSettlementFinalizationEvent(event, inputs, forfeitOutputScript, connectorsGraph) {
|
|
966
955
|
// the signed forfeits transactions to submit
|
|
967
956
|
const signedForfeits = [];
|
|
968
|
-
const vtxos = await this.
|
|
957
|
+
const vtxos = await this.getVtxos();
|
|
969
958
|
let settlementPsbt = Transaction.fromPSBT(base64.decode(event.commitmentTx));
|
|
970
959
|
let hasBoardingUtxos = false;
|
|
971
960
|
let connectorIndex = 0;
|
|
@@ -1284,7 +1273,7 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1284
1273
|
const recipients = validateRecipients(args, Number(this.dustAmount));
|
|
1285
1274
|
const address = await this.getAddress();
|
|
1286
1275
|
const outputAddress = ArkAddress.decode(address);
|
|
1287
|
-
const virtualCoins = await this.
|
|
1276
|
+
const virtualCoins = await this.getVtxos({
|
|
1288
1277
|
withRecoverable: false,
|
|
1289
1278
|
});
|
|
1290
1279
|
// keep track of asset changes
|
|
@@ -1419,16 +1408,12 @@ export class Wallet extends ReadonlyWallet {
|
|
|
1419
1408
|
* @returns The ark transaction id and server-signed checkpoint PSBTs (for bookkeeping)
|
|
1420
1409
|
*/
|
|
1421
1410
|
async buildAndSubmitOffchainTx(inputs, outputs) {
|
|
1422
|
-
const
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
...input,
|
|
1429
|
-
tapLeafScript,
|
|
1430
|
-
tapTree,
|
|
1431
|
-
})), outputs, this.serverUnrollScript);
|
|
1411
|
+
const offchainTx = buildOffchainTx(inputs.map((input) => {
|
|
1412
|
+
return {
|
|
1413
|
+
...input,
|
|
1414
|
+
tapLeafScript: input.forfeitTapLeafScript,
|
|
1415
|
+
};
|
|
1416
|
+
}), outputs, this.serverUnrollScript);
|
|
1432
1417
|
const signedVirtualTx = await this.identity.sign(offchainTx.arkTx);
|
|
1433
1418
|
const { arkTxid, signedCheckpointTxs } = await this.arkProvider.submitTx(base64.encode(signedVirtualTx.toPSBT()), offchainTx.checkpoints.map((c) => base64.encode(c.toPSBT())));
|
|
1434
1419
|
const finalCheckpoints = await Promise.all(signedCheckpointTxs.map(async (c) => {
|
|
@@ -1,18 +1,49 @@
|
|
|
1
1
|
export const DEFAULT_DB_NAME = "arkade-service-worker";
|
|
2
|
+
export const DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 10000;
|
|
3
|
+
function normalizeOptions(pathOrOptions) {
|
|
4
|
+
if (typeof pathOrOptions === "string") {
|
|
5
|
+
return {
|
|
6
|
+
path: pathOrOptions,
|
|
7
|
+
activationTimeoutMs: DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS,
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
path: pathOrOptions.path,
|
|
12
|
+
activationTimeoutMs: pathOrOptions.activationTimeoutMs ??
|
|
13
|
+
DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function waitForServiceWorkerReady(timeoutMs) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const timeoutId = setTimeout(() => {
|
|
19
|
+
reject(new Error(`Service worker activation timed out after ${timeoutMs}ms`));
|
|
20
|
+
}, timeoutMs);
|
|
21
|
+
navigator.serviceWorker.ready
|
|
22
|
+
.then((registration) => {
|
|
23
|
+
clearTimeout(timeoutId);
|
|
24
|
+
resolve(registration);
|
|
25
|
+
})
|
|
26
|
+
.catch((error) => {
|
|
27
|
+
clearTimeout(timeoutId);
|
|
28
|
+
reject(error);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
}
|
|
2
32
|
/**
|
|
3
33
|
* setupServiceWorker sets up the service worker.
|
|
4
|
-
* @param
|
|
34
|
+
* @param pathOrOptions - the path to the service worker script or setup options
|
|
5
35
|
* @throws if service workers are not supported or activation fails
|
|
6
36
|
* @example
|
|
7
37
|
* ```typescript
|
|
8
38
|
* const worker = await setupServiceWorker("/service-worker.js");
|
|
9
39
|
* ```
|
|
10
40
|
*/
|
|
11
|
-
export async function setupServiceWorker(
|
|
41
|
+
export async function setupServiceWorker(pathOrOptions) {
|
|
12
42
|
// check if service workers are supported
|
|
13
43
|
if (!("serviceWorker" in navigator)) {
|
|
14
44
|
throw new Error("Service workers are not supported in this browser");
|
|
15
45
|
}
|
|
46
|
+
const { path, activationTimeoutMs } = normalizeOptions(pathOrOptions);
|
|
16
47
|
// register service worker
|
|
17
48
|
const registration = await navigator.serviceWorker.register(path);
|
|
18
49
|
// force update to ensure the service worker is active
|
|
@@ -21,28 +52,12 @@ export async function setupServiceWorker(path) {
|
|
|
21
52
|
if (!serviceWorker) {
|
|
22
53
|
throw new Error("Failed to get service worker instance");
|
|
23
54
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const onError = () => {
|
|
33
|
-
cleanup();
|
|
34
|
-
reject(new Error("Service worker failed to activate"));
|
|
35
|
-
};
|
|
36
|
-
const timeout = setTimeout(() => {
|
|
37
|
-
cleanup();
|
|
38
|
-
reject(new Error("Service worker activation timed out"));
|
|
39
|
-
}, 10000);
|
|
40
|
-
const cleanup = () => {
|
|
41
|
-
navigator.serviceWorker.removeEventListener("activate", onActivate);
|
|
42
|
-
navigator.serviceWorker.removeEventListener("error", onError);
|
|
43
|
-
clearTimeout(timeout);
|
|
44
|
-
};
|
|
45
|
-
navigator.serviceWorker.addEventListener("activate", onActivate);
|
|
46
|
-
navigator.serviceWorker.addEventListener("error", onError);
|
|
47
|
-
});
|
|
55
|
+
if (serviceWorker.state === "activated") {
|
|
56
|
+
return serviceWorker;
|
|
57
|
+
}
|
|
58
|
+
const readyRegistration = await waitForServiceWorkerReady(activationTimeoutMs);
|
|
59
|
+
if (!readyRegistration.active) {
|
|
60
|
+
throw new Error("Service worker registration is ready but has no active worker");
|
|
61
|
+
}
|
|
62
|
+
return readyRegistration.active;
|
|
48
63
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Packet } from "../extension/asset";
|
|
2
|
-
import { Asset, Recipient, VirtualCoin } from "./index";
|
|
2
|
+
import { Asset, ExtendedVirtualCoin, Recipient, VirtualCoin } from "./index";
|
|
3
3
|
/**
|
|
4
4
|
* Creates an asset packet from asset inputs and receivers.
|
|
5
5
|
* Groups inputs and outputs by asset ID and creates the Packet object
|
|
@@ -13,8 +13,8 @@ export declare function createAssetPacket(assetInputs: Map<number, Asset[]>, rec
|
|
|
13
13
|
* Selects coins that contain a specific asset.
|
|
14
14
|
* Returns coins sorted by amount (smallest first for better coin selection).
|
|
15
15
|
*/
|
|
16
|
-
export declare function selectCoinsWithAsset(coins:
|
|
17
|
-
selected:
|
|
16
|
+
export declare function selectCoinsWithAsset(coins: ExtendedVirtualCoin[], assetId: string, requiredAmount: bigint): {
|
|
17
|
+
selected: ExtendedVirtualCoin[];
|
|
18
18
|
totalAssetAmount: bigint;
|
|
19
19
|
};
|
|
20
20
|
export declare function computeAssetChange(inputAssets: Map<string, bigint>, outputAssets: Map<string, bigint>): Map<string, bigint>;
|
|
@@ -3,7 +3,7 @@ import { SettlementEvent } from "../../providers/ark";
|
|
|
3
3
|
import { Identity, ReadonlyIdentity } from "../../identity";
|
|
4
4
|
import { WalletRepository } from "../../repositories/walletRepository";
|
|
5
5
|
import { ContractRepository } from "../../repositories/contractRepository";
|
|
6
|
-
import { ResponseGetStatus, WalletUpdaterRequest, WalletUpdaterResponse } from "./wallet-message-handler";
|
|
6
|
+
import { RequestInitWallet, ResponseGetStatus, WalletUpdaterRequest, WalletUpdaterResponse } from "./wallet-message-handler";
|
|
7
7
|
import type { IContractManager } from "../../contracts/contractManager";
|
|
8
8
|
import type { IDelegatorManager } from "../delegator";
|
|
9
9
|
type PrivateKeyIdentity = Identity & {
|
|
@@ -56,6 +56,20 @@ export type ServiceWorkerWalletCreateOptions = ServiceWorkerWalletOptions & {
|
|
|
56
56
|
};
|
|
57
57
|
export type ServiceWorkerWalletSetupOptions = ServiceWorkerWalletOptions & {
|
|
58
58
|
serviceWorkerPath: string;
|
|
59
|
+
serviceWorkerActivationTimeoutMs?: number;
|
|
60
|
+
};
|
|
61
|
+
type MessageBusInitConfig = {
|
|
62
|
+
wallet: {
|
|
63
|
+
privateKey: string;
|
|
64
|
+
} | {
|
|
65
|
+
publicKey: string;
|
|
66
|
+
};
|
|
67
|
+
arkServer: {
|
|
68
|
+
url: string;
|
|
69
|
+
publicKey?: string;
|
|
70
|
+
};
|
|
71
|
+
delegatorUrl?: string;
|
|
72
|
+
timeoutMs?: number;
|
|
59
73
|
};
|
|
60
74
|
export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
|
|
61
75
|
readonly serviceWorker: ServiceWorker;
|
|
@@ -64,6 +78,10 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
|
|
|
64
78
|
readonly contractRepository: ContractRepository;
|
|
65
79
|
readonly identity: ReadonlyIdentity;
|
|
66
80
|
private readonly _readonlyAssetManager;
|
|
81
|
+
protected initConfig: MessageBusInitConfig | null;
|
|
82
|
+
protected initWalletPayload: RequestInitWallet["payload"] | null;
|
|
83
|
+
protected messageBusTimeoutMs?: number;
|
|
84
|
+
private reinitPromise;
|
|
67
85
|
get assetManager(): IReadonlyAssetManager;
|
|
68
86
|
protected constructor(serviceWorker: ServiceWorker, identity: ReadonlyIdentity, walletRepository: WalletRepository, contractRepository: ContractRepository, messageTag: string);
|
|
69
87
|
static create(options: ServiceWorkerWalletCreateOptions): Promise<ServiceWorkerReadonlyWallet>;
|
|
@@ -89,7 +107,9 @@ export declare class ServiceWorkerReadonlyWallet implements IReadonlyWallet {
|
|
|
89
107
|
* ```
|
|
90
108
|
*/
|
|
91
109
|
static setup(options: ServiceWorkerWalletSetupOptions): Promise<ServiceWorkerReadonlyWallet>;
|
|
110
|
+
private sendMessageDirect;
|
|
92
111
|
protected sendMessage(request: WalletUpdaterRequest): Promise<WalletUpdaterResponse>;
|
|
112
|
+
private reinitialize;
|
|
93
113
|
clear(): Promise<void>;
|
|
94
114
|
getAddress(): Promise<string>;
|
|
95
115
|
getBoardingAddress(): Promise<string>;
|
|
@@ -7,7 +7,7 @@ import { OnchainProvider } from "../providers/onchain";
|
|
|
7
7
|
import { ArkProvider, SettlementEvent, SignedIntent } from "../providers/ark";
|
|
8
8
|
import { SignerSession } from "../tree/signingSession";
|
|
9
9
|
import { Identity, ReadonlyIdentity } from "../identity";
|
|
10
|
-
import { ArkTransaction, Recipient, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IReadonlyWallet, IWallet, ReadonlyWalletConfig, SendBitcoinParams, SettleParams,
|
|
10
|
+
import { ArkTransaction, Recipient, Coin, ExtendedCoin, ExtendedVirtualCoin, GetVtxosFilter, IReadonlyWallet, IWallet, ReadonlyWalletConfig, SendBitcoinParams, SettleParams, WalletBalance, WalletConfig, IAssetManager, IReadonlyAssetManager } from ".";
|
|
11
11
|
import { CSVMultisigTapscript } from "../script/tapscript";
|
|
12
12
|
import { Intent } from "../intent";
|
|
13
13
|
import { IndexerProvider } from "../providers/indexer";
|
|
@@ -74,7 +74,6 @@ export declare class ReadonlyWallet implements IReadonlyWallet {
|
|
|
74
74
|
getBoardingAddress(): Promise<string>;
|
|
75
75
|
getBalance(): Promise<WalletBalance>;
|
|
76
76
|
getVtxos(filter?: GetVtxosFilter): Promise<ExtendedVirtualCoin[]>;
|
|
77
|
-
protected getVirtualCoins(filter?: GetVtxosFilter): Promise<VirtualCoin[]>;
|
|
78
77
|
getTransactionHistory(): Promise<ArkTransaction[]>;
|
|
79
78
|
getBoardingTxs(): Promise<{
|
|
80
79
|
boardingTxs: ArkTransaction[];
|
|
@@ -193,6 +192,10 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
193
192
|
*/
|
|
194
193
|
toReadonly(): Promise<ReadonlyWallet>;
|
|
195
194
|
getDelegatorManager(): Promise<IDelegatorManager | undefined>;
|
|
195
|
+
/**
|
|
196
|
+
* @deprecated Use `send`
|
|
197
|
+
* @param params
|
|
198
|
+
*/
|
|
196
199
|
sendBitcoin(params: SendBitcoinParams): Promise<string>;
|
|
197
200
|
settle(params?: SettleParams, eventCallback?: (event: SettlementEvent) => void): Promise<string>;
|
|
198
201
|
private handleSettlementFinalizationEvent;
|
|
@@ -238,7 +241,7 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
238
241
|
* sign it, submit to the ark provider, and finalize.
|
|
239
242
|
* @returns The ark transaction id and server-signed checkpoint PSBTs (for bookkeeping)
|
|
240
243
|
*/
|
|
241
|
-
buildAndSubmitOffchainTx(inputs:
|
|
244
|
+
buildAndSubmitOffchainTx(inputs: ExtendedVirtualCoin[], outputs: TransactionOutput[]): Promise<{
|
|
242
245
|
arkTxid: string;
|
|
243
246
|
signedCheckpointTxs: string[];
|
|
244
247
|
}>;
|
|
@@ -251,8 +254,8 @@ export declare class Wallet extends ReadonlyWallet implements IWallet {
|
|
|
251
254
|
* @param targetAmount Target amount to reach in satoshis
|
|
252
255
|
* @returns Selected coins and change amount
|
|
253
256
|
*/
|
|
254
|
-
export declare function selectVirtualCoins(coins:
|
|
255
|
-
inputs:
|
|
257
|
+
export declare function selectVirtualCoins(coins: ExtendedVirtualCoin[], targetAmount: number): {
|
|
258
|
+
inputs: ExtendedVirtualCoin[];
|
|
256
259
|
changeAmount: bigint;
|
|
257
260
|
};
|
|
258
261
|
/**
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
export declare const DEFAULT_DB_NAME = "arkade-service-worker";
|
|
2
|
+
export declare const DEFAULT_SERVICE_WORKER_ACTIVATION_TIMEOUT_MS = 10000;
|
|
3
|
+
type SetupServiceWorkerOptions = {
|
|
4
|
+
path: string;
|
|
5
|
+
activationTimeoutMs?: number;
|
|
6
|
+
};
|
|
2
7
|
/**
|
|
3
8
|
* setupServiceWorker sets up the service worker.
|
|
4
|
-
* @param
|
|
9
|
+
* @param pathOrOptions - the path to the service worker script or setup options
|
|
5
10
|
* @throws if service workers are not supported or activation fails
|
|
6
11
|
* @example
|
|
7
12
|
* ```typescript
|
|
8
13
|
* const worker = await setupServiceWorker("/service-worker.js");
|
|
9
14
|
* ```
|
|
10
15
|
*/
|
|
11
|
-
export declare function setupServiceWorker(
|
|
16
|
+
export declare function setupServiceWorker(pathOrOptions: string | SetupServiceWorkerOptions): Promise<ServiceWorker>;
|
|
17
|
+
export {};
|