@arkade-os/sdk 0.4.17 → 0.4.19

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 (70) 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 -215
  4. package/dist/cjs/contracts/contractWatcher.js +86 -115
  5. package/dist/cjs/providers/ark.js +36 -33
  6. package/dist/cjs/repositories/indexedDB/manager.js +6 -3
  7. package/dist/cjs/repositories/indexedDB/schema.js +47 -2
  8. package/dist/cjs/repositories/indexedDB/walletRepository.js +21 -2
  9. package/dist/cjs/repositories/realm/contractRepository.js +0 -4
  10. package/dist/cjs/repositories/realm/index.js +3 -1
  11. package/dist/cjs/repositories/realm/schemas.js +50 -1
  12. package/dist/cjs/repositories/realm/walletRepository.js +8 -4
  13. package/dist/cjs/repositories/scriptFromAddress.js +16 -0
  14. package/dist/cjs/repositories/sqlite/contractRepository.js +2 -6
  15. package/dist/cjs/repositories/sqlite/walletRepository.js +121 -33
  16. package/dist/cjs/utils/syncCursors.js +48 -56
  17. package/dist/cjs/wallet/expo/background.js +0 -13
  18. package/dist/cjs/wallet/expo/wallet.js +1 -6
  19. package/dist/cjs/wallet/serviceWorker/wallet-message-handler.js +16 -7
  20. package/dist/cjs/wallet/serviceWorker/wallet.js +19 -0
  21. package/dist/cjs/wallet/utils.js +41 -10
  22. package/dist/cjs/wallet/vtxo-manager.js +222 -40
  23. package/dist/cjs/wallet/wallet.js +149 -211
  24. package/dist/cjs/worker/expo/processors/contractPollProcessor.js +9 -13
  25. package/dist/cjs/worker/expo/taskRunner.js +2 -11
  26. package/dist/esm/contracts/arkcontract.js +0 -2
  27. package/dist/esm/contracts/contractManager.js +113 -217
  28. package/dist/esm/contracts/contractWatcher.js +86 -115
  29. package/dist/esm/providers/ark.js +36 -33
  30. package/dist/esm/repositories/indexedDB/manager.js +6 -3
  31. package/dist/esm/repositories/indexedDB/schema.js +46 -2
  32. package/dist/esm/repositories/indexedDB/walletRepository.js +21 -2
  33. package/dist/esm/repositories/realm/contractRepository.js +0 -4
  34. package/dist/esm/repositories/realm/index.js +1 -1
  35. package/dist/esm/repositories/realm/schemas.js +48 -0
  36. package/dist/esm/repositories/realm/walletRepository.js +8 -4
  37. package/dist/esm/repositories/scriptFromAddress.js +13 -0
  38. package/dist/esm/repositories/sqlite/contractRepository.js +2 -6
  39. package/dist/esm/repositories/sqlite/walletRepository.js +121 -33
  40. package/dist/esm/utils/syncCursors.js +47 -53
  41. package/dist/esm/wallet/expo/background.js +0 -13
  42. package/dist/esm/wallet/expo/wallet.js +2 -7
  43. package/dist/esm/wallet/serviceWorker/wallet-message-handler.js +17 -8
  44. package/dist/esm/wallet/serviceWorker/wallet.js +19 -0
  45. package/dist/esm/wallet/utils.js +41 -9
  46. package/dist/esm/wallet/vtxo-manager.js +222 -40
  47. package/dist/esm/wallet/wallet.js +152 -214
  48. package/dist/esm/worker/expo/processors/contractPollProcessor.js +9 -13
  49. package/dist/esm/worker/expo/taskRunner.js +3 -12
  50. package/dist/types/contracts/arkcontract.d.ts +0 -2
  51. package/dist/types/contracts/contractManager.d.ts +38 -9
  52. package/dist/types/contracts/contractWatcher.d.ts +22 -21
  53. package/dist/types/contracts/types.d.ts +0 -7
  54. package/dist/types/repositories/indexedDB/manager.d.ts +5 -2
  55. package/dist/types/repositories/indexedDB/schema.d.ts +3 -2
  56. package/dist/types/repositories/realm/index.d.ts +1 -1
  57. package/dist/types/repositories/realm/schemas.d.ts +41 -0
  58. package/dist/types/repositories/scriptFromAddress.d.ts +9 -0
  59. package/dist/types/repositories/serialization.d.ts +1 -1
  60. package/dist/types/repositories/sqlite/walletRepository.d.ts +22 -0
  61. package/dist/types/repositories/walletRepository.d.ts +10 -2
  62. package/dist/types/utils/syncCursors.d.ts +25 -23
  63. package/dist/types/wallet/index.d.ts +1 -1
  64. package/dist/types/wallet/serviceWorker/wallet-message-handler.d.ts +15 -3
  65. package/dist/types/wallet/utils.d.ts +20 -4
  66. package/dist/types/wallet/vtxo-manager.d.ts +29 -6
  67. package/dist/types/wallet/wallet.d.ts +8 -17
  68. package/dist/types/worker/expo/processors/contractPollProcessor.d.ts +9 -4
  69. package/dist/types/worker/expo/taskRunner.d.ts +6 -3
  70. 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
