import { BulkAccountLoader, DataAndSlot, NotSubscribedError, PublicKey, } from '@drift-labs/sdk'; import { Program } from '@coral-xyz/anchor'; import StrictEventEmitter from 'strict-event-emitter-types'; import { EventEmitter } from 'events'; import { DriftCompetitions } from '../types/drift_competitions'; import { DriftCompetitionsProgramAccountBaseEvents, DriftCompetitionsProgramAccountSubscriber, } from '../types/types'; export abstract class PollingProgramAccountSubscriberBase< Account, AccountEvents extends DriftCompetitionsProgramAccountBaseEvents > implements DriftCompetitionsProgramAccountSubscriber { protected program: Program; protected _isSubscribed: boolean; protected pubkey: PublicKey; protected account?: DataAndSlot; protected _eventEmitter: StrictEventEmitter; protected accountLoader: BulkAccountLoader; protected callbackId: string | null = null; protected errorCallbackId: string | null = null; constructor( program: Program, accountPubkey: PublicKey, accountLoader: BulkAccountLoader ) { this.accountLoader = accountLoader; this._isSubscribed = false; this.pubkey = accountPubkey; this.program = program; // @ts-ignore this._eventEmitter = new EventEmitter(); } get isSubscribed(): boolean { return this._isSubscribed; } get eventEmitter(): StrictEventEmitter { return this._eventEmitter; } async subscribe(): Promise { if (this._isSubscribed) { return true; } try { await this.addToAccountLoader(); await this.fetchIfUnloaded(); if (this.account) { // @ts-ignore this._eventEmitter.emit('update'); } this._isSubscribed = true; return true; } catch (err) { console.error(err); this._isSubscribed = false; return false; } } async unsubscribe(): Promise { if (!this._isSubscribed) { return; } this.accountLoader.removeAccount(this.pubkey, this.callbackId); this.callbackId = undefined; this.accountLoader.removeErrorCallbacks(this.errorCallbackId); this.errorCallbackId = undefined; this._isSubscribed = false; } async fetchIfUnloaded(): Promise { if (this.account === undefined) { await this.fetch(); } } assertIsSubscribed(): void { if (!this._isSubscribed) { throw new NotSubscribedError( 'You must call `subscribe` before using this function' ); } } getAccountAndSlot(): DataAndSlot { this.assertIsSubscribed(); return this.account; } abstract addToAccountLoader(): Promise; abstract fetch(): Promise; abstract updateData(account: Account, slot: number): void; }