@arkade-os/sdk 0.4.48-rc.0 → 0.4.49-rc.0

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 (85) hide show
  1. package/dist/adapters/expo.cjs +5 -5
  2. package/dist/adapters/expo.d.cts +2 -2
  3. package/dist/adapters/expo.d.ts +2 -2
  4. package/dist/adapters/expo.js +3 -3
  5. package/dist/adapters/indexedDB.cjs +5 -5
  6. package/dist/adapters/indexedDB.js +4 -4
  7. package/dist/{ark-CoJgwTi_.d.cts → ark-Bz4VxTY7.d.cts} +482 -265
  8. package/dist/{ark-CoJgwTi_.d.ts → ark-Bz4VxTY7.d.ts} +482 -265
  9. package/dist/{asyncStorageTaskQueue-CwknfM_j.d.cts → asyncStorageTaskQueue-BgQaK3El.d.cts} +2 -2
  10. package/dist/{asyncStorageTaskQueue-D2Zl7yAB.d.ts → asyncStorageTaskQueue-UIyDWZ_d.d.ts} +2 -2
  11. package/dist/{chunk-4AIHKC65.js → chunk-27DACHF6.js} +5 -4
  12. package/dist/chunk-27DACHF6.js.map +1 -0
  13. package/dist/{chunk-ZRY2KSK5.js → chunk-COB6VP4M.js} +4 -31
  14. package/dist/chunk-COB6VP4M.js.map +1 -0
  15. package/dist/{chunk-L5ND2FJJ.cjs → chunk-E7HB3GKK.cjs} +190 -2
  16. package/dist/chunk-E7HB3GKK.cjs.map +1 -0
  17. package/dist/{chunk-3HUZAIKI.cjs → chunk-EEYMJVF6.cjs} +17 -44
  18. package/dist/chunk-EEYMJVF6.cjs.map +1 -0
  19. package/dist/{chunk-C7TXXTMS.js → chunk-EFNLTS6Q.js} +172 -3
  20. package/dist/chunk-EFNLTS6Q.js.map +1 -0
  21. package/dist/{chunk-GM6JRQP5.js → chunk-ITT2S4O3.js} +169 -42
  22. package/dist/chunk-ITT2S4O3.js.map +1 -0
  23. package/dist/{chunk-RQSA7ADZ.cjs → chunk-LJQ2VQBE.cjs} +211 -230
  24. package/dist/chunk-LJQ2VQBE.cjs.map +1 -0
  25. package/dist/{chunk-XN3554FT.js → chunk-MBSYQ3WX.js} +183 -201
  26. package/dist/chunk-MBSYQ3WX.js.map +1 -0
  27. package/dist/{chunk-RJMFTTRJ.cjs → chunk-R3LGVLKP.cjs} +756 -661
  28. package/dist/chunk-R3LGVLKP.cjs.map +1 -0
  29. package/dist/{chunk-CFQO5MCM.cjs → chunk-RNNCQHAI.cjs} +178 -50
  30. package/dist/chunk-RNNCQHAI.cjs.map +1 -0
  31. package/dist/{chunk-SV4PCGKM.js → chunk-T2IVKSZP.js} +490 -392
  32. package/dist/chunk-T2IVKSZP.js.map +1 -0
  33. package/dist/{chunk-EA3IWZ4W.cjs → chunk-XWZY6NHJ.cjs} +9 -8
  34. package/dist/chunk-XWZY6NHJ.cjs.map +1 -0
  35. package/dist/contracts/handlers/index.cjs +8 -8
  36. package/dist/contracts/handlers/index.d.cts +3 -3
  37. package/dist/contracts/handlers/index.d.ts +3 -3
  38. package/dist/contracts/handlers/index.js +2 -2
  39. package/dist/{delegate-CuaDqqQi.d.ts → delegate-BoIZ4Sjj.d.ts} +1 -1
  40. package/dist/{delegate-Dl87_zMA.d.cts → delegate-D_7Gh5H4.d.cts} +1 -1
  41. package/dist/{index-CKwpZEpw.d.ts → index-Cu6QYjYT.d.ts} +2 -2
  42. package/dist/{index-BC_G0VTh.d.cts → index-jAMA6NXc.d.cts} +2 -2
  43. package/dist/index.cjs +208 -188
  44. package/dist/index.d.cts +35 -18
  45. package/dist/index.d.ts +35 -18
  46. package/dist/index.js +4 -4
  47. package/dist/repositories/realm/index.cjs +19 -19
  48. package/dist/repositories/realm/index.d.cts +2 -2
  49. package/dist/repositories/realm/index.d.ts +2 -2
  50. package/dist/repositories/realm/index.js +5 -5
  51. package/dist/repositories/sqlite/index.cjs +18 -18
  52. package/dist/repositories/sqlite/index.d.cts +2 -2
  53. package/dist/repositories/sqlite/index.d.ts +2 -2
  54. package/dist/repositories/sqlite/index.js +5 -5
  55. package/dist/{taskRunner-RYihSj9E.d.cts → taskRunner-Ck0AouQU.d.cts} +2 -2
  56. package/dist/{taskRunner-Df8YQpn1.d.ts → taskRunner-bs1AE_BL.d.ts} +2 -2
  57. package/dist/wallet/expo/background.cjs +14 -14
  58. package/dist/wallet/expo/background.d.cts +3 -3
  59. package/dist/wallet/expo/background.d.ts +3 -3
  60. package/dist/wallet/expo/background.js +6 -6
  61. package/dist/wallet/expo/index.cjs +14 -14
  62. package/dist/wallet/expo/index.cjs.map +1 -1
  63. package/dist/wallet/expo/index.d.cts +5 -5
  64. package/dist/wallet/expo/index.d.ts +5 -5
  65. package/dist/wallet/expo/index.js +5 -5
  66. package/dist/wallet/expo/index.js.map +1 -1
  67. package/dist/{wallet-B6s7_5bJ.d.cts → wallet-CJ_8yiIz.d.ts} +31 -31
  68. package/dist/{wallet-C61huH8i.d.ts → wallet-CkTb2epm.d.cts} +31 -31
  69. package/dist/worker/expo/index.cjs +9 -9
  70. package/dist/worker/expo/index.d.cts +4 -4
  71. package/dist/worker/expo/index.d.ts +4 -4
  72. package/dist/worker/expo/index.js +5 -5
  73. package/package.json +3 -2
  74. package/dist/chunk-3HUZAIKI.cjs.map +0 -1
  75. package/dist/chunk-4AIHKC65.js.map +0 -1
  76. package/dist/chunk-C7TXXTMS.js.map +0 -1
  77. package/dist/chunk-CFQO5MCM.cjs.map +0 -1
  78. package/dist/chunk-EA3IWZ4W.cjs.map +0 -1
  79. package/dist/chunk-GM6JRQP5.js.map +0 -1
  80. package/dist/chunk-L5ND2FJJ.cjs.map +0 -1
  81. package/dist/chunk-RJMFTTRJ.cjs.map +0 -1
  82. package/dist/chunk-RQSA7ADZ.cjs.map +0 -1
  83. package/dist/chunk-SV4PCGKM.js.map +0 -1
  84. package/dist/chunk-XN3554FT.js.map +0 -1
  85. package/dist/chunk-ZRY2KSK5.js.map +0 -1
@@ -524,6 +524,285 @@ type EncodedVtxoScript = {
524
524
  */
525
525
  declare function getSequence(tapLeafScript: TapLeafScript): number | undefined;
526
526
 
527
+ type NetworkName = "bitcoin" | "testnet" | "signet" | "mutinynet" | "regtest";
528
+ interface Network {
529
+ hrp: string;
530
+ bech32: string;
531
+ pubKeyHash: number;
532
+ scriptHash: number;
533
+ wif: number;
534
+ }
535
+ declare const getNetwork: (network: NetworkName) => Network;
536
+ declare const networks: {
537
+ bitcoin: Network;
538
+ testnet: Network;
539
+ signet: Network;
540
+ mutinynet: Network;
541
+ regtest: Network;
542
+ };
543
+
544
+ /**
545
+ * The default base URLs for esplora API providers.
546
+ *
547
+ * Mainnet, mutinynet, and signet point at Ark Labs–operated
548
+ * mempool deployments (mempool.space-compatible esplora API).
549
+ * Testnet falls back to the public mempool.space deployment
550
+ * because Ark doesn't host it. Regtest assumes a local arkade-regtest
551
+ * stack exposing mempool's esplora API on the standard port.
552
+ */
553
+ declare const ESPLORA_URL: Record<NetworkName, string>;
554
+ type ExplorerTransaction = {
555
+ txid: string;
556
+ /**
557
+ * Inputs as returned by Esplora's `/address/:addr/txs`, each carrying the
558
+ * outpoint it spends (`txid:vout`). Optional: not every provider populates
559
+ * it (the electrum provider omits inputs), so consumers that correlate
560
+ * spenders must tolerate its absence. Used to recover a boarding output's
561
+ * spending (commitment) tx when `/outspends` omits the spender txid.
562
+ */
563
+ vin?: {
564
+ txid: string;
565
+ vout: number;
566
+ }[];
567
+ vout: {
568
+ scriptpubkey_address: string;
569
+ value: string;
570
+ }[];
571
+ status: {
572
+ confirmed: boolean;
573
+ block_time: number;
574
+ };
575
+ };
576
+ interface OnchainProvider {
577
+ /**
578
+ * Fetch spendable onchain outputs for an address.
579
+ *
580
+ * @param address - Bitcoin address to query
581
+ * @returns Spendable onchain outputs for the address
582
+ * @see Coin
583
+ */
584
+ getCoins(address: string): Promise<Coin[]>;
585
+ /**
586
+ * Fetch the current fastest fee rate estimate.
587
+ *
588
+ * @returns Fee rate in sats/vB, if available
589
+ * @remarks
590
+ * Implementations may return `undefined` when the backing service does not expose
591
+ * a usable fee estimate.
592
+ */
593
+ getFeeRate(): Promise<number | undefined>;
594
+ /**
595
+ * Broadcast a single transaction or a 1P1C package.
596
+ *
597
+ * @param txs - One or more raw transaction hex strings
598
+ * @returns Broadcast transaction id
599
+ * @throws Error if the broadcast request fails or the package shape is invalid
600
+ */
601
+ broadcastTransaction(...txs: string[]): Promise<string>;
602
+ /**
603
+ * Fetch outspend information for every output in a transaction.
604
+ *
605
+ * @param txid - Transaction id to inspect
606
+ * @returns Per-output spend status information. `txid` (the spender) may be
607
+ * absent even when `spent` is true: some Esplora deployments
608
+ * (e.g. mempool.arkade.sh) omit it from `/outspends`.
609
+ * @see getTxStatus
610
+ */
611
+ getTxOutspends(txid: string): Promise<{
612
+ spent: boolean;
613
+ txid?: string;
614
+ }[]>;
615
+ /**
616
+ * Fetch transactions associated with an address.
617
+ *
618
+ * @param address - Bitcoin address to query
619
+ * @returns Transactions involving the address
620
+ * @see ExplorerTransaction
621
+ */
622
+ getTransactions(address: string): Promise<ExplorerTransaction[]>;
623
+ /**
624
+ * Fetch confirmation status for a transaction.
625
+ *
626
+ * @param txid - Transaction id to inspect
627
+ * @returns Confirmation status and block metadata when confirmed
628
+ * @see getTxOutspends
629
+ */
630
+ getTxStatus(txid: string): Promise<{
631
+ confirmed: false;
632
+ } | {
633
+ confirmed: true;
634
+ blockTime: number;
635
+ blockHeight: number;
636
+ }>;
637
+ /**
638
+ * Fetch the current chain tip.
639
+ *
640
+ * @returns Current chain height, block time, and block hash
641
+ */
642
+ getChainTip(): Promise<{
643
+ height: number;
644
+ time: number;
645
+ hash: string;
646
+ }>;
647
+ /**
648
+ * Watch a set of addresses and invoke the callback when transactions are observed.
649
+ *
650
+ * @param addresses - Addresses to monitor
651
+ * @param eventCallback - Callback invoked when matching transactions are seen
652
+ * @returns Stop function that cancels the watch
653
+ * @remarks
654
+ * Implementations may use websockets, server-sent events, polling, or a hybrid strategy.
655
+ * @see getTransactions
656
+ */
657
+ watchAddresses(addresses: string[], eventCallback: (txs: ExplorerTransaction[]) => void): Promise<() => void>;
658
+ }
659
+ /**
660
+ * Implementation of the onchain provider interface for esplora REST API.
661
+ *
662
+ * @see https://mempool.space/docs/api/rest
663
+ * @example
664
+ * ```typescript
665
+ * const provider = new EsploraProvider("https://mempool.space/api");
666
+ * const outputs = await provider.getCoins("bcrt1q679zsd45msawvr7782r0twvmukns3drlstjt77");
667
+ * ```
668
+ */
669
+ declare class EsploraProvider implements OnchainProvider {
670
+ private baseUrl;
671
+ readonly pollingInterval: number;
672
+ readonly forcePolling: boolean;
673
+ constructor(baseUrl?: string, opts?: {
674
+ /** Polling interval in milliseconds. */
675
+ pollingInterval?: number;
676
+ /** Force polling even when websocket transport is available. */
677
+ forcePolling?: boolean;
678
+ });
679
+ getCoins(address: string): Promise<Coin[]>;
680
+ getFeeRate(): Promise<number | undefined>;
681
+ broadcastTransaction(...txs: string[]): Promise<string>;
682
+ getTxOutspends(txid: string): Promise<{
683
+ spent: boolean;
684
+ txid?: string;
685
+ }[]>;
686
+ getTransactions(address: string): Promise<ExplorerTransaction[]>;
687
+ getTxStatus(txid: string): Promise<{
688
+ confirmed: false;
689
+ } | {
690
+ confirmed: true;
691
+ blockTime: number;
692
+ blockHeight: number;
693
+ }>;
694
+ watchAddresses(addresses: string[], callback: (txs: ExplorerTransaction[]) => void): Promise<() => void>;
695
+ getChainTip(): Promise<{
696
+ height: number;
697
+ time: number;
698
+ hash: string;
699
+ }>;
700
+ private broadcastPackage;
701
+ private broadcastTx;
702
+ }
703
+
704
+ /**
705
+ * The current moment, as the expiry predicates need it.
706
+ *
707
+ * `height` is optional: offline-first paths have no chain tip at hand. When it is absent,
708
+ * height-based expiry cannot be evaluated and reads as not expired.
709
+ */
710
+ type TimeHeight = {
711
+ timestamp: Date;
712
+ height?: number;
713
+ };
714
+ /**
715
+ * A {@link VirtualCoin} that has passed through {@link normalizeVtxo}: every fact the capability
716
+ * predicates read is present.
717
+ *
718
+ * Internal signatures take this rather than `VirtualCoin` so the compiler rejects un-normalized
719
+ * input — on the public shape these facts are optional, and `undefined` is falsy, so a legacy coin
720
+ * would silently read as "not swept", "not spent", and drop out of the wrong bucket.
721
+ *
722
+ * It is deliberately a *subtype* of `VirtualCoin`, so normalized coins are returned to consumers
723
+ * directly and no egress projection exists.
724
+ */
725
+ type NormalizedVirtualCoin = Omit<VirtualCoin, "isSwept" | "isPreconfirmed" | "isSpent" | "spentBy" | "commitmentTxIds"> & {
726
+ isSwept: boolean;
727
+ isPreconfirmed: boolean;
728
+ isSpent: boolean;
729
+ spentBy: string;
730
+ commitmentTxIds: string[];
731
+ };
732
+ type NormalizedExtendedVirtualCoin = ExtendedVirtualCoin & NormalizedVirtualCoin;
733
+ /**
734
+ * Whether a virtual output has been consumed and can never be spent again.
735
+ *
736
+ * @remarks
737
+ * Unions all three spend facts rather than trusting any one of them. The wire contract permits
738
+ * `isSpent: true` with an empty `spentBy` (settlement inputs needing no forfeit are written that
739
+ * way), so a `spentBy || settledBy` definition would classify a spent VTXO as spendable — inflating
740
+ * balance and selecting it for a send that must fail.
741
+ */
742
+ declare function hasTerminalSpend(vtxo: VirtualCoin): boolean;
743
+ /**
744
+ * Whether a virtual output's batch expiry has passed. Pure expiry — swept is a separate fact, ORed
745
+ * in explicitly by {@link canSpendOffchain} / {@link canRecoverOnchain}.
746
+ *
747
+ * @remarks
748
+ * Not named `isExpired`: the deprecated {@link isExpired} also returns `true` for a swept VTXO, and
749
+ * two same-named predicates with different truth conditions is how a call site gets silently
750
+ * rewired.
751
+ *
752
+ * Height-based expiry is only evaluated when `now.height` is supplied.
753
+ */
754
+ declare function isPastExpiry(vtxo: VirtualCoin, now: TimeHeight): boolean;
755
+ /** Whether a virtual output can be spent in an offchain transaction. The send/coin-selection test. */
756
+ declare function canSpendOffchain(vtxo: VirtualCoin, now: TimeHeight): boolean;
757
+ /**
758
+ * Whether a virtual output must be recovered into a fresh batch rather than spent offchain. The
759
+ * recovery/renewal test and the `recoverable` balance bucket.
760
+ */
761
+ declare function canRecoverOnchain(vtxo: VirtualCoin, now: TimeHeight): boolean;
762
+ /**
763
+ * Narrow a settle input to a virtual output.
764
+ *
765
+ * @remarks
766
+ * Keyed on `script`: it is required on `VirtualCoin` (so legacy and canonical shapes both have it)
767
+ * and absent from `ExtendedCoin`, which the optional canonical facts cannot claim.
768
+ *
769
+ * The `typeof` guard is load-bearing — `settle` accepts arknote strings, and `in` throws a
770
+ * `TypeError` on a primitive rather than returning false.
771
+ */
772
+ declare function isVirtualCoin<T>(input: T): input is T & VirtualCoin;
773
+ /**
774
+ * Return whether a virtual output is still spendable.
775
+ *
776
+ * @param vtxo - virtual output to inspect
777
+ * @returns `true` when the virtual output has not been consumed
778
+ *
779
+ * @deprecated Ambiguous: `true` for swept or expired virtual outputs, which cannot in fact be spent
780
+ * offchain. Use {@link canSpendOffchain}.
781
+ */
782
+ declare function isSpendable(vtxo: VirtualCoin): boolean;
783
+ /**
784
+ * Return whether a virtual output is recoverable.
785
+ *
786
+ * @param vtxo - virtual output to inspect
787
+ * @returns `true` when the virtual output is swept but not yet consumed
788
+ *
789
+ * @deprecated Swept-only: ignores virtual outputs that are past expiry but not yet swept, which are
790
+ * equally recoverable. Use {@link canRecoverOnchain}.
791
+ */
792
+ declare function isRecoverable(vtxo: VirtualCoin): boolean;
793
+ /**
794
+ * Return whether a virtual output should be treated as expired.
795
+ *
796
+ * @param vtxo - virtual output to inspect
797
+ * @returns `true` when the virtual output is swept or its wall-clock batch expiry has passed
798
+ *
799
+ * @deprecated Conflates swept with expired, and cannot evaluate height-based expiry — being
800
+ * synchronous, it has no source for the current chain tip, so it ignores `expiresAtHeight` exactly
801
+ * as it always has. For the recovery decision use {@link canRecoverOnchain}; to reproduce this
802
+ * truth condition use `v.isSwept || isPastExpiry(v, now)`.
803
+ */
804
+ declare function isExpired(vtxo: VirtualCoin): boolean;
805
+
527
806
  /**
528
807
  * Machine-readable classification of a contract's server signer relative to a
529
808
  * fresh {@link ArkInfo} snapshot. Drives both the migration selection (Section
@@ -826,7 +1105,7 @@ interface DeprecatedSignerReport {
826
1105
  * exceeds the server's per-output ceiling (`vtxoMaxAmount`) — see
827
1106
  * {@link MigrationLegReport.oversized}.
828
1107
  */
829
- type MigrationLegSkipReason = "below-dust" | "oversized-only";
1108
+ type MigrationLegSkipReason = "below-dust" | "oversized-only" | "not-spendable-only";
830
1109
  /**
831
1110
  * Why the whole pass submitted nothing, before either leg was built.
832
1111
  * `no-deprecated-vtxos` means BOTH migratable sets (VTXO and boarding) were
@@ -863,6 +1142,16 @@ interface MigrationLegReport {
863
1142
  * absent when the server advertises no ceiling (`vtxoMaxAmount < 0`).
864
1143
  */
865
1144
  oversized?: MigrationVtxoRef[];
1145
+ /**
1146
+ * Inputs the leg's submit path would have rejected — for the VTXO leg, no longer
1147
+ * cooperatively spendable at this pass's chain tip (past batch expiry, since swept and spent
1148
+ * inputs are already excluded upstream) or carrying no batch expiry at all.
1149
+ *
1150
+ * Partitioned out rather than submitted, because the send path validates the batch as a
1151
+ * whole: one rejected input would abort the entire leg and strand every other migratable
1152
+ * VTXO until the next pass. Present only when non-empty.
1153
+ */
1154
+ notSpendableOffchain?: MigrationVtxoRef[];
866
1155
  /** Error message when this leg's submission failed; the other leg still runs. */
867
1156
  error?: string;
868
1157
  }
@@ -905,7 +1194,7 @@ interface DeprecatedSignerMigrationReport {
905
1194
  * - **Expiry monitoring**: Check for virtual outputs that are expiring soon
906
1195
  *
907
1196
  * Virtual outputs become recoverable when:
908
- * - The Arkade server sweeps them (virtualStatus.state === "swept") and they remain spendable
1197
+ * - The Arkade server sweeps them (`isSwept`) and they remain spendable
909
1198
  * - They are preconfirmed subdust (to consolidate small amounts without locking liquidity on settled virtual outputs)
910
1199
  *
911
1200
  * @example
@@ -1092,7 +1381,15 @@ declare class VtxoManager implements AsyncDisposable, IVtxoManager {
1092
1381
  * }
1093
1382
  * ```
1094
1383
  */
1095
- getExpiringVtxos(thresholdMs?: number): Promise<ExtendedVirtualCoin[]>;
1384
+ getExpiringVtxos(thresholdMs?: number): Promise<NormalizedExtendedVirtualCoin[]>;
1385
+ /**
1386
+ * {@link getExpiringVtxos}, against a caller-supplied chain tip.
1387
+ *
1388
+ * The settle paths select, re-select after pre-flight, and sort by expiry within one pass;
1389
+ * each of those judges expiry, so they share one tip rather than fetching (and possibly
1390
+ * disagreeing on) one apiece. Fetches its own when `now` is omitted.
1391
+ */
1392
+ private selectExpiringVtxos;
1096
1393
  /**
1097
1394
  * Renew expiring virtual outputs by settling them back to the wallet's address
1098
1395
  *
@@ -1381,183 +1678,6 @@ declare class VtxoManager implements AsyncDisposable, IVtxoManager {
1381
1678
  [Symbol.asyncDispose](): Promise<void>;
1382
1679
  }
1383
1680
 
1384
- type NetworkName = "bitcoin" | "testnet" | "signet" | "mutinynet" | "regtest";
1385
- interface Network {
1386
- hrp: string;
1387
- bech32: string;
1388
- pubKeyHash: number;
1389
- scriptHash: number;
1390
- wif: number;
1391
- }
1392
- declare const getNetwork: (network: NetworkName) => Network;
1393
- declare const networks: {
1394
- bitcoin: Network;
1395
- testnet: Network;
1396
- signet: Network;
1397
- mutinynet: Network;
1398
- regtest: Network;
1399
- };
1400
-
1401
- /**
1402
- * The default base URLs for esplora API providers.
1403
- *
1404
- * Mainnet, mutinynet, and signet point at Ark Labs–operated
1405
- * mempool deployments (mempool.space-compatible esplora API).
1406
- * Testnet falls back to the public mempool.space deployment
1407
- * because Ark doesn't host it. Regtest assumes a local arkade-regtest
1408
- * stack exposing mempool's esplora API on the standard port.
1409
- */
1410
- declare const ESPLORA_URL: Record<NetworkName, string>;
1411
- type ExplorerTransaction = {
1412
- txid: string;
1413
- /**
1414
- * Inputs as returned by Esplora's `/address/:addr/txs`, each carrying the
1415
- * outpoint it spends (`txid:vout`). Optional: not every provider populates
1416
- * it (the electrum provider omits inputs), so consumers that correlate
1417
- * spenders must tolerate its absence. Used to recover a boarding output's
1418
- * spending (commitment) tx when `/outspends` omits the spender txid.
1419
- */
1420
- vin?: {
1421
- txid: string;
1422
- vout: number;
1423
- }[];
1424
- vout: {
1425
- scriptpubkey_address: string;
1426
- value: string;
1427
- }[];
1428
- status: {
1429
- confirmed: boolean;
1430
- block_time: number;
1431
- };
1432
- };
1433
- interface OnchainProvider {
1434
- /**
1435
- * Fetch spendable onchain outputs for an address.
1436
- *
1437
- * @param address - Bitcoin address to query
1438
- * @returns Spendable onchain outputs for the address
1439
- * @see Coin
1440
- */
1441
- getCoins(address: string): Promise<Coin[]>;
1442
- /**
1443
- * Fetch the current fastest fee rate estimate.
1444
- *
1445
- * @returns Fee rate in sats/vB, if available
1446
- * @remarks
1447
- * Implementations may return `undefined` when the backing service does not expose
1448
- * a usable fee estimate.
1449
- */
1450
- getFeeRate(): Promise<number | undefined>;
1451
- /**
1452
- * Broadcast a single transaction or a 1P1C package.
1453
- *
1454
- * @param txs - One or more raw transaction hex strings
1455
- * @returns Broadcast transaction id
1456
- * @throws Error if the broadcast request fails or the package shape is invalid
1457
- */
1458
- broadcastTransaction(...txs: string[]): Promise<string>;
1459
- /**
1460
- * Fetch outspend information for every output in a transaction.
1461
- *
1462
- * @param txid - Transaction id to inspect
1463
- * @returns Per-output spend status information. `txid` (the spender) may be
1464
- * absent even when `spent` is true: some Esplora deployments
1465
- * (e.g. mempool.arkade.sh) omit it from `/outspends`.
1466
- * @see getTxStatus
1467
- */
1468
- getTxOutspends(txid: string): Promise<{
1469
- spent: boolean;
1470
- txid?: string;
1471
- }[]>;
1472
- /**
1473
- * Fetch transactions associated with an address.
1474
- *
1475
- * @param address - Bitcoin address to query
1476
- * @returns Transactions involving the address
1477
- * @see ExplorerTransaction
1478
- */
1479
- getTransactions(address: string): Promise<ExplorerTransaction[]>;
1480
- /**
1481
- * Fetch confirmation status for a transaction.
1482
- *
1483
- * @param txid - Transaction id to inspect
1484
- * @returns Confirmation status and block metadata when confirmed
1485
- * @see getTxOutspends
1486
- */
1487
- getTxStatus(txid: string): Promise<{
1488
- confirmed: false;
1489
- } | {
1490
- confirmed: true;
1491
- blockTime: number;
1492
- blockHeight: number;
1493
- }>;
1494
- /**
1495
- * Fetch the current chain tip.
1496
- *
1497
- * @returns Current chain height, block time, and block hash
1498
- */
1499
- getChainTip(): Promise<{
1500
- height: number;
1501
- time: number;
1502
- hash: string;
1503
- }>;
1504
- /**
1505
- * Watch a set of addresses and invoke the callback when transactions are observed.
1506
- *
1507
- * @param addresses - Addresses to monitor
1508
- * @param eventCallback - Callback invoked when matching transactions are seen
1509
- * @returns Stop function that cancels the watch
1510
- * @remarks
1511
- * Implementations may use websockets, server-sent events, polling, or a hybrid strategy.
1512
- * @see getTransactions
1513
- */
1514
- watchAddresses(addresses: string[], eventCallback: (txs: ExplorerTransaction[]) => void): Promise<() => void>;
1515
- }
1516
- /**
1517
- * Implementation of the onchain provider interface for esplora REST API.
1518
- *
1519
- * @see https://mempool.space/docs/api/rest
1520
- * @example
1521
- * ```typescript
1522
- * const provider = new EsploraProvider("https://mempool.space/api");
1523
- * const outputs = await provider.getCoins("bcrt1q679zsd45msawvr7782r0twvmukns3drlstjt77");
1524
- * ```
1525
- */
1526
- declare class EsploraProvider implements OnchainProvider {
1527
- private baseUrl;
1528
- readonly pollingInterval: number;
1529
- readonly forcePolling: boolean;
1530
- constructor(baseUrl?: string, opts?: {
1531
- /** Polling interval in milliseconds. */
1532
- pollingInterval?: number;
1533
- /** Force polling even when websocket transport is available. */
1534
- forcePolling?: boolean;
1535
- });
1536
- getCoins(address: string): Promise<Coin[]>;
1537
- getFeeRate(): Promise<number | undefined>;
1538
- broadcastTransaction(...txs: string[]): Promise<string>;
1539
- getTxOutspends(txid: string): Promise<{
1540
- spent: boolean;
1541
- txid?: string;
1542
- }[]>;
1543
- getTransactions(address: string): Promise<ExplorerTransaction[]>;
1544
- getTxStatus(txid: string): Promise<{
1545
- confirmed: false;
1546
- } | {
1547
- confirmed: true;
1548
- blockTime: number;
1549
- blockHeight: number;
1550
- }>;
1551
- watchAddresses(addresses: string[], callback: (txs: ExplorerTransaction[]) => void): Promise<() => void>;
1552
- getChainTip(): Promise<{
1553
- height: number;
1554
- time: number;
1555
- hash: string;
1556
- }>;
1557
- private broadcastPackage;
1558
- private broadcastTx;
1559
- }
1560
-
1561
1681
  interface WalletState {
1562
1682
  /** Arbitrary stored wallet settings. */
1563
1683
  settings?: Record<string, any>;
@@ -1792,24 +1912,43 @@ type RefreshVtxosOptions = {
1792
1912
  includeInactive?: boolean;
1793
1913
  };
1794
1914
  /**
1795
- * A single `Discoverable` handler's `discoverAt` rejection, captured during
1796
- * a {@link IContractManager.scanContracts} run instead of aborting the loop.
1915
+ * A single `Discoverable` handler's discovery failure, captured during a
1916
+ * {@link IContractManager.scanContracts} run instead of aborting the loop.
1917
+ *
1918
+ * TODO(next major): rename `index` → `fromIndex` so the pair reads
1919
+ * `fromIndex`/`toIndex`. It stays `index` here only to keep this exported
1920
+ * shape backward-compatible.
1797
1921
  */
1798
1922
  interface HandlerError {
1799
1923
  handler: string;
1924
+ /** The failed index, or the first index of a failed `discoverRange` window. */
1800
1925
  index: number;
1926
+ /** Inclusive end of a failed `discoverRange` window; absent for a single index. */
1927
+ toIndex?: number;
1801
1928
  error: unknown;
1802
1929
  }
1803
1930
  /**
1804
1931
  * Outcome of a {@link IContractManager.scanContracts} run.
1805
- *
1806
- * `lastIndexUsed` is the highest HD index at which any handler discovered a
1807
- * contract (`-1` if nothing was found). `handlerErrors` collects per-handler
1808
- * `discoverAt` failures — non-empty means the gap window may have closed
1809
- * early and the caller should surface this (the scan itself still resolved).
1810
1932
  */
1811
1933
  interface ScanResult {
1934
+ /** @deprecated Alias of {@link ScanResult.highestConfirmedUsedIndex}. */
1812
1935
  lastIndexUsed: number;
1936
+ /**
1937
+ * Highest HD index at which any handler confirmed a contract (`-1` if none),
1938
+ * including hits past {@link ScanResult.truncatedAt}. Safe to record
1939
+ * unconditionally: the HD watermark it feeds is a monotonic max over a scan
1940
+ * that always restarts at 0, so it cannot skip an index — while withholding
1941
+ * it risks re-issuing a funded index as a fresh receive address.
1942
+ */
1943
+ highestConfirmedUsedIndex: number;
1944
+ /**
1945
+ * First index a handler failed at, making it *indeterminate* — neither a hit
1946
+ * nor a confirmed miss. The scan stops there, so indices `>= truncatedAt` are
1947
+ * unverified and the caller must retry (scanning is idempotent). `undefined`
1948
+ * when the scan closed a genuine gap.
1949
+ */
1950
+ truncatedAt?: number;
1951
+ /** Per-handler discovery failures. Non-empty implies `truncatedAt` is set. */
1813
1952
  handlerErrors: HandlerError[];
1814
1953
  }
1815
1954
  /**
@@ -1819,11 +1958,13 @@ interface ScanContractsOptions {
1819
1958
  /** Default 20. A non-positive / non-integer value throws. */
1820
1959
  gapLimit?: number;
1821
1960
  /**
1822
- * Number of HD indices probed concurrently per window (default
1823
- * {@link DEFAULT_SCAN_BATCH}). Pure latency knob: the gap loop stays
1824
- * gap-limit bounded and the discovered set is identical regardless of
1825
- * batch size. A non-positive / non-integer value throws. Ignored when
1826
- * `hd` is false (the static pass probes only index 0).
1961
+ * Number of HD indices probed per window (default
1962
+ * {@link DEFAULT_SCAN_BATCH}). The gap loop stays gap-limit bounded and
1963
+ * the discovered set is identical regardless of batch size; the window is
1964
+ * also the unit a batching handler ({@link Discoverable.discoverRange})
1965
+ * collapses into one request, so it doubles as the batch width. A
1966
+ * non-positive / non-integer value throws. Ignored when `hd` is false (the
1967
+ * static pass probes only index 0).
1827
1968
  */
1828
1969
  batchSize?: number;
1829
1970
  /** HD mode → unbounded gap loop guided by the gap counter; false → probe only index 0 (single static pass). */
@@ -1895,7 +2036,7 @@ interface IContractManager extends Disposable {
1895
2036
  * in wallet/handler code, and keeps the wallet from silently stamping the
1896
2037
  * default tapscript onto a non-default vtxo.
1897
2038
  */
1898
- annotateVtxos(vtxos: VirtualCoin[]): Promise<ExtendedVirtualCoin[]>;
2039
+ annotateVtxos(vtxos: VirtualCoin[]): Promise<NormalizedExtendedVirtualCoin[]>;
1899
2040
  /**
1900
2041
  * Update mutable contract fields.
1901
2042
  *
@@ -1960,16 +2101,19 @@ interface IContractManager extends Disposable {
1960
2101
  * resets the gap counter, so swap discovery keeps the HD window open.
1961
2102
  *
1962
2103
  * Error contract (safety-critical — see spec §4):
1963
- * - A handler's `discoverAt` rejecting is **collected** into
1964
- * `handlerErrors` and the loop **continues**; it never aborts the
1965
- * scan or throws.
2104
+ * - A handler's discovery rejecting is **collected** into `handlerErrors`
2105
+ * and makes its index *indeterminate*: it never advances the gap
2106
+ * counter, and the scan **stops verifying** there rather than closing a
2107
+ * window it never observed close. A batched
2108
+ * {@link Discoverable.discoverRange} failure makes its whole requested
2109
+ * range indeterminate. It still never throws.
1966
2110
  * - A fatal operational error — `materialize()` throwing, or
1967
2111
  * `createContract` rejecting — **propagates** out of `scanContracts`
1968
2112
  * (it invalidates the gap-window signal, so a silent truncation
1969
2113
  * would risk hiding user funds).
1970
2114
  *
1971
2115
  * @param opts See {@link ScanContractsOptions}.
1972
- * @returns `{ lastIndexUsed, handlerErrors }` — the caller surfaces
2116
+ * @returns See {@link ScanResult}. The caller surfaces `truncatedAt` /
1973
2117
  * `handlerErrors` *after* the inline VTXO pull.
1974
2118
  */
1975
2119
  scanContracts(opts: ScanContractsOptions): Promise<ScanResult>;
@@ -2182,26 +2326,41 @@ declare class ContractManager implements IContractManager {
2182
2326
  * Safety-critical invariants (spec §2.C / §4):
2183
2327
  * - `opts.materialize(i)` throwing is structural/fatal: it is NOT
2184
2328
  * wrapped — it propagates and aborts the scan.
2185
- * - A `discoverAt` rejection is collected into `handlerErrors` and the
2186
- * loop continues (the gap counter still advances for that index if no
2187
- * other handler hit it).
2329
+ * - A discovery rejection is collected into `handlerErrors` and makes its
2330
+ * index *indeterminate*: it does NOT advance the gap counter, and the
2331
+ * scan stops verifying there, reporting `truncatedAt`. Only an index
2332
+ * every handler answered for can be a confirmed miss.
2188
2333
  * - `persistAndWatchContract` rejecting is operational/fatal and
2189
- * propagates (only `discoverAt` is guarded).
2190
- * - Within an index the handler probes run concurrently (independent
2191
- * network reads); their hits are persisted sequentially in
2192
- * `discoverables` order to preserve the first-wins collision tie-break.
2193
- * - Indices are probed `batchSize` at a time (a second concurrency layer
2194
- * over the per-index probes), but each window is CAPPED to
2334
+ * propagates (only the handler calls are guarded).
2335
+ * - A handler exposing {@link Discoverable.discoverRange} is asked for the
2336
+ * whole window in ONE call instead of one per index — the batching that
2337
+ * keeps a large restore from bursting into an operator's rate limiter.
2338
+ * Its failures are therefore range-wide: every index in the window goes
2339
+ * indeterminate and truncation lands on the window's first index. Its
2340
+ * answer must cover every requested index; an incomplete map is treated
2341
+ * as a rejection (see `Discoverable.discoverRange`).
2342
+ * - Handlers are probed concurrently (independent network reads); their
2343
+ * hits are persisted sequentially in `discoverables` order to preserve
2344
+ * the first-wins collision tie-break.
2345
+ * - Indices are probed `batchSize` at a time, but each window is CAPPED to
2195
2346
  * `gapLimit - unused` indices — the most a serial scan could still reach
2196
2347
  * before the gap window is guaranteed to close. So every index probed in
2197
2348
  * a window is one a one-index-at-a-time scan would also reach: nothing is
2198
- * over-scanned, nothing is discarded, and `materialize`/`discoverAt` are
2349
+ * over-scanned, nothing is discarded, and `materialize`/discovery are
2199
2350
  * invoked on exactly the same index set. The window's hits are still
2200
2351
  * processed strictly in ascending index order, so the discovered set,
2201
- * persisted rows, `lastIndexUsed`, and `handlerErrors` are byte-for-byte
2202
- * identical to the serial path — only the wall-clock differs.
2352
+ * persisted rows, `highestConfirmedUsedIndex`, and `handlerErrors` are
2353
+ * byte-for-byte identical to the serial path — only the wall-clock
2354
+ * differs. Truncation is the one exception: a window's concurrent probes
2355
+ * can surface hits above the failed index that a serial scan would never
2356
+ * have reached, so a truncated batched scan discovers a superset — never
2357
+ * a subset — of the serial one.
2358
+ * - The whole scan runs inside one coalesced subscription scope, so N
2359
+ * discovered contracts cost ONE `subscribeForScripts` instead of N
2360
+ * growing ones (see {@link ContractWatcher.withCoalescedSubscription}).
2203
2361
  */
2204
2362
  scanContracts(opts: ScanContractsOptions): Promise<ScanResult>;
2363
+ private runScan;
2205
2364
  /**
2206
2365
  * Get contracts with optional filters.
2207
2366
  *
@@ -2219,7 +2378,7 @@ declare class ContractManager implements IContractManager {
2219
2378
  */
2220
2379
  getContracts(filter?: GetContractsFilter): Promise<Contract[]>;
2221
2380
  getContractsWithVtxos(filter?: GetContractsFilter, pageSize?: number): Promise<ContractWithVtxos[]>;
2222
- annotateVtxos(vtxos: VirtualCoin[]): Promise<ExtendedVirtualCoin[]>;
2381
+ annotateVtxos(vtxos: VirtualCoin[]): Promise<NormalizedExtendedVirtualCoin[]>;
2223
2382
  private buildContractsDbFilter;
2224
2383
  /**
2225
2384
  * Update a contract.
@@ -2438,7 +2597,7 @@ type ContractVtxo = VirtualCoin & Partial<TapLeaves & EncodedVtxoScript> & {
2438
2597
  * should enforce that annotation has happened — e.g. `saveVtxos` and
2439
2598
  * forfeit transaction construction.
2440
2599
  */
2441
- type ExtendedContractVtxo = ExtendedVirtualCoin & {
2600
+ type ExtendedContractVtxo = NormalizedExtendedVirtualCoin & {
2442
2601
  contractScript: string;
2443
2602
  };
2444
2603
  /**
@@ -2631,6 +2790,27 @@ interface DiscoveryDeps {
2631
2790
  */
2632
2791
  interface Discoverable {
2633
2792
  discoverAt(index: number, descriptor: string, deps: DiscoveryDeps): Promise<DiscoveredContract[]>;
2793
+ /**
2794
+ * Optional: answer for a whole scan window in one batched round-trip.
2795
+ * The scanner prefers it over per-index `discoverAt` calls when present,
2796
+ * which is what keeps a 10-index window to 1-2 indexer requests instead
2797
+ * of one per index. Handlers whose source is inherently per-address (e.g.
2798
+ * boarding, on Esplora) implement only `discoverAt`.
2799
+ *
2800
+ * **All-or-nothing per call.** Either resolve with a map covering *every*
2801
+ * requested index (empty array = confirmed miss), or reject — a handler
2802
+ * whose inner chunk fails partway must discard the partial results and
2803
+ * reject, because a missing index would otherwise read as "no funds here"
2804
+ * and let restore close its gap window on a failed request. The scanner
2805
+ * enforces this rather than trusting it: an incomplete map is treated as a
2806
+ * rejection, making the whole requested range indeterminate (hits present
2807
+ * in it are still persisted) and truncating the scan at the range's first
2808
+ * index. Indices that were not requested are ignored.
2809
+ */
2810
+ discoverRange?(entries: readonly {
2811
+ index: number;
2812
+ descriptor: string;
2813
+ }[], deps: DiscoveryDeps): Promise<Map<number, DiscoveredContract[]>>;
2634
2814
  }
2635
2815
  /** Duck-typed guard (mirrors `hasReceiveRotatorFactory`). */
2636
2816
  declare function isDiscoverable(handler: ContractHandler<unknown> | undefined): handler is ContractHandler<unknown> & Discoverable;
@@ -2792,6 +2972,9 @@ declare class ContractWatcher {
2792
2972
  private reconnectAttempts;
2793
2973
  private reconnectTimeoutId?;
2794
2974
  private failsafePollIntervalId?;
2975
+ /** See {@link withCoalescedSubscription}. */
2976
+ private subscriptionBatchDepth;
2977
+ private subscriptionUpdateDeferred;
2795
2978
  /**
2796
2979
  * Create a contract watcher with the given providers and polling settings.
2797
2980
  *
@@ -2886,6 +3069,24 @@ declare class ContractWatcher {
2886
3069
  * Poll specific contracts and emit events for changes.
2887
3070
  */
2888
3071
  private pollContracts;
3072
+ /**
3073
+ * Run `fn` with subscription updates coalesced into a single
3074
+ * `subscribeForScripts` on the way out.
3075
+ *
3076
+ * {@link addContract} re-subscribes eagerly (the watcher may already be
3077
+ * running), and every subscribe posts the *whole* accumulated script list —
3078
+ * so a restore scan that discovers N contracts sends N growing POSTs,
3079
+ * quadratic in script-slots. Inside this scope those updates are only
3080
+ * marked dirty, flushed once on the way out (success and error path alike).
3081
+ *
3082
+ * A contract added inside the scope is therefore not streaming until the
3083
+ * flush. Nothing in the watcher closes that window — the failsafe poll
3084
+ * replays repository state and cannot see VTXOs no one has fetched yet. The
3085
+ * one caller, `scanContracts`, is covered because `Wallet.restore` follows
3086
+ * it with a bulk `refreshVtxos`. A new caller must provide its own
3087
+ * equivalent catch-up, or keep the scope short enough not to need one.
3088
+ */
3089
+ withCoalescedSubscription<T>(fn: () => Promise<T>): Promise<T>;
2889
3090
  private tryUpdateSubscription;
2890
3091
  /**
2891
3092
  * Update the subscription with scripts that should be watched.
@@ -2899,6 +3100,10 @@ declare class ContractWatcher {
2899
3100
  private listenLoop;
2900
3101
  /**
2901
3102
  * Handle a subscription update.
3103
+ *
3104
+ * Normalization boundary: `getSubscription` is part of the public `IndexerProvider` interface,
3105
+ * so a consumer implementation may yield legacy-shaped VTXOs. Normalizing on ingest also fixes
3106
+ * the shape of the payloads emitted to external event consumers.
2902
3107
  */
2903
3108
  private handleSubscriptionUpdate;
2904
3109
  /**
@@ -3676,7 +3881,13 @@ interface BurnParams {
3676
3881
  * @see Output
3677
3882
  */
3678
3883
  interface SettleParams {
3679
- /** Offchain virtual outputs and/or onchain boarding inputs to settle. */
3884
+ /**
3885
+ * Offchain virtual outputs and/or onchain boarding inputs to settle.
3886
+ *
3887
+ * @remarks
3888
+ * Arknotes are settled by passing the `ArkNote` itself (it is an `ExtendedCoin`), not its
3889
+ * string form — `ArkNote.fromString(note)`.
3890
+ */
3680
3891
  inputs: ExtendedCoin[];
3681
3892
  /** Optional onchain outputs to create (i.e., exit to). */
3682
3893
  outputs: Output[];
@@ -3706,7 +3917,13 @@ interface Status {
3706
3917
  block_time?: number;
3707
3918
  }
3708
3919
  /**
3709
- * Virtual output status
3920
+ * Virtual output status.
3921
+ *
3922
+ * @deprecated Use the canonical facts on {@link VirtualCoin} — `isSwept`, `isPreconfirmed`,
3923
+ * `isSpent`, `expiresAt`, `expiresAtHeight`, `commitmentTxIds`, `spentBy`, `settledBy` — and the
3924
+ * capability predicates {@link canSpendOffchain}, {@link canRecoverOnchain},
3925
+ * {@link hasTerminalSpend}, {@link isPastExpiry}. `state` collapses independent facts into one
3926
+ * lossy label; this object is retained only as a backward-compatible projection.
3710
3927
  */
3711
3928
  interface VirtualStatus {
3712
3929
  /**
@@ -3717,25 +3934,24 @@ interface VirtualStatus {
3717
3934
  * - `swept`: expired/swept and recoverable in a new batch
3718
3935
  * - `spent`: destroyed by a later transaction
3719
3936
  *
3720
- * @remarks
3721
- * `state` is the high-level lifecycle summary used throughout wallet balance,
3722
- * recovery, and transaction history logic.
3937
+ * @deprecated Lossy: the states are not orthogonal and collapse with precedence
3938
+ * `spent` > `swept` > `preconfirmed` > `settled`, so a spent VTXO that was also swept reports
3939
+ * only `spent`. Read `isSpent`/`isSwept`/`isPreconfirmed` instead, or a capability predicate.
3723
3940
  */
3724
3941
  state: "preconfirmed" | "settled" | "swept" | "spent";
3725
3942
  /**
3726
3943
  * Which batch commitment transaction(s) this virtual output depends on.
3727
3944
  *
3728
- * @remarks
3729
- * The history builder uses these ids to group received batch transactions and
3730
- * relate refreshed or forfeited virtual outputs back to the same batch.
3945
+ * @deprecated Use {@link VirtualCoin.commitmentTxIds}.
3731
3946
  */
3732
3947
  commitmentTxIds?: string[];
3733
3948
  /**
3734
- * The earliest point at which this virtual output stops being safely preconfirmed.
3949
+ * The earliest point at which this virtual output stops being safely preconfirmed,
3950
+ * in milliseconds.
3735
3951
  *
3736
- * @remarks
3737
- * The value is stored in milliseconds in the wallet model and is used by expiry
3738
- * and recovery logic to decide when a virtual output can be swept or renewed.
3952
+ * @deprecated Unit-ambiguous: the server returns a single scalar that is either unix seconds or
3953
+ * a block height, and both land here multiplied by 1000. Use {@link VirtualCoin.expiresAt} and
3954
+ * {@link VirtualCoin.expiresAtHeight}, which disambiguate the two.
3739
3955
  */
3740
3956
  batchExpiry?: number;
3741
3957
  }
@@ -3760,8 +3976,16 @@ interface Coin extends Outpoint {
3760
3976
  /**
3761
3977
  * Virtual output data.
3762
3978
  *
3979
+ * @remarks
3980
+ * The canonical facts (`isSwept`, `isPreconfirmed`, `isSpent`, `expiresAt`, `expiresAtHeight`,
3981
+ * `commitmentTxIds`) are optional because `VirtualCoin` is also a *construction* type: custom
3982
+ * {@link IndexerProvider} and {@link WalletRepository} implementations may hand back coins without
3983
+ * them. The SDK normalizes every incoming coin, so coins it returns always carry the facts that are
3984
+ * determinable; do not read these fields off a coin the SDK has not returned to you — use
3985
+ * {@link canSpendOffchain} / {@link canRecoverOnchain} / {@link hasTerminalSpend} /
3986
+ * {@link isPastExpiry}, which normalize defensively.
3987
+ *
3763
3988
  * @see Coin
3764
- * @see VirtualStatus
3765
3989
  */
3766
3990
  interface VirtualCoin extends Coin {
3767
3991
  /** Creation time of the virtual output. */
@@ -3775,13 +3999,40 @@ interface VirtualCoin extends Coin {
3775
3999
  * This is not set to true if the virtual output is unrolled or swept, only when it's spent offchain.
3776
4000
  */
3777
4001
  isSpent?: boolean;
4002
+ /** Whether the server has swept the batch this virtual output belongs to. */
4003
+ isSwept?: boolean;
4004
+ /** Whether this virtual output is not yet finalized in a batch. */
4005
+ isPreconfirmed?: boolean;
3778
4006
  /** ID of the onchain commitment transaction that settled this output, if applicable. */
3779
4007
  settledBy?: string;
3780
- /** ID of the offchain checkpoint transaction that spent this output, if applicable. */
4008
+ /**
4009
+ * ID of the offchain checkpoint transaction that spent this output.
4010
+ *
4011
+ * @remarks
4012
+ * The empty string means "not spent by anything" — test truthiness, never presence.
4013
+ */
3781
4014
  spentBy?: string;
3782
4015
  /** ID of the offchain Arkade transaction that spent the above checkpoint output, if applicable. */
3783
4016
  arkTxId?: string;
3784
- /** Virtual output status */
4017
+ /** Batch commitment transaction(s) this virtual output depends on. */
4018
+ commitmentTxIds?: string[];
4019
+ /**
4020
+ * Wall-clock batch expiry, when the server expressed expiry as a timestamp.
4021
+ *
4022
+ * @remarks
4023
+ * Mutually exclusive with `expiresAtHeight`; both are absent when there is no expiry.
4024
+ */
4025
+ expiresAt?: Date;
4026
+ /**
4027
+ * Block-height batch expiry, when the server expressed expiry as a height (regtest-like
4028
+ * deployments). Evaluating it needs a chain tip — see {@link isPastExpiry}.
4029
+ */
4030
+ expiresAtHeight?: number;
4031
+ /**
4032
+ * Virtual output status.
4033
+ *
4034
+ * @deprecated See {@link VirtualStatus}.
4035
+ */
3785
4036
  virtualStatus: VirtualStatus;
3786
4037
  /** Assets carried by this virtual output, if any. */
3787
4038
  assets?: Asset[];
@@ -3854,42 +4105,7 @@ type ExtendedCoin = TapLeaves & EncodedVtxoScript & Coin & {
3854
4105
  type ExtendedVirtualCoin = TapLeaves & EncodedVtxoScript & VirtualCoin & {
3855
4106
  extraWitness?: Bytes[];
3856
4107
  };
3857
- /**
3858
- * Return whether a virtual output is still spendable.
3859
- *
3860
- * @param vtxo - virtual output to inspect
3861
- * @returns `true` when the virtual output is not marked as spent
3862
- *
3863
- * @see isRecoverable
3864
- * @see isExpired
3865
- */
3866
- declare function isSpendable(vtxo: VirtualCoin): boolean;
3867
- /**
3868
- * Return whether a virtual output is recoverable.
3869
- *
3870
- * @param vtxo - virtual output to inspect
3871
- * @returns `true` when the virtual output is swept but still spendable
3872
- *
3873
- * @remarks
3874
- * Recoverable virtual outputs are typically re-settled into fresh virtual outputs by the virtual output manager.
3875
- *
3876
- * @see isSpendable
3877
- * @see isExpired
3878
- */
3879
- declare function isRecoverable(vtxo: VirtualCoin): boolean;
3880
- /**
3881
- * Return whether a virtual output should be treated as expired.
3882
- *
3883
- * @param vtxo - virtual output to inspect
3884
- * @returns `true` when the virtual output is swept or its batch expiry has passed
3885
- * @remarks
3886
- * On regtest-like environments the upstream expiry value may be expressed as a block
3887
- * height instead of a timestamp. This helper intentionally ignores obviously non-time
3888
- * values to avoid false positives.
3889
- *
3890
- * @see VirtualStatus.batchExpiry
3891
- */
3892
- declare function isExpired(vtxo: VirtualCoin): boolean;
4108
+
3893
4109
  /**
3894
4110
  * Return whether a virtual output is below the dust threshold.
3895
4111
  *
@@ -4031,10 +4247,11 @@ interface IReadonlyWallet {
4031
4247
  * Get virtual outputs tracked by the wallet.
4032
4248
  *
4033
4249
  * @param filter - Optional filtering flags
4034
- * @returns virtual outputs with tapscript and witness data
4250
+ * @returns virtual outputs with tapscript and witness data, normalized: every canonical fact
4251
+ * the capability predicates read is populated, whatever the underlying repository stored
4035
4252
  * @see GetVtxosFilter
4036
4253
  */
4037
- getVtxos(filter?: GetVtxosFilter): Promise<ExtendedVirtualCoin[]>;
4254
+ getVtxos(filter?: GetVtxosFilter): Promise<NormalizedExtendedVirtualCoin[]>;
4038
4255
  /** @returns Onchain boarding inputs tracked by the wallet. */
4039
4256
  getBoardingUtxos(): Promise<ExtendedCoin[]>;
4040
4257
  /** @returns Wallet transaction history derived from boarding and Arkade activity. */
@@ -4810,4 +5027,4 @@ declare namespace ProtoTypes {
4810
5027
  export { };
4811
5028
  }
4812
5029
 
4813
- export { type Network as $, type ArkTransaction as A, type VirtualTxRepository as B, type ContractRepository as C, type VirtualTx as D, type ExtendedVirtualCoin as E, type VtxoBranch as F, type GetVtxosFilter as G, ChainedTxType as H, type IWallet as I, type BatchStartedEvent as J, type TreeSigningStartedEvent as K, TxTree as L, type TreeNoncesEvent as M, type BatchFinalizationEvent as N, type Outpoint as O, type BatchFinalizedEvent as P, type BatchFailedEvent as Q, type Recipient as R, type SendBitcoinParams as S, type TxNotification as T, type TreeTxEvent as U, VtxoScript as V, type WalletRepository as W, type TreeSignatureEvent as X, type DescriptorProvider as Y, type IReadonlyWallet as Z, type ReadonlyIdentity as _, type Identity as a, ConditionCSVMultisigTapscript as a$, type OnchainProvider as a0, type DelegateProvider as a1, type ReadonlyWalletConfig as a2, type ExitCaptureMode as a3, type ExitDataSource as a4, type IReadonlyAssetManager as a5, type ContractSyncState as a6, type NetworkName as a7, type ArkInfo as a8, ArkAddress as a9, type ContractWithVtxos as aA, type PathSelection as aB, type ContractEvent as aC, type AssetDetails as aD, type IssuanceResult as aE, type DelegateInfo as aF, type MigrationGlobalSkipReason as aG, type MigrationLegSkipReason as aH, type SignerStatus as aI, type StorageConfig as aJ, type IVtxoManager as aK, type ExplorerTransaction as aL, type EncodedVtxoScript as aM, type Status as aN, type ChainTx as aO, type PathContext as aP, type ActivityIntent as aQ, type ActivityResolver as aR, type ArkIntentState as aS, type ArkTapscript as aT, type AssetMetadata as aU, type BaseWalletConfig as aV, type BatchInfo as aW, type BatchSignableIdentity as aX, CLTVMultisigTapscript as aY, ChainTxType as aZ, type CommitmentTx as a_, type Coin as aa, ContractManager as ab, CSVMultisigTapscript as ac, type SettlementConfig as ad, VtxoManager as ae, type SignerSession as af, type SignedIntent as ag, Intent as ah, type DescriptorSigningRequest as ai, Transaction as aj, type IntentFeeConfig as ak, type OffchainInput as al, FeeAmount as am, type OnchainInput as an, type FeeOutput as ao, type ContractWatcherConfig as ap, type Asset as aq, type FeeInfo as ar, type CreateContractParams as as, type GetContractsFilter as at, type GetSpendablePathsOptions as au, type GetAllSpendingPathsOptions as av, type IssuanceParams as aw, type ReissuanceParams as ax, type BurnParams as ay, type RenewVtxosOptions as az, type WalletConfig as b, type WalletMode as b$, ConditionMultisigTapscript as b0, type ContractBalance as b1, type ContractEventCallback as b2, type ContractHandler as b3, type ContractManagerConfig as b4, type ContractState as b5, type ContractVtxo as b6, ContractWatcher as b7, DelegateManagerImpl as b8, type DelegateOptions as b9, PartialSig as bA, type ProviderClass as bB, RestDelegateProvider as bC, RestDelegatorProvider as bD, type ScanContractsOptions as bE, type ScanResult as bF, type ScheduledSession as bG, SettlementEventType as bH, type SignRequest as bI, type SignerClassification as bJ, type SignerSet as bK, type SubscriptionEvent as bL, type SubscriptionHeartbeat as bM, type TapLeaves as bN, TapTreeCoder as bO, TapscriptType as bP, type TreeNonces as bQ, type TreePartialSigs as bR, type Tx as bS, type TxHistoryRecord as bT, type TxKey as bU, type TxTreeNode as bV, TxType as bW, type VirtualStatus as bX, type Vtxo as bY, type VtxoChain as bZ, type VtxoType as b_, DelegatorManagerImpl as ba, type DelegatorProvider as bb, type DeprecatedSignerMigrationReport as bc, type DeprecatedSignerReport as bd, DigestMismatchError as be, type Discoverable as bf, type DiscoveredContract as bg, type DiscoveryDeps as bh, ESPLORA_URL as bi, EsploraProvider as bj, type ExitChainResolver as bk, type ExtendedContractVtxo as bl, type GroupMembership as bm, type HandlerError as bn, type IDelegatorManager as bo, INTENT_TERMINAL_STATES as bp, IndexerTxType as bq, type KnownMetadata as br, type MigrateDeprecatedSignerOptions as bs, type MigrationLegReport as bt, type MigrationVtxoRef as bu, MultisigTapscript as bv, type Nonces as bw, type Output as bx, type PageResponse as by, type PaginationOptions as bz, type WalletBalance as c, boardingResolver as c0, classifyAgainstSignerSet as c1, classifyContractSigner as c2, createDefaultActivityRegistry as c3, createExitChainResolver as c4, decodeTapscript as c5, getNetwork as c6, getSequence as c7, isBatchSignable as c8, isCooperativelyMigratable as c9, isDiscoverable as ca, isExpired as cb, isRecoverable as cc, isSpendable as cd, isSubdust as ce, isTerminalIntentState as cf, isVtxoExpiringSoon as cg, networks as ch, signerSetFromInfo as ci, toXOnlySignerHex as cj, type TapscriptDeriving as ck, type ExtendedCoin as d, ActivityRegistry as e, type Activity as f, type IContractManager as g, type IDelegateManager as h, type SettleParams as i, type SettlementEvent as j, type IAssetManager as k, RestArkProvider as l, RestIndexerProvider as m, type SubscriptionResponse as n, type ArkProvider as o, type IndexerProvider as p, type RelativeTimelock as q, type TapLeafScript as r, type VirtualCoin as s, type Contract as t, type VtxoRepositoryKey as u, type WalletState as v, type ContractFilter as w, type IntentRepository as x, type ArkIntent as y, type IntentFilter as z };
5030
+ export { type ReadonlyIdentity as $, type ArkTransaction as A, type VirtualTxRepository as B, type ContractRepository as C, type VirtualTx as D, type ExtendedCoin as E, type VtxoBranch as F, type GetVtxosFilter as G, ChainedTxType as H, type IWallet as I, type BatchStartedEvent as J, type TreeSigningStartedEvent as K, TxTree as L, type TreeNoncesEvent as M, type NormalizedExtendedVirtualCoin as N, type Outpoint as O, type BatchFinalizationEvent as P, type BatchFinalizedEvent as Q, type Recipient as R, type SendBitcoinParams as S, type TxNotification as T, type BatchFailedEvent as U, VtxoScript as V, type WalletRepository as W, type TreeTxEvent as X, type TreeSignatureEvent as Y, type DescriptorProvider as Z, type IReadonlyWallet as _, type Identity as a, ChainTxType as a$, type Network as a0, type OnchainProvider as a1, type DelegateProvider as a2, type ReadonlyWalletConfig as a3, type ExitCaptureMode as a4, type ExitDataSource as a5, type IReadonlyAssetManager as a6, type ContractSyncState as a7, type NetworkName as a8, type ArkInfo as a9, type BurnParams as aA, type RenewVtxosOptions as aB, type ContractWithVtxos as aC, type PathSelection as aD, type ContractEvent as aE, type AssetDetails as aF, type IssuanceResult as aG, type DelegateInfo as aH, type MigrationGlobalSkipReason as aI, type MigrationLegSkipReason as aJ, type SignerStatus as aK, type StorageConfig as aL, type IVtxoManager as aM, type ExplorerTransaction as aN, type EncodedVtxoScript as aO, type Status as aP, type ChainTx as aQ, type PathContext as aR, type ActivityIntent as aS, type ActivityResolver as aT, type ArkIntentState as aU, type ArkTapscript as aV, type AssetMetadata as aW, type BaseWalletConfig as aX, type BatchInfo as aY, type BatchSignableIdentity as aZ, CLTVMultisigTapscript as a_, ArkAddress as aa, type Coin as ab, ContractManager as ac, CSVMultisigTapscript as ad, type SettlementConfig as ae, VtxoManager as af, type SignerSession as ag, type SignedIntent as ah, Intent as ai, type TimeHeight as aj, type DescriptorSigningRequest as ak, Transaction as al, type IntentFeeConfig as am, type OffchainInput as an, FeeAmount as ao, type OnchainInput as ap, type FeeOutput as aq, type ContractWatcherConfig as ar, type Asset as as, type FeeInfo as at, type CreateContractParams as au, type GetContractsFilter as av, type GetSpendablePathsOptions as aw, type GetAllSpendingPathsOptions as ax, type IssuanceParams as ay, type ReissuanceParams as az, type WalletConfig as b, type VtxoChain as b$, type CommitmentTx as b0, ConditionCSVMultisigTapscript as b1, ConditionMultisigTapscript as b2, type ContractBalance as b3, type ContractEventCallback as b4, type ContractHandler as b5, type ContractManagerConfig as b6, type ContractState as b7, type ContractVtxo as b8, ContractWatcher as b9, type PageResponse as bA, type PaginationOptions as bB, PartialSig as bC, type ProviderClass as bD, RestDelegateProvider as bE, RestDelegatorProvider as bF, type ScanContractsOptions as bG, type ScanResult as bH, type ScheduledSession as bI, SettlementEventType as bJ, type SignRequest as bK, type SignerClassification as bL, type SignerSet as bM, type SubscriptionEvent as bN, type SubscriptionHeartbeat as bO, type TapLeaves as bP, TapTreeCoder as bQ, TapscriptType as bR, type TreeNonces as bS, type TreePartialSigs as bT, type Tx as bU, type TxHistoryRecord as bV, type TxKey as bW, type TxTreeNode as bX, TxType as bY, type VirtualStatus as bZ, type Vtxo as b_, DelegateManagerImpl as ba, type DelegateOptions as bb, DelegatorManagerImpl as bc, type DelegatorProvider as bd, type DeprecatedSignerMigrationReport as be, type DeprecatedSignerReport as bf, DigestMismatchError as bg, type Discoverable as bh, type DiscoveredContract as bi, type DiscoveryDeps as bj, ESPLORA_URL as bk, EsploraProvider as bl, type ExitChainResolver as bm, type ExtendedContractVtxo as bn, type GroupMembership as bo, type HandlerError as bp, type IDelegatorManager as bq, INTENT_TERMINAL_STATES as br, IndexerTxType as bs, type KnownMetadata as bt, type MigrateDeprecatedSignerOptions as bu, type MigrationLegReport as bv, type MigrationVtxoRef as bw, MultisigTapscript as bx, type Nonces as by, type Output as bz, type WalletBalance as c, type VtxoType as c0, type WalletMode as c1, boardingResolver as c2, canRecoverOnchain as c3, canSpendOffchain as c4, classifyAgainstSignerSet as c5, classifyContractSigner as c6, createDefaultActivityRegistry as c7, createExitChainResolver as c8, decodeTapscript as c9, getNetwork as ca, getSequence as cb, hasTerminalSpend as cc, isBatchSignable as cd, isCooperativelyMigratable as ce, isDiscoverable as cf, isExpired as cg, isPastExpiry as ch, isRecoverable as ci, isSpendable as cj, isSubdust as ck, isTerminalIntentState as cl, isVirtualCoin as cm, isVtxoExpiringSoon as cn, networks as co, signerSetFromInfo as cp, toXOnlySignerHex as cq, type TapscriptDeriving as cr, ActivityRegistry as d, type Activity as e, type IContractManager as f, type IDelegateManager as g, type SettleParams as h, type SettlementEvent as i, type IAssetManager as j, RestArkProvider as k, RestIndexerProvider as l, type SubscriptionResponse as m, type ArkProvider as n, type IndexerProvider as o, type RelativeTimelock as p, type TapLeafScript as q, type VirtualCoin as r, type Contract as s, type ExtendedVirtualCoin as t, type VtxoRepositoryKey as u, type WalletState as v, type ContractFilter as w, type IntentRepository as x, type ArkIntent as y, type IntentFilter as z };