@@ -81,29 +81,16 @@ export class ContractManager {
81
81
  if (this.initialized) {
82
82
  return;
83
83
  }
84
- // 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.
85
89
  const contracts = await this.config.contractRepository.getContracts();
86
- // Delta-sync: fetch only virtual outputs that changed since the last cursor,
87
- // falling back to a full bootstrap for scripts seen for the first time.
88
- await this.deltaSyncContracts(contracts, undefined, true);
89
- // Reconcile the pending frontier: fetch all not-yet-finalized virtual outputs
90
- // to catch any that the delta window may have missed.
91
- if (contracts.length > 0) {
92
- await this.reconcilePendingFrontier(contracts);
93
- }
94
- // add all contracts to the watcher
95
- const now = Date.now();
96
90
  for (const contract of contracts) {
97
- // Check for expired contracts and mark as inactive
98
- if (contract.state === "active" &&
99
- contract.expiresAt &&
100
- contract.expiresAt <= now) {
101
- contract.state = "inactive";
102
- await this.config.contractRepository.saveContract(contract);
103
- }
104
- // Add to watcher
105
91
  await this.watcher.addContract(contract);
106
92
  }
93
+ await this.reconcileWatched();
107
94
  this.initialized = true;
108
95
  // Start watching automatically
109
96
  this.stopWatcherFn = await this.watcher.startWatching((event) => {
@@ -112,6 +99,23 @@ export class ContractManager {
112
99
  });
113
100
  });
114
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
+ }
115
119
  /**
116
120
  * Create and register a new contract.
117
121
  *
@@ -156,15 +160,7 @@ export class ContractManager {
156
160
  // Persist
157
161
  await this.config.contractRepository.saveContract(contract);
158
162
  // fetch all virtual outputs (including spent/swept) for this contract
159
- const requestStartedAt = Date.now();
160
- await this.fetchContractVxosFromIndexer([contract], true);
161
- // Advance the sync cursor so that the watcher's vtxo_received
162
- // event (triggered by addContract below) doesn't re-bootstrap
163
- // the same script via deltaSyncContracts.
164
- const cutoff = cursorCutoff(requestStartedAt);
165
- await advanceSyncCursors(this.config.walletRepository, {
166
- [contract.script]: cutoff,
167
- });
163
+ await this.fetchContractVxosFromIndexer([contract]);
168
164
  // Add to watcher
169
165
  await this.watcher.addContract(contract);
170
166
  return contract;
@@ -190,12 +186,26 @@ export class ContractManager {
190
186
  }
191
187
  async getContractsWithVtxos(filter, pageSize) {
192
188
  const contracts = await this.getContracts(filter);
193
- const vtxos = await this.getVtxosForContracts(contracts, pageSize);
189
+ await this.syncContracts({ contracts, pageSize });
190
+ const vtxos = await this.getVtxosForContracts(contracts);
194
191
  return contracts.map((contract) => ({
195
192
  contract,
196
- vtxos: vtxos.get(contract.script) ?? [],
193
+ vtxos: vtxos.filter((vtxo) => vtxo.contractScript === contract.script),
197
194
  }));
198
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
+ }
199
209
  buildContractsDbFilter(filter) {
200
210
  return {
201
211
  script: filter.script,
@@ -336,41 +346,18 @@ export class ContractManager {
336
346
  /**
337
347
  * Force refresh virtual outputs from the indexer.
338
348
  *
339
- * Without options, clears all sync cursors and re-fetches every contract.
349
+ * Without options, re-fetches every contract and advances the global cursor.
340
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.
341
352
  */
342
353
  async refreshVtxos(opts) {
343
- let contracts = await this.config.contractRepository.getContracts();
344
- if (opts?.scripts && opts.scripts.length > 0) {
345
- const scriptSet = new Set(opts.scripts);
346
- contracts = contracts.filter((c) => scriptSet.has(c.script));
347
- }
348
- const syncWindow = opts?.after !== undefined || opts?.before !== undefined
349
- ? {
350
- after: opts.after ?? 0,
351
- before: opts.before ?? Date.now(),
352
- }
354
+ const contracts = opts?.scripts
355
+ ? await this.getContracts({ script: opts.scripts })
353
356
  : undefined;
354
- if (!syncWindow) {
355
- // Full refresh — clear cursors so the next delta sync re-bootstraps.
356
- if (opts?.scripts && opts.scripts.length > 0) {
357
- await clearSyncCursors(this.config.walletRepository, opts.scripts);
358
- }
359
- else {
360
- await clearSyncCursors(this.config.walletRepository);
361
- }
362
- }
363
- const requestStartedAt = Date.now();
364
- const fetched = await this.fetchContractVxosFromIndexer(contracts, true, undefined, syncWindow);
365
- // Persist cursors so subsequent incremental syncs don't re-bootstrap.
366
- const cutoff = cursorCutoff(requestStartedAt);
367
- const cursorUpdates = {};
368
- for (const script of fetched.keys()) {
369
- cursorUpdates[script] = cutoff;
370
- }
371
- if (Object.keys(cursorUpdates).length > 0) {
372
- await advanceSyncCursors(this.config.walletRepository, cursorUpdates);
373
- }
357
+ await this.syncContracts({
358
+ contracts,
359
+ window: { after: opts?.after, before: opts?.before },
360
+ });
374
361
  }
375
362
  /**
376
363
  * Check if currently watching.
@@ -399,94 +386,54 @@ export class ContractManager {
399
386
  // Delta-sync only the changed virtual outputs for this contract.
400
387
  case "vtxo_received":
401
388
  case "vtxo_spent":
402
- await this.deltaSyncContracts([event.contract]);
389
+ await this.syncContracts({ contracts: [event.contract] });
403
390
  break;
404
- case "connection_reset": {
405
- // After a reconnect we don't know what we missed — full refetch.
406
- const activeWatchedContracts = this.watcher.getActiveContracts();
407
- 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();
408
396
  break;
409
- }
410
- case "contract_expired":
411
- // just update DB
412
- await this.config.contractRepository.saveContract(event.contract);
413
397
  }
414
398
  // Forward to all callbacks
415
399
  this.emitEvent(event);
416
400
  }
417
- async getVtxosForContracts(contracts, pageSize) {
418
- if (contracts.length === 0) {
419
- return new Map();
420
- }
421
- // Deduplicate concurrent callers against an in-flight fetch so we don't
422
- // issue redundant round-trips. Once the fetch settles we clear the
423
- // reference so the next call triggers a fresh fetch.
424
- // TODO: can be removed once we fix the persistence layer (address vs scripts)
425
- if (this.syncVtxosCallInflight) {
426
- return this.syncVtxosCallInflight;
427
- }
428
- this.syncVtxosCallInflight = this.fetchContractVxosFromIndexer(contracts, true, pageSize).finally(() => {
429
- this.syncVtxosCallInflight = undefined;
430
- });
431
- return this.syncVtxosCallInflight;
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();
432
407
  }
433
408
  /**
434
- * Incrementally sync virtual outputs for the given contracts.
435
- * Uses per-script cursors to fetch only what changed since the last sync.
436
- * 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.
437
417
  */
438
- async deltaSyncContracts(contracts, pageSize, force) {
439
- if (contracts.length === 0)
440
- return new Map();
441
- // If forced, we are treating all contracts as boostrapped and we clean the VTXO list
442
- if (force === true) {
443
- await Promise.all(contracts.map((contract) => this.config.walletRepository.deleteVtxos(contract.address)));
444
- }
445
- const cursors = await getAllSyncCursors(this.config.walletRepository);
446
- // Partition into bootstrap (no cursor) and delta (has cursor) groups.
447
- const bootstrap = [];
448
- const delta = [];
449
- for (const c of contracts) {
450
- if (force) {
451
- bootstrap.push(c);
452
- continue;
453
- }
454
- if (cursors[c.script] !== undefined) {
455
- delta.push(c);
456
- }
457
- else {
458
- bootstrap.push(c);
459
- }
460
- }
461
- const result = new Map();
462
- const cursorUpdates = {};
463
- // Full bootstrap for new scripts.
464
- if (bootstrap.length > 0) {
465
- const requestStartedAt = Date.now();
466
- 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) {
467
435
  const cutoff = cursorCutoff(requestStartedAt);
468
- for (const [script, vtxos] of fetched) {
469
- result.set(script, vtxos);
470
- cursorUpdates[script] = cutoff;
471
- }
472
- }
473
- // Delta sync for scripts with an existing cursor.
474
- if (delta.length > 0) {
475
- // Use the oldest cursor so the shared window covers every script.
476
- const minCursor = Math.min(...delta.map((c) => cursors[c.script]));
477
- const window = computeSyncWindow(minCursor);
478
- if (window) {
479
- const requestStartedAt = Date.now();
480
- const fetched = await this.fetchContractVxosFromIndexer(delta, true, pageSize, window);
481
- const cutoff = cursorCutoff(requestStartedAt);
482
- for (const [script, vtxos] of fetched) {
483
- result.set(script, vtxos);
484
- cursorUpdates[script] = cutoff;
485
- }
486
- }
487
- }
488
- if (Object.keys(cursorUpdates).length > 0) {
489
- await advanceSyncCursors(this.config.walletRepository, cursorUpdates);
436
+ await advanceSyncCursor(this.config.walletRepository, cutoff);
490
437
  }
491
438
  return result;
492
439
  }
@@ -502,21 +449,20 @@ export class ContractManager {
502
449
  scripts,
503
450
  pendingOnly: true,
504
451
  });
505
- // 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);
506
456
  const byContract = new Map();
507
- for (const vtxo of vtxos) {
508
- if (!vtxo.script)
509
- continue;
457
+ for (const vtxo of annotated) {
510
458
  const contract = scriptToContract.get(vtxo.script);
511
- if (!contract)
512
- continue;
513
459
  let arr = byContract.get(contract.address);
514
460
  if (!arr) {
515
461
  arr = [];
516
462
  byContract.set(contract.address, arr);
517
463
  }
518
464
  arr.push({
519
- ...extendVtxoFromContract(vtxo, contract),
465
+ ...vtxo,
520
466
  contractScript: contract.script,
521
467
  });
522
468
  }
@@ -524,8 +470,8 @@ export class ContractManager {
524
470
  await this.config.walletRepository.saveVtxos(addr, contractVtxos);
525
471
  }
526
472
  }
527
- async fetchContractVxosFromIndexer(contracts, includeSpent, pageSize, syncWindow) {
528
- const fetched = await this.fetchContractVtxosBulk(contracts, includeSpent, pageSize, syncWindow);
473
+ async fetchContractVxosFromIndexer(contracts, pageSize, syncWindow) {
474
+ const fetched = await this.fetchContractVtxosBulk(contracts, pageSize, syncWindow);
529
475
  const result = new Map();
530
476
  for (const [contractScript, vtxos] of fetched) {
531
477
  result.set(contractScript, vtxos);
@@ -536,23 +482,17 @@ export class ContractManager {
536
482
  }
537
483
  return result;
538
484
  }
539
- async fetchContractVtxosBulk(contracts, includeSpent, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
485
+ async fetchContractVtxosBulk(contracts, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
540
486
  if (contracts.length === 0) {
541
487
  return new Map();
542
488
  }
543
- // For a single contract, use the paginated path directly.
544
- if (contracts.length === 1) {
545
- const contract = contracts[0];
546
- const vtxos = await this.fetchContractVtxosPaginated(contract, includeSpent, pageSize, syncWindow);
547
- return new Map([[contract.script, vtxos]]);
548
- }
549
- // For multiple contracts, batch all scripts into a single indexer call
550
- // per page to minimise round-trips. Results are keyed by script so we
551
- // 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.
552
493
  const scriptToContract = new Map(contracts.map((c) => [c.script, c]));
553
494
  const result = new Map(contracts.map((c) => [c.script, []]));
554
495
  const scripts = contracts.map((c) => c.script);
555
- const opts = includeSpent ? {} : { spendableOnly: true };
556
496
  const windowOpts = syncWindow
557
497
  ? {
558
498
  ...(syncWindow.after !== undefined && {
@@ -568,22 +508,20 @@ export class ContractManager {
568
508
  while (hasMore) {
569
509
  const { vtxos, page } = await this.config.indexerProvider.getVtxos({
570
510
  scripts,
571
- ...opts,
572
511
  ...windowOpts,
573
512
  pageIndex,
574
513
  pageSize,
575
514
  });
576
- for (const vtxo of vtxos) {
577
- // Match the virtual output back to its contract via the script field
578
- // populated by the indexer.
579
- if (!vtxo.script)
580
- continue;
581
- const contract = scriptToContract.get(vtxo.script);
582
- if (!contract)
583
- continue;
584
- result.get(contract.script).push({
585
- ...extendVtxoFromContract(vtxo, contract),
586
- 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,
587
525
  });
588
526
  }
589
527
  hasMore = page ? vtxos.length === pageSize : false;
@@ -593,42 +531,6 @@ export class ContractManager {
593
531
  }
594
532
  return result;
595
533
  }
596
- async fetchContractVtxosPaginated(contract, includeSpent, pageSize = DEFAULT_PAGE_SIZE, syncWindow) {
597
- const allVtxos = [];
598
- let pageIndex = 0;
599
- let hasMore = true;
600
- const opts = includeSpent ? {} : { spendableOnly: true };
601
- const windowOpts = syncWindow
602
- ? {
603
- ...(syncWindow.after !== undefined && {
604
- after: syncWindow.after,
605
- }),
606
- ...(syncWindow.before !== undefined && {
607
- before: syncWindow.before,
608
- }),
609
- }
610
- : {};
611
- while (hasMore) {
612
- const { vtxos, page } = await this.config.indexerProvider.getVtxos({
613
- scripts: [contract.script],
614
- ...opts,
615
- ...windowOpts,
616
- pageIndex,
617
- pageSize,
618
- });
619
- for (const vtxo of vtxos) {
620
- allVtxos.push({
621
- ...extendVtxoFromContract(vtxo, contract),
622
- contractScript: contract.script,
623
- });
624
- }
625
- hasMore = page ? vtxos.length === pageSize : false;
626
- pageIndex++;
627
- if (hasMore)
628
- await new Promise((r) => setTimeout(r, 500));
629
- }
630
- return allVtxos;
631
- }
632
534
  /**
633
535
  * Dispose of the ContractManager and release all resources.
634
536
  *
@@ -657,12 +559,6 @@ export class ContractManager {
657
559
  * ```
658
560
  */
659
561
  [Symbol.dispose]() {
660
- // Stop watching
661
- this.stopWatcherFn?.();
662
- this.stopWatcherFn = undefined;
663
- // Clear callbacks
664
- this.eventCallbacks.clear();
665
- // Mark as uninitialized
666
- this.initialized = false;
562
+ this.dispose();
667
563
  }
668
564
  }