Skip to main content
Account subscribers provide real-time updates for on-chain account data in Drift Protocol. The SDK offers three subscription mechanisms: WebSocket, Polling, and gRPC, each optimized for different use cases.

Subscription Types

Drift Protocol v2 provides three primary subscription mechanisms:

WebSocket Subscribers

WebSocket-based subscribers use Solana’s native accountSubscribe RPC method to receive real-time updates. Best for:
  • Low-latency requirements
  • Real-time trading applications
  • When using public RPC endpoints
Key Classes:
  • WebSocketUserAccountSubscriber - Subscribe to user account updates
  • WebSocketDriftClientAccountSubscriber - Subscribe to markets, state, and oracle data
  • WebSocketProgramAccountSubscriber - Subscribe to multiple accounts by program

Polling Subscribers

Polling-based subscribers use the BulkAccountLoader to periodically fetch account data. Best for:
  • Rate-limited RPC endpoints
  • Batch processing scenarios
  • When WebSocket connections are unreliable
Key Classes:
  • PollingUserAccountSubscriber - Poll user account updates
  • PollingDriftClientAccountSubscriber - Poll markets, state, and oracle data
  • BulkAccountLoader - Efficient batch account loading

gRPC Subscribers

gRPC-based subscribers use Yellowstone or Laser gRPC streams for high-performance data streaming. Best for:
  • High-frequency trading
  • Maximum throughput requirements
  • When using dedicated gRPC infrastructure
Key Classes:
  • grpcUserAccountSubscriber - Subscribe via gRPC to user accounts
  • grpcDriftClientAccountSubscriber - Subscribe via gRPC to markets and oracles
  • grpcAccountSubscriber - Generic gRPC account subscriber

Core Interfaces

AccountSubscriber

Base interface for single account subscriptions:
interface AccountSubscriber<T> {
  dataAndSlot?: DataAndSlot<T>;
  subscribe(onChange: (data: T) => void): Promise<void>;
  fetch(): Promise<void>;
  unsubscribe(): Promise<void>;
  setData(account: T, slot?: number): void;
}

UserAccountSubscriber

Interface for user account subscriptions:
interface UserAccountSubscriber {
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
  isSubscribed: boolean;
  
  subscribe(userAccount?: UserAccount): Promise<boolean>;
  fetch(): Promise<void>;
  updateData(userAccount: UserAccount, slot: number): void;
  unsubscribe(): Promise<void>;
  
  getUserAccountAndSlot(): DataAndSlot<UserAccount>;
}

DriftClientAccountSubscriber

Interface for subscribing to Drift protocol accounts (markets, state, oracles):
interface DriftClientAccountSubscriber {
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
  isSubscribed: boolean;
  
  subscribe(): Promise<boolean>;
  fetch(): Promise<void>;
  unsubscribe(): Promise<void>;
  
  addPerpMarket(marketIndex: number): Promise<boolean>;
  addSpotMarket(marketIndex: number): Promise<boolean>;
  addOracle(oracleInfo: OracleInfo): Promise<boolean>;
  
  getStateAccountAndSlot(): DataAndSlot<StateAccount>;
  getMarketAccountAndSlot(marketIndex: number): DataAndSlot<PerpMarketAccount> | undefined;
  getSpotMarketAccountAndSlot(marketIndex: number): DataAndSlot<SpotMarketAccount> | undefined;
  getOraclePriceDataAndSlot(oracleId: string): DataAndSlot<OraclePriceData> | undefined;
}

Common Types

DataAndSlot

Wraps account data with its slot number:
type DataAndSlot<T> = {
  data: T;
  slot: number;
};

ResubOpts

Configuration for automatic resubscription:
type ResubOpts = {
  resubTimeoutMs?: number;           // Timeout before resubscribing
  logResubMessages?: boolean;        // Log resubscription events
  usePollingInsteadOfResub?: boolean; // Use polling for resubscription
  pollingIntervalMs?: number;        // Polling interval in ms
};

Event Types

UserAccountEvents

interface UserAccountEvents {
  userAccountUpdate: (payload: UserAccount) => void;
  update: void;
  error: (e: Error) => void;
}

DriftClientAccountEvents

interface DriftClientAccountEvents {
  stateAccountUpdate: (payload: StateAccount) => void;
  perpMarketAccountUpdate: (payload: PerpMarketAccount) => void;
  spotMarketAccountUpdate: (payload: SpotMarketAccount) => void;
  oraclePriceUpdate: (
    publicKey: PublicKey,
    oracleSource: OracleSource,
    data: OraclePriceData
  ) => void;
  userAccountUpdate: (payload: UserAccount) => void;
  update: void;
  error: (e: Error) => void;
}

Choosing a Subscriber Type

FeatureWebSocketPollinggRPC
LatencyLowMediumVery Low
Resource UsageMediumLowHigh
ReliabilityMediumHighHigh
RPC CallsMinimalFrequentNone
Setup ComplexityLowLowHigh
Best ForGeneral useRate limitsHFT

Error Handling

All subscribers can throw NotSubscribedError if methods are called before subscribing:
class NotSubscribedError extends Error {
  name = 'NotSubscribedError';
}
Handle errors using event emitters:
subscriber.eventEmitter.on('error', (error: Error) => {
  console.error('Subscription error:', error);
});

Next Steps

WebSocket Subscribers

Learn about WebSocket-based subscriptions

Polling Subscribers

Learn about polling-based subscriptions

gRPC Subscribers

Learn about gRPC-based subscriptions

Build docs developers (and LLMs) love