@arkade-os/sdk 0.4.58 → 0.4.60

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 (88) hide show
  1. package/README.md +34 -6
  2. package/dist/adapters/expo.cjs +5 -5
  3. package/dist/adapters/expo.d.cts +2 -2
  4. package/dist/adapters/expo.d.ts +2 -2
  5. package/dist/adapters/expo.js +3 -3
  6. package/dist/adapters/indexedDB.cjs +5 -5
  7. package/dist/adapters/indexedDB.js +4 -4
  8. package/dist/{ark-ClaVUg4e.d.cts → ark-D2_yNhfz.d.cts} +1077 -522
  9. package/dist/{ark-ClaVUg4e.d.ts → ark-D2_yNhfz.d.ts} +1077 -522
  10. package/dist/{asyncStorageTaskQueue-CnYrhpS-.d.cts → asyncStorageTaskQueue-CqijAAPG.d.cts} +1 -1
  11. package/dist/{asyncStorageTaskQueue-Bgau-mvD.d.ts → asyncStorageTaskQueue-Vsp6uk8e.d.ts} +1 -1
  12. package/dist/{chunk-IPPASJE5.js → chunk-2DINXHGQ.js} +1665 -1149
  13. package/dist/chunk-2DINXHGQ.js.map +1 -0
  14. package/dist/{chunk-SYZEQF5D.cjs → chunk-2XLNQHG4.cjs} +10 -10
  15. package/dist/chunk-2XLNQHG4.cjs.map +1 -0
  16. package/dist/{chunk-AGWZXGG2.js → chunk-3Q7BBUWO.js} +4 -4
  17. package/dist/{chunk-AGWZXGG2.js.map → chunk-3Q7BBUWO.js.map} +1 -1
  18. package/dist/{chunk-D6RGHPZ5.cjs → chunk-4A4GRSTB.cjs} +86 -13
  19. package/dist/chunk-4A4GRSTB.cjs.map +1 -0
  20. package/dist/{chunk-SGWMB4W2.js → chunk-B66RYHTB.js} +5 -5
  21. package/dist/chunk-B66RYHTB.js.map +1 -0
  22. package/dist/{chunk-TV2RLVOB.js → chunk-FSKANUV4.js} +12 -5
  23. package/dist/chunk-FSKANUV4.js.map +1 -0
  24. package/dist/{chunk-I4YEGI2R.cjs → chunk-H7JCHFM6.cjs} +17 -17
  25. package/dist/{chunk-I4YEGI2R.cjs.map → chunk-H7JCHFM6.cjs.map} +1 -1
  26. package/dist/{chunk-VROHHJL4.js → chunk-JBULXTCC.js} +77 -14
  27. package/dist/chunk-JBULXTCC.js.map +1 -0
  28. package/dist/{chunk-6PY4WYXI.cjs → chunk-KNS5MTTI.cjs} +3869 -2935
  29. package/dist/chunk-KNS5MTTI.cjs.map +1 -0
  30. package/dist/{chunk-COLJARV4.cjs → chunk-MZNLCMUP.cjs} +1686 -1169
  31. package/dist/chunk-MZNLCMUP.cjs.map +1 -0
  32. package/dist/{chunk-F4C67JYB.cjs → chunk-PRFL62ID.cjs} +22 -15
  33. package/dist/chunk-PRFL62ID.cjs.map +1 -0
  34. package/dist/{chunk-S3BFUTC5.js → chunk-Y54MUBT7.js} +3512 -2593
  35. package/dist/chunk-Y54MUBT7.js.map +1 -0
  36. package/dist/contracts/handlers/index.cjs +12 -8
  37. package/dist/contracts/handlers/index.d.cts +3 -3
  38. package/dist/contracts/handlers/index.d.ts +3 -3
  39. package/dist/contracts/handlers/index.js +2 -2
  40. package/dist/{delegate-CVrkEepn.d.cts → delegate-DNEuM_nK.d.cts} +1 -1
  41. package/dist/{delegate-YDYq1El_.d.ts → delegate-DidNTsKm.d.ts} +1 -1
  42. package/dist/{index-BmqkJBAf.d.cts → index-BP8-zniX.d.cts} +182 -26
  43. package/dist/{index-CuSHBkq7.d.ts → index-DzQbNQUG.d.ts} +182 -26
  44. package/dist/index.cjs +323 -223
  45. package/dist/index.d.cts +311 -51
  46. package/dist/index.d.ts +311 -51
  47. package/dist/index.js +4 -4
  48. package/dist/repositories/realm/index.cjs +43 -23
  49. package/dist/repositories/realm/index.cjs.map +1 -1
  50. package/dist/repositories/realm/index.d.cts +21 -9
  51. package/dist/repositories/realm/index.d.ts +21 -9
  52. package/dist/repositories/realm/index.js +29 -9
  53. package/dist/repositories/realm/index.js.map +1 -1
  54. package/dist/repositories/sqlite/index.cjs +48 -23
  55. package/dist/repositories/sqlite/index.cjs.map +1 -1
  56. package/dist/repositories/sqlite/index.d.cts +9 -3
  57. package/dist/repositories/sqlite/index.d.ts +9 -3
  58. package/dist/repositories/sqlite/index.js +35 -10
  59. package/dist/repositories/sqlite/index.js.map +1 -1
  60. package/dist/{taskRunner-CFN61bBM.d.ts → taskRunner-9gnlYEpb.d.ts} +1 -1
  61. package/dist/{taskRunner-CFkGkD85.d.cts → taskRunner-BEQNFEa4.d.cts} +1 -1
  62. package/dist/wallet/expo/background.cjs +14 -14
  63. package/dist/wallet/expo/background.d.cts +3 -3
  64. package/dist/wallet/expo/background.d.ts +3 -3
  65. package/dist/wallet/expo/background.js +6 -6
  66. package/dist/wallet/expo/index.cjs +17 -14
  67. package/dist/wallet/expo/index.cjs.map +1 -1
  68. package/dist/wallet/expo/index.d.cts +5 -4
  69. package/dist/wallet/expo/index.d.ts +5 -4
  70. package/dist/wallet/expo/index.js +8 -5
  71. package/dist/wallet/expo/index.js.map +1 -1
  72. package/dist/{wallet-hQp2qC_M.d.cts → wallet-C_MYHyOj.d.ts} +176 -17
  73. package/dist/{wallet-DnK09TtT.d.ts → wallet-ipUA1XCp.d.cts} +176 -17
  74. package/dist/worker/expo/index.cjs +9 -9
  75. package/dist/worker/expo/index.d.cts +5 -5
  76. package/dist/worker/expo/index.d.ts +5 -5
  77. package/dist/worker/expo/index.js +5 -5
  78. package/package.json +9 -2
  79. package/dist/chunk-6PY4WYXI.cjs.map +0 -1
  80. package/dist/chunk-COLJARV4.cjs.map +0 -1
  81. package/dist/chunk-D6RGHPZ5.cjs.map +0 -1
  82. package/dist/chunk-F4C67JYB.cjs.map +0 -1
  83. package/dist/chunk-IPPASJE5.js.map +0 -1
  84. package/dist/chunk-S3BFUTC5.js.map +0 -1
  85. package/dist/chunk-SGWMB4W2.js.map +0 -1
  86. package/dist/chunk-SYZEQF5D.cjs.map +0 -1
  87. package/dist/chunk-TV2RLVOB.js.map +0 -1
  88. package/dist/chunk-VROHHJL4.js.map +0 -1
@@ -532,6 +532,17 @@ interface Network {
532
532
  pubKeyHash: number;
533
533
  scriptHash: number;
534
534
  wif: number;
535
+ /**
536
+ * Canonical name this network was resolved from, when known.
537
+ *
538
+ * `bech32` cannot separate the tb-family — testnet, signet and mutinynet
539
+ * share every field above — so anything that has to tell them apart (a
540
+ * per-network timelock floor, for one) has no other handle. Optional
541
+ * because a hand-built `Network` carries no name: consumers must read a
542
+ * missing one as "unknown" and fall back to their strictest branch rather
543
+ * than guess at one.
544
+ */
545
+ name?: NetworkName;
535
546
  }
536
547
  declare const getNetwork: (network: NetworkName) => Network;
537
548
  declare const networks: {
@@ -541,6 +552,62 @@ declare const networks: {
541
552
  mutinynet: Network;
542
553
  regtest: Network;
543
554
  };
555
+ /** Covenant co-signer ("emulator") for the mainnet Arkade deployment. */
556
+ declare const BITCOIN_EMULATOR_PUBKEY: "0239c196415da47b26456a101daaa12ba9e445bfe153197f1e2b750bf40e52092e";
557
+ /** Covenant co-signer ("emulator") for the hosted mutinynet Arkade deployment. */
558
+ declare const MUTINYNET_EMULATOR_PUBKEY: "03f823b9b2febc81f4af967e77aed2f541cbd3397c6d8f5a72e32eb7b471af889a";
559
+ /** Covenant co-signer ("emulator") shipped with the `arkade-regtest` stack. */
560
+ declare const REGTEST_EMULATOR_PUBKEY: "02999413c46fa10ada5cbc4bcc79a1d09160c2ba3cfc812705d7a13e5e545fb2a9";
561
+ /**
562
+ * The pinned co-signer key for `network`, as 33-byte compressed lowercase hex.
563
+ *
564
+ * @throws if the network carries no name, or names one with no deployed
565
+ * emulator. Fail closed on both counts: the return value ends up in a
566
+ * covenant leaf that decides who can move the funds, so there is no
567
+ * defensible fallback. Guessing a neighbour's key (the tb-family share every
568
+ * other field on {@link Network}) would build a leaf co-signed by a service
569
+ * that will never sign for it — funds locked to a key nobody holds — and
570
+ * returning an empty string just moves the same failure somewhere harder to
571
+ * read. A hand-assembled `Network` carries no `name` and lands here too,
572
+ * matching how the per-network timelock floors treat an unnamed network.
573
+ *
574
+ * Deliberately NOT falling back to whatever key the emulator reports about
575
+ * itself: that is the self-report this pin exists to stop trusting, and doing
576
+ * it only on unpinned networks would make the guarantee depend on
577
+ * `network.name` with no signal to the caller that it had lapsed. The
578
+ * unnamed case is the sharp one — a hand-built `Network` can carry
579
+ * bitcoin-equivalent parameters, so a silent fallback would drop to
580
+ * "trust the endpoint" on exactly the input that looks most like mainnet.
581
+ * Callers who mean it pass {@link resolveEmulatorPubkey}'s override, which
582
+ * the thrown message names.
583
+ */
584
+ declare function defaultEmulatorPubkey(network: Network): string;
585
+ /**
586
+ * Resolve the co-signer key for `network`, letting a caller substitute its own.
587
+ *
588
+ * The override is the escape hatch for three situations, and the first is the
589
+ * one that matters operationally:
590
+ *
591
+ * 1. **A network rotated its emulator key and this SDK has not shipped the new
592
+ * constant yet.** Since `Arkade.connect` no longer asks the service which
593
+ * key it signs with, a rotation is invisible here until the constant is
594
+ * updated — covenants keep building against the retired key and fail only
595
+ * when a claim is attempted. Passing the new key restores service without
596
+ * waiting for a release, so a rotation is a config change rather than an
597
+ * outage.
598
+ * 2. A private or self-hosted emulator, including on a network with no pinned
599
+ * key at all (signet, testnet, a hand-built `Network`).
600
+ * 3. Tests and local stacks.
601
+ *
602
+ * Supplying it means co-signing with a different service: every covenant built
603
+ * from the returned key can be completed by whoever holds it and by no one
604
+ * else, so it is a statement that you trust that operator in place of the
605
+ * network's. Prefer {@link defaultEmulatorPubkey} unless that is deliberate.
606
+ *
607
+ * A malformed override throws rather than being passed through — a typo here
608
+ * would otherwise surface as an unspendable contract long after the fact.
609
+ */
610
+ declare function resolveEmulatorPubkey(network: Network, override?: string): string;
544
611
 
545
612
  /**
546
613
  * The default base URLs for esplora API providers.
@@ -903,6 +970,27 @@ declare function classifyContractSigner(contractServerPubKeyHex: string, info: A
903
970
  */
904
971
  declare function isCooperativelyMigratable(status: SignerStatus): boolean;
905
972
 
973
+ /**
974
+ * Outpoints (`txid:vout`) of VTXOs that are NOT cooperatively spendable because
975
+ * their contract's signer is past its cutoff (`EXPIRED`) and the VTXO has not
976
+ * yet been swept by the server. Such funds are unspendable until they recover
977
+ * (the server sweeps the batch at expiry, then the swept output re-settles under
978
+ * the active signer), so `getBalance` buckets them under `pendingRecovery` and
979
+ * coin selection skips them — otherwise a send would pick a VTXO the operator
980
+ * will not co-sign and fail at submit.
981
+ *
982
+ * Pure + offline: classification uses a cached {@link SignerSet}, never a fresh
983
+ * GetInfo. `MIGRATABLE` / `DUE_NOW` (still cooperatively spendable), already-
984
+ * swept (recoverable), `CURRENT`, and `UNKNOWN_SIGNER` rows are all left alone.
985
+ */
986
+ declare function selectPendingRecoveryOutpoints(contractsWithVtxos: ReadonlyArray<{
987
+ contract: {
988
+ params: {
989
+ serverPubKey?: string;
990
+ };
991
+ };
992
+ vtxos: ReadonlyArray<VirtualCoin>;
993
+ }>, signerSet: SignerSet, nowSeconds?: number): Set<string>;
906
994
  /**
907
995
  * Configuration options for automatic virtual output renewal
908
996
  *
@@ -1769,9 +1857,19 @@ interface ContractFilter {
1769
1857
  state?: ContractState | ContractState[];
1770
1858
  /** Filter by contract type(s) */
1771
1859
  type?: string | string[];
1860
+ /**
1861
+ * Filter by watch state(s). Rows written before the field existed
1862
+ * have no stored value and match `"watched"`.
1863
+ * @see ContractWatchState
1864
+ */
1865
+ watch?: ContractWatchState | ContractWatchState[];
1772
1866
  }
1773
1867
  interface ContractRepository extends AsyncDisposable {
1774
- readonly version: 1;
1868
+ /**
1869
+ * 2 — {@link Contract.watch}. An implementation must persist and
1870
+ * round-trip it, and treat a row without one as `"watched"`.
1871
+ */
1872
+ readonly version: 2;
1775
1873
  /**
1776
1874
  * Clear all data from storage.
1777
1875
  */
@@ -1905,150 +2003,521 @@ interface VirtualTxRepository extends AsyncDisposable {
1905
2003
  pruneForSpentVtxo(vtxo: Outpoint): Promise<void>;
1906
2004
  }
1907
2005
 
1908
- type RefreshVtxosOptions = {
2006
+ /** One transaction's participation in one logical action. */
2007
+ interface GroupMembership {
1909
2008
  /**
1910
- * Narrow the refresh to these scripts. A subset query, so the
1911
- * cursor is not advanced: contracts outside the list may have data
1912
- * we'd skip.
2009
+ * Stable id of the action; txs sharing it group together. Third-party
2010
+ * resolvers should namespace it (`"vendor:thing"`) to avoid colliding with
2011
+ * other resolvers' groups. SDK built-ins use namespaced ids such as `boarding:`.
2012
+ * A membership with an empty groupId is dropped.
1913
2013
  */
1914
- scripts?: string[];
2014
+ groupId: string;
2015
+ /** Human label for the action, e.g. "Dice game". */
2016
+ label?: string;
2017
+ /** App category for icon/filtering, e.g. "game". */
2018
+ kind?: string;
1915
2019
  /**
1916
- * Time window overriding the cursor-derived one. The cursor never
1917
- * advances on a windowed query because the window may skip data
1918
- * outside its bounds.
2020
+ * Free-form row data. Same-group metadata is shallow-merged with
2021
+ * earlier-resolver keys winning.
1919
2022
  */
1920
- after?: number;
1921
- /** @see after */
1922
- before?: number;
2023
+ metadata?: Record<string, unknown>;
1923
2024
  /**
1924
- * When true and `scripts` is not set, refresh every contract in
1925
- * the repository rather than the watcher's watched set which
1926
- * differs only for rows the watcher never registered, since
1927
- * retirement doesn't narrow that set
1928
- * (see {@link ContractWatcher.getWatchedContracts}).
1929
- *
1930
- * Because this is a *superset* of the watched set, the cursor
1931
- * invariant still holds and the cursor advances normally (unless
1932
- * `after` / `before` is also supplied).
1933
- *
1934
- * @defaultValue `false`
2025
+ * This tx's unsigned sat contribution to this group. Defaults to the tx's
2026
+ * full amount; the builder applies direction. Use it to split a batched tx
2027
+ * across groups. Same-key receive rows paired with a sent row are treated
2028
+ * as change and excluded from `Activity.amount`. A membership with a
2029
+ * non-finite amount (NaN/Infinity) is dropped.
1935
2030
  */
1936
- includeInactive?: boolean;
1937
- };
1938
- /**
1939
- * A single `Discoverable` handler's discovery failure, captured during a
1940
- * {@link IContractManager.scanContracts} run instead of aborting the loop.
1941
- *
1942
- * TODO(next major): rename `index` → `fromIndex` so the pair reads
1943
- * `fromIndex`/`toIndex`. It stays `index` here only to keep this exported
1944
- * shape backward-compatible.
1945
- */
1946
- interface HandlerError {
1947
- handler: string;
1948
- /** The failed index, or the first index of a failed `discoverRange` window. */
1949
- index: number;
1950
- /** Inclusive end of a failed `discoverRange` window; absent for a single index. */
1951
- toIndex?: number;
1952
- error: unknown;
2031
+ amount?: number;
1953
2032
  }
1954
- /**
1955
- * Outcome of a {@link IContractManager.scanContracts} run.
1956
- */
1957
- interface ScanResult {
1958
- /** @deprecated Alias of {@link ScanResult.highestConfirmedUsedIndex}. */
1959
- lastIndexUsed: number;
2033
+ /** A pluggable resolver keyed by `id`. */
2034
+ interface ActivityResolver {
1960
2035
  /**
1961
- * Highest HD index at which any handler confirmed a contract (`-1` if none),
1962
- * including hits past {@link ScanResult.truncatedAt}. Safe to record
1963
- * unconditionally: the HD watermark it feeds is a monotonic max over a scan
1964
- * that always restarts at 0, so it cannot skip an index — while withholding
1965
- * it risks re-issuing a funded index as a fresh receive address.
2036
+ * Registry key override or remove by it. Namespace it (`"vendor:games"`)
2037
+ * so independent libraries don't clobber each other; `use()` overwrites
2038
+ * silently on a duplicate id.
1966
2039
  */
1967
- highestConfirmedUsedIndex: number;
2040
+ id: string;
1968
2041
  /**
1969
- * First index a handler failed at, making it *indeterminate* neither a hit
1970
- * nor a confirmed miss. The scan stops there, so indices `>= truncatedAt` are
1971
- * unverified and the caller must retry (scanning is idempotent). `undefined`
1972
- * when the scan closed a genuine gap.
2042
+ * Load correlation data before `resolve` runs. If it rejects, this resolver
2043
+ * contributes no memberships.
1973
2044
  */
1974
- truncatedAt?: number;
1975
- /** Per-handler discovery failures. Non-empty implies `truncatedAt` is set. */
1976
- handlerErrors: HandlerError[];
2045
+ prepare?(): Promise<void>;
2046
+ /** Pure and synchronous. The groups this tx belongs to, or undefined to leave it plain. */
2047
+ resolve(tx: ArkTransaction): GroupMembership[] | undefined;
2048
+ }
2049
+ /** The non-id, non-amount part of a {@link GroupMembership}. */
2050
+ interface ActivityIntent {
2051
+ /** Human label for the action, e.g. "Dice game". */
2052
+ label?: string;
2053
+ /** App category for icon/filtering, e.g. "game". */
2054
+ kind?: string;
2055
+ /** Free-form row data, shallow-merged across the group's resolvers (first-writer-wins). */
2056
+ metadata?: Record<string, unknown>;
2057
+ }
2058
+ /** One logical activity. */
2059
+ interface Activity {
2060
+ /** The groupId, or the natural tx key for untagged rows. */
2061
+ id: string;
2062
+ /** Merged intent for the group, if any resolver tagged it. */
2063
+ intent?: ActivityIntent;
2064
+ /** Member txs, oldest-first. */
2065
+ txs: ArkTransaction[];
2066
+ /** Signed net sats: positive received, negative sent; same-key change rows are excluded. */
2067
+ amount: number;
2068
+ /** Earliest member createdAt (ms since epoch). */
2069
+ createdAt: number;
2070
+ /** True once every member tx is settled. */
2071
+ settled: boolean;
2072
+ }
2073
+ /** Resolver registry keyed by id. */
2074
+ declare class ActivityRegistry {
2075
+ private readonly resolvers;
2076
+ /** Add a resolver, or override an existing one with the same id (kept in place). */
2077
+ use(resolver: ActivityResolver): void;
2078
+ /** Remove a resolver (built-in or custom) by id. */
2079
+ remove(id: string): void;
2080
+ /** The registered resolver ids, in registration order. */
2081
+ list(): string[];
2082
+ /** All registered resolvers, in registration (priority) order. */
2083
+ all(): ActivityResolver[];
1977
2084
  }
2085
+ /** Built-in resolver: labels on-chain boarding (deposit) transactions. */
2086
+ declare function boardingResolver(): ActivityResolver;
2087
+ /** Built-in resolver: labels collaborative exits (VTXOs forfeited to chain in a batch). */
2088
+ declare function collabExitResolver(): ActivityResolver;
1978
2089
  /**
1979
- * Options for {@link IContractManager.scanContracts}.
2090
+ * Built-in resolver: labels the genesis transaction of a minted asset — "Asset
2091
+ * mint" on the issuer's sent tx, "Asset receive" when the fresh supply arrives
2092
+ * in the genesis tx itself. An asset id encodes its genesis txid; reissues and
2093
+ * transfers carry the asset under a different `arkTxid`, so they are left plain.
2094
+ *
2095
+ * `metadata.amount` is the decimal string of the asset's `bigint` amount, kept
2096
+ * as a string so large supplies survive JSON round-trips without truncation.
2097
+ * Recover the value with `BigInt(metadata.amount as string)` — using it
2098
+ * directly in arithmetic coerces (`"10" + 1` is `"101"`, not `11`) and loses
2099
+ * precision past `Number.MAX_SAFE_INTEGER`.
1980
2100
  */
1981
- interface ScanContractsOptions {
1982
- /** Default 20. A non-positive / non-integer value throws. */
1983
- gapLimit?: number;
1984
- /**
1985
- * Number of HD indices probed per window (default
1986
- * {@link DEFAULT_SCAN_BATCH}). The gap loop stays gap-limit bounded and
1987
- * the discovered set is identical regardless of batch size; the window is
1988
- * also the unit a batching handler ({@link Discoverable.discoverRange})
1989
- * collapses into one request, so it doubles as the batch width. A
1990
- * non-positive / non-integer value throws. Ignored when `hd` is false (the
1991
- * static pass probes only index 0).
1992
- */
1993
- batchSize?: number;
1994
- /** HD mode → unbounded gap loop guided by the gap counter; false → probe only index 0 (single static pass). */
1995
- hd: boolean;
1996
- /**
1997
- * Materialize the descriptor at an HD index. Pure derivation; a throw
1998
- * here is structural/fatal and propagates out of `scanContracts`.
1999
- */
2000
- materialize: (index: number) => string;
2001
- /** Read-only context injected into every `discoverAt` call. */
2002
- deps: DiscoveryDeps;
2003
- }
2101
+ declare function assetMintResolver(): ActivityResolver;
2004
2102
  /**
2005
- * Freshness of the ContractManager's provider-backed sync. `degraded` means the
2006
- * most recent sync (boot, best-effort read, or contract hydration) hit a
2007
- * retryable indexer/operator failure and the manager is serving repository
2008
- * state; it returns to `online` on the next successful sync. This only
2009
- * describes sync freshness — never wallet data itself.
2103
+ * A registry pre-populated with the SDK's built-in resolvers: `boarding`,
2104
+ * `collab-exit`, and `asset-mint`.
2010
2105
  */
2011
- type ContractSyncState = {
2012
- mode: "online";
2013
- lastSyncedAt?: number;
2014
- } | {
2015
- mode: "degraded";
2016
- reason: string;
2017
- lastSyncedAt?: number;
2018
- };
2019
- interface IContractManager extends Disposable {
2106
+ declare function createDefaultActivityRegistry(): ActivityRegistry;
2107
+
2108
+ /**
2109
+ * Intent proof implementation for Bitcoin message signing.
2110
+ *
2111
+ * Intent proof defines a standard for signing Bitcoin messages as well as proving
2112
+ * ownership of outputs.
2113
+ *
2114
+ * This namespace provides utilities for creating and validating Intent proof.
2115
+ *
2116
+ * It is greatly inspired by BIP322.
2117
+ * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
2118
+ *
2119
+ * @example
2120
+ * ```typescript
2121
+ * // Create a Intent proof
2122
+ * const proof = Intent.create(
2123
+ * "Hello Bitcoin!",
2124
+ * [input],
2125
+ * [output]
2126
+ * );
2127
+ *
2128
+ * // Sign the proof
2129
+ * const signedProof = await identity.sign(proof);
2130
+ *
2131
+ */
2132
+ declare namespace Intent {
2133
+ type Proof = Transaction;
2020
2134
  /**
2021
- * Create and register a new contract.
2022
- *
2023
- * Implementations may validate that:
2024
- * - A handler exists for `params.type`
2025
- * - `params.script` matches the script derived from `params.params`
2135
+ * Creates a new Intent proof unsigned transaction.
2026
2136
  *
2027
- * The contract script is used as the unique identifier.
2028
- */
2029
- createContract(params: CreateContractParams): Promise<Contract>;
2030
- /**
2031
- * List contracts with optional filters.
2137
+ * This function constructs a special transaction that can be signed to prove
2138
+ * ownership of onchain and virtual outputs. The proof includes the message to be
2139
+ * signed and the inputs/outputs that demonstrate ownership.
2032
2140
  *
2033
- * @example
2034
- * ```typescript
2035
- * const vhtlcs = await manager.getContracts({ type: "vhtlc" });
2036
- * const active = await manager.getContracts({ state: "active" });
2037
- * ```
2141
+ * @param message - The Intent message to be signed, either raw string of Message object
2142
+ * @param ins - Array of transaction inputs to prove ownership of
2143
+ * @param outputs - Optional array of transaction outputs
2144
+ * @returns An unsigned Intent proof transaction
2038
2145
  */
2039
- getContracts(filter?: GetContractsFilter): Promise<Contract[]>;
2146
+ function create(message: string | Message, ins: (TransactionInput | ExtendedCoin)[], outputs?: TransactionOutput[]): Proof;
2040
2147
  /**
2041
- * List contracts and their current virtual outputs.
2148
+ * Compute the fee paid by an intent proof transaction.
2042
2149
  *
2043
- * If no filter is provided, returns all contracts with their virtual outputs.
2044
- */
2045
- getContractsWithVtxos(filter?: GetContractsFilter): Promise<ContractWithVtxos[]>;
2046
- /**
2047
- * Latest provider-sync health (online vs. degraded to repository data).
2048
- * See {@link ContractSyncState}.
2150
+ * @param proof - Intent proof transaction
2151
+ * @returns The fee in satoshis
2049
2152
  */
2050
- getSyncState(): ContractSyncState;
2051
- /**
2153
+ function fee(proof: Proof): number;
2154
+ type RegisterMessage = {
2155
+ type: "register";
2156
+ onchain_output_indexes: number[];
2157
+ valid_at: number;
2158
+ expire_at: number;
2159
+ cosigners_public_keys: string[];
2160
+ };
2161
+ type DeleteMessage = {
2162
+ type: "delete";
2163
+ expire_at: number;
2164
+ };
2165
+ type GetPendingTxMessage = {
2166
+ type: "get-pending-tx";
2167
+ expire_at: number;
2168
+ };
2169
+ type Message = RegisterMessage | DeleteMessage | GetPendingTxMessage;
2170
+ /**
2171
+ * Serialize an intent message to the canonical JSON string used for signing.
2172
+ *
2173
+ * @param message - Intent message payload
2174
+ * @returns Canonical string form of the message
2175
+ */
2176
+ function encodeMessage(message: Message): string;
2177
+ }
2178
+
2179
+ /**
2180
+ * A source of unilateral-exit chain data for a set of VTXOs. Sources are tried
2181
+ * in order by the resolver; a source returns `null` (chain) or omits keys (psbts)
2182
+ * for data it cannot supply — a "miss" — and the resolver falls through.
2183
+ */
2184
+ interface ExitDataSource {
2185
+ readonly name: string;
2186
+ /** Full ancestry chain for a vtxo (all pages merged), or null on a miss. */
2187
+ getVtxoChain(vtxo: Outpoint): Promise<ChainTx[] | null>;
2188
+ /** Base64 PSBTs this source has, keyed by (unsigned) txid. Absent key = miss. */
2189
+ getVirtualTxs(txids: string[]): Promise<Map<string, string>>;
2190
+ }
2191
+ /** Reads exit chain data through an ordered chain of sources. */
2192
+ interface ExitChainResolver {
2193
+ getVtxoChain(vtxo: Outpoint): Promise<ChainTx[]>;
2194
+ getVirtualTxs(txids: string[]): Promise<string[]>;
2195
+ }
2196
+ /**
2197
+ * Assemble the standard exit-data resolver: local repo (if configured) → any
2198
+ * extra sources (e.g. a future provider) → indexer. Read-through persists to the
2199
+ * repository. With no repository this is exactly the indexer path (a no-op seam).
2200
+ */
2201
+ declare function createExitChainResolver(params: {
2202
+ indexer: IndexerProvider;
2203
+ repository?: VirtualTxRepository;
2204
+ extraSources?: ExitDataSource[];
2205
+ }): ExitChainResolver;
2206
+
2207
+ type ExitCaptureMode = "lite" | "full";
2208
+
2209
+ /**
2210
+ * Delegate identity and fee information returned by `getDelegateInfo`.
2211
+ */
2212
+ interface DelegateInfo {
2213
+ /** Delegate public key. */
2214
+ pubkey: string;
2215
+ /** Delegate fee amount or expression returned by the delegate. */
2216
+ fee: string;
2217
+ /** Address for delegate fee collection. Sourced from `delegatorAddress` in Fulmine response, for now. */
2218
+ delegateAddress: string;
2219
+ /** @deprecated alias for @see DelegateInfo.delegateAddress */
2220
+ delegatorAddress?: string;
2221
+ }
2222
+ /**
2223
+ * Optional delegate behavior flags.
2224
+ */
2225
+ interface DelegateOptions {
2226
+ /**
2227
+ * Instruct the delegate not to replace an existing delegation
2228
+ * (meaning a signed register intent and its forfeit transactions)
2229
+ * that already includes at least one virtual output from this request.
2230
+ *
2231
+ * @defaultValue `false`
2232
+ */
2233
+ rejectReplace?: boolean;
2234
+ }
2235
+ /**
2236
+ * Provider interface for remote delegation service.
2237
+ */
2238
+ interface DelegateProvider {
2239
+ /**
2240
+ * Request delegation for a signed register intent and its forfeit transactions.
2241
+ *
2242
+ * @param intent - Signed register intent to delegate
2243
+ * @param forfeitTxs - Forfeit transactions associated with the delegation request
2244
+ * @param options - Optional delegate behavior flags
2245
+ */
2246
+ delegate(intent: SignedIntent<Intent.RegisterMessage>, forfeitTxs: string[], options?: DelegateOptions): Promise<void>;
2247
+ /**
2248
+ * Fetch delegate metadata such as pubkey, fee, and delegate address.
2249
+ *
2250
+ * @returns Delegate identity and fee information
2251
+ */
2252
+ getDelegateInfo(): Promise<DelegateInfo>;
2253
+ }
2254
+ /** @deprecated alias for @see DelegateProvider */
2255
+ type DelegatorProvider = DelegateProvider;
2256
+ /**
2257
+ * REST-based delegate provider implementation.
2258
+ * @example
2259
+ * ```typescript
2260
+ * const provider = new RestDelegateProvider('https://delegate.example.com');
2261
+ * const info = await provider.getDelegateInfo();
2262
+ * await provider.delegate(intent, forfeitTxs);
2263
+ * ```
2264
+ */
2265
+ declare class RestDelegateProvider implements DelegateProvider {
2266
+ url: string;
2267
+ /**
2268
+ * Create a REST delegate provider targeting the given base URL.
2269
+ *
2270
+ * @param url - Base URL of the remote delegation service.
2271
+ */
2272
+ constructor(url: string);
2273
+ /**
2274
+ * Submit a delegation request to the remote delegation service.
2275
+ *
2276
+ * @param intent - Signed register intent to delegate
2277
+ * @param forfeitTxs - Forfeit transactions associated with the delegation request
2278
+ * @param options - Optional delegate behavior flags
2279
+ * @throws Error if the remote service rejects the request
2280
+ */
2281
+ delegate(intent: SignedIntent<Intent.RegisterMessage>, forfeitTxs: string[], options?: DelegateOptions): Promise<void>;
2282
+ /**
2283
+ * Fetch delegate metadata exposed by the remote delegation service.
2284
+ *
2285
+ * @returns Delegate identity and fee information
2286
+ * @throws Error if the remote service returns invalid data
2287
+ */
2288
+ getDelegateInfo(): Promise<DelegateInfo>;
2289
+ }
2290
+ /** @deprecated alias for @see RestDelegateProvider */
2291
+ declare const RestDelegatorProvider: typeof RestDelegateProvider;
2292
+ type RestDelegatorProvider = RestDelegateProvider;
2293
+
2294
+ interface IDelegateManager {
2295
+ /**
2296
+ * Delegate virtual outputs to the remote delegation service.
2297
+ *
2298
+ * Vtxos that are not locked to a delegate-type contract (no tap leaf
2299
+ * matches the delegate's pubkey) are filtered out silently, since they
2300
+ * cannot be co-signed by the delegate.
2301
+ *
2302
+ * @param vtxos - Virtual outputs to delegate
2303
+ * @param destination - Arkade address that should receive renewed funds
2304
+ * @param delegateAt - Optional timestamp to force a specific delegation time
2305
+ * @returns Successfully delegated and failed outpoint groups
2306
+ */
2307
+ delegate(vtxos: ContractVtxo[], destination: string, delegateAt?: Date): Promise<{
2308
+ delegated: Outpoint[];
2309
+ failed: {
2310
+ outpoints: Outpoint[];
2311
+ error: unknown;
2312
+ }[];
2313
+ }>;
2314
+ /** Fetch delegate metadata such as pubkey, fee, and delegate address. */
2315
+ getDelegateInfo(): Promise<DelegateInfo>;
2316
+ }
2317
+ /** @deprecated alias for @see IDelegateManager */
2318
+ type IDelegatorManager = IDelegateManager;
2319
+ declare class DelegateManagerImpl implements IDelegateManager {
2320
+ readonly delegateProvider: DelegateProvider;
2321
+ readonly arkInfoProvider: Pick<ArkProvider, "getInfo">;
2322
+ readonly identity: Identity;
2323
+ /** Create a delegate manager from the configured provider, Arkade info source, and wallet identity. */
2324
+ constructor(delegateProvider: DelegateProvider, arkInfoProvider: Pick<ArkProvider, "getInfo">, identity: Identity);
2325
+ getDelegateInfo(): Promise<DelegateInfo>;
2326
+ delegate(vtxos: ContractVtxo[], destination: string, delegateAt?: Date): Promise<{
2327
+ delegated: Outpoint[];
2328
+ failed: {
2329
+ outpoints: Outpoint[];
2330
+ error: unknown;
2331
+ }[];
2332
+ }>;
2333
+ }
2334
+ /** @deprecated alias for @see DelegateManagerImpl */
2335
+ declare const DelegatorManagerImpl: typeof DelegateManagerImpl;
2336
+ type DelegatorManagerImpl = DelegateManagerImpl;
2337
+
2338
+ /**
2339
+ * Tapscript fields derived purely from a contract's params. They are identical
2340
+ * for every VTXO locked to the same contract, so they can be memoized per
2341
+ * contract (see {@link ContractTapscriptCache}).
2342
+ */
2343
+ type ContractTapscripts = Pick<ExtendedVirtualCoin, "forfeitTapLeafScript" | "intentTapLeafScript" | "tapTree">;
2344
+ /**
2345
+ * Cache of per-contract tapscript data, keyed by `contract.script`. Building
2346
+ * the taproot tree via `handler.createScript(contract.params)` is the dominant
2347
+ * cost when annotating large VTXO sets; passing a shared cache across an
2348
+ * annotation batch rebuilds it once per distinct contract instead of once per
2349
+ * VTXO (see #521).
2350
+ */
2351
+ type ContractTapscriptCache = Map<string, ContractTapscripts>;
2352
+ declare function getRandomId(): string;
2353
+ /**
2354
+ * What a recipient Arkade address must match. An address failing either check
2355
+ * belongs to another network or operator, so this wallet's operator cannot
2356
+ * create the VTXO where the recipient's wallet expects it.
2357
+ */
2358
+ type RecipientAddressContext = {
2359
+ hrp: string;
2360
+ signerSet: SignerSet;
2361
+ };
2362
+
2363
+ /**
2364
+ * An input for {@link IContractManager.assertSpendableNow}.
2365
+ *
2366
+ * The outpoint and script are what identify the owning contract. A full
2367
+ * {@link VirtualCoin} is accepted and preferred: a relative (CSV) timelock is
2368
+ * measured from this coin's own confirmation, so `status.block_height` /
2369
+ * `status.block_time` are the only way to answer one. Pass the coin where you
2370
+ * have it; the bare shape still answers every absolute (CLTV) question.
2371
+ */
2372
+ type AssertSpendableInput = {
2373
+ txid: string;
2374
+ vout: number;
2375
+ script: string;
2376
+ };
2377
+ type RefreshVtxosOptions = {
2378
+ /**
2379
+ * Narrow the refresh to these scripts. A subset query, so the
2380
+ * cursor is not advanced: contracts outside the list may have data
2381
+ * we'd skip.
2382
+ */
2383
+ scripts?: string[];
2384
+ /**
2385
+ * Time window overriding the cursor-derived one. The cursor never
2386
+ * advances on a windowed query because the window may skip data
2387
+ * outside its bounds.
2388
+ */
2389
+ after?: number;
2390
+ /** @see after */
2391
+ before?: number;
2392
+ /**
2393
+ * When true and `scripts` is not set, refresh every contract in
2394
+ * the repository rather than the watcher's watched set — which
2395
+ * differs only for rows the watcher never registered, since
2396
+ * retirement doesn't narrow that set
2397
+ * (see {@link ContractWatcher.getWatchedContracts}).
2398
+ *
2399
+ * Because this is a *superset* of the watched set, the cursor
2400
+ * invariant still holds and the cursor advances normally (unless
2401
+ * `after` / `before` is also supplied).
2402
+ *
2403
+ * @defaultValue `false`
2404
+ */
2405
+ includeInactive?: boolean;
2406
+ };
2407
+ /**
2408
+ * A single `Discoverable` handler's discovery failure, captured during a
2409
+ * {@link IContractManager.scanContracts} run instead of aborting the loop.
2410
+ *
2411
+ * TODO(next major): rename `index` → `fromIndex` so the pair reads
2412
+ * `fromIndex`/`toIndex`. It stays `index` here only to keep this exported
2413
+ * shape backward-compatible.
2414
+ */
2415
+ interface HandlerError {
2416
+ handler: string;
2417
+ /** The failed index, or the first index of a failed `discoverRange` window. */
2418
+ index: number;
2419
+ /** Inclusive end of a failed `discoverRange` window; absent for a single index. */
2420
+ toIndex?: number;
2421
+ error: unknown;
2422
+ }
2423
+ /**
2424
+ * Outcome of a {@link IContractManager.scanContracts} run.
2425
+ */
2426
+ interface ScanResult {
2427
+ /** @deprecated Alias of {@link ScanResult.highestConfirmedUsedIndex}. */
2428
+ lastIndexUsed: number;
2429
+ /**
2430
+ * Highest HD index at which any handler confirmed a contract (`-1` if none),
2431
+ * including hits past {@link ScanResult.truncatedAt}. Safe to record
2432
+ * unconditionally: the HD watermark it feeds is a monotonic max over a scan
2433
+ * that always restarts at 0, so it cannot skip an index — while withholding
2434
+ * it risks re-issuing a funded index as a fresh receive address.
2435
+ */
2436
+ highestConfirmedUsedIndex: number;
2437
+ /**
2438
+ * First index a handler failed at, making it *indeterminate* — neither a hit
2439
+ * nor a confirmed miss. The scan stops there, so indices `>= truncatedAt` are
2440
+ * unverified and the caller must retry (scanning is idempotent). `undefined`
2441
+ * when the scan closed a genuine gap.
2442
+ */
2443
+ truncatedAt?: number;
2444
+ /** Per-handler discovery failures. Non-empty implies `truncatedAt` is set. */
2445
+ handlerErrors: HandlerError[];
2446
+ }
2447
+ /**
2448
+ * Options for {@link IContractManager.scanContracts}.
2449
+ */
2450
+ interface ScanContractsOptions {
2451
+ /** Default 20. A non-positive / non-integer value throws. */
2452
+ gapLimit?: number;
2453
+ /**
2454
+ * Number of HD indices probed per window (default
2455
+ * {@link DEFAULT_SCAN_BATCH}). The gap loop stays gap-limit bounded and
2456
+ * the discovered set is identical regardless of batch size; the window is
2457
+ * also the unit a batching handler ({@link Discoverable.discoverRange})
2458
+ * collapses into one request, so it doubles as the batch width. A
2459
+ * non-positive / non-integer value throws. Ignored when `hd` is false (the
2460
+ * static pass probes only index 0).
2461
+ */
2462
+ batchSize?: number;
2463
+ /** HD mode → unbounded gap loop guided by the gap counter; false → probe only index 0 (single static pass). */
2464
+ hd: boolean;
2465
+ /**
2466
+ * Materialize the descriptor at an HD index. Pure derivation; a throw
2467
+ * here is structural/fatal and propagates out of `scanContracts`.
2468
+ */
2469
+ materialize: (index: number) => string;
2470
+ /** Read-only context injected into every `discoverAt` call. */
2471
+ deps: DiscoveryDeps;
2472
+ }
2473
+ /**
2474
+ * Freshness of the ContractManager's provider-backed sync. `degraded` means the
2475
+ * most recent sync (boot, best-effort read, or contract hydration) hit a
2476
+ * retryable indexer/operator failure and the manager is serving repository
2477
+ * state; it returns to `online` on the next successful sync. This only
2478
+ * describes sync freshness — never wallet data itself.
2479
+ */
2480
+ type ContractSyncState = {
2481
+ mode: "online";
2482
+ lastSyncedAt?: number;
2483
+ } | {
2484
+ mode: "degraded";
2485
+ reason: string;
2486
+ lastSyncedAt?: number;
2487
+ };
2488
+ interface IContractManager extends Disposable {
2489
+ /**
2490
+ * Create and register a new contract.
2491
+ *
2492
+ * Implementations may validate that:
2493
+ * - A handler exists for `params.type`
2494
+ * - `params.script` matches the script derived from `params.params`
2495
+ *
2496
+ * The contract script is used as the unique identifier.
2497
+ */
2498
+ createContract(params: CreateContractParams): Promise<Contract>;
2499
+ /**
2500
+ * List contracts with optional filters.
2501
+ *
2502
+ * @example
2503
+ * ```typescript
2504
+ * const vhtlcs = await manager.getContracts({ type: "vhtlc" });
2505
+ * const active = await manager.getContracts({ state: "active" });
2506
+ * ```
2507
+ */
2508
+ getContracts(filter?: GetContractsFilter): Promise<Contract[]>;
2509
+ /**
2510
+ * List contracts and their current virtual outputs.
2511
+ *
2512
+ * If no filter is provided, returns all contracts with their virtual outputs.
2513
+ */
2514
+ getContractsWithVtxos(filter?: GetContractsFilter): Promise<ContractWithVtxos[]>;
2515
+ /**
2516
+ * Latest provider-sync health (online vs. degraded to repository data).
2517
+ * See {@link ContractSyncState}.
2518
+ */
2519
+ getSyncState(): ContractSyncState;
2520
+ /**
2052
2521
  * Stamp raw virtual outputs with the correct per-contract tapscripts
2053
2522
  * (forfeit, intent, tap tree).
2054
2523
  *
@@ -2060,7 +2529,43 @@ interface IContractManager extends Disposable {
2060
2529
  * in wallet/handler code, and keeps the wallet from silently stamping the
2061
2530
  * default tapscript onto a non-default vtxo.
2062
2531
  */
2063
- annotateVtxos(vtxos: VirtualCoin[]): Promise<NormalizedExtendedVirtualCoin[]>;
2532
+ annotateVtxos(vtxos: VirtualCoin[], tapscripts?: ContractTapscriptCache): Promise<NormalizedExtendedVirtualCoin[]>;
2533
+ /**
2534
+ * Throw unless every one of `vtxos` still has an annotatable contract.
2535
+ *
2536
+ * Spending does not re-derive tapscripts — it uses the ones stored on the
2537
+ * coin — so a contract that stopped being annotatable (handler no longer
2538
+ * registered, or params its handler now rejects) still builds and submits a
2539
+ * transaction fine, and only fails afterwards, in the bookkeeping that
2540
+ * re-annotates the inputs. Calling this before submitting turns that into a
2541
+ * refusal to spend, naming the contract, rather than a broadcast whose local
2542
+ * state could not be recorded.
2543
+ */
2544
+ assertAnnotatable(vtxos: readonly {
2545
+ txid: string;
2546
+ vout: number;
2547
+ script: string;
2548
+ }[]): Promise<void>;
2549
+ /**
2550
+ * Throw when one of `vtxos` belongs to a contract that provably cannot be
2551
+ * spent right now, asking each owning handler's
2552
+ * {@link ContractHandler.assertSpendableNow}.
2553
+ *
2554
+ * The complement of {@link isContractGenericallySpendable}, which keeps
2555
+ * escrow out of generic selection and leaves explicit-input APIs open on
2556
+ * purpose. This does not close that door — it makes walking through it too
2557
+ * early report itself locally, naming the timelock, instead of coming back
2558
+ * as a protocol-level rejection after the round trip.
2559
+ *
2560
+ * Handlers answer only where they are certain, so contracts with no opinion
2561
+ * (which is all of them but VHTLC today) pass through untouched and cost
2562
+ * nothing — not even a chain-tip read.
2563
+ *
2564
+ * Optional so that adding it is not a breaking change for an embedder with
2565
+ * its own `IContractManager`. An implementation that omits it simply offers
2566
+ * no opinion, which is the same answer every non-VHTLC contract gives.
2567
+ */
2568
+ assertSpendableNow?(vtxos: readonly AssertSpendableInput[], walletPubKey?: () => Promise<string | undefined>): Promise<void>;
2064
2569
  /**
2065
2570
  * Update mutable contract fields.
2066
2571
  *
@@ -2069,13 +2574,29 @@ interface IContractManager extends Disposable {
2069
2574
  updateContract(script: string, updates: Partial<Omit<Contract, "script" | "createdAt">>): Promise<Contract>;
2070
2575
  /**
2071
2576
  * Convenience helper to update only the contract state. Note
2072
- * `inactive` does not stop watching; see {@link ContractState} and
2073
- * {@link deleteContract}.
2577
+ * `inactive` governs receive-address selection and does not stop
2578
+ * watching; see {@link ContractState} and
2579
+ * {@link setContractWatchState}.
2074
2580
  */
2075
2581
  setContractState(script: string, state: ContractState): Promise<void>;
2076
2582
  /**
2077
- * Delete a contract by script and stop watching it. This — not
2078
- * retiring via {@link setContractState} — is the stop-watching path.
2583
+ * Convenience helper to update only the contract's watch state.
2584
+ *
2585
+ * `retained` is how an owner says "this script is done": it leaves
2586
+ * the subscription and the sweep, while the row — and so history,
2587
+ * annotation and restore — is untouched. `awaiting-funds` asks for
2588
+ * coverage only until the script is funded, after which the manager
2589
+ * demotes it to `retained` itself.
2590
+ *
2591
+ * @see ContractWatchState
2592
+ */
2593
+ setContractWatchState(script: string, watch: ContractWatchState): Promise<void>;
2594
+ /**
2595
+ * Delete a contract by script, dropping both the row and the watch.
2596
+ * Destructive: the row is what keeps the contract's VTXOs
2597
+ * annotatable and its transactions readable in history, so to stop
2598
+ * watching a finished contract use
2599
+ * {@link setContractWatchState}(`"retained"`) instead.
2079
2600
  */
2080
2601
  deleteContract(script: string): Promise<void>;
2081
2602
  /**
@@ -2127,6 +2648,17 @@ interface IContractManager extends Disposable {
2127
2648
  * coalesce into a single drain.
2128
2649
  */
2129
2650
  refillLookAhead(): Promise<void>;
2651
+ /**
2652
+ * Allocate the next signing descriptor through the manager-owned HD
2653
+ * watermark path. Returns `undefined` when look-ahead/allocation is not
2654
+ * configured.
2655
+ */
2656
+ getNextSigningDescriptor(): Promise<string | undefined>;
2657
+ /**
2658
+ * Advance the HD signing descriptor watermark to `index` and refill the
2659
+ * watched look-ahead band. No-op when look-ahead/allocation is not configured.
2660
+ */
2661
+ advanceSigningDescriptorWatermark(index: number): Promise<void>;
2130
2662
  /**
2131
2663
  * Explicit, gap-limit contract discovery used by `wallet.restore()`.
2132
2664
  *
@@ -2223,6 +2755,33 @@ interface ContractManagerConfig {
2223
2755
  * {@link ContractManager.refillLookAhead}.
2224
2756
  */
2225
2757
  lookAhead?: LookAheadConfig;
2758
+ /**
2759
+ * Current chain tip height, for the `blockHeight` a {@link PathContext}
2760
+ * carries. Absent, or resolving `undefined`, leaves `blockHeight` unset.
2761
+ *
2762
+ * `isCltvSatisfied` answers `false` outright for a height-typed locktime
2763
+ * when `blockHeight` is missing, so every such path was reported
2764
+ * unspendable however mature it was. Nothing populated this before, which
2765
+ * made that the only behaviour available. Seconds-typed locktimes read
2766
+ * `currentTime` and are unaffected either way.
2767
+ *
2768
+ * Block-typed CSV is not fixed by this. `isCsvSpendable` also needs the
2769
+ * VTXO's confirmation height, and `status.block_height` is never populated
2770
+ * for a virtual coin, so it stays `false` regardless of the tip.
2771
+ *
2772
+ * Both fields matter. `height` answers height-typed timelocks; `time` (the
2773
+ * tip's timestamp, in SECONDS) is what seconds-typed ones should be judged
2774
+ * against, because the machine's clock is an estimate of chain time and a
2775
+ * drifting one reads the boundary wrong.
2776
+ *
2777
+ * Resolve `undefined` rather than rejecting when the tip cannot be read:
2778
+ * the callers treat it as "unknown", which is the pre-existing behaviour,
2779
+ * and a path query is not worth failing over a provider hiccup.
2780
+ */
2781
+ chainTip?: () => Promise<{
2782
+ height: number;
2783
+ time: number;
2784
+ } | undefined>;
2226
2785
  }
2227
2786
  /**
2228
2787
  * Wallet-injected surface backing the HD look-ahead window. Kept as a
@@ -2234,6 +2793,10 @@ interface LookAheadConfig {
2234
2793
  size: number;
2235
2794
  /** Current allocation watermark (`lastIndexUsed ?? -1`). */
2236
2795
  currentWatermark(): Promise<number>;
2796
+ /** Allocate the next signing descriptor, advancing the watermark. */
2797
+ allocate?(): Promise<string | undefined>;
2798
+ /** Advance the allocation watermark to a confirmed/restored index. */
2799
+ advanceWatermark?(index: number): Promise<void>;
2237
2800
  /** Signing descriptor at an HD index. Pure derivation. */
2238
2801
  materialize(index: number): string;
2239
2802
  /**
@@ -2242,7 +2805,10 @@ interface LookAheadConfig {
2242
2805
  * `rotateServerSigner` fans the new signer set.
2243
2806
  */
2244
2807
  candidateDeps(): CandidateDeps;
2245
- /** Fired after a speculative entry at `index` is promoted to a real row. */
2808
+ /**
2809
+ * Fired after a speculative entry at `index` is promoted to a real row.
2810
+ * @deprecated Use `advanceWatermark`; kept for external LookAheadConfig users.
2811
+ */
2246
2812
  onPromoted?(index: number): Promise<void>;
2247
2813
  }
2248
2814
  /**
@@ -2309,12 +2875,24 @@ declare class ContractManager implements IContractManager {
2309
2875
  private syncDegradedReason?;
2310
2876
  /** Epoch-ms of the last successful provider sync, if any. */
2311
2877
  private lastSyncedAt?;
2878
+ /** Last chain tip read, with the epoch-ms it was read at. @see currentChainTip */
2879
+ private chainTipCache?;
2880
+ /** In-flight chain tip read, so concurrent cache misses share one. */
2881
+ private chainTipInflight?;
2312
2882
  /** Speculative look-ahead scripts, keyed by script. @see LookAheadEntry */
2313
2883
  private lookAheadEntries;
2314
2884
  /** In-flight look-ahead drain, if any. @see scheduleLookAheadDrain */
2315
2885
  private lookAheadDrain?;
2316
2886
  /** A refill was requested while a drain was running. */
2317
2887
  private lookAheadDirty;
2888
+ /** A fire-and-forget drain failed, so the band is behind the watermark and
2889
+ * owes a retry. @see requestLookAheadDrain */
2890
+ private lookAheadRefillOwed;
2891
+ /** Set by {@link dispose}, cleared by a re-`initialize`. A drain is a
2892
+ * fire-and-forget async loop that outlives the synchronous `dispose()`,
2893
+ * so it re-checks this at every await boundary instead of running on
2894
+ * against a torn-down watcher. */
2895
+ private disposed;
2318
2896
  private constructor();
2319
2897
  /**
2320
2898
  * Static factory method for creating a new ContractManager.
@@ -2334,6 +2912,17 @@ declare class ContractManager implements IContractManager {
2334
2912
  */
2335
2913
  getSyncState(): ContractSyncState;
2336
2914
  private markSyncOnline;
2915
+ /** Contracts a sync could not annotate, as `script → reason`. */
2916
+ private annotationFailures;
2917
+ private annotationDegradedReason;
2918
+ /**
2919
+ * Fold one batch's verdict in: what it annotated clears, what it could not
2920
+ * sets. Merged rather than replaced because a batch can cover a subset of
2921
+ * the wallet's contracts (a single-contract fetch, the pending-only
2922
+ * reconcile), and those must not erase what a wider sync found. A row
2923
+ * repaired by an upgrade clears itself on the next batch that includes it.
2924
+ */
2925
+ private recordAnnotationFailures;
2337
2926
  private markSyncDegraded;
2338
2927
  private initialize;
2339
2928
  /**
@@ -2358,6 +2947,10 @@ declare class ContractManager implements IContractManager {
2358
2947
  private reconcileStaleIntents;
2359
2948
  /** @see IContractManager.refillLookAhead */
2360
2949
  refillLookAhead(): Promise<void>;
2950
+ /** @see IContractManager.getNextSigningDescriptor */
2951
+ getNextSigningDescriptor(): Promise<string | undefined>;
2952
+ /** @see IContractManager.advanceSigningDescriptorWatermark */
2953
+ advanceSigningDescriptorWatermark(index: number): Promise<void>;
2361
2954
  /**
2362
2955
  * Serialized drain of the look-ahead band: concurrent callers join the
2363
2956
  * active drain and mark it dirty, an idle call starts a new one. Promotion
@@ -2368,7 +2961,13 @@ declare class ContractManager implements IContractManager {
2368
2961
  private scheduleLookAheadDrain;
2369
2962
  /**
2370
2963
  * Request a drain without awaiting it. Used from inside a sync (promotion),
2371
- * where awaiting the drain that the sync itself is part of would deadlock.
2964
+ * and after an allocation the drain must not be able to fail the
2965
+ * watermark already moved, and a retry would burn another index.
2966
+ *
2967
+ * A failure here is not terminal: it leaves the watch band behind the
2968
+ * watermark, so funded indices inside it would go unregistered and the
2969
+ * balance would under-report for the rest of the session. Record the debt
2970
+ * so the next contract event retries it. @see handleContractEvent
2372
2971
  */
2373
2972
  private requestLookAheadDrain;
2374
2973
  /**
@@ -2401,6 +3000,7 @@ declare class ContractManager implements IContractManager {
2401
3000
  * exist. Targeted + explicitly windowed, so the global cursor stays put.
2402
3001
  */
2403
3002
  private runLookAheadCatchUp;
3003
+ private advanceLookAheadWatermark;
2404
3004
  /**
2405
3005
  * Promote every look-ahead entry funded by `vtxos` into a real repository
2406
3006
  * row, returning the persisted rows keyed by script.
@@ -2505,7 +3105,15 @@ declare class ContractManager implements IContractManager {
2505
3105
  */
2506
3106
  getContracts(filter?: GetContractsFilter): Promise<Contract[]>;
2507
3107
  getContractsWithVtxos(filter?: GetContractsFilter, pageSize?: number): Promise<ContractWithVtxos[]>;
2508
- annotateVtxos(vtxos: VirtualCoin[]): Promise<NormalizedExtendedVirtualCoin[]>;
3108
+ annotateVtxos(vtxos: VirtualCoin[], tapscripts?: ContractTapscriptCache): Promise<NormalizedExtendedVirtualCoin[]>;
3109
+ /** @inheritdoc */
3110
+ assertAnnotatable(vtxos: readonly {
3111
+ txid: string;
3112
+ vout: number;
3113
+ script: string;
3114
+ }[]): Promise<void>;
3115
+ /** @inheritdoc */
3116
+ assertSpendableNow(vtxos: readonly AssertSpendableInput[], walletPubKey?: () => Promise<string | undefined>): Promise<void>;
2509
3117
  private buildContractsDbFilter;
2510
3118
  /**
2511
3119
  * Update a contract.
@@ -2526,18 +3134,39 @@ declare class ContractManager implements IContractManager {
2526
3134
  updateContractParams(script: string, updates: Contract["params"]): Promise<Contract>;
2527
3135
  /**
2528
3136
  * Set a contract's state. Retiring (`inactive`) keeps it watched;
2529
- * see {@link ContractState}. To stop watching, use
2530
- * {@link deleteContract}.
3137
+ * see {@link ContractState}. To stop watching while keeping the row,
3138
+ * use {@link setContractWatchState}.
2531
3139
  */
2532
3140
  setContractState(script: string, state: ContractState): Promise<void>;
3141
+ /** @see IContractManager.setContractWatchState */
3142
+ setContractWatchState(script: string, watch: ContractWatchState): Promise<void>;
2533
3143
  /**
2534
- * Delete a contract. Also removes it from the watcher the only way
2535
- * to stop watching a contract (retiring it via
2536
- * {@link setContractState} does not).
3144
+ * Delete a contract, dropping the row along with the watch. To stop
3145
+ * watching a finished contract without losing its history, use
3146
+ * {@link setContractWatchState}(`"retained"`).
2537
3147
  *
2538
3148
  * @param script - Contract script
2539
3149
  */
2540
3150
  deleteContract(script: string): Promise<void>;
3151
+ /**
3152
+ * Chain tip height for a {@link PathContext}, or `undefined` when there is
3153
+ * no source configured or it cannot be read.
3154
+ *
3155
+ * Cached for {@link CHAIN_TIP_TTL_MS} so a caller resolving paths for many
3156
+ * contracts does not pay a provider round trip each time. Blocks arrive
3157
+ * ~10 minutes apart, so a cache this short can only ever be one block
3158
+ * stale, and a stale-low height is the conservative direction: a path is
3159
+ * reported unspendable slightly longer than it truly is, never spendable
3160
+ * before it is.
3161
+ */
3162
+ private currentChainTip;
3163
+ /**
3164
+ * One chain tip read, bounded by {@link CHAIN_TIP_TIMEOUT_MS}. Never
3165
+ * rejects: an unreadable tip is "unknown", which is what the callers did
3166
+ * before a tip existed at all, and a path query is not worth failing over
3167
+ * a provider hiccup.
3168
+ */
3169
+ private readChainTip;
2541
3170
  /**
2542
3171
  * Get currently spendable paths for a contract.
2543
3172
  *
@@ -2547,6 +3176,11 @@ declare class ContractManager implements IContractManager {
2547
3176
  /**
2548
3177
  * Get every currently valid spending path for a contract.
2549
3178
  *
3179
+ * No `blockHeight`: this enumerates paths "regardless of current
3180
+ * spendability", so no handler evaluates a timelock here and the tip would
3181
+ * be fetched only to be discarded — leaving a purely local answer waiting
3182
+ * on the network for nothing.
3183
+ *
2550
3184
  * @param options - Options for getting spending paths
2551
3185
  */
2552
3186
  getAllSpendingPaths(options: GetAllSpendingPathsOptions): Promise<PathSelection[]>;
@@ -2603,6 +3237,20 @@ declare class ContractManager implements IContractManager {
2603
3237
  * other contracts still need to pick up.
2604
3238
  */
2605
3239
  private syncContracts;
3240
+ /**
3241
+ * Demote every `awaiting-funds` contract in `contracts` that has been
3242
+ * funded — the automatic half of {@link ContractWatchState}.
3243
+ *
3244
+ * Runs after the sync has persisted, so the funding VTXO is saved
3245
+ * while the contract is still watched, and reads the repository
3246
+ * rather than this sync's delta: funds that landed while the app was
3247
+ * closed are outside every later window, and a contract asked to
3248
+ * watch until it is funded must still stop once it is.
3249
+ *
3250
+ * Best-effort. A demotion that fails costs coverage that is merely
3251
+ * no longer needed, and must not fail the sync that carried it.
3252
+ */
3253
+ private demoteFundedAwaitingContracts;
2606
3254
  /**
2607
3255
  * Fetch all pending (unfinalized) virtual outputs and upsert them into the
2608
3256
  * repository. This catches virtual outputs whose state changed outside the delta
@@ -2634,14 +3282,48 @@ declare class ContractManager implements IContractManager {
2634
3282
  }
2635
3283
 
2636
3284
  /**
2637
- * Contract lifecycle state. Both states stay monitored the watcher
2638
- * subscribes and sweeps every registered contract regardless
2639
- * (see {@link ContractWatcher.getWatchedContracts}), because a retired
2640
- * receive address can still be paid. `inactive` only demotes a contract
2641
- * out of receive-address selection; it does **not** unsubscribe it.
2642
- * Use {@link IContractManager.deleteContract} to stop watching.
3285
+ * Contract lifecycle state. Neither state affects coverage: the watcher
3286
+ * subscribes and sweeps a contract according to {@link Contract.watch}
3287
+ * alone, because a retired receive address can still be paid. `inactive`
3288
+ * only demotes a contract out of receive-address selection; it does
3289
+ * **not** unsubscribe it. To stop watching but keep the row, set
3290
+ * {@link ContractWatchState} to `retained`; to drop both, use
3291
+ * {@link IContractManager.deleteContract}.
2643
3292
  */
2644
3293
  type ContractState = "active" | "inactive";
3294
+ /**
3295
+ * Whether a contract is covered by background monitoring — the
3296
+ * subscription and the failsafe/indexer sweep
3297
+ * ({@link ContractWatcher.getWatchedContracts}).
3298
+ *
3299
+ * Orthogonal to {@link ContractState}, which governs receive-address
3300
+ * selection only. A contract can be the wallet's display address and
3301
+ * watched, or terminal and retained; the two questions never answer each
3302
+ * other.
3303
+ *
3304
+ * NArk calls this `ContractActivityState` (`Active` / `Inactive` /
3305
+ * `AwaitingFundsBeforeDeactivate`). The concept is the same; the names
3306
+ * differ because TS already spends `active`/`inactive` on
3307
+ * {@link ContractState}, and a row reading `state: "active",
3308
+ * activityState: "inactive"` would be unreadable.
3309
+ */
3310
+ type ContractWatchState =
3311
+ /** Subscribed and polled. */
3312
+ "watched"
3313
+ /**
3314
+ * Watched until the first VTXO lands at the script, then
3315
+ * automatically demoted to `retained` by the contract manager.
3316
+ * For one-shot destinations — a refund address, a swap lockup —
3317
+ * that only need coverage until they are funded.
3318
+ */
3319
+ | "awaiting-funds"
3320
+ /**
3321
+ * Kept for history, restore and classification, but absent from
3322
+ * every background channel. The row still resolves in
3323
+ * `getContracts`, still annotates its VTXOs, and still feeds
3324
+ * transaction history; nothing subscribes or polls it.
3325
+ */
3326
+ | "retained";
2645
3327
  /**
2646
3328
  * Represents a contract that can receive and manage virtual outputs.
2647
3329
  *
@@ -2692,6 +3374,11 @@ interface Contract {
2692
3374
  address: string;
2693
3375
  /** Current state of the contract. */
2694
3376
  state: ContractState;
3377
+ /**
3378
+ * Background-monitoring scope. Absent means `watched`.
3379
+ * @see ContractWatchState
3380
+ */
3381
+ watch?: ContractWatchState;
2695
3382
  /** Unix timestamp in milliseconds when this contract was created. */
2696
3383
  createdAt: number;
2697
3384
  /**
@@ -2766,6 +3453,17 @@ interface PathContext {
2766
3453
  * fallback — against the contract's sender/receiver params.
2767
3454
  */
2768
3455
  role?: string;
3456
+ /**
3457
+ * Chain tip timestamp in SECONDS, when known.
3458
+ *
3459
+ * Timelocks mature against chain time, not the machine's clock, so any
3460
+ * seconds-typed comparison should prefer this and fall back to
3461
+ * {@link currentTime} only when it is absent. The two differ by more than
3462
+ * pedantry: the server matures absolute locktimes against median-time-past,
3463
+ * which trails wall clock, and a host whose clock drifts turns a local
3464
+ * decision into a wrong one in whichever direction it drifted.
3465
+ */
3466
+ chainTime?: number;
2769
3467
  /** The specific virtual output being evaluated. */
2770
3468
  vtxo?: VirtualCoin;
2771
3469
  }
@@ -2837,6 +3535,50 @@ interface ContractHandler<P = Record<string, unknown>, S extends VtxoScript = Vt
2837
3535
  * Returns empty array if no paths are available.
2838
3536
  */
2839
3537
  getSpendablePaths(script: S, contract: Contract, context: PathContext): PathSelection[];
3538
+ /**
3539
+ * Whether this contract's VTXOs may be picked by *generic* wallet spending —
3540
+ * send, settle, renewal, asset operations, offboard, `available` balance.
3541
+ * Explicit-input APIs (`settle({ inputs })`, `sendBitcoin({ selectedVtxos })`,
3542
+ * …) stay open regardless: naming an outpoint is the intent this gate protects.
3543
+ *
3544
+ * Pure, synchronous and offline — it runs inside the service worker, so no
3545
+ * chain tip, no network, no live plugin object. Absent or `false` ⇒ NOT
3546
+ * spendable: a type core cannot reason about must not leak by omission.
3547
+ *
3548
+ * No `script` parameter: deriving it costs a taproot tree per contract on a
3549
+ * read path (#521) and no shipped handler needs it. A handler that does can
3550
+ * call its own `createScript(contract.params)`.
3551
+ */
3552
+ isGenericallySpendable?(contract: Contract): boolean;
3553
+ /**
3554
+ * Refuse a spend this contract definitively cannot make right now, with a
3555
+ * reason the caller can act on. Called before anything is signed or
3556
+ * submitted, for inputs the caller named explicitly.
3557
+ *
3558
+ * This is the counterpart to {@link isGenericallySpendable}, not a
3559
+ * duplicate of it. That gate keeps escrow out of GENERIC selection and
3560
+ * deliberately leaves explicit-input APIs open, because naming an outpoint
3561
+ * is the intent it protects. Naming one too early is still a mistake
3562
+ * though, and without this it is a mistake the server reports — as a
3563
+ * protocol-level rejection, after the round trip, in terms that do not name
3564
+ * the timelock that was not yet mature.
3565
+ *
3566
+ * **Throw only on a definite no.** Absent, silent, or unsure all mean "no
3567
+ * opinion" and the spend proceeds. A handler must not refuse merely because
3568
+ * it found no path: `getSpendablePaths` legitimately returns empty for
3569
+ * spendable contracts — `arkade`'s skips every covenant leaf, so a program
3570
+ * spendable only through its emulator-signed leaf reports nothing — and an
3571
+ * unreadable timelock (height-typed with no chain tip) is unknown, not
3572
+ * immature. @see cltvMaturity, which keeps those apart.
3573
+ *
3574
+ * Returning a promise is allowed so a handler needing I/O is not forced to
3575
+ * throw synchronously — callers await the result. Prefer synchronous where
3576
+ * possible: this runs on the path between a caller's decision to spend and
3577
+ * the spend itself.
3578
+ *
3579
+ * @throws Error when the contract provably cannot be spent at `context`
3580
+ */
3581
+ assertSpendableNow?(script: S, contract: Contract, context: PathContext): void | Promise<void>;
2840
3582
  }
2841
3583
  /**
2842
3584
  * What a {@link Discoverable.discoverAt} call returns — exactly the
@@ -3116,6 +3858,8 @@ declare class ContractWatcher {
3116
3858
  /** See {@link withCoalescedSubscription}. */
3117
3859
  private subscriptionBatchDepth;
3118
3860
  private subscriptionUpdateDeferred;
3861
+ /** See {@link reportEventSourceUnavailable} — said once, not per attempt. */
3862
+ private eventSourceReported;
3119
3863
  /**
3120
3864
  * Create a contract watcher with the given providers and polling settings.
3121
3865
  *
@@ -3127,7 +3871,8 @@ declare class ContractWatcher {
3127
3871
  * Add a contract to be watched.
3128
3872
  *
3129
3873
  * Once watching, every contract is subscribed and polled whatever
3130
- * its state.
3874
+ * its {@link ContractState} — a `retained` one is held for reads
3875
+ * only, and never enters a background channel.
3131
3876
  *
3132
3877
  * @see getWatchedContracts
3133
3878
  */
@@ -3153,15 +3898,22 @@ declare class ContractWatcher {
3153
3898
  */
3154
3899
  getAllContracts(): Contract[];
3155
3900
  /**
3156
- * Every registered contract, retired (`inactive`) ones included.
3901
+ * Every registered contract except the `retained` ones, retired
3902
+ * (`inactive`) receive addresses included.
3157
3903
  *
3158
3904
  * Feeds both the subscription and the indexer sweep scope, so
3159
3905
  * narrowing it drops a contract from every background channel at
3160
- * once. Nothing may be narrowed out: an Ark receive address can be
3906
+ * once. `state` may never narrow it: an Ark receive address can be
3161
3907
  * paid again after the wallet has rotated past it, and a payment
3162
3908
  * that lands outside every background channel is invisible until
3163
3909
  * some foreground read happens to sweep it. Retirement therefore
3164
3910
  * governs receive-address selection, not coverage.
3911
+ *
3912
+ * {@link ContractWatchState} is the one thing that does narrow it,
3913
+ * and only when an owner has explicitly said the script is done —
3914
+ * a settled swap lockup, a funded one-shot destination. The row
3915
+ * stays in {@link getAllContracts} so reads, annotation and history
3916
+ * are unaffected.
3165
3917
  */
3166
3918
  getWatchedContracts(): Contract[];
3167
3919
  /**
@@ -3198,400 +3950,82 @@ declare class ContractWatcher {
3198
3950
  */
3199
3951
  private connect;
3200
3952
  /**
3201
- * Schedule a reconnection attempt.
3202
- */
3203
- private scheduleReconnect;
3204
- /**
3205
- * Start the failsafe polling interval.
3206
- */
3207
- private startFailsafePolling;
3208
- private pollAllContracts;
3209
- /**
3210
- * Poll specific contracts and emit events for changes.
3211
- */
3212
- private pollContracts;
3213
- /**
3214
- * Run `fn` with subscription updates coalesced into a single
3215
- * `subscribeForScripts` on the way out.
3216
- *
3217
- * {@link addContract} re-subscribes eagerly (the watcher may already be
3218
- * running), and every subscribe posts the *whole* accumulated script list —
3219
- * so a restore scan that discovers N contracts sends N growing POSTs,
3220
- * quadratic in script-slots. Inside this scope those updates are only
3221
- * marked dirty, flushed once on the way out (success and error path alike).
3222
- *
3223
- * A contract added inside the scope is therefore not streaming until the
3224
- * flush. Nothing in the watcher closes that window — the failsafe poll
3225
- * replays repository state and cannot see VTXOs no one has fetched yet. The
3226
- * one caller, `scanContracts`, is covered because `Wallet.restore` follows
3227
- * it with a bulk `refreshVtxos`. A new caller must provide its own
3228
- * equivalent catch-up, or keep the scope short enough not to need one.
3229
- */
3230
- withCoalescedSubscription<T>(fn: () => Promise<T>): Promise<T>;
3231
- private tryUpdateSubscription;
3232
- /**
3233
- * Update the subscription with scripts that should be watched.
3234
- *
3235
- * @see getWatchedContracts
3236
- */
3237
- private updateSubscription;
3238
- /**
3239
- * Main listening loop for subscription events.
3240
- */
3241
- private listenLoop;
3242
- /**
3243
- * Handle a subscription update.
3244
- *
3245
- * Normalization boundary: `getSubscription` is part of the public `IndexerProvider` interface,
3246
- * so a consumer implementation may yield legacy-shaped VTXOs. Normalizing on ingest also fixes
3247
- * the shape of the payloads emitted to external event consumers.
3248
- */
3249
- private handleSubscriptionUpdate;
3250
- /**
3251
- * Process virtual outputs from subscription and route each VTXO to the
3252
- * single contract that actually locks it via `vtxo.script`. If the script
3253
- * doesn't match any watched contract, skip the VTXO rather than fan it
3254
- * out to every matching contract — fan-out produced phantom state in
3255
- * non-owning contracts that then never reconciled.
3256
- */
3257
- private processSubscriptionVtxos;
3258
- /**
3259
- * Emit a virtual output event for a contract.
3260
- */
3261
- private emitVtxoEvent;
3262
- }
3263
-
3264
- /**
3265
- * Intent proof implementation for Bitcoin message signing.
3266
- *
3267
- * Intent proof defines a standard for signing Bitcoin messages as well as proving
3268
- * ownership of outputs.
3269
- *
3270
- * This namespace provides utilities for creating and validating Intent proof.
3271
- *
3272
- * It is greatly inspired by BIP322.
3273
- * @see https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki
3274
- *
3275
- * @example
3276
- * ```typescript
3277
- * // Create a Intent proof
3278
- * const proof = Intent.create(
3279
- * "Hello Bitcoin!",
3280
- * [input],
3281
- * [output]
3282
- * );
3283
- *
3284
- * // Sign the proof
3285
- * const signedProof = await identity.sign(proof);
3286
- *
3287
- */
3288
- declare namespace Intent {
3289
- type Proof = Transaction;
3290
- /**
3291
- * Creates a new Intent proof unsigned transaction.
3292
- *
3293
- * This function constructs a special transaction that can be signed to prove
3294
- * ownership of onchain and virtual outputs. The proof includes the message to be
3295
- * signed and the inputs/outputs that demonstrate ownership.
3296
- *
3297
- * @param message - The Intent message to be signed, either raw string of Message object
3298
- * @param ins - Array of transaction inputs to prove ownership of
3299
- * @param outputs - Optional array of transaction outputs
3300
- * @returns An unsigned Intent proof transaction
3301
- */
3302
- function create(message: string | Message, ins: (TransactionInput | ExtendedCoin)[], outputs?: TransactionOutput[]): Proof;
3303
- /**
3304
- * Compute the fee paid by an intent proof transaction.
3305
- *
3306
- * @param proof - Intent proof transaction
3307
- * @returns The fee in satoshis
3308
- */
3309
- function fee(proof: Proof): number;
3310
- type RegisterMessage = {
3311
- type: "register";
3312
- onchain_output_indexes: number[];
3313
- valid_at: number;
3314
- expire_at: number;
3315
- cosigners_public_keys: string[];
3316
- };
3317
- type DeleteMessage = {
3318
- type: "delete";
3319
- expire_at: number;
3320
- };
3321
- type GetPendingTxMessage = {
3322
- type: "get-pending-tx";
3323
- expire_at: number;
3324
- };
3325
- type Message = RegisterMessage | DeleteMessage | GetPendingTxMessage;
3326
- /**
3327
- * Serialize an intent message to the canonical JSON string used for signing.
3328
- *
3329
- * @param message - Intent message payload
3330
- * @returns Canonical string form of the message
3331
- */
3332
- function encodeMessage(message: Message): string;
3333
- }
3334
-
3335
- /**
3336
- * Delegate identity and fee information returned by `getDelegateInfo`.
3337
- */
3338
- interface DelegateInfo {
3339
- /** Delegate public key. */
3340
- pubkey: string;
3341
- /** Delegate fee amount or expression returned by the delegate. */
3342
- fee: string;
3343
- /** Address for delegate fee collection. Sourced from `delegatorAddress` in Fulmine response, for now. */
3344
- delegateAddress: string;
3345
- /** @deprecated alias for @see DelegateInfo.delegateAddress */
3346
- delegatorAddress?: string;
3347
- }
3348
- /**
3349
- * Optional delegate behavior flags.
3350
- */
3351
- interface DelegateOptions {
3352
- /**
3353
- * Instruct the delegate not to replace an existing delegation
3354
- * (meaning a signed register intent and its forfeit transactions)
3355
- * that already includes at least one virtual output from this request.
3356
- *
3357
- * @defaultValue `false`
3358
- */
3359
- rejectReplace?: boolean;
3360
- }
3361
- /**
3362
- * Provider interface for remote delegation service.
3363
- */
3364
- interface DelegateProvider {
3365
- /**
3366
- * Request delegation for a signed register intent and its forfeit transactions.
3367
- *
3368
- * @param intent - Signed register intent to delegate
3369
- * @param forfeitTxs - Forfeit transactions associated with the delegation request
3370
- * @param options - Optional delegate behavior flags
3371
- */
3372
- delegate(intent: SignedIntent<Intent.RegisterMessage>, forfeitTxs: string[], options?: DelegateOptions): Promise<void>;
3373
- /**
3374
- * Fetch delegate metadata such as pubkey, fee, and delegate address.
3953
+ * Handle "this environment has no `EventSource`": say so once, loudly and
3954
+ * actionably, and answer whether the caller should skip reconnecting.
3375
3955
  *
3376
- * @returns Delegate identity and fee information
3956
+ * Reconnecting is pointless here a missing global is not a dropped
3957
+ * connection, and the default backoff (unlimited attempts, capped at 5s)
3958
+ * would otherwise retry it forever, logging each failure and firing a
3959
+ * `connection_reset` every few seconds for the life of the wallet. Not even
3960
+ * one goes out: subscribers read that event as "the stream dropped, resync
3961
+ * and expect it back", and here it never opened and never will.
3962
+ * Failsafe polling keeps running, so the watcher stays correct and merely
3963
+ * slower; what it loses is push latency.
3377
3964
  */
3378
- getDelegateInfo(): Promise<DelegateInfo>;
3379
- }
3380
- /** @deprecated alias for @see DelegateProvider */
3381
- type DelegatorProvider = DelegateProvider;
3382
- /**
3383
- * REST-based delegate provider implementation.
3384
- * @example
3385
- * ```typescript
3386
- * const provider = new RestDelegateProvider('https://delegate.example.com');
3387
- * const info = await provider.getDelegateInfo();
3388
- * await provider.delegate(intent, forfeitTxs);
3389
- * ```
3390
- */
3391
- declare class RestDelegateProvider implements DelegateProvider {
3392
- url: string;
3965
+ private reportEventSourceUnavailable;
3393
3966
  /**
3394
- * Create a REST delegate provider targeting the given base URL.
3395
- *
3396
- * @param url - Base URL of the remote delegation service.
3967
+ * Schedule a reconnection attempt.
3397
3968
  */
3398
- constructor(url: string);
3969
+ private scheduleReconnect;
3399
3970
  /**
3400
- * Submit a delegation request to the remote delegation service.
3401
- *
3402
- * @param intent - Signed register intent to delegate
3403
- * @param forfeitTxs - Forfeit transactions associated with the delegation request
3404
- * @param options - Optional delegate behavior flags
3405
- * @throws Error if the remote service rejects the request
3971
+ * Start the failsafe polling interval.
3406
3972
  */
3407
- delegate(intent: SignedIntent<Intent.RegisterMessage>, forfeitTxs: string[], options?: DelegateOptions): Promise<void>;
3973
+ private startFailsafePolling;
3974
+ private pollAllContracts;
3408
3975
  /**
3409
- * Fetch delegate metadata exposed by the remote delegation service.
3410
- *
3411
- * @returns Delegate identity and fee information
3412
- * @throws Error if the remote service returns invalid data
3976
+ * Poll specific contracts and emit events for changes.
3413
3977
  */
3414
- getDelegateInfo(): Promise<DelegateInfo>;
3415
- }
3416
- /** @deprecated alias for @see RestDelegateProvider */
3417
- declare const RestDelegatorProvider: typeof RestDelegateProvider;
3418
- type RestDelegatorProvider = RestDelegateProvider;
3419
-
3420
- interface IDelegateManager {
3978
+ private pollContracts;
3421
3979
  /**
3422
- * Delegate virtual outputs to the remote delegation service.
3980
+ * Run `fn` with subscription updates coalesced into a single
3981
+ * `subscribeForScripts` on the way out.
3423
3982
  *
3424
- * Vtxos that are not locked to a delegate-type contract (no tap leaf
3425
- * matches the delegate's pubkey) are filtered out silently, since they
3426
- * cannot be co-signed by the delegate.
3983
+ * {@link addContract} re-subscribes eagerly (the watcher may already be
3984
+ * running), and every subscribe posts the *whole* accumulated script list —
3985
+ * so a restore scan that discovers N contracts sends N growing POSTs,
3986
+ * quadratic in script-slots. Inside this scope those updates are only
3987
+ * marked dirty, flushed once on the way out (success and error path alike).
3427
3988
  *
3428
- * @param vtxos - Virtual outputs to delegate
3429
- * @param destination - Arkade address that should receive renewed funds
3430
- * @param delegateAt - Optional timestamp to force a specific delegation time
3431
- * @returns Successfully delegated and failed outpoint groups
3989
+ * A contract added inside the scope is therefore not streaming until the
3990
+ * flush. Nothing in the watcher closes that window the failsafe poll
3991
+ * replays repository state and cannot see VTXOs no one has fetched yet. The
3992
+ * one caller, `scanContracts`, is covered because `Wallet.restore` follows
3993
+ * it with a bulk `refreshVtxos`. A new caller must provide its own
3994
+ * equivalent catch-up, or keep the scope short enough not to need one.
3432
3995
  */
3433
- delegate(vtxos: ContractVtxo[], destination: string, delegateAt?: Date): Promise<{
3434
- delegated: Outpoint[];
3435
- failed: {
3436
- outpoints: Outpoint[];
3437
- error: unknown;
3438
- }[];
3439
- }>;
3440
- /** Fetch delegate metadata such as pubkey, fee, and delegate address. */
3441
- getDelegateInfo(): Promise<DelegateInfo>;
3442
- }
3443
- /** @deprecated alias for @see IDelegateManager */
3444
- type IDelegatorManager = IDelegateManager;
3445
- declare class DelegateManagerImpl implements IDelegateManager {
3446
- readonly delegateProvider: DelegateProvider;
3447
- readonly arkInfoProvider: Pick<ArkProvider, "getInfo">;
3448
- readonly identity: Identity;
3449
- /** Create a delegate manager from the configured provider, Arkade info source, and wallet identity. */
3450
- constructor(delegateProvider: DelegateProvider, arkInfoProvider: Pick<ArkProvider, "getInfo">, identity: Identity);
3451
- getDelegateInfo(): Promise<DelegateInfo>;
3452
- delegate(vtxos: ContractVtxo[], destination: string, delegateAt?: Date): Promise<{
3453
- delegated: Outpoint[];
3454
- failed: {
3455
- outpoints: Outpoint[];
3456
- error: unknown;
3457
- }[];
3458
- }>;
3459
- }
3460
- /** @deprecated alias for @see DelegateManagerImpl */
3461
- declare const DelegatorManagerImpl: typeof DelegateManagerImpl;
3462
- type DelegatorManagerImpl = DelegateManagerImpl;
3463
-
3464
- /** One transaction's participation in one logical action. */
3465
- interface GroupMembership {
3996
+ withCoalescedSubscription<T>(fn: () => Promise<T>): Promise<T>;
3997
+ private tryUpdateSubscription;
3466
3998
  /**
3467
- * Stable id of the action; txs sharing it group together. Third-party
3468
- * resolvers should namespace it (`"vendor:thing"`) to avoid colliding with
3469
- * other resolvers' groups. SDK built-ins use namespaced ids such as `boarding:`.
3470
- * A membership with an empty groupId is dropped.
3999
+ * Update the subscription with scripts that should be watched.
4000
+ *
4001
+ * @see getWatchedContracts
3471
4002
  */
3472
- groupId: string;
3473
- /** Human label for the action, e.g. "Dice game". */
3474
- label?: string;
3475
- /** App category for icon/filtering, e.g. "game". */
3476
- kind?: string;
4003
+ private updateSubscription;
3477
4004
  /**
3478
- * Free-form row data. Same-group metadata is shallow-merged with
3479
- * earlier-resolver keys winning.
4005
+ * Main listening loop for subscription events.
3480
4006
  */
3481
- metadata?: Record<string, unknown>;
4007
+ private listenLoop;
3482
4008
  /**
3483
- * This tx's unsigned sat contribution to this group. Defaults to the tx's
3484
- * full amount; the builder applies direction. Use it to split a batched tx
3485
- * across groups. Same-key receive rows paired with a sent row are treated
3486
- * as change and excluded from `Activity.amount`. A membership with a
3487
- * non-finite amount (NaN/Infinity) is dropped.
4009
+ * Handle a subscription update.
4010
+ *
4011
+ * Normalization boundary: `getSubscription` is part of the public `IndexerProvider` interface,
4012
+ * so a consumer implementation may yield legacy-shaped VTXOs. Normalizing on ingest also fixes
4013
+ * the shape of the payloads emitted to external event consumers.
3488
4014
  */
3489
- amount?: number;
3490
- }
3491
- /** A pluggable resolver keyed by `id`. */
3492
- interface ActivityResolver {
4015
+ private handleSubscriptionUpdate;
3493
4016
  /**
3494
- * Registry key override or remove by it. Namespace it (`"vendor:games"`)
3495
- * so independent libraries don't clobber each other; `use()` overwrites
3496
- * silently on a duplicate id.
4017
+ * Process virtual outputs from subscription and route each VTXO to the
4018
+ * single contract that actually locks it via `vtxo.script`. If the script
4019
+ * doesn't match any watched contract, skip the VTXO rather than fan it
4020
+ * out to every matching contract — fan-out produced phantom state in
4021
+ * non-owning contracts that then never reconciled.
3497
4022
  */
3498
- id: string;
4023
+ private processSubscriptionVtxos;
3499
4024
  /**
3500
- * Load correlation data before `resolve` runs. If it rejects, this resolver
3501
- * contributes no memberships.
4025
+ * Emit a virtual output event for a contract.
3502
4026
  */
3503
- prepare?(): Promise<void>;
3504
- /** Pure and synchronous. The groups this tx belongs to, or undefined to leave it plain. */
3505
- resolve(tx: ArkTransaction): GroupMembership[] | undefined;
3506
- }
3507
- /** The non-id, non-amount part of a {@link GroupMembership}. */
3508
- interface ActivityIntent {
3509
- /** Human label for the action, e.g. "Dice game". */
3510
- label?: string;
3511
- /** App category for icon/filtering, e.g. "game". */
3512
- kind?: string;
3513
- /** Free-form row data, shallow-merged across the group's resolvers (first-writer-wins). */
3514
- metadata?: Record<string, unknown>;
3515
- }
3516
- /** One logical activity. */
3517
- interface Activity {
3518
- /** The groupId, or the natural tx key for untagged rows. */
3519
- id: string;
3520
- /** Merged intent for the group, if any resolver tagged it. */
3521
- intent?: ActivityIntent;
3522
- /** Member txs, oldest-first. */
3523
- txs: ArkTransaction[];
3524
- /** Signed net sats: positive received, negative sent; same-key change rows are excluded. */
3525
- amount: number;
3526
- /** Earliest member createdAt (ms since epoch). */
3527
- createdAt: number;
3528
- /** True once every member tx is settled. */
3529
- settled: boolean;
3530
- }
3531
- /** Resolver registry keyed by id. */
3532
- declare class ActivityRegistry {
3533
- private readonly resolvers;
3534
- /** Add a resolver, or override an existing one with the same id (kept in place). */
3535
- use(resolver: ActivityResolver): void;
3536
- /** Remove a resolver (built-in or custom) by id. */
3537
- remove(id: string): void;
3538
- /** The registered resolver ids, in registration order. */
3539
- list(): string[];
3540
- /** All registered resolvers, in registration (priority) order. */
3541
- all(): ActivityResolver[];
3542
- }
3543
- /** Built-in resolver: labels on-chain boarding (deposit) transactions. */
3544
- declare function boardingResolver(): ActivityResolver;
3545
- /** Built-in resolver: labels collaborative exits (VTXOs forfeited to chain in a batch). */
3546
- declare function collabExitResolver(): ActivityResolver;
3547
- /**
3548
- * Built-in resolver: labels the genesis transaction of a minted asset — "Asset
3549
- * mint" on the issuer's sent tx, "Asset receive" when the fresh supply arrives
3550
- * in the genesis tx itself. An asset id encodes its genesis txid; reissues and
3551
- * transfers carry the asset under a different `arkTxid`, so they are left plain.
3552
- *
3553
- * `metadata.amount` is the decimal string of the asset's `bigint` amount, kept
3554
- * as a string so large supplies survive JSON round-trips without truncation.
3555
- * Recover the value with `BigInt(metadata.amount as string)` — using it
3556
- * directly in arithmetic coerces (`"10" + 1` is `"101"`, not `11`) and loses
3557
- * precision past `Number.MAX_SAFE_INTEGER`.
3558
- */
3559
- declare function assetMintResolver(): ActivityResolver;
3560
- /**
3561
- * A registry pre-populated with the SDK's built-in resolvers: `boarding`,
3562
- * `collab-exit`, and `asset-mint`.
3563
- */
3564
- declare function createDefaultActivityRegistry(): ActivityRegistry;
3565
-
3566
- /**
3567
- * A source of unilateral-exit chain data for a set of VTXOs. Sources are tried
3568
- * in order by the resolver; a source returns `null` (chain) or omits keys (psbts)
3569
- * for data it cannot supply — a "miss" — and the resolver falls through.
3570
- */
3571
- interface ExitDataSource {
3572
- readonly name: string;
3573
- /** Full ancestry chain for a vtxo (all pages merged), or null on a miss. */
3574
- getVtxoChain(vtxo: Outpoint): Promise<ChainTx[] | null>;
3575
- /** Base64 PSBTs this source has, keyed by (unsigned) txid. Absent key = miss. */
3576
- getVirtualTxs(txids: string[]): Promise<Map<string, string>>;
3577
- }
3578
- /** Reads exit chain data through an ordered chain of sources. */
3579
- interface ExitChainResolver {
3580
- getVtxoChain(vtxo: Outpoint): Promise<ChainTx[]>;
3581
- getVirtualTxs(txids: string[]): Promise<string[]>;
4027
+ private emitVtxoEvent;
3582
4028
  }
3583
- /**
3584
- * Assemble the standard exit-data resolver: local repo (if configured) → any
3585
- * extra sources (e.g. a future provider) → indexer. Read-through persists to the
3586
- * repository. With no repository this is exactly the indexer path (a no-op seam).
3587
- */
3588
- declare function createExitChainResolver(params: {
3589
- indexer: IndexerProvider;
3590
- repository?: VirtualTxRepository;
3591
- extraSources?: ExitDataSource[];
3592
- }): ExitChainResolver;
3593
-
3594
- type ExitCaptureMode = "lite" | "full";
3595
4029
 
3596
4030
  /**
3597
4031
  * Wallet receive-address strategy.
@@ -3675,8 +4109,10 @@ interface BaseWalletConfig {
3675
4109
  /**
3676
4110
  * Minimum accepted checkpoint exit delay decoded from `ArkInfo.checkpointTapscript`,
3677
4111
  * as wall-clock seconds. Defaults per network — see
3678
- * `defaultCheckpointExitDelayPolicy`. Lowering it below the default relaxes a
3679
- * fund-safety bound; intended for local testing.
4112
+ * `defaultCheckpointExitDelayPolicy`, which already carries the value the
4113
+ * hosted signet and mutinynet Arkade Services advertise, so neither needs
4114
+ * this set. Lowering it below the default relaxes a fund-safety bound;
4115
+ * intended for local testing.
3680
4116
  */
3681
4117
  minCheckpointExitDelaySeconds?: bigint;
3682
4118
  /**
@@ -3900,8 +4336,15 @@ interface WalletBalance {
3900
4336
  pendingRecovery: number;
3901
4337
  /** Total balance across offchain, recoverable, pending-recovery, and boarding funds. */
3902
4338
  total: number;
3903
- /** Asset balance entries (`assetId` & `amount`) */
4339
+ /** Asset balance entries (`assetId` & `amount`) the wallet owns. */
3904
4340
  assets: Asset[];
4341
+ /**
4342
+ * The subset of {@link assets} generic spending will accept, i.e. the asset
4343
+ * analogue of {@link available}. Assets have no owned/spendable split of
4344
+ * their own, so `assets - availableAssets` is what is held but not
4345
+ * selectable — escrowed, intent-locked or awaiting recovery.
4346
+ */
4347
+ availableAssets: Asset[];
3905
4348
  }
3906
4349
  /**
3907
4350
  * Parameters accepted by `OnchainWallet.send`.
@@ -3924,7 +4367,13 @@ interface SendBitcoinParams {
3924
4367
  * @deprecated Does not appear to have ever been used.
3925
4368
  */
3926
4369
  memo?: string;
3927
- /** Optional explicit virtual output selection used by `Wallet.sendBitcoin`. */
4370
+ /**
4371
+ * Optional explicit virtual output selection used by `Wallet.sendBitcoin`.
4372
+ * Ungated, like `settle({ inputs })`: whatever is named here is spent, even
4373
+ * if generic selection would skip it.
4374
+ *
4375
+ * @see IReadonlyWallet.getSpendableVtxos
4376
+ */
3928
4377
  selectedVtxos?: ExtendedVirtualCoin[];
3929
4378
  }
3930
4379
  /**
@@ -4453,6 +4902,20 @@ interface IReadonlyWallet {
4453
4902
  * @see GetVtxosFilter
4454
4903
  */
4455
4904
  getVtxos(filter?: GetVtxosFilter): Promise<NormalizedExtendedVirtualCoin[]>;
4905
+ /**
4906
+ * The subset of {@link getVtxos} that generic spending may select: the same
4907
+ * filter, minus contracts the generic-spending gate closes, minus funds
4908
+ * awaiting recovery under a past-cutoff signer, minus outpoints locked by an
4909
+ * in-flight intent. Every implicit coin selection in the SDK reads this;
4910
+ * `getVtxos` stays the raw reporting/recovery read.
4911
+ *
4912
+ * Both exclusion sets are derived from one contract snapshot, so they cannot
4913
+ * disagree about which VTXOs exist.
4914
+ *
4915
+ * @param filter - Same flags, same defaults, as {@link getVtxos}
4916
+ * @see GetVtxosFilter
4917
+ */
4918
+ getSpendableVtxos(filter?: GetVtxosFilter): Promise<NormalizedExtendedVirtualCoin[]>;
4456
4919
  /** @returns Onchain boarding inputs tracked by the wallet. */
4457
4920
  getBoardingUtxos(): Promise<ExtendedCoin[]>;
4458
4921
  /** @returns Wallet transaction history derived from boarding and Arkade activity. */
@@ -4477,6 +4940,94 @@ interface IReadonlyWallet {
4477
4940
  clear(): Promise<void>;
4478
4941
  }
4479
4942
 
4943
+ /**
4944
+ * Where the SDK gets its `EventSource`.
4945
+ *
4946
+ * Every server-sent-events stream this SDK opens — settlement events, the tx
4947
+ * feed, the indexer script subscription — needs one, and until now each site
4948
+ * reached for the global. That works in browsers and, via
4949
+ * {@link ExpoArkProvider}/{@link ExpoIndexerProvider}, in React Native. It does
4950
+ * NOT work in Node, which exposes `EventSource` only behind
4951
+ * `--experimental-eventsource` (24.x) — so a CLI, a server-side wallet or a
4952
+ * background worker got a `ReferenceError` out of the stream and everything
4953
+ * event-driven degraded to whatever polling happened to be running.
4954
+ *
4955
+ * Resolution order, per call: an explicit per-provider factory, then whatever
4956
+ * {@link configureEventSource} was given, then the global. When none answers,
4957
+ * {@link resolveEventSource} throws {@link EventSourceUnavailableError} rather
4958
+ * than a bare `ReferenceError`, so callers can tell "this environment has no
4959
+ * SSE" from "the connection dropped" — a distinction that matters, because the
4960
+ * first is not worth reconnecting for and the second is.
4961
+ */
4962
+ /**
4963
+ * The slice of `EventSource` this SDK actually uses.
4964
+ *
4965
+ * Structural rather than the DOM type on purpose: Node's `eventsource` package
4966
+ * and the React Native polyfills are not DOM `EventSource`s, and demanding one
4967
+ * made every consumer launder a perfectly capable object through
4968
+ * `as unknown as EventSource` — a cast that hides real mismatches as readily as
4969
+ * it waves through this one.
4970
+ */
4971
+ interface EventSourceLike {
4972
+ addEventListener(type: "message" | "error", listener: (event: MessageEvent) => void): void;
4973
+ removeEventListener(type: "message" | "error", listener: (event: MessageEvent) => void): void;
4974
+ close(): void;
4975
+ }
4976
+ /** Opens an SSE connection to `url`. `new EventSource(url)`, as a value. */
4977
+ type EventSourceFactory = (url: string) => EventSourceLike;
4978
+ /** Options shared by every provider that opens an SSE stream. */
4979
+ interface EventSourceCapable {
4980
+ /**
4981
+ * Where this provider gets its `EventSource`, overriding
4982
+ * {@link configureEventSource} and the global. Use it when one process
4983
+ * needs different transports per connection; otherwise configure once.
4984
+ */
4985
+ eventSource?: EventSourceFactory;
4986
+ }
4987
+ /**
4988
+ * No `EventSource` could be resolved, so no SSE stream can be opened.
4989
+ *
4990
+ * A property of the environment, not of the connection: retrying cannot fix it,
4991
+ * which is why {@link ContractWatcher} reports it once and stops reconnecting
4992
+ * instead of looping on it.
4993
+ */
4994
+ declare class EventSourceUnavailableError extends Error {
4995
+ constructor();
4996
+ }
4997
+ /**
4998
+ * Type guard for {@link EventSourceUnavailableError}.
4999
+ *
5000
+ * Falls back to the `name` because a custom error crossing the service-worker
5001
+ * `postMessage` boundary arrives as a plain `Error` — the same reason
5002
+ * `ProviderUnavailableError` keeps its state in the message.
5003
+ */
5004
+ declare function isEventSourceUnavailableError(error: unknown): error is Error;
5005
+ /**
5006
+ * Set the `EventSource` every provider uses by default. Call once at startup,
5007
+ * before opening a wallet; pass `undefined` to go back to the global.
5008
+ *
5009
+ * This exists because the provider-level option cannot reach providers the SDK
5010
+ * builds for you — `Wallet.create({ arkServerUrl })` constructs both itself.
5011
+ *
5012
+ * @example
5013
+ * ```typescript
5014
+ * import { EventSource } from "eventsource";
5015
+ * configureEventSource((url) => new EventSource(url));
5016
+ * ```
5017
+ */
5018
+ declare function configureEventSource(factory?: EventSourceFactory): void;
5019
+ /** What {@link configureEventSource} last set, if anything. */
5020
+ declare function getConfiguredEventSource(): EventSourceFactory | undefined;
5021
+ /**
5022
+ * Resolve the factory to open a stream with, or throw
5023
+ * {@link EventSourceUnavailableError}.
5024
+ *
5025
+ * The global is read on every call rather than captured at module load: a test
5026
+ * (or a polyfill) that assigns `globalThis.EventSource` after import must still
5027
+ * be seen.
5028
+ */
5029
+ declare function resolveEventSource(override?: EventSourceFactory): EventSourceFactory;
5030
+
4480
5031
  type PaginationOptions = {
4481
5032
  pageIndex?: number;
4482
5033
  pageSize?: number;
@@ -4744,7 +5295,9 @@ interface IndexerProvider {
4744
5295
  */
4745
5296
  declare class RestIndexerProvider implements IndexerProvider {
4746
5297
  serverUrl: string;
4747
- constructor(serverUrl?: string);
5298
+ /** Overrides {@link configureEventSource} for this provider's subscription. */
5299
+ protected readonly eventSource?: EventSourceFactory;
5300
+ constructor(serverUrl?: string, options?: EventSourceCapable);
4748
5301
  getVtxoTree(batchOutpoint: Outpoint, opts?: PaginationOptions): Promise<{
4749
5302
  vtxoTree: Tx[];
4750
5303
  page?: PageResponse;
@@ -5040,7 +5593,9 @@ interface ArkProvider {
5040
5593
  */
5041
5594
  declare class RestArkProvider implements ArkProvider {
5042
5595
  serverUrl: string;
5043
- constructor(serverUrl?: string);
5596
+ /** Overrides {@link configureEventSource} for this provider's streams. */
5597
+ protected readonly eventSource?: EventSourceFactory;
5598
+ constructor(serverUrl?: string, options?: EventSourceCapable);
5044
5599
  /**
5045
5600
  * Last server-info digest seen (from {@link getInfo}). Sent as `X-Digest`
5046
5601
  * on outgoing requests so arkd can reject a client whose cached info is
@@ -5234,4 +5789,4 @@ declare namespace ProtoTypes {
5234
5789
  export { };
5235
5790
  }
5236
5791
 
5237
- export { type TreeSignatureEvent 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 Network as J, CSVMultisigTapscript as K, type SignerSet as L, type BatchStartedEvent as M, type NormalizedExtendedVirtualCoin as N, type Outpoint as O, type TreeSigningStartedEvent as P, TxTree as Q, type Recipient as R, type SendBitcoinParams as S, type TxNotification as T, type TreeNoncesEvent as U, VtxoScript as V, type WalletRepository as W, type BatchFinalizationEvent as X, type BatchFinalizedEvent as Y, type BatchFailedEvent as Z, type TreeTxEvent as _, type Identity as a, type BatchSignableIdentity as a$, type DescriptorProvider as a0, type IReadonlyWallet as a1, type ReadonlyIdentity as a2, type OnchainProvider as a3, type DelegateProvider as a4, type ReadonlyWalletConfig as a5, type ExitCaptureMode as a6, type ExitDataSource as a7, type IReadonlyAssetManager as a8, type ContractSyncState as a9, type IssuanceParams as aA, type ReissuanceParams as aB, type BurnParams as aC, type RenewVtxosOptions as aD, type ContractWithVtxos as aE, type PathSelection as aF, type ContractEvent as aG, type AssetDetails as aH, type IssuanceResult as aI, type DelegateInfo as aJ, type MigrationGlobalSkipReason as aK, type MigrationLegSkipReason as aL, type SignerStatus as aM, type StorageConfig as aN, type IVtxoManager as aO, type ExplorerTransaction as aP, type EncodedVtxoScript as aQ, type Status as aR, type ChainTx as aS, type PathContext as aT, type ActivityIntent as aU, type ActivityResolver as aV, type ArkIntentState as aW, type ArkTapscript as aX, type AssetMetadata as aY, type BaseWalletConfig as aZ, type BatchInfo as a_, type NetworkName as aa, type ArkInfo as ab, ArkAddress as ac, type Coin as ad, ContractManager as ae, type ContractManagerConfig as af, type SettlementConfig as ag, VtxoManager as ah, type SignerSession as ai, type SignedIntent as aj, Intent as ak, type TimeHeight as al, type DescriptorSigningRequest as am, Transaction as an, type IntentFeeConfig as ao, type OffchainInput as ap, FeeAmount as aq, type OnchainInput as ar, type FeeOutput as as, type ContractWatcherConfig as at, type Asset as au, type FeeInfo as av, type CreateContractParams as aw, type GetContractsFilter as ax, type GetSpendablePathsOptions as ay, type GetAllSpendingPathsOptions as az, type WalletConfig as b, type VirtualStatus as b$, CLTVMultisigTapscript as b0, type CandidateDeps as b1, ChainTxType as b2, type CommitmentTx as b3, ConditionCSVMultisigTapscript as b4, ConditionMultisigTapscript as b5, type ContractBalance as b6, type ContractEventCallback as b7, type ContractHandler as b8, type ContractState as b9, type Nonces as bA, type Output as bB, type PageResponse as bC, type PaginationOptions as bD, PartialSig as bE, type ProviderClass as bF, RestDelegateProvider as bG, RestDelegatorProvider as bH, type ScanContractsOptions as bI, type ScanResult as bJ, type ScheduledSession as bK, SettlementEventType as bL, type SignRequest as bM, type SignerClassification as bN, type SubscriptionEvent as bO, type SubscriptionHeartbeat as bP, type TapLeaves as bQ, TapTreeCoder as bR, TapscriptType as bS, type TreeNonces as bT, type TreePartialSigs as bU, type Tx as bV, type TxHistoryRecord as bW, type TxKey as bX, type TxTag as bY, type TxTreeNode as bZ, TxType as b_, type ContractVtxo as ba, ContractWatcher as bb, DelegateManagerImpl as bc, type DelegateOptions as bd, DelegatorManagerImpl as be, type DelegatorProvider as bf, type DeprecatedSignerMigrationReport as bg, type DeprecatedSignerReport as bh, DigestMismatchError as bi, type Discoverable as bj, type DiscoveredContract as bk, type DiscoveryDeps as bl, ESPLORA_URL as bm, EsploraProvider as bn, type ExitChainResolver as bo, type ExtendedContractVtxo as bp, type GroupMembership as bq, type HandlerError as br, type IDelegatorManager as bs, INTENT_TERMINAL_STATES as bt, IndexerTxType as bu, type KnownMetadata as bv, type MigrateDeprecatedSignerOptions as bw, type MigrationLegReport as bx, type MigrationVtxoRef as by, MultisigTapscript as bz, type WalletBalance as c, type Vtxo as c0, type VtxoChain as c1, type VtxoType as c2, type WalletMode as c3, assetMintResolver as c4, boardingResolver as c5, canRecoverOnchain as c6, canSpendOffchain as c7, classifyAgainstSignerSet as c8, classifyContractSigner as c9, collabExitResolver as ca, createDefaultActivityRegistry as cb, createExitChainResolver as cc, decodeTapscript as cd, getNetwork as ce, getSequence as cf, hasCandidates as cg, hasTerminalSpend as ch, isBatchSignable as ci, isCooperativelyMigratable as cj, isDiscoverable as ck, isExpired as cl, isPastExpiry as cm, isRecoverable as cn, isSpendable as co, isSubdust as cp, isTerminalIntentState as cq, isVirtualCoin as cr, isVtxoExpiringSoon as cs, networks as ct, scriptFromTapLeafScript as cu, signerSetFromInfo as cv, toXOnlySignerHex as cw, type TapscriptDeriving as cx, 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 };
5792
+ export { type DescriptorProvider 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 Network as J, CSVMultisigTapscript as K, type BatchStartedEvent as L, type TreeSigningStartedEvent as M, type NormalizedExtendedVirtualCoin as N, type Outpoint as O, TxTree as P, type TreeNoncesEvent as Q, type Recipient as R, type SendBitcoinParams as S, type TxNotification as T, type BatchFinalizationEvent as U, VtxoScript as V, type WalletRepository as W, type BatchFinalizedEvent as X, type BatchFailedEvent as Y, type TreeTxEvent as Z, type TreeSignatureEvent as _, type Identity as a, type BaseWalletConfig as a$, type IReadonlyWallet as a0, type ReadonlyIdentity as a1, type OnchainProvider as a2, type DelegateProvider as a3, type ReadonlyWalletConfig as a4, type ExitCaptureMode as a5, type ExitDataSource as a6, type IReadonlyAssetManager as a7, type ContractSyncState as a8, type RecipientAddressContext as a9, type GetSpendablePathsOptions as aA, type GetAllSpendingPathsOptions as aB, type IssuanceParams as aC, type ReissuanceParams as aD, type BurnParams as aE, type RenewVtxosOptions as aF, type PathSelection as aG, type ContractEvent as aH, type AssetDetails as aI, type IssuanceResult as aJ, type DelegateInfo as aK, type MigrationGlobalSkipReason as aL, type MigrationLegSkipReason as aM, type SignerStatus as aN, type StorageConfig as aO, type IVtxoManager as aP, type ExplorerTransaction as aQ, type EncodedVtxoScript as aR, type Status as aS, type ChainTx as aT, type PathContext as aU, type ActivityIntent as aV, type ActivityResolver as aW, type ArkIntentState as aX, type ArkTapscript as aY, type AssetMetadata as aZ, BITCOIN_EMULATOR_PUBKEY as a_, type NetworkName as aa, type ArkInfo as ab, ArkAddress as ac, type ContractWithVtxos as ad, selectPendingRecoveryOutpoints as ae, type Coin as af, ContractManager as ag, type ContractManagerConfig as ah, type SettlementConfig as ai, VtxoManager as aj, type SignerSession as ak, type SignedIntent as al, Intent as am, type TimeHeight as an, type DescriptorSigningRequest as ao, Transaction as ap, type IntentFeeConfig as aq, type OffchainInput as ar, FeeAmount as as, type OnchainInput as at, type FeeOutput as au, type ContractWatcherConfig as av, type Asset as aw, type FeeInfo as ax, type CreateContractParams as ay, type GetContractsFilter as az, type WalletConfig as b, TapscriptType as b$, type BatchInfo as b0, type BatchSignableIdentity as b1, CLTVMultisigTapscript as b2, type CandidateDeps as b3, ChainTxType as b4, type CommitmentTx as b5, ConditionCSVMultisigTapscript as b6, ConditionMultisigTapscript as b7, type ContractBalance as b8, type ContractEventCallback as b9, IndexerTxType as bA, type KnownMetadata as bB, MUTINYNET_EMULATOR_PUBKEY as bC, type MigrateDeprecatedSignerOptions as bD, type MigrationLegReport as bE, type MigrationVtxoRef as bF, MultisigTapscript as bG, type Nonces as bH, type Output as bI, type PageResponse as bJ, type PaginationOptions as bK, PartialSig as bL, type ProviderClass as bM, REGTEST_EMULATOR_PUBKEY as bN, RestDelegateProvider as bO, RestDelegatorProvider as bP, type ScanContractsOptions as bQ, type ScanResult as bR, type ScheduledSession as bS, SettlementEventType as bT, type SignRequest as bU, type SignerClassification as bV, type SignerSet as bW, type SubscriptionEvent as bX, type SubscriptionHeartbeat as bY, type TapLeaves as bZ, TapTreeCoder as b_, type ContractHandler as ba, type ContractState as bb, type ContractVtxo as bc, ContractWatcher as bd, DelegateManagerImpl as be, type DelegateOptions as bf, DelegatorManagerImpl as bg, type DelegatorProvider as bh, type DeprecatedSignerMigrationReport as bi, type DeprecatedSignerReport as bj, DigestMismatchError as bk, type Discoverable as bl, type DiscoveredContract as bm, type DiscoveryDeps as bn, ESPLORA_URL as bo, EsploraProvider as bp, type EventSourceCapable as bq, type EventSourceFactory as br, type EventSourceLike as bs, EventSourceUnavailableError as bt, type ExitChainResolver as bu, type ExtendedContractVtxo as bv, type GroupMembership as bw, type HandlerError as bx, type IDelegatorManager as by, INTENT_TERMINAL_STATES as bz, type WalletBalance as c, type TreeNonces as c0, type TreePartialSigs as c1, type Tx as c2, type TxHistoryRecord as c3, type TxKey as c4, type TxTag as c5, type TxTreeNode as c6, TxType as c7, type VirtualStatus as c8, type Vtxo as c9, isPastExpiry as cA, isRecoverable as cB, isSpendable as cC, isSubdust as cD, isTerminalIntentState as cE, isVirtualCoin as cF, isVtxoExpiringSoon as cG, networks as cH, resolveEmulatorPubkey as cI, resolveEventSource as cJ, scriptFromTapLeafScript as cK, signerSetFromInfo as cL, toXOnlySignerHex as cM, type TapscriptDeriving as cN, type VtxoChain as ca, type VtxoType as cb, type WalletMode as cc, assetMintResolver as cd, boardingResolver as ce, canRecoverOnchain as cf, canSpendOffchain as cg, classifyAgainstSignerSet as ch, classifyContractSigner as ci, collabExitResolver as cj, configureEventSource as ck, createDefaultActivityRegistry as cl, createExitChainResolver as cm, decodeTapscript as cn, defaultEmulatorPubkey as co, getConfiguredEventSource as cp, getNetwork as cq, getRandomId as cr, getSequence as cs, hasCandidates as ct, hasTerminalSpend as cu, isBatchSignable as cv, isCooperativelyMigratable as cw, isDiscoverable as cx, isEventSourceUnavailableError as cy, isExpired as cz, 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 };