@arkade-os/sdk 0.3.7 → 0.3.9

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 (41) hide show
  1. package/README.md +78 -1
  2. package/dist/cjs/identity/singleKey.js +33 -1
  3. package/dist/cjs/index.js +17 -2
  4. package/dist/cjs/intent/index.js +31 -2
  5. package/dist/cjs/providers/ark.js +9 -3
  6. package/dist/cjs/providers/indexer.js +2 -2
  7. package/dist/cjs/wallet/batch.js +183 -0
  8. package/dist/cjs/wallet/index.js +15 -0
  9. package/dist/cjs/wallet/serviceWorker/request.js +0 -2
  10. package/dist/cjs/wallet/serviceWorker/wallet.js +98 -34
  11. package/dist/cjs/wallet/serviceWorker/worker.js +169 -69
  12. package/dist/cjs/wallet/utils.js +2 -2
  13. package/dist/cjs/wallet/vtxo-manager.js +5 -0
  14. package/dist/cjs/wallet/wallet.js +399 -356
  15. package/dist/esm/identity/singleKey.js +31 -0
  16. package/dist/esm/index.js +12 -7
  17. package/dist/esm/intent/index.js +31 -2
  18. package/dist/esm/providers/ark.js +9 -3
  19. package/dist/esm/providers/indexer.js +2 -2
  20. package/dist/esm/wallet/batch.js +180 -0
  21. package/dist/esm/wallet/index.js +14 -0
  22. package/dist/esm/wallet/serviceWorker/request.js +0 -2
  23. package/dist/esm/wallet/serviceWorker/wallet.js +96 -33
  24. package/dist/esm/wallet/serviceWorker/worker.js +171 -71
  25. package/dist/esm/wallet/utils.js +2 -2
  26. package/dist/esm/wallet/vtxo-manager.js +6 -1
  27. package/dist/esm/wallet/wallet.js +400 -359
  28. package/dist/types/identity/index.d.ts +5 -3
  29. package/dist/types/identity/singleKey.d.ts +20 -1
  30. package/dist/types/index.d.ts +11 -8
  31. package/dist/types/intent/index.d.ts +19 -2
  32. package/dist/types/providers/ark.d.ts +9 -8
  33. package/dist/types/providers/indexer.d.ts +2 -2
  34. package/dist/types/wallet/batch.d.ts +87 -0
  35. package/dist/types/wallet/index.d.ts +75 -16
  36. package/dist/types/wallet/serviceWorker/request.d.ts +5 -1
  37. package/dist/types/wallet/serviceWorker/wallet.d.ts +46 -15
  38. package/dist/types/wallet/serviceWorker/worker.d.ts +6 -3
  39. package/dist/types/wallet/utils.d.ts +8 -3
  40. package/dist/types/wallet/wallet.d.ts +96 -35
  41. package/package.json +123 -113
@@ -1,7 +1,7 @@
1
1
  /// <reference lib="webworker" />
2
- import { SingleKey } from '../../identity/singleKey.js';
3
- import { isRecoverable, isSpendable, isSubdust, } from '../index.js';
4
- import { Wallet } from '../wallet.js';
2
+ import { ReadonlySingleKey, SingleKey } from '../../identity/singleKey.js';
3
+ import { isExpired, isRecoverable, isSpendable, isSubdust, } from '../index.js';
4
+ import { ReadonlyWallet, Wallet } from '../wallet.js';
5
5
  import { Request } from './request.js';
6
6
  import { Response } from './response.js';
7
7
  import { RestArkProvider } from '../../providers/ark.js';
@@ -12,9 +12,70 @@ import { IndexedDBStorageAdapter } from '../../storage/indexedDB.js';
12
12
  import { WalletRepositoryImpl, } from '../../repositories/walletRepository.js';
13
13
  import { extendCoin, extendVirtualCoin } from '../utils.js';
14
14
  import { DEFAULT_DB_NAME } from './utils.js';
15
+ class ReadonlyHandler {
16
+ constructor(wallet) {
17
+ this.wallet = wallet;
18
+ }
19
+ get offchainTapscript() {
20
+ return this.wallet.offchainTapscript;
21
+ }
22
+ get boardingTapscript() {
23
+ return this.wallet.boardingTapscript;
24
+ }
25
+ get onchainProvider() {
26
+ return this.wallet.onchainProvider;
27
+ }
28
+ get dustAmount() {
29
+ return this.wallet.dustAmount;
30
+ }
31
+ get identity() {
32
+ return this.wallet.identity;
33
+ }
34
+ notifyIncomingFunds(...args) {
35
+ return this.wallet.notifyIncomingFunds(...args);
36
+ }
37
+ getAddress() {
38
+ return this.wallet.getAddress();
39
+ }
40
+ getBoardingAddress() {
41
+ return this.wallet.getBoardingAddress();
42
+ }
43
+ getBoardingTxs() {
44
+ return this.wallet.getBoardingTxs();
45
+ }
46
+ async handleReload(_) {
47
+ const pending = await this.wallet.fetchPendingTxs();
48
+ return { pending, finalized: [] };
49
+ }
50
+ async handleSettle(..._) {
51
+ return undefined;
52
+ }
53
+ async handleSendBitcoin(..._) {
54
+ return undefined;
55
+ }
56
+ }
57
+ class Handler extends ReadonlyHandler {
58
+ constructor(wallet) {
59
+ super(wallet);
60
+ this.wallet = wallet;
61
+ }
62
+ async handleReload(vtxos) {
63
+ return this.wallet.finalizePendingTxs(vtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
64
+ vtxo.virtualStatus.state !== "settled"));
65
+ }
66
+ async handleSettle(...args) {
67
+ return this.wallet.settle(...args);
68
+ }
69
+ async handleSendBitcoin(...args) {
70
+ return this.wallet.sendBitcoin(...args);
71
+ }
72
+ }
15
73
  /**
16
- * Worker is a class letting to interact with ServiceWorkerWallet from the client
17
- * it aims to be run in a service worker context
74
+ * Worker is a class letting to interact with ServiceWorkerWallet and ServiceWorkerReadonlyWallet from
75
+ * the client; it aims to be run in a service worker context.
76
+ *
77
+ * The messages requiring a Wallet rather than a ReadonlyWallet result in no-op
78
+ * without errors.
18
79
  */
19
80
  export class Worker {
20
81
  constructor(dbName = DEFAULT_DB_NAME, dbVersion = 1, messageCallback = () => { }) {
@@ -28,9 +89,9 @@ export class Worker {
28
89
  * Get spendable vtxos for the current wallet address
29
90
  */
30
91
  async getSpendableVtxos() {
31
- if (!this.wallet)
92
+ if (!this.handler)
32
93
  return [];
33
- const address = await this.wallet.getAddress();
94
+ const address = await this.handler.getAddress();
34
95
  const allVtxos = await this.walletRepository.getVtxos(address);
35
96
  return allVtxos.filter(isSpendable);
36
97
  }
@@ -38,9 +99,9 @@ export class Worker {
38
99
  * Get swept vtxos for the current wallet address
39
100
  */
40
101
  async getSweptVtxos() {
41
- if (!this.wallet)
102
+ if (!this.handler)
42
103
  return [];
43
- const address = await this.wallet.getAddress();
104
+ const address = await this.handler.getAddress();
44
105
  const allVtxos = await this.walletRepository.getVtxos(address);
45
106
  return allVtxos.filter((vtxo) => vtxo.virtualStatus.state === "swept");
46
107
  }
@@ -48,9 +109,9 @@ export class Worker {
48
109
  * Get all vtxos categorized by type
49
110
  */
50
111
  async getAllVtxos() {
51
- if (!this.wallet)
112
+ if (!this.handler)
52
113
  return { spendable: [], spent: [] };
53
- const address = await this.wallet.getAddress();
114
+ const address = await this.handler.getAddress();
54
115
  const allVtxos = await this.walletRepository.getVtxos(address);
55
116
  return {
56
117
  spendable: allVtxos.filter(isSpendable),
@@ -61,17 +122,17 @@ export class Worker {
61
122
  * Get all boarding utxos from wallet repository
62
123
  */
63
124
  async getAllBoardingUtxos() {
64
- if (!this.wallet)
125
+ if (!this.handler)
65
126
  return [];
66
- const address = await this.wallet.getBoardingAddress();
127
+ const address = await this.handler.getBoardingAddress();
67
128
  return await this.walletRepository.getUtxos(address);
68
129
  }
69
130
  async getTransactionHistory() {
70
- if (!this.wallet)
131
+ if (!this.handler)
71
132
  return [];
72
133
  let txs = [];
73
134
  try {
74
- const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.wallet.getBoardingTxs();
135
+ const { boardingTxs, commitmentsToIgnore: roundsToIgnore } = await this.handler.getBoardingTxs();
75
136
  const { spendable, spent } = await this.getAllVtxos();
76
137
  // convert VTXOs to offchain transactions
77
138
  const offchainTxs = vtxosToTxs(spendable, spent, roundsToIgnore);
@@ -114,7 +175,7 @@ export class Worker {
114
175
  await this.storage.clear();
115
176
  // Reset in-memory caches by recreating the repository
116
177
  this.walletRepository = new WalletRepositoryImpl(this.storage);
117
- this.wallet = undefined;
178
+ this.handler = undefined;
118
179
  this.arkProvider = undefined;
119
180
  this.indexerProvider = undefined;
120
181
  }
@@ -122,26 +183,34 @@ export class Worker {
122
183
  await this.onWalletInitialized();
123
184
  }
124
185
  async onWalletInitialized() {
125
- if (!this.wallet ||
186
+ if (!this.handler ||
126
187
  !this.arkProvider ||
127
188
  !this.indexerProvider ||
128
- !this.wallet.offchainTapscript ||
129
- !this.wallet.boardingTapscript) {
189
+ !this.handler.offchainTapscript ||
190
+ !this.handler.boardingTapscript) {
130
191
  return;
131
192
  }
132
193
  // Get public key script and set the initial vtxos state
133
- const script = hex.encode(this.wallet.offchainTapscript.pkScript);
194
+ const script = hex.encode(this.handler.offchainTapscript.pkScript);
134
195
  const response = await this.indexerProvider.getVtxos({
135
196
  scripts: [script],
136
197
  });
137
- const vtxos = response.vtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo));
198
+ const vtxos = response.vtxos.map((vtxo) => extendVirtualCoin(this.handler, vtxo));
199
+ try {
200
+ // recover pending transactions if possible
201
+ const { pending, finalized } = await this.handler.handleReload(vtxos);
202
+ console.info(`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`);
203
+ }
204
+ catch (error) {
205
+ console.error("Error recovering pending transactions:", error);
206
+ }
138
207
  // Get wallet address and save vtxos using unified repository
139
- const address = await this.wallet.getAddress();
208
+ const address = await this.handler.getAddress();
140
209
  await this.walletRepository.saveVtxos(address, vtxos);
141
210
  // Fetch boarding utxos and save using unified repository
142
- const boardingAddress = await this.wallet.getBoardingAddress();
143
- const coins = await this.wallet.onchainProvider.getCoins(boardingAddress);
144
- await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => extendCoin(this.wallet, utxo)));
211
+ const boardingAddress = await this.handler.getBoardingAddress();
212
+ const coins = await this.handler.onchainProvider.getCoins(boardingAddress);
213
+ await this.walletRepository.saveUtxos(boardingAddress, coins.map((utxo) => extendCoin(this.handler, utxo)));
145
214
  // Get transaction history to cache boarding txs
146
215
  const txs = await this.getTransactionHistory();
147
216
  if (txs)
@@ -150,13 +219,13 @@ export class Worker {
150
219
  if (this.incomingFundsSubscription)
151
220
  this.incomingFundsSubscription();
152
221
  // subscribe for incoming funds and notify all clients when new funds arrive
153
- this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
222
+ this.incomingFundsSubscription = await this.handler.notifyIncomingFunds(async (funds) => {
154
223
  if (funds.type === "vtxo") {
155
224
  const newVtxos = funds.newVtxos.length > 0
156
- ? funds.newVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
225
+ ? funds.newVtxos.map((vtxo) => extendVirtualCoin(this.handler, vtxo))
157
226
  : [];
158
227
  const spentVtxos = funds.spentVtxos.length > 0
159
- ? funds.spentVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
228
+ ? funds.spentVtxos.map((vtxo) => extendVirtualCoin(this.handler, vtxo))
160
229
  : [];
161
230
  if ([...newVtxos, ...spentVtxos].length === 0)
162
231
  return;
@@ -169,8 +238,8 @@ export class Worker {
169
238
  await this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
170
239
  }
171
240
  if (funds.type === "utxo") {
172
- const utxos = funds.coins.map((utxo) => extendCoin(this.wallet, utxo));
173
- const boardingAddress = await this.wallet?.getBoardingAddress();
241
+ const utxos = funds.coins.map((utxo) => extendCoin(this.handler, utxo));
242
+ const boardingAddress = await this.handler?.getBoardingAddress();
174
243
  // save utxos using unified repository
175
244
  await this.walletRepository.clearUtxos(boardingAddress);
176
245
  await this.walletRepository.saveUtxos(boardingAddress, utxos);
@@ -186,31 +255,46 @@ export class Worker {
186
255
  }
187
256
  }
188
257
  async handleInitWallet(event) {
189
- const message = event.data;
190
- if (!Request.isInitWallet(message)) {
191
- console.error("Invalid INIT_WALLET message format", message);
192
- event.source?.postMessage(Response.error(message.id, "Invalid INIT_WALLET message format"));
193
- return;
194
- }
195
- if (!message.privateKey) {
196
- const err = "Missing privateKey";
197
- event.source?.postMessage(Response.error(message.id, err));
198
- console.error(err);
258
+ if (!Request.isInitWallet(event.data)) {
259
+ console.error("Invalid INIT_WALLET message format", event.data);
260
+ event.source?.postMessage(Response.error(event.data.id, "Invalid INIT_WALLET message format"));
199
261
  return;
200
262
  }
263
+ const message = event.data;
264
+ const { arkServerPublicKey, arkServerUrl } = message;
265
+ this.arkProvider = new RestArkProvider(arkServerUrl);
266
+ this.indexerProvider = new RestIndexerProvider(arkServerUrl);
201
267
  try {
202
- const { arkServerPublicKey, arkServerUrl, privateKey } = message;
203
- const identity = SingleKey.fromHex(privateKey);
204
- this.arkProvider = new RestArkProvider(arkServerUrl);
205
- this.indexerProvider = new RestIndexerProvider(arkServerUrl);
206
- this.wallet = await Wallet.create({
207
- identity,
208
- arkServerUrl,
209
- arkServerPublicKey,
210
- storage: this.storage, // Use unified storage for wallet too
211
- });
212
- event.source?.postMessage(Response.walletInitialized(message.id));
213
- await this.onWalletInitialized();
268
+ if ("privateKey" in message.key &&
269
+ typeof message.key.privateKey === "string") {
270
+ const { key: { privateKey }, } = message;
271
+ const identity = SingleKey.fromHex(privateKey);
272
+ const wallet = await Wallet.create({
273
+ identity,
274
+ arkServerUrl,
275
+ arkServerPublicKey,
276
+ storage: this.storage, // Use unified storage for wallet too
277
+ });
278
+ this.handler = new Handler(wallet);
279
+ }
280
+ else if ("publicKey" in message.key &&
281
+ typeof message.key.publicKey === "string") {
282
+ const { key: { publicKey }, } = message;
283
+ const identity = ReadonlySingleKey.fromPublicKey(hex.decode(publicKey));
284
+ const wallet = await ReadonlyWallet.create({
285
+ identity,
286
+ arkServerUrl,
287
+ arkServerPublicKey,
288
+ storage: this.storage, // Use unified storage for wallet too
289
+ });
290
+ this.handler = new ReadonlyHandler(wallet);
291
+ }
292
+ else {
293
+ const err = "Missing privateKey or publicKey in key object";
294
+ event.source?.postMessage(Response.error(message.id, err));
295
+ console.error(err);
296
+ return;
297
+ }
214
298
  }
215
299
  catch (error) {
216
300
  console.error("Error initializing wallet:", error);
@@ -218,7 +302,10 @@ export class Worker {
218
302
  ? error.message
219
303
  : "Unknown error occurred";
220
304
  event.source?.postMessage(Response.error(message.id, errorMessage));
305
+ return;
221
306
  }
307
+ event.source?.postMessage(Response.walletInitialized(message.id));
308
+ await this.onWalletInitialized();
222
309
  }
223
310
  async handleSettle(event) {
224
311
  const message = event.data;
@@ -228,15 +315,20 @@ export class Worker {
228
315
  return;
229
316
  }
230
317
  try {
231
- if (!this.wallet) {
318
+ if (!this.handler) {
232
319
  console.error("Wallet not initialized");
233
320
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
234
321
  return;
235
322
  }
236
- const txid = await this.wallet.settle(message.params, (e) => {
323
+ const txid = await this.handler.handleSettle(message.params, (e) => {
237
324
  event.source?.postMessage(Response.settleEvent(message.id, e));
238
325
  });
239
- event.source?.postMessage(Response.settleSuccess(message.id, txid));
326
+ if (txid) {
327
+ event.source?.postMessage(Response.settleSuccess(message.id, txid));
328
+ }
329
+ else {
330
+ event.source?.postMessage(Response.error(message.id, "Operation not supported in readonly mode"));
331
+ }
240
332
  }
241
333
  catch (error) {
242
334
  console.error("Error settling:", error);
@@ -253,14 +345,19 @@ export class Worker {
253
345
  event.source?.postMessage(Response.error(message.id, "Invalid SEND_BITCOIN message format"));
254
346
  return;
255
347
  }
256
- if (!this.wallet) {
348
+ if (!this.handler) {
257
349
  console.error("Wallet not initialized");
258
350
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
259
351
  return;
260
352
  }
261
353
  try {
262
- const txid = await this.wallet.sendBitcoin(message.params);
263
- event.source?.postMessage(Response.sendBitcoinSuccess(message.id, txid));
354
+ const txid = await this.handler.handleSendBitcoin(message.params);
355
+ if (txid) {
356
+ event.source?.postMessage(Response.sendBitcoinSuccess(message.id, txid));
357
+ }
358
+ else {
359
+ event.source?.postMessage(Response.error(message.id, "Operation not supported in readonly mode"));
360
+ }
264
361
  }
265
362
  catch (error) {
266
363
  console.error("Error sending bitcoin:", error);
@@ -277,13 +374,13 @@ export class Worker {
277
374
  event.source?.postMessage(Response.error(message.id, "Invalid GET_ADDRESS message format"));
278
375
  return;
279
376
  }
280
- if (!this.wallet) {
377
+ if (!this.handler) {
281
378
  console.error("Wallet not initialized");
282
379
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
283
380
  return;
284
381
  }
285
382
  try {
286
- const address = await this.wallet.getAddress();
383
+ const address = await this.handler.getAddress();
287
384
  event.source?.postMessage(Response.address(message.id, address));
288
385
  }
289
386
  catch (error) {
@@ -301,13 +398,13 @@ export class Worker {
301
398
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BOARDING_ADDRESS message format"));
302
399
  return;
303
400
  }
304
- if (!this.wallet) {
401
+ if (!this.handler) {
305
402
  console.error("Wallet not initialized");
306
403
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
307
404
  return;
308
405
  }
309
406
  try {
310
- const address = await this.wallet.getBoardingAddress();
407
+ const address = await this.handler.getBoardingAddress();
311
408
  event.source?.postMessage(Response.boardingAddress(message.id, address));
312
409
  }
313
410
  catch (error) {
@@ -325,7 +422,7 @@ export class Worker {
325
422
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BALANCE message format"));
326
423
  return;
327
424
  }
328
- if (!this.wallet) {
425
+ if (!this.handler) {
329
426
  console.error("Wallet not initialized");
330
427
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
331
428
  return;
@@ -394,14 +491,14 @@ export class Worker {
394
491
  event.source?.postMessage(Response.error(message.id, "Invalid GET_VTXOS message format"));
395
492
  return;
396
493
  }
397
- if (!this.wallet) {
494
+ if (!this.handler) {
398
495
  console.error("Wallet not initialized");
399
496
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
400
497
  return;
401
498
  }
402
499
  try {
403
500
  const vtxos = await this.getSpendableVtxos();
404
- const dustAmount = this.wallet.dustAmount;
501
+ const dustAmount = this.handler.dustAmount;
405
502
  const includeRecoverable = message.filter?.withRecoverable ?? false;
406
503
  const filteredVtxos = includeRecoverable
407
504
  ? vtxos
@@ -412,6 +509,9 @@ export class Worker {
412
509
  if (isRecoverable(v)) {
413
510
  return false;
414
511
  }
512
+ if (isExpired(v)) {
513
+ return false;
514
+ }
415
515
  return true;
416
516
  });
417
517
  event.source?.postMessage(Response.vtxos(message.id, filteredVtxos));
@@ -431,7 +531,7 @@ export class Worker {
431
531
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BOARDING_UTXOS message format"));
432
532
  return;
433
533
  }
434
- if (!this.wallet) {
534
+ if (!this.handler) {
435
535
  console.error("Wallet not initialized");
436
536
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
437
537
  return;
@@ -455,7 +555,7 @@ export class Worker {
455
555
  event.source?.postMessage(Response.error(message.id, "Invalid GET_TRANSACTION_HISTORY message format"));
456
556
  return;
457
557
  }
458
- if (!this.wallet) {
558
+ if (!this.handler) {
459
559
  console.error("Wallet not initialized");
460
560
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
461
561
  return;
@@ -479,10 +579,10 @@ export class Worker {
479
579
  event.source?.postMessage(Response.error(message.id, "Invalid GET_STATUS message format"));
480
580
  return;
481
581
  }
482
- const pubKey = this.wallet
483
- ? await this.wallet.identity.xOnlyPublicKey()
582
+ const pubKey = this.handler
583
+ ? await this.handler.identity.xOnlyPublicKey()
484
584
  : undefined;
485
- event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined, pubKey));
585
+ event.source?.postMessage(Response.walletStatus(message.id, this.handler !== undefined, pubKey));
486
586
  }
487
587
  async handleMessage(event) {
488
588
  this.messageCallback(event);
@@ -561,7 +661,7 @@ export class Worker {
561
661
  event.source?.postMessage(Response.error(message.id, "Invalid RELOAD_WALLET message format"));
562
662
  return;
563
663
  }
564
- if (!this.wallet) {
664
+ if (!this.handler) {
565
665
  console.error("Wallet not initialized");
566
666
  event.source?.postMessage(Response.walletReloaded(message.id, false));
567
667
  return;
@@ -2,7 +2,7 @@ export function extendVirtualCoin(wallet, vtxo) {
2
2
  return {
3
3
  ...vtxo,
4
4
  forfeitTapLeafScript: wallet.offchainTapscript.forfeit(),
5
- intentTapLeafScript: wallet.offchainTapscript.exit(),
5
+ intentTapLeafScript: wallet.offchainTapscript.forfeit(),
6
6
  tapTree: wallet.offchainTapscript.encode(),
7
7
  };
8
8
  }
@@ -10,7 +10,7 @@ export function extendCoin(wallet, utxo) {
10
10
  return {
11
11
  ...utxo,
12
12
  forfeitTapLeafScript: wallet.boardingTapscript.forfeit(),
13
- intentTapLeafScript: wallet.boardingTapscript.exit(),
13
+ intentTapLeafScript: wallet.boardingTapscript.forfeit(),
14
14
  tapTree: wallet.boardingTapscript.encode(),
15
15
  };
16
16
  }
@@ -1,4 +1,4 @@
1
- import { isRecoverable, isSubdust } from './index.js';
1
+ import { isExpired, isRecoverable, isSpendable, isSubdust, } from './index.js';
2
2
  export const DEFAULT_THRESHOLD_MS = 3 * 24 * 60 * 60 * 1000; // 3 days
3
3
  /**
4
4
  * Default renewal configuration values
@@ -26,6 +26,10 @@ function getRecoverableVtxos(vtxos, dustAmount) {
26
26
  if (isRecoverable(vtxo)) {
27
27
  return true;
28
28
  }
29
+ // also include vtxos that are not swept but expired
30
+ if (isSpendable(vtxo) && isExpired(vtxo)) {
31
+ return true;
32
+ }
29
33
  // Recover preconfirmed subdust to consolidate small amounts
30
34
  if (vtxo.virtualStatus.state === "preconfirmed" &&
31
35
  isSubdust(vtxo, dustAmount)) {
@@ -100,6 +104,7 @@ export function isVtxoExpiringSoon(vtxo, thresholdMs // in milliseconds
100
104
  export function getExpiringAndRecoverableVtxos(vtxos, thresholdMs, dustAmount) {
101
105
  return vtxos.filter((vtxo) => isVtxoExpiringSoon(vtxo, thresholdMs) ||
102
106
  isRecoverable(vtxo) ||
107
+ (isSpendable(vtxo) && isExpired(vtxo)) ||
103
108
  isSubdust(vtxo, dustAmount));
104
109
  }
105
110
  /**