@arkade-os/sdk 0.4.16 → 0.4.18

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 (68) hide show
  1. package/README.md +16 -6
  2. package/dist/cjs/contracts/arkcontract.js +0 -2
  3. package/dist/cjs/contracts/contractManager.js +111 -199
  4. package/dist/cjs/contracts/contractWatcher.js +86 -115
  5. package/dist/cjs/repositories/indexedDB/manager.js +6 -3
  6. package/dist/cjs/repositories/indexedDB/schema.js +47 -2
  7. package/dist/cjs/repositories/indexedDB/walletRepository.js +21 -2
  8. package/dist/cjs/repositories/realm/contractRepository.js +0 -4
  9. package/dist/cjs/repositories/realm/index.js +3 -1
  10. package/dist/cjs/repositories/realm/schemas.js +50 -1
  11. package/dist/cjs/repositories/realm/walletRepository.js +8 -4
  12. package/dist/cjs/repositories/scriptFromAddress.js +16 -0
  13. package/dist/cjs/repositories/sqlite/contractRepository.js +2 -6
  14. package/dist/cjs/repositories/sqlite/walletRepository.js +121 -33
  15. package/dist/cjs/utils/syncCursors.js +48 -56
  16. package/dist/cjs/wallet/expo/background.js +0 -13
  17. package/dist/cjs/wallet/expo/wallet.js +1 -6
  18. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +16 -7
  19. package/dist/cjs/wallet/serviceWorker/wallet.js +19 -0
  20. package/dist/cjs/wallet/utils.js +41 -10
  21. package/dist/cjs/wallet/vtxo-manager.js +153 -39
  22. package/dist/cjs/wallet/wallet.js +84 -202
  23. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +9 -13
  24. package/dist/cjs/worker/expo/taskRunner.js +2 -11
  25. package/dist/esm/contracts/arkcontract.js +0 -2
  26. package/dist/esm/contracts/contractManager.js +113 -201
  27. package/dist/esm/contracts/contractWatcher.js +86 -115
  28. package/dist/esm/repositories/indexedDB/manager.js +6 -3
  29. package/dist/esm/repositories/indexedDB/schema.js +46 -2
  30. package/dist/esm/repositories/indexedDB/walletRepository.js +21 -2
  31. package/dist/esm/repositories/realm/contractRepository.js +0 -4
  32. package/dist/esm/repositories/realm/index.js +1 -1
  33. package/dist/esm/repositories/realm/schemas.js +48 -0
  34. package/dist/esm/repositories/realm/walletRepository.js +8 -4
  35. package/dist/esm/repositories/scriptFromAddress.js +13 -0
  36. package/dist/esm/repositories/sqlite/contractRepository.js +2 -6
  37. package/dist/esm/repositories/sqlite/walletRepository.js +121 -33
  38. package/dist/esm/utils/syncCursors.js +47 -53
  39. package/dist/esm/wallet/expo/background.js +0 -13
  40. package/dist/esm/wallet/expo/wallet.js +2 -7
  41. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +17 -8
  42. package/dist/esm/wallet/serviceWorker/wallet.js +19 -0
  43. package/dist/esm/wallet/utils.js +41 -9
  44. package/dist/esm/wallet/vtxo-manager.js +153 -39
  45. package/dist/esm/wallet/wallet.js +87 -205
  46. package/dist/esm/worker/expo/processors/contractPollProcessor.js +9 -13
  47. package/dist/esm/worker/expo/taskRunner.js +3 -12
  48. package/dist/types/contracts/arkcontract.d.ts +0 -2
  49. package/dist/types/contracts/contractManager.d.ts +38 -12
  50. package/dist/types/contracts/contractWatcher.d.ts +22 -21
  51. package/dist/types/contracts/types.d.ts +0 -7
  52. package/dist/types/repositories/indexedDB/manager.d.ts +5 -2
  53. package/dist/types/repositories/indexedDB/schema.d.ts +3 -2
  54. package/dist/types/repositories/realm/index.d.ts +1 -1
  55. package/dist/types/repositories/realm/schemas.d.ts +41 -0
  56. package/dist/types/repositories/scriptFromAddress.d.ts +9 -0
  57. package/dist/types/repositories/serialization.d.ts +1 -1
  58. package/dist/types/repositories/sqlite/walletRepository.d.ts +22 -0
  59. package/dist/types/repositories/walletRepository.d.ts +10 -2
  60. package/dist/types/utils/syncCursors.d.ts +25 -23
  61. package/dist/types/wallet/index.d.ts +1 -1
  62. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +15 -3
  63. package/dist/types/wallet/utils.d.ts +20 -4
  64. package/dist/types/wallet/vtxo-manager.d.ts +16 -6
  65. package/dist/types/wallet/wallet.d.ts +5 -17
  66. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +9 -4
  67. package/dist/types/worker/expo/taskRunner.d.ts +6 -3
  68. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { hex } from "@scure/base";
2
2
  import { ContractWatcher } from './contractWatcher.js';
3
3
  import { contractHandlers } from './handlers/index.js';
4
- import { extendVtxoFromContract } from '../wallet/utils.js';
5
- import { advanceSyncCursors, clearSyncCursors, computeSyncWindow, cursorCutoff, getAllSyncCursors, } from '../utils/syncCursors.js';
4
+ import { extendVirtualCoinForContract } from '../wallet/utils.js';
5
+ import { advanceSyncCursor, computeSyncWindow, cursorCutoff, getSyncCursor, } from '../utils/syncCursors.js';
6
6
  const DEFAULT_PAGE_SIZE = 500;
7
7
  /**
8
8
  * Central manager for contract lifecycle and operations.
@@ -11,7 +11,7 @@ const DEFAULT_PAGE_SIZE = 500;
11
11
  * - Create and persist contracts
12
12
  * - Query stored contracts (optionally with their virtual outputs)
13
13
  * - Provide spendable path selection for a contract
14
- * - Emit contract-related events (virtual output received/spent/expired, connection reset)
14
+ * - Emit contract-related events (virtual output received/spent, connection reset)
15
15
  *
16
16
  * Notes:
17
17
  * - Implementations typically start watching automatically during initialization
@@ -22,8 +22,6 @@ const DEFAULT_PAGE_SIZE = 500;
22
22
  * const manager = await ContractManager.create({
23
23
  * indexerProvider: wallet.indexerProvider,
24
24
  * contractRepository: wallet.contractRepository,
25
- * walletRepository: wallet.walletRepository,
26
- * getDefaultAddress: () => wallet.getAddress(),
27
25
  * });
28
26
  *
29
27
  * // Create a new VHTLC contract
@@ -83,29 +81,16 @@ export class ContractManager {
83
81
  if (this.initialized) {
84
82
  return;
85
83
  }
86
- // Load persisted contracts
84
+ // Register persisted contracts with the watcher BEFORE the first
85
+ // sync. `addContract` seeds `lastKnownVtxos` from the repo without
86
+ // starting to poll, so it's cheap, and it populates
87
+ // `getWatchedContracts()` so the sync below can scope itself to the
88
+ // real watched set instead of every contract ever persisted.
87
89
  const contracts = await this.config.contractRepository.getContracts();
88
- // Delta-sync: fetch only virtual outputs that changed since the last cursor,
89
- // falling back to a full bootstrap for scripts seen for the first time.
90
- await this.deltaSyncContracts(contracts);
91
- // Reconcile the pending frontier: fetch all not-yet-finalized virtual outputs
92
- // to catch any that the delta window may have missed.
93
- if (contracts.length > 0) {
94
- await this.reconcilePendingFrontier(contracts);
95
- }
96
- // add all contracts to the watcher
97
- const now = Date.now();
98
90
  for (const contract of contracts) {
99
- // Check for expired contracts and mark as inactive
100
- if (contract.state === "active" &&
101
- contract.expiresAt &&
102
- contract.expiresAt <= now) {
103
- contract.state = "inactive";
104
- await this.config.contractRepository.saveContract(contract);
105
- }
106
- // Add to watcher
107
91
  await this.watcher.addContract(contract);
108
92
  }
93
+ await this.reconcileWatched();
109
94
  this.initialized = true;
110
95
  // Start watching automatically
111
96
  this.stopWatcherFn = await this.watcher.startWatching((event) => {
@@ -114,6 +99,23 @@ export class ContractManager {
114
99
  });
115
100
  });
116
101
  }
102
+ /**
103
+ * Delta-sync the full watched set and reconcile the pending frontier.
104
+ *
105
+ * Shared recovery path used on initial boot and after a subscription
106
+ * reconnect. `syncContracts({})` scopes to the current watched set
107
+ * (see {@link ContractWatcher.getWatchedContracts}), uses the
108
+ * cursor-derived delta window, and advances the cursor on success.
109
+ * `reconcilePendingFrontier` catches not-yet-finalized virtual
110
+ * outputs that could sit outside any delta window.
111
+ */
112
+ async reconcileWatched() {
113
+ await this.syncContracts({});
114
+ const watched = this.watcher.getWatchedContracts();
115
+ if (watched.length > 0) {
116
+ await this.reconcilePendingFrontier(watched);
117
+ }
118
+ }
117
119
  /**
118
120
  * Create and register a new contract.
119
121
  *
@@ -158,15 +160,7 @@ export class ContractManager {
158
160
  // Persist
159
161
  await this.config.contractRepository.saveContract(contract);
160
162
  // fetch all virtual outputs (including spent/swept) for this contract
161
- const requestStartedAt = Date.now();
162
- await this.fetchContractVxosFromIndexer([contract], true);
163
- // Advance the sync cursor so that the watcher's vtxo_received
164
- // event (triggered by addContract below) doesn't re-bootstrap
165
- // the same script via deltaSyncContracts.
166
- const cutoff = cursorCutoff(requestStartedAt);
167
- await advanceSyncCursors(this.config.walletRepository, {
168
- [contract.script]: cutoff,
169
- });
163
+ await this.fetchContractVxosFromIndexer([contract]);
170
164
  // Add to watcher
171
165
  await this.watcher.addContract(contract);
172
166
  return contract;
@@ -192,12 +186,26 @@ export class ContractManager {
192
186
  }
193
187
  async getContractsWithVtxos(filter, pageSize) {
194
188
  const contracts = await this.getContracts(filter);
195
- const vtxos = await this.getVtxosForContracts(contracts, pageSize);
189
+ await this.syncContracts({ contracts, pageSize });
190
+ const vtxos = await this.getVtxosForContracts(contracts);
196
191
  return contracts.map((contract) => ({
197
192
  contract,
198
- vtxos: vtxos.get(contract.script) ?? [],
193
+ vtxos: vtxos.filter((vtxo) => vtxo.contractScript === contract.script),
199
194
  }));
200
195
  }
196
+ async annotateVtxos(vtxos) {
197
+ if (vtxos.length === 0)
198
+ return [];
199
+ const scripts = Array.from(new Set(vtxos.map((v) => v.script)));
200
+ const byScript = new Map();
201
+ const contracts = await this.config.contractRepository.getContracts({
202
+ script: scripts,
203
+ });
204
+ for (const contract of contracts) {
205
+ byScript.set(contract.script, contract);
206
+ }
207
+ return vtxos.map((vtxo) => extendVirtualCoinForContract(vtxo, byScript));
208
+ }
201
209
  buildContractsDbFilter(filter) {
202
210
  return {
203
211
  script: filter.script,
@@ -338,41 +346,18 @@ export class ContractManager {
338
346
  /**
339
347
  * Force refresh virtual outputs from the indexer.
340
348
  *
341
- * Without options, clears all sync cursors and re-fetches every contract.
349
+ * Without options, re-fetches every contract and advances the global cursor.
342
350
  * With options, narrows the refresh to specific scripts and/or a time window.
351
+ * Subset refreshes (scripts filter) intentionally do not advance the cursor.
343
352
  */
344
353
  async refreshVtxos(opts) {
345
- let contracts = await this.config.contractRepository.getContracts();
346
- if (opts?.scripts && opts.scripts.length > 0) {
347
- const scriptSet = new Set(opts.scripts);
348
- contracts = contracts.filter((c) => scriptSet.has(c.script));
349
- }
350
- const syncWindow = opts?.after !== undefined || opts?.before !== undefined
351
- ? {
352
- after: opts.after ?? 0,
353
- before: opts.before ?? Date.now(),
354
- }
354
+ const contracts = opts?.scripts
355
+ ? await this.getContracts({ script: opts.scripts })
355
356
  : undefined;
356
- if (!syncWindow) {
357
- // Full refresh — clear cursors so the next delta sync re-bootstraps.
358
- if (opts?.scripts && opts.scripts.length > 0) {
359
- await clearSyncCursors(this.config.walletRepository, opts.scripts);
360
- }
361
- else {
362
- await clearSyncCursors(this.config.walletRepository);
363
- }
364
- }
365
- const requestStartedAt = Date.now();
366
- const fetched = await this.fetchContractVxosFromIndexer(contracts, true, undefined, syncWindow);
367
- // Persist cursors so subsequent incremental syncs don't re-bootstrap.
368
- const cutoff = cursorCutoff(requestStartedAt);
369
- const cursorUpdates = {};
370
- for (const script of fetched.keys()) {
371
- cursorUpdates[script] = cutoff;
372
- }
373
- if (Object.keys(cursorUpdates).length > 0) {
374
- await advanceSyncCursors(this.config.walletRepository, cursorUpdates);
375
- }
357
+ await this.syncContracts({
358
+ contracts,
359
+ window: { after: opts?.after, before: opts?.before },
360
+ });
376
361
  }
377
362
  /**
378
363
  * Check if currently watching.
@@ -401,76 +386,54 @@ export class ContractManager {
401
386
  // Delta-sync only the changed virtual outputs for this contract.
402
387
  case "vtxo_received":
403
388
  case "vtxo_spent":
404
- await this.deltaSyncContracts([event.contract]);
389
+ await this.syncContracts({ contracts: [event.contract] });
405
390
  break;
406
- case "connection_reset": {
407
- // After a reconnect we don't know what we missed — full refetch.
408
- const activeWatchedContracts = this.watcher.getActiveContracts();
409
- await this.fetchContractVxosFromIndexer(activeWatchedContracts, true);
391
+ case "connection_reset":
392
+ // Same recovery path as boot: delta-sync the watched set
393
+ // and reconcile the pending frontier. `advanceSyncCursor`
394
+ // is monotonic so this never rewinds the cursor.
395
+ await this.reconcileWatched();
410
396
  break;
411
- }
412
- case "contract_expired":
413
- // just update DB
414
- await this.config.contractRepository.saveContract(event.contract);
415
397
  }
416
398
  // Forward to all callbacks
417
399
  this.emitEvent(event);
418
400
  }
419
- async getVtxosForContracts(contracts, pageSize) {
420
- if (contracts.length === 0) {
421
- return new Map();
422
- }
423
- return await this.fetchContractVxosFromIndexer(contracts, false, pageSize);
401
+ async getVtxosForContracts(contracts) {
402
+ const res = await Promise.all(contracts.map(({ script, address }) => this.config.walletRepository.getVtxos(address).then((vtxos) => vtxos.map((vtxo) => ({
403
+ ...vtxo,
404
+ contractScript: script,
405
+ })))));
406
+ return res.flat();
424
407
  }
425
408
  /**
426
- * Incrementally sync virtual outputs for the given contracts.
427
- * Uses per-script cursors to fetch only what changed since the last sync.
428
- * Scripts without a cursor are bootstrapped with a full fetch.
409
+ * Sync virtual outputs for the given contracts against the indexer.
410
+ *
411
+ * When `options.contracts` is omitted the sync covers the full
412
+ * watched set (active contracts plus any inactive contracts still
413
+ * holding cached VTXOs) and the global cursor is advanced on
414
+ * success. Passing an explicit subset leaves the cursor alone so a
415
+ * narrow poll can't hide data that other contracts still need to
416
+ * pick up.
429
417
  */
430
- async deltaSyncContracts(contracts, pageSize) {
431
- if (contracts.length === 0)
432
- return new Map();
433
- const cursors = await getAllSyncCursors(this.config.walletRepository);
434
- // Partition into bootstrap (no cursor) and delta (has cursor) groups.
435
- const bootstrap = [];
436
- const delta = [];
437
- for (const c of contracts) {
438
- if (cursors[c.script] !== undefined) {
439
- delta.push(c);
440
- }
441
- else {
442
- bootstrap.push(c);
443
- }
444
- }
445
- const result = new Map();
446
- const cursorUpdates = {};
447
- // Full bootstrap for new scripts.
448
- if (bootstrap.length > 0) {
449
- const requestStartedAt = Date.now();
450
- const fetched = await this.fetchContractVxosFromIndexer(bootstrap, true);
418
+ async syncContracts(options) {
419
+ const cursor = await getSyncCursor(this.config.walletRepository);
420
+ const window = options.window ?? computeSyncWindow(cursor);
421
+ // Advance the global cursor only on full-scope, cursor-derived delta
422
+ // syncs. A caller-supplied window is targeted (e.g. `refreshVtxos`)
423
+ // and must not move the cursor — it may skip data outside its bounds.
424
+ // `<=` lets the bootstrap case (cursor=0, window.after=0) write the
425
+ // migration marker on first boot; otherwise the marker would never
426
+ // be written and every subsequent boot would treat the cursor as
427
+ // legacy and re-bootstrap.
428
+ const mustUpdateCursor = options.contracts === undefined &&
429
+ options.window === undefined &&
430
+ (window.after ?? 0) <= cursor;
431
+ const contracts = options.contracts ?? this.watcher.getWatchedContracts();
432
+ const requestStartedAt = Date.now();
433
+ const result = await this.fetchContractVxosFromIndexer(contracts, options.pageSize, window);
434
+ if (mustUpdateCursor) {
451
435
  const cutoff = cursorCutoff(requestStartedAt);
452
- for (const [script, vtxos] of fetched) {
453
- result.set(script, vtxos);
454
- cursorUpdates[script] = cutoff;
455
- }
456
- }
457
- // Delta sync for scripts with an existing cursor.
458
- if (delta.length > 0) {
459
- // Use the oldest cursor so the shared window covers every script.
460
- const minCursor = Math.min(...delta.map((c) => cursors[c.script]));
461
- const window = computeSyncWindow(minCursor);
462
- if (window) {
463
- const requestStartedAt = Date.now();
464
- const fetched = await this.fetchContractVxosFromIndexer(delta, true, pageSize, window);
465
- const cutoff = cursorCutoff(requestStartedAt);
466
- for (const [script, vtxos] of fetched) {
467
- result.set(script, vtxos);
468
- cursorUpdates[script] = cutoff;
469
- }
470
- }
471
- }
472
- if (Object.keys(cursorUpdates).length > 0) {
473
- await advanceSyncCursors(this.config.walletRepository, cursorUpdates);
436
+ await advanceSyncCursor(this.config.walletRepository, cutoff);
474
437
  }
475
438
  return result;
476
439
  }
@@ -486,21 +449,20 @@ export class ContractManager {
486
449
  scripts,
487
450
  pendingOnly: true,
488
451
  });
489
- // Group by contract and upsert.
452
+ // Share the annotation path with external callers so the two entry
453
+ // points can't drift.
454
+ const owned = vtxos.filter((v) => scriptToContract.has(v.script));
455
+ const annotated = await this.annotateVtxos(owned);
490
456
  const byContract = new Map();
491
- for (const vtxo of vtxos) {
492
- if (!vtxo.script)
493
- continue;
457
+ for (const vtxo of annotated) {
494
458
  const contract = scriptToContract.get(vtxo.script);
495
- if (!contract)
496
- continue;
497
459
  let arr = byContract.get(contract.address);
498
460
  if (!arr) {
499
461
  arr = [];
500
462
  byContract.set(contract.address, arr);
501
463
  }
502
464
  arr.push({
503
- ...extendVtxoFromContract(vtxo, contract),
465
+ ...vtxo,
504
466
  contractScript: contract.script,
505
467
  });
506
468
  }
@@ -508,8 +470,8 @@ export class ContractManager {
508
470
  await this.config.walletRepository.saveVtxos(addr, contractVtxos);
509
471
  }
510
472
  }
511
- async fetchContractVxosFromIndexer(contracts, includeSpent, pageSize, syncWindow) {
512
- const fetched = await this.fetchContractVtxosBulk(contracts, includeSpent, pageSize, syncWindow);
473
+ async fetchContractVxosFromIndexer(contracts, pageSize, syncWindow) {
474
+ const fetched = await this.fetchContractVtxosBulk(contracts, pageSize, syncWindow);
513
475
  const result = new Map();
514
476
  for (const [contractScript, vtxos] of fetched) {
515
477
  result.set(contractScript, vtxos);
@@ -520,23 +482,17 @@ export class ContractManager {
520
482
  }
521
483
  return result;
522
484
  }
523
- async fetchContractVtxosBulk(contracts, includeSpent, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
485
+ async fetchContractVtxosBulk(contracts, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
524
486
  if (contracts.length === 0) {
525
487
  return new Map();
526
488
  }
527
- // For a single contract, use the paginated path directly.
528
- if (contracts.length === 1) {
529
- const contract = contracts[0];
530
- const vtxos = await this.fetchContractVtxosPaginated(contract, includeSpent, pageSize, syncWindow);
531
- return new Map([[contract.script, vtxos]]);
532
- }
533
- // For multiple contracts, batch all scripts into a single indexer call
534
- // per page to minimise round-trips. Results are keyed by script so we
535
- // can distribute them back to the correct contract afterwards.
489
+ // Batch all scripts into a single indexer call per page to minimise
490
+ // round-trips. Results are keyed by script so we can distribute them
491
+ // back to the correct contract afterwards. Always fetches the full
492
+ // history (spent/swept included) so the repo is the source of truth.
536
493
  const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
537
494
  const result = new Map(contracts.map((c) => [c.script, []]));
538
495
  const scripts = contracts.map((c) => c.script);
539
- const opts = includeSpent ? {} : { spendableOnly: true };
540
496
  const windowOpts = syncWindow
541
497
  ? {
542
498
  ...(syncWindow.after !== undefined && {
@@ -552,22 +508,20 @@ export class ContractManager {
552
508
  while (hasMore) {
553
509
  const { vtxos, page } = await this.config.indexerProvider.getVtxos({
554
510
  scripts,
555
- ...opts,
556
511
  ...windowOpts,
557
512
  pageIndex,
558
513
  pageSize,
559
514
  });
560
- for (const vtxo of vtxos) {
561
- // Match the virtual output back to its contract via the script field
562
- // populated by the indexer.
563
- if (!vtxo.script)
564
- continue;
565
- const contract = scriptToContract.get(vtxo.script);
566
- if (!contract)
567
- continue;
568
- result.get(contract.script).push({
569
- ...extendVtxoFromContract(vtxo, contract),
570
- contractScript: contract.script,
515
+ // Match virtual outputs back to their contract via the script field
516
+ // populated by the indexer, then share the annotation path with
517
+ // external callers via annotateVtxos so the two entry points can't
518
+ // drift.
519
+ const owned = vtxos.filter((v) => scriptToContract.has(v.script));
520
+ const annotated = await this.annotateVtxos(owned);
521
+ for (const vtxo of annotated) {
522
+ result.get(vtxo.script).push({
523
+ ...vtxo,
524
+ contractScript: vtxo.script,
571
525
  });
572
526
  }
573
527
  hasMore = page ? vtxos.length === pageSize : false;
@@ -577,42 +531,6 @@ export class ContractManager {
577
531
  }
578
532
  return result;
579
533
  }
580
- async fetchContractVtxosPaginated(contract, includeSpent, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
581
- const allVtxos = [];
582
- let pageIndex = 0;
583
- let hasMore = true;
584
- const opts = includeSpent ? {} : { spendableOnly: true };
585
- const windowOpts = syncWindow
586
- ? {
587
- ...(syncWindow.after !== undefined && {
588
- after: syncWindow.after,
589
- }),
590
- ...(syncWindow.before !== undefined && {
591
- before: syncWindow.before,
592
- }),
593
- }
594
- : {};
595
- while (hasMore) {
596
- const { vtxos, page } = await this.config.indexerProvider.getVtxos({
597
- scripts: [contract.script],
598
- ...opts,
599
- ...windowOpts,
600
- pageIndex,
601
- pageSize,
602
- });
603
- for (const vtxo of vtxos) {
604
- allVtxos.push({
605
- ...extendVtxoFromContract(vtxo, contract),
606
- contractScript: contract.script,
607
- });
608
- }
609
- hasMore = page ? vtxos.length === pageSize : false;
610
- pageIndex++;
611
- if (hasMore)
612
- await new Promise((r) => setTimeout(r, 500));
613
- }
614
- return allVtxos;
615
- }
616
534
  /**
617
535
  * Dispose of the ContractManager and release all resources.
618
536
  *
@@ -641,12 +559,6 @@ export class ContractManager {
641
559
  * ```
642
560
  */
643
561
  [Symbol.dispose]() {
644
- // Stop watching
645
- this.stopWatcherFn?.();
646
- this.stopWatcherFn = undefined;
647
- // Clear callbacks
648
- this.eventCallbacks.clear();
649
- // Mark as uninitialized
650
- this.initialized = false;
562
+ this.dispose();
651
563
  }
652
564
  }