Stamp raw virtual outputs with the correct per-contract tapscripts (forfeit, intent, tap tree).
Resolves each vtxo's script to its owning contract via the contract
repository and attaches the matching tapscripts. Throws when any vtxo
references a script with no registered contract — callers are expected
to register the contract before asking for annotation. This is the
single shared path that replaces scattered extendVirtualCoin* calls
in wallet/handler code, and keeps the wallet from silently stamping the
default tapscript onto a non-default vtxo.
Optionaltapscripts: ContractTapscriptCacheThrow unless every one of vtxos still has an annotatable contract.
Spending does not re-derive tapscripts — it uses the ones stored on the coin — so a contract that stopped being annotatable (handler no longer registered, or params its handler now rejects) still builds and submits a transaction fine, and only fails afterwards, in the bookkeeping that re-annotates the inputs. Calling this before submitting turns that into a refusal to spend, naming the contract, rather than a broadcast whose local state could not be recorded.
OptionalassertThrow when one of vtxos belongs to a contract that provably cannot be
spent right now, asking each owning handler's
ContractHandler.assertSpendableNow.
The complement of isContractGenericallySpendable, which keeps escrow out of generic selection and leaves explicit-input APIs open on purpose. This does not close that door — it makes walking through it too early report itself locally, naming the timelock, instead of coming back as a protocol-level rejection after the round trip.
Handlers answer only where they are certain, so contracts with no opinion (which is all of them but VHTLC today) pass through untouched and cost nothing — not even a chain-tip read.
Optional so that adding it is not a breaking change for an embedder with
its own IContractManager. An implementation that omits it simply offers
no opinion, which is the same answer every non-VHTLC contract gives.
OptionalwalletDescriptor: () => Promise<string | undefined>Create and register a new contract.
Implementations may validate that:
params.typeparams.script matches the script derived from params.paramsThe contract script is used as the unique identifier.
OptionalcreatecreateContract for a set, one indexer round trip instead of N.
Optional so adding it breaks no implementer of this public interface
(serviceWorker/wallet.ts proxies each method over wire): fall back.
Delete a contract by script, dropping both the row and the watch.
Destructive: the row is what keeps the contract's VTXOs
annotatable and its transactions readable in history, so to stop
watching a finished contract use
setContractWatchState("retained") instead.
Release resources (stop watching, clear listeners).
Get all possible spending paths for a contract.
Returns an empty array if the contract or its handler cannot be found.
List contracts with optional filters.
Optionalfilter: ContractFilterList contracts and their current virtual outputs.
If no filter is provided, returns all contracts with their virtual outputs.
Optionalfilter: ContractFilterAllocate the next signing descriptor through the manager-owned HD
watermark path. Returns undefined when look-ahead/allocation is not
configured.
Get all currently spendable paths for a contract.
Returns an empty array if the contract or its handler cannot be found.
Latest provider-sync health (online vs. degraded to repository data). See ContractSyncState.
OptionalgetEvery script registered via watchScript. Async for the same reason isWatching is: a service worker answers over the bus.
Whether the underlying watcher is currently active.
Subscribe to contract events.
Unsubscribe function
Rebuild the HD look-ahead watch window around the current allocation
watermark. No-op when the manager was configured without lookAhead.
Call after anything that moves the watermark (restore, boarding allocation, receive rotation, server-signer rotation). Concurrent calls coalesce into a single drain.
Reconcile specific outpoints with the indexer's authoritative state and upsert the result into the wallet repository.
The cursor-derived delta sync filters by created_at, so a VTXO that
was created before the cursor but spent recently won't surface in a
standard refreshVtxos() call. This method is the surgical recovery
path for that case: when something hands us a stale outpoint (e.g. the
server returns VTXO_ALREADY_SPENT with a vtxo_outpoint in its
error metadata), call this to pull the latest state and unblock the
caller — no full re-scan, no cursor change.
Outpoints not owned by any tracked contract are silently dropped.
Force a virtual output refresh from the indexer.
Without options, refreshes all contracts from scratch. With options, narrows the refresh to specific scripts and/or a time window.
Optionalopts: RefreshVtxosOptionsExplicit, gap-limit contract discovery used by wallet.restore().
Walks HD indices from 0, asking every registered Discoverable
handler whether it owns a contract anchored at that index, and
registers each find via the idempotent createContract. A hit
at index i (by any handler, including an injected swap handler)
resets the gap counter, so swap discovery keeps the HD window open.
Error contract (safety-critical — see spec §4):
handlerErrors
and makes its index indeterminate: it never advances the gap
counter, and the scan stops verifying there rather than closing a
window it never observed close. A batched
Discoverable.discoverRange failure makes its whole requested
range indeterminate. It still never throws.materialize() throwing, or
createContract rejecting — propagates out of scanContracts
(it invalidates the gap-window signal, so a silent truncation
would risk hiding user funds).See ScanContractsOptions.
See ScanResult. The caller surfaces truncatedAt /
handlerErrors after the inline VTXO pull.
Convenience helper to update only the contract state. Note
inactive governs receive-address selection and does not stop
watching; see ContractState and
setContractWatchState.
Convenience helper to update only the contract's watch state.
retained is how an owner says "this script is done": it leaves
the subscription and the sweep, while the row — and so history,
annotation and restore — is untouched. awaiting-funds asks for
coverage only until the script is funded, after which the manager
demotes it to retained itself.
OptionalunspendableWhich of vtxos their owning handler refuses right now, keyed by outpoint
(txid:vout) and valued with the handler's own explanation.
The predicate form of assertSpendableNow, for callers that must DROP a refused input rather than fail the batch holding it. Keyed per outpoint, not per script: a relative (CSV) timelock is measured from each coin's own confirmation, so two coins on one contract can disagree.
Optional for the same reason assertSpendableNow is.
OptionalwalletDescriptor: () => Promise<string | undefined>OptionalunwatchStop watching a script registered via watchScript. Takes one or a set, and rebuilds the subscription once either way.
OptionalwatchReport VTXO activity at script without registering it as a contract,
and without the wallet owning it. Activity arrives as
ContractEvent vtxo_received / vtxo_spent without the
contract — narrow with isContractVtxoEvent;
nothing is persisted and the outputs never enter the wallet's balance,
renewal or recovery paths.
At-least-once, and re-announces on restart — the registration is
in-memory, so there is no baseline to diff a restart against.
Deduplicate by outpoint, and tolerate a vtxo_spent with no
preceding vtxo_received — an output can be created and spent
inside one gap in the stream. Re-registering a watched script is a
no-op, so a caller may re-derive its whole set on a timer.
Reports spendable outputs: a preconfirmed one counts, so a fresh funding is not missed, but one already recoverable or swept is not reported.
Takes one script or a set. A set costs one subscription update and one indexer read however many scripts it carries, which is the difference between registering and re-registering cheaply and paying N round trips — what a service watching an address per payment does on every restart.
Optional so adding it does not break an embedder with its own
IContractManager; both shipped implementations provide it for real.
Optionaloptions: { label?: string }
Advance the HD signing descriptor watermark to
indexand refill the watched look-ahead band. No-op when look-ahead/allocation is not configured.