@arkade-os/sdk 0.3.8 → 0.3.10

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 +15 -5
  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 +163 -72
  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 +358 -360
  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 +15 -5
  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 +165 -74
  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 +359 -363
  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 +76 -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 +87 -36
  41. package/package.json +1 -1
@@ -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,35 +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));
138
199
  try {
139
- // recover pending transactions
140
- const { finalized, pending } = await this.wallet.finalizePendingTxs(vtxos.filter((vtxo) => vtxo.virtualStatus.state !== "swept" &&
141
- vtxo.virtualStatus.state !== "settled"));
200
+ // recover pending transactions if possible
201
+ const { pending, finalized } = await this.handler.handleReload(vtxos);
142
202
  console.info(`Recovered ${finalized.length}/${pending.length} pending transactions: ${finalized.join(", ")}`);
143
203
  }
144
204
  catch (error) {
145
205
  console.error("Error recovering pending transactions:", error);
146
206
  }
147
207
  // Get wallet address and save vtxos using unified repository
148
- const address = await this.wallet.getAddress();
208
+ const address = await this.handler.getAddress();
149
209
  await this.walletRepository.saveVtxos(address, vtxos);
150
210
  // Fetch boarding utxos and save using unified repository
151
- const boardingAddress = await this.wallet.getBoardingAddress();
152
- const coins = await this.wallet.onchainProvider.getCoins(boardingAddress);
153
- 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)));
154
214
  // Get transaction history to cache boarding txs
155
215
  const txs = await this.getTransactionHistory();
156
216
  if (txs)
@@ -159,13 +219,13 @@ export class Worker {
159
219
  if (this.incomingFundsSubscription)
160
220
  this.incomingFundsSubscription();
161
221
  // subscribe for incoming funds and notify all clients when new funds arrive
162
- this.incomingFundsSubscription = await this.wallet.notifyIncomingFunds(async (funds) => {
222
+ this.incomingFundsSubscription = await this.handler.notifyIncomingFunds(async (funds) => {
163
223
  if (funds.type === "vtxo") {
164
224
  const newVtxos = funds.newVtxos.length > 0
165
- ? funds.newVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
225
+ ? funds.newVtxos.map((vtxo) => extendVirtualCoin(this.handler, vtxo))
166
226
  : [];
167
227
  const spentVtxos = funds.spentVtxos.length > 0
168
- ? funds.spentVtxos.map((vtxo) => extendVirtualCoin(this.wallet, vtxo))
228
+ ? funds.spentVtxos.map((vtxo) => extendVirtualCoin(this.handler, vtxo))
169
229
  : [];
170
230
  if ([...newVtxos, ...spentVtxos].length === 0)
171
231
  return;
@@ -178,8 +238,8 @@ export class Worker {
178
238
  await this.sendMessageToAllClients(Response.vtxoUpdate(newVtxos, spentVtxos));
179
239
  }
180
240
  if (funds.type === "utxo") {
181
- const utxos = funds.coins.map((utxo) => extendCoin(this.wallet, utxo));
182
- const boardingAddress = await this.wallet?.getBoardingAddress();
241
+ const utxos = funds.coins.map((utxo) => extendCoin(this.handler, utxo));
242
+ const boardingAddress = await this.handler?.getBoardingAddress();
183
243
  // save utxos using unified repository
184
244
  await this.walletRepository.clearUtxos(boardingAddress);
185
245
  await this.walletRepository.saveUtxos(boardingAddress, utxos);
@@ -195,31 +255,46 @@ export class Worker {
195
255
  }
196
256
  }
197
257
  async handleInitWallet(event) {
198
- const message = event.data;
199
- if (!Request.isInitWallet(message)) {
200
- console.error("Invalid INIT_WALLET message format", message);
201
- event.source?.postMessage(Response.error(message.id, "Invalid INIT_WALLET message format"));
202
- return;
203
- }
204
- if (!message.privateKey) {
205
- const err = "Missing privateKey";
206
- event.source?.postMessage(Response.error(message.id, err));
207
- 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"));
208
261
  return;
209
262
  }
263
+ const message = event.data;
264
+ const { arkServerPublicKey, arkServerUrl } = message;
265
+ this.arkProvider = new RestArkProvider(arkServerUrl);
266
+ this.indexerProvider = new RestIndexerProvider(arkServerUrl);
210
267
  try {
211
- const { arkServerPublicKey, arkServerUrl, privateKey } = message;
212
- const identity = SingleKey.fromHex(privateKey);
213
- this.arkProvider = new RestArkProvider(arkServerUrl);
214
- this.indexerProvider = new RestIndexerProvider(arkServerUrl);
215
- this.wallet = await Wallet.create({
216
- identity,
217
- arkServerUrl,
218
- arkServerPublicKey,
219
- storage: this.storage, // Use unified storage for wallet too
220
- });
221
- event.source?.postMessage(Response.walletInitialized(message.id));
222
- 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
+ }
223
298
  }
224
299
  catch (error) {
225
300
  console.error("Error initializing wallet:", error);
@@ -227,7 +302,10 @@ export class Worker {
227
302
  ? error.message
228
303
  : "Unknown error occurred";
229
304
  event.source?.postMessage(Response.error(message.id, errorMessage));
305
+ return;
230
306
  }
307
+ event.source?.postMessage(Response.walletInitialized(message.id));
308
+ await this.onWalletInitialized();
231
309
  }
232
310
  async handleSettle(event) {
233
311
  const message = event.data;
@@ -237,15 +315,20 @@ export class Worker {
237
315
  return;
238
316
  }
239
317
  try {
240
- if (!this.wallet) {
318
+ if (!this.handler) {
241
319
  console.error("Wallet not initialized");
242
320
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
243
321
  return;
244
322
  }
245
- const txid = await this.wallet.settle(message.params, (e) => {
323
+ const txid = await this.handler.handleSettle(message.params, (e) => {
246
324
  event.source?.postMessage(Response.settleEvent(message.id, e));
247
325
  });
248
- 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
+ }
249
332
  }
250
333
  catch (error) {
251
334
  console.error("Error settling:", error);
@@ -262,14 +345,19 @@ export class Worker {
262
345
  event.source?.postMessage(Response.error(message.id, "Invalid SEND_BITCOIN message format"));
263
346
  return;
264
347
  }
265
- if (!this.wallet) {
348
+ if (!this.handler) {
266
349
  console.error("Wallet not initialized");
267
350
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
268
351
  return;
269
352
  }
270
353
  try {
271
- const txid = await this.wallet.sendBitcoin(message.params);
272
- 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
+ }
273
361
  }
274
362
  catch (error) {
275
363
  console.error("Error sending bitcoin:", error);
@@ -286,13 +374,13 @@ export class Worker {
286
374
  event.source?.postMessage(Response.error(message.id, "Invalid GET_ADDRESS message format"));
287
375
  return;
288
376
  }
289
- if (!this.wallet) {
377
+ if (!this.handler) {
290
378
  console.error("Wallet not initialized");
291
379
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
292
380
  return;
293
381
  }
294
382
  try {
295
- const address = await this.wallet.getAddress();
383
+ const address = await this.handler.getAddress();
296
384
  event.source?.postMessage(Response.address(message.id, address));
297
385
  }
298
386
  catch (error) {
@@ -310,13 +398,13 @@ export class Worker {
310
398
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BOARDING_ADDRESS message format"));
311
399
  return;
312
400
  }
313
- if (!this.wallet) {
401
+ if (!this.handler) {
314
402
  console.error("Wallet not initialized");
315
403
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
316
404
  return;
317
405
  }
318
406
  try {
319
- const address = await this.wallet.getBoardingAddress();
407
+ const address = await this.handler.getBoardingAddress();
320
408
  event.source?.postMessage(Response.boardingAddress(message.id, address));
321
409
  }
322
410
  catch (error) {
@@ -334,7 +422,7 @@ export class Worker {
334
422
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BALANCE message format"));
335
423
  return;
336
424
  }
337
- if (!this.wallet) {
425
+ if (!this.handler) {
338
426
  console.error("Wallet not initialized");
339
427
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
340
428
  return;
@@ -403,14 +491,14 @@ export class Worker {
403
491
  event.source?.postMessage(Response.error(message.id, "Invalid GET_VTXOS message format"));
404
492
  return;
405
493
  }
406
- if (!this.wallet) {
494
+ if (!this.handler) {
407
495
  console.error("Wallet not initialized");
408
496
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
409
497
  return;
410
498
  }
411
499
  try {
412
500
  const vtxos = await this.getSpendableVtxos();
413
- const dustAmount = this.wallet.dustAmount;
501
+ const dustAmount = this.handler.dustAmount;
414
502
  const includeRecoverable = message.filter?.withRecoverable ?? false;
415
503
  const filteredVtxos = includeRecoverable
416
504
  ? vtxos
@@ -421,6 +509,9 @@ export class Worker {
421
509
  if (isRecoverable(v)) {
422
510
  return false;
423
511
  }
512
+ if (isExpired(v)) {
513
+ return false;
514
+ }
424
515
  return true;
425
516
  });
426
517
  event.source?.postMessage(Response.vtxos(message.id, filteredVtxos));
@@ -440,7 +531,7 @@ export class Worker {
440
531
  event.source?.postMessage(Response.error(message.id, "Invalid GET_BOARDING_UTXOS message format"));
441
532
  return;
442
533
  }
443
- if (!this.wallet) {
534
+ if (!this.handler) {
444
535
  console.error("Wallet not initialized");
445
536
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
446
537
  return;
@@ -464,7 +555,7 @@ export class Worker {
464
555
  event.source?.postMessage(Response.error(message.id, "Invalid GET_TRANSACTION_HISTORY message format"));
465
556
  return;
466
557
  }
467
- if (!this.wallet) {
558
+ if (!this.handler) {
468
559
  console.error("Wallet not initialized");
469
560
  event.source?.postMessage(Response.error(message.id, "Wallet not initialized"));
470
561
  return;
@@ -488,10 +579,10 @@ export class Worker {
488
579
  event.source?.postMessage(Response.error(message.id, "Invalid GET_STATUS message format"));
489
580
  return;
490
581
  }
491
- const pubKey = this.wallet
492
- ? await this.wallet.identity.xOnlyPublicKey()
582
+ const pubKey = this.handler
583
+ ? await this.handler.identity.xOnlyPublicKey()
493
584
  : undefined;
494
- event.source?.postMessage(Response.walletStatus(message.id, this.wallet !== undefined, pubKey));
585
+ event.source?.postMessage(Response.walletStatus(message.id, this.handler !== undefined, pubKey));
495
586
  }
496
587
  async handleMessage(event) {
497
588
  this.messageCallback(event);
@@ -570,7 +661,7 @@ export class Worker {
570
661
  event.source?.postMessage(Response.error(message.id, "Invalid RELOAD_WALLET message format"));
571
662
  return;
572
663
  }
573
- if (!this.wallet) {
664
+ if (!this.handler) {
574
665
  console.error("Wallet not initialized");
575
666
  event.source?.postMessage(Response.walletReloaded(message.id, false));
576
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
  /**