UNPKG

@drift-labs/sdk

Version:
146 lines 7.09 kB
/// <reference types="node" /> /// <reference types="node" /> import { BufferAndSlot, ProgramAccountSubscriber, ResubOpts } from './types'; import { Program } from '@coral-xyz/anchor'; import { Commitment, Context, MemcmpFilter, PublicKey } from '@solana/web3.js'; import { AccountInfoBase, AccountInfoWithBase58EncodedData, AccountInfoWithBase64EncodedData, Address } from 'gill'; /** * WebSocketProgramAccountsSubscriberV2 * * High-level overview * - WebSocket-first subscriber for Solana program accounts that also layers in * targeted polling to detect missed updates reliably. * - Emits decoded account updates via the provided `onChange` callback. * - Designed to focus extra work on the specific accounts the consumer cares * about ("monitored accounts") while keeping baseline WS behavior for the * full program subscription. * * Why polling if this is a WebSocket subscriber? * - WS infra can stall, drop, or reorder notifications under network stress or * provider hiccups. When that happens, critical account changes can be missed. * - To mitigate this, the class accepts a set of accounts (provided via constructor) to monitor * and uses light polling to verify whether a WS change was missed. * - If polling detects a newer slot with different data than the last seen * buffer, a centralized resubscription is triggered to restore a clean stream. * * Initial fetch (on subscribe) * - On `subscribe()`, we first perform a single batched fetch of all monitored * accounts ("initial monitor fetch"). * - Purpose: seed the internal `bufferAndSlotMap` and emit the latest state so * consumers have up-to-date data immediately, even before WS events arrive. * - This step does not decide resubscription; it only establishes ground truth. * * Continuous polling (only for monitored accounts) * - After seeding, each monitored account is put into a monitoring cycle: * 1) If no WS notification for an account is observed for `pollingIntervalMs`, * we enqueue it for a batched fetch (buffered for a short window). * 2) Once an account enters the "currently polling" set, a shared batch poll * runs every `pollingIntervalMs` across all such accounts. * 3) If WS notifications resume for an account, that account is removed from * the polling set and returns to passive monitoring. * - Polling compares the newly fetched buffer with the last stored buffer at a * later slot. A difference indicates a missed update; we schedule a single * resubscription (coalesced across accounts) to re-sync. * * Accounts the consumer cares about * - Provide accounts up-front via the constructor `accountsToMonitor`, or add * them dynamically with `addAccountToMonitor()` and remove with * `removeAccountFromMonitor()`. * - Only these accounts incur additional polling safeguards; other accounts are * still processed from the WS stream normally. * * Resubscription strategy * - Missed updates from any monitored account are coalesced and trigger a single * resubscription after a short delay. This avoids rapid churn. * - If `resubOpts.resubTimeoutMs` is set, an inactivity timer also performs a * batch check of monitored accounts. If a missed update is found, the same * centralized resubscription flow is used. * * Tuning knobs * - `setPollingInterval(ms)`: adjust how often monitoring/polling runs * (default 30s). Shorter = faster detection, higher RPC load. * - Debounced immediate poll (~100ms): batches accounts added to polling right after inactivity. * - Batch size for `getMultipleAccounts` is limited to 100, requests are chunked * and processed concurrently. */ export declare class WebSocketProgramAccountsSubscriberV2<T> implements ProgramAccountSubscriber<T> { subscriptionName: string; accountDiscriminator: string; bufferAndSlotMap: Map<string, BufferAndSlot>; program: Program; decodeBuffer: (accountName: string, ix: Buffer) => T; onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void; listenerId?: number; resubOpts: ResubOpts; isUnsubscribing: boolean; timeoutId?: ReturnType<typeof setTimeout>; options: { filters: MemcmpFilter[]; commitment?: Commitment; }; receivingData: boolean; private rpc; private rpcSubscriptions; private abortController?; private accountsToMonitor; private pollingIntervalMs; private pollingTimeouts; private lastWsNotificationTime; private accountsCurrentlyPolling; private batchPollingTimeout?; private debouncedImmediatePollTimeout?; private debouncedImmediatePollMs; private missedChangeDetected; private resubscriptionTimeout?; private accountsWithMissedUpdates; constructor(subscriptionName: string, accountDiscriminator: string, program: Program, decodeBufferFn: (accountName: string, ix: Buffer) => T, options?: { filters: MemcmpFilter[]; commitment?: Commitment; }, resubOpts?: ResubOpts, accountsToMonitor?: PublicKey[]); private handleNotificationLoop; subscribe(onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void): Promise<void>; protected setTimeout(): void; handleRpcResponse(context: { slot: bigint; }, accountId: Address, accountInfo?: AccountInfoBase & (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData)['data']): void; private startMonitoringForAccounts; private startMonitoringForAccount; private scheduleDebouncedImmediatePoll; private startBatchPolling; private pollAllAccounts; /** * Fetches and populates all monitored accounts data without checking for missed changes * This is used during initial subscription to populate data */ private fetchAndPopulateAllMonitoredAccounts; /** * Fetches all monitored accounts and checks for missed changes * Returns true if a missed change was detected and resubscription is needed */ private fetchAllMonitoredAccounts; private fetchAccountsBatch; private clearPollingTimeouts; /** * Centralized resubscription handler that only resubscribes once after checking all accounts */ private handleResubscription; /** * Signal that a missed change was detected and schedule resubscription */ private signalMissedChange; unsubscribe(onResub?: boolean): Promise<void>; /** * Add an account to the monitored set. * - Monitored accounts are subject to initial fetch and periodic batch polls * if WS notifications are not observed within `pollingIntervalMs`. */ addAccountToMonitor(accountId: PublicKey): void; removeAccountFromMonitor(accountId: PublicKey): void; /** * Set the monitoring/polling interval for monitored accounts. * Shorter intervals detect missed updates sooner but increase RPC load. */ setPollingInterval(intervalMs: number): void; private updateBufferAndHandleChange; } //# sourceMappingURL=webSocketProgramAccountsSubscriberV2.d.ts.map