import { type PublicClient } from "viem"; import type { ChainHeadSubscriber } from "./client.js"; import type { ClientConfig } from "./config.js"; import type { Debug } from "./debug.js"; import type { MaterializerStore } from "./store.js"; /** * A live watch. `stop()` releases it (idempotent); the underlying * subscription is shared and torn down when the last handle stops. * * @category live data */ export interface WatchHandle { /** * Release this handle's reference on its watch scope (idempotent — extra calls * are no-ops). Handles on the same scope share one subscription; stopping the * LAST one tears down the scope's local materialization after a short linger * (which absorbs unmount/remount without re-snapshotting): the subscription is * dropped and the market's fills/orders are purged, so `getLive*` reads for it * return empty again. The market row itself is kept for list views. */ stop(): void; } /** * Per-market watch state: `"unwatched"` (no active watch — `getLive*` reads * return empty), `"hydrating"` (watch registered; snapshot/backfill/reconnect * in progress), `"live"` (streaming; reads are current to the last block). * * @category live data */ export type WatchStatus = "unwatched" | "hydrating" | "live"; /** * What a LiveTail runs against. Each `createClient()` passes its own config + * store + WebSocket client, so two clients never share tail state. */ export interface TailDeps { getConfig: () => ClientConfig; store: MaterializerStore; getClient: () => PublicClient; /** Opens the `newHeads` stream on the same connection as `getClient`, already * bound to it. The tail consumes parsed heads and never sees a transport. */ subscribeHeads: ChainHeadSubscriber; /** * The owning client's debug channel. It is a no-op when debugging is disabled. * * @internal */ dbg: Debug; } declare class LiveTail { private deps; private client; private unwatchHeads; private unwatchLogs; private retryTimer; private retryDelay; private poolRefs; private allRefs; private discoverRefs; private userRefs; /** Pools covered by the all-markets watch (snapshot set + live discoveries). */ private allPools; /** Pools whose seam has been sealed — their store rows are current. */ private hydratedPools; /** Users whose history snapshot has landed. */ private hydratedUsers; /** Scope-keyed linger timers (pool: | all | user:). */ private lingers; /** In-flight scope hydrations, so concurrent watchers share one snapshot. */ private hydrations; /** Pools whose scope is mid-hydration — their logs buffer in the inbox. */ private pendingPools; private live; private reconnecting; private lastBlock; private lastHeadAt; private headsWatchdog; private probingHeads; private watchAddresses; private watchSet; private blockTs; private processed; private inbox; constructor(deps: TailDeps); private get chainId(); /** * Creation-event sources watched in discovery mode: the MarketCreator * factory (its 13-field MarketCreated) AND the BinaryMarketsModule (its own * 19-field MarketCreated — module-created markets never pass through the * MarketCreator). Either address may be absent from the config — skip it. */ private get discoverySources(); /** Every pool an active watch covers. */ private activePools; /** Watch one market: hydrate its snapshot and stream its events. */ watchMarket(pool: string): Promise; /** * Watch every market the indexer knows; `discover` also watches the * MarketCreator factory + the BinaryMarketsModule so markets created later * join live (module-created markets emit only the module's MarketCreated). */ watchAllMarkets(discover: boolean): Promise; /** * Hydrate one account's past orders + fills (indexer, once). Live events * are attributed to every account on watched markets regardless — this only * supplies history, and only stays current within watched markets. */ watchUser(user: string): Promise; /** Per-market watch state (see {@link WatchStatus}). */ getWatchStatus(pool: string): WatchStatus; /** * Tear down everything: all watches, subscriptions, timers. The store keeps * its last state (reads keep answering, stale). */ stopLive(): void; private acquire; private release; private releaseNow; private handle; private linger; private cancelLinger; private teardownPool; private teardownAll; private ensureHydration; private hydratePools; private hydrateAll; /** Backfill [snapshot+1, head] for the scope, then replay its buffered logs. */ private sealSeam; private markLive; /** A scope's watch addresses: its pools + their BinaryMarket contracts. */ private addressesFor; /** * Recompute the watch set (active pools ∪ their markets ∪ discovery * sources) and (re)subscribe. No-op if unchanged. Opens the socket on * first use. */ private ensureSubscriptions; private subscribeHeads; private onHead; private onLogs; /** * True when `addr` is a BinaryMarket contract whose POOL is mid-hydration — * its status events must buffer with the rest of the scope. */ private pendingMarketOf; /** * If a batch created new markets (MarketCreated, discovery mode), grow the * all-markets set + subscriptions and sweep the creation range so any * same-block activity on the new pool isn't lost across the re-subscribe. */ private afterApply; /** Fetch + reduce logs for `addresses` in [from, to] (chunked). */ private backfill; /** * Replay buffered logs past `after` for `scope` addresses (all, if omitted); * other scopes' buffered logs stay queued. */ private replayInbox; /** Decode, timestamp, dedupe, order, and reduce a batch of raw logs. */ private applyLogs; private decode; private onWsError; private startHeadsWatchdog; private stopHeadsWatchdog; /** No newHeads for HEADS_STALL_MS while live: probe the chain head over the * request path. If the chain moved without the sub telling us, the sub is * dead — run the WS-error healing path (which force-resubscribes). If even * the probe fails, the socket itself is gone — same path. A chain that * simply hasn't minted (idle anvil) is left alone. */ private checkHeadsStall; private scheduleReconnect; /** * Heal after a socket drop: resubscribe (buffering), backfill everything the * store missed since `lastBlock` straight from chain, replay, go live. The * indexer is NOT consulted — the store's own state is the seam. */ private reconnect; private pruneMaps; } export { LiveTail };