@drift-labs/sdk 2.142.0-beta.13 → 2.142.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,5 @@
1
- import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber';
1
+ import StrictEventEmitter from 'strict-event-emitter-types';
2
+ import { EventEmitter } from 'events';
2
3
  import { OracleInfo, OraclePriceData } from '../oracles/types';
3
4
  import { Program } from '@coral-xyz/anchor';
4
5
  import { PublicKey } from '@solana/web3.js';
@@ -6,11 +7,17 @@ import { findAllMarketAndOracles } from '../config';
6
7
  import {
7
8
  getDriftStateAccountPublicKey,
8
9
  getPerpMarketPublicKey,
10
+ getPerpMarketPublicKeySync,
9
11
  getSpotMarketPublicKey,
12
+ getSpotMarketPublicKeySync,
10
13
  } from '../addresses/pda';
11
14
  import {
15
+ AccountSubscriber,
12
16
  DataAndSlot,
13
17
  DelistedMarketSetting,
18
+ DriftClientAccountEvents,
19
+ DriftClientAccountSubscriber,
20
+ NotSubscribedError,
14
21
  GrpcConfigs,
15
22
  ResubOpts,
16
23
  } from './types';
@@ -21,14 +28,51 @@ import {
21
28
  getOracleId,
22
29
  getPublicKeyAndSourceFromOracleId,
23
30
  } from '../oracles/oracleId';
31
+ import { OracleClientCache } from '../oracles/oracleClientCache';
32
+ import { findDelistedPerpMarketsAndOracles } from './utils';
24
33
 
25
- export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAccountSubscriber {
34
+ export class grpcDriftClientAccountSubscriberV2
35
+ implements DriftClientAccountSubscriber
36
+ {
26
37
  private grpcConfigs: GrpcConfigs;
27
38
  private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
28
39
  private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
29
- private oracleMultiSubscriber?: grpcMultiAccountSubscriber<OraclePriceData>;
40
+ private oracleMultiSubscriber?: grpcMultiAccountSubscriber<
41
+ OraclePriceData,
42
+ OracleInfo
43
+ >;
30
44
  private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
31
45
  private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
46
+ private delistedMarketSetting: DelistedMarketSetting;
47
+
48
+ public eventEmitter: StrictEventEmitter<
49
+ EventEmitter,
50
+ DriftClientAccountEvents
51
+ >;
52
+ public isSubscribed: boolean;
53
+ public isSubscribing: boolean;
54
+ public program: Program;
55
+ public perpMarketIndexes: number[];
56
+ public spotMarketIndexes: number[];
57
+ public shouldFindAllMarketsAndOracles: boolean;
58
+ public oracleInfos: OracleInfo[];
59
+ public initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
60
+ public initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
61
+ public initialOraclePriceData: Map<string, OraclePriceData>;
62
+ public perpOracleMap = new Map<number, PublicKey>();
63
+ public perpOracleStringMap = new Map<number, string>();
64
+ public spotOracleMap = new Map<number, PublicKey>();
65
+ public spotOracleStringMap = new Map<number, string>();
66
+ private oracleIdToOracleDataMap = new Map<
67
+ string,
68
+ DataAndSlot<OraclePriceData>
69
+ >();
70
+ public stateAccountSubscriber?: AccountSubscriber<StateAccount>;
71
+ oracleClientCache = new OracleClientCache();
72
+ private resubOpts?: ResubOpts;
73
+
74
+ private subscriptionPromise: Promise<boolean>;
75
+ protected subscriptionPromiseResolver: (val: boolean) => void;
32
76
 
33
77
  constructor(
34
78
  grpcConfigs: GrpcConfigs,
@@ -40,16 +84,156 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
40
84
  delistedMarketSetting: DelistedMarketSetting,
41
85
  resubOpts?: ResubOpts
42
86
  ) {
43
- super(
44
- program,
45
- perpMarketIndexes,
46
- spotMarketIndexes,
47
- oracleInfos,
48
- shouldFindAllMarketsAndOracles,
49
- delistedMarketSetting,
50
- resubOpts
51
- );
87
+ this.eventEmitter = new EventEmitter();
88
+ this.isSubscribed = false;
89
+ this.isSubscribing = false;
90
+ this.program = program;
91
+ this.perpMarketIndexes = perpMarketIndexes;
92
+ this.spotMarketIndexes = spotMarketIndexes;
93
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
94
+ this.oracleInfos = oracleInfos;
95
+ this.initialPerpMarketAccountData = new Map();
96
+ this.initialSpotMarketAccountData = new Map();
97
+ this.initialOraclePriceData = new Map();
98
+ this.perpOracleMap = new Map();
99
+ this.perpOracleStringMap = new Map();
100
+ this.spotOracleMap = new Map();
101
+ this.spotOracleStringMap = new Map();
52
102
  this.grpcConfigs = grpcConfigs;
103
+ this.resubOpts = resubOpts;
104
+ this.delistedMarketSetting = delistedMarketSetting;
105
+ }
106
+
107
+ chunks = <T>(array: readonly T[], size: number): T[][] => {
108
+ return new Array(Math.ceil(array.length / size))
109
+ .fill(null)
110
+ .map((_, index) => index * size)
111
+ .map((begin) => array.slice(begin, begin + size));
112
+ };
113
+
114
+ async setInitialData(): Promise<void> {
115
+ const connection = this.program.provider.connection;
116
+
117
+ if (
118
+ !this.initialPerpMarketAccountData ||
119
+ this.initialPerpMarketAccountData.size === 0
120
+ ) {
121
+ const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) =>
122
+ getPerpMarketPublicKeySync(this.program.programId, marketIndex)
123
+ );
124
+ const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
125
+ const perpMarketAccountInfos = (
126
+ await Promise.all(
127
+ perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) =>
128
+ connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)
129
+ )
130
+ )
131
+ ).flat();
132
+ this.initialPerpMarketAccountData = new Map(
133
+ perpMarketAccountInfos
134
+ .filter((accountInfo) => !!accountInfo)
135
+ .map((accountInfo) => {
136
+ const perpMarket = this.program.coder.accounts.decode(
137
+ 'PerpMarket',
138
+ accountInfo.data
139
+ );
140
+ return [perpMarket.marketIndex, perpMarket];
141
+ })
142
+ );
143
+ }
144
+
145
+ if (
146
+ !this.initialSpotMarketAccountData ||
147
+ this.initialSpotMarketAccountData.size === 0
148
+ ) {
149
+ const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) =>
150
+ getSpotMarketPublicKeySync(this.program.programId, marketIndex)
151
+ );
152
+ const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
153
+ const spotMarketAccountInfos = (
154
+ await Promise.all(
155
+ spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) =>
156
+ connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)
157
+ )
158
+ )
159
+ ).flat();
160
+ this.initialSpotMarketAccountData = new Map(
161
+ spotMarketAccountInfos
162
+ .filter((accountInfo) => !!accountInfo)
163
+ .map((accountInfo) => {
164
+ const spotMarket = this.program.coder.accounts.decode(
165
+ 'SpotMarket',
166
+ accountInfo.data
167
+ );
168
+ return [spotMarket.marketIndex, spotMarket];
169
+ })
170
+ );
171
+ }
172
+
173
+ const oracleAccountPubkeyChunks = this.chunks(
174
+ this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey),
175
+ 75
176
+ );
177
+ const oracleAccountInfos = (
178
+ await Promise.all(
179
+ oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) =>
180
+ connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)
181
+ )
182
+ )
183
+ ).flat();
184
+ this.initialOraclePriceData = new Map(
185
+ this.oracleInfos.reduce((result, oracleInfo, i) => {
186
+ if (!oracleAccountInfos[i]) {
187
+ return result;
188
+ }
189
+ const oracleClient = this.oracleClientCache.get(
190
+ oracleInfo.source,
191
+ connection,
192
+ this.program
193
+ );
194
+ const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(
195
+ oracleAccountInfos[i].data
196
+ );
197
+ result.push([
198
+ getOracleId(oracleInfo.publicKey, oracleInfo.source),
199
+ oraclePriceData,
200
+ ]);
201
+ return result;
202
+ }, [])
203
+ );
204
+ }
205
+
206
+ async addPerpMarket(_marketIndex: number): Promise<boolean> {
207
+ if (!this.perpMarketIndexes.includes(_marketIndex)) {
208
+ this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
209
+ }
210
+ return true;
211
+ }
212
+
213
+ async addSpotMarket(_marketIndex: number): Promise<boolean> {
214
+ return true;
215
+ }
216
+
217
+ async addOracle(oracleInfo: OracleInfo): Promise<boolean> {
218
+ if (oracleInfo.publicKey.equals(PublicKey.default)) {
219
+ return true;
220
+ }
221
+
222
+ const exists = this.oracleInfos.some(
223
+ (o) =>
224
+ o.source === oracleInfo.source &&
225
+ o.publicKey.equals(oracleInfo.publicKey)
226
+ );
227
+ if (!exists) {
228
+ this.oracleInfos = this.oracleInfos.concat(oracleInfo);
229
+ }
230
+
231
+ if (this.oracleMultiSubscriber) {
232
+ await this.unsubscribeFromOracles();
233
+ await this.subscribeToOracles();
234
+ }
235
+
236
+ return true;
53
237
  }
54
238
 
55
239
  public async subscribe(): Promise<boolean> {
@@ -133,7 +317,37 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
133
317
  return true;
134
318
  }
135
319
 
136
- override getMarketAccountAndSlot(
320
+ public async fetch(): Promise<void> {
321
+ await this.stateAccountSubscriber?.fetch();
322
+ await this.perpMarketsSubscriber?.fetch();
323
+ await this.spotMarketsSubscriber?.fetch();
324
+ await this.oracleMultiSubscriber?.fetch();
325
+ }
326
+
327
+ private assertIsSubscribed(): void {
328
+ if (!this.isSubscribed) {
329
+ throw new NotSubscribedError(
330
+ 'You must call `subscribe` before using this function'
331
+ );
332
+ }
333
+ }
334
+
335
+ public getStateAccountAndSlot(): DataAndSlot<StateAccount> {
336
+ this.assertIsSubscribed();
337
+ return this.stateAccountSubscriber.dataAndSlot;
338
+ }
339
+
340
+ public getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[] {
341
+ const map = this.perpMarketsSubscriber?.getAccountDataMap();
342
+ return Array.from(map?.values() ?? []);
343
+ }
344
+
345
+ public getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[] {
346
+ const map = this.spotMarketsSubscriber?.getAccountDataMap();
347
+ return Array.from(map?.values() ?? []);
348
+ }
349
+
350
+ getMarketAccountAndSlot(
137
351
  marketIndex: number
138
352
  ): DataAndSlot<PerpMarketAccount> | undefined {
139
353
  return this.perpMarketsSubscriber?.getAccountData(
@@ -141,7 +355,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
141
355
  );
142
356
  }
143
357
 
144
- override getSpotMarketAccountAndSlot(
358
+ getSpotMarketAccountAndSlot(
145
359
  marketIndex: number
146
360
  ): DataAndSlot<SpotMarketAccount> | undefined {
147
361
  return this.spotMarketsSubscriber?.getAccountData(
@@ -149,7 +363,52 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
149
363
  );
150
364
  }
151
365
 
152
- override async setPerpOracleMap() {
366
+ public getOraclePriceDataAndSlot(
367
+ oracleId: string
368
+ ): DataAndSlot<OraclePriceData> | undefined {
369
+ this.assertIsSubscribed();
370
+ // we need to rely on a map we store in this class because the grpcMultiAccountSubscriber does not track a mapping or oracle ID.
371
+ // DO NOT call getAccountData on the oracleMultiSubscriber, it will not return the correct data in certain cases(BONK spot and perp market subscribed too at once).
372
+ return this.oracleIdToOracleDataMap.get(oracleId);
373
+ }
374
+
375
+ public getOraclePriceDataAndSlotForPerpMarket(
376
+ marketIndex: number
377
+ ): DataAndSlot<OraclePriceData> | undefined {
378
+ const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
379
+ const oracle = this.perpOracleMap.get(marketIndex);
380
+ const oracleId = this.perpOracleStringMap.get(marketIndex);
381
+ if (!perpMarketAccount || !oracleId) {
382
+ return undefined;
383
+ }
384
+
385
+ if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
386
+ // If the oracle has changed, we need to update the oracle map in background
387
+ this.setPerpOracleMap();
388
+ }
389
+
390
+ return this.getOraclePriceDataAndSlot(oracleId);
391
+ }
392
+
393
+ public getOraclePriceDataAndSlotForSpotMarket(
394
+ marketIndex: number
395
+ ): DataAndSlot<OraclePriceData> | undefined {
396
+ const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
397
+ const oracle = this.spotOracleMap.get(marketIndex);
398
+ const oracleId = this.spotOracleStringMap.get(marketIndex);
399
+ if (!spotMarketAccount || !oracleId) {
400
+ return undefined;
401
+ }
402
+
403
+ if (!spotMarketAccount.data.oracle.equals(oracle)) {
404
+ // If the oracle has changed, we need to update the oracle map in background
405
+ this.setSpotOracleMap();
406
+ }
407
+
408
+ return this.getOraclePriceDataAndSlot(oracleId);
409
+ }
410
+
411
+ async setPerpOracleMap() {
153
412
  const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
154
413
  const perpMarkets = Array.from(perpMarketsMap.values());
155
414
  const addOraclePromises = [];
@@ -161,7 +420,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
161
420
  const perpMarketIndex = perpMarketAccount.marketIndex;
162
421
  const oracle = perpMarketAccount.amm.oracle;
163
422
  const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
164
- if (!this.oracleSubscribers.has(oracleId)) {
423
+ if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
165
424
  addOraclePromises.push(
166
425
  this.addOracle({
167
426
  publicKey: oracle,
@@ -175,7 +434,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
175
434
  await Promise.all(addOraclePromises);
176
435
  }
177
436
 
178
- override async setSpotOracleMap() {
437
+ async setSpotOracleMap() {
179
438
  const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
180
439
  const spotMarkets = Array.from(spotMarketsMap.values());
181
440
  const addOraclePromises = [];
@@ -187,7 +446,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
187
446
  const spotMarketIndex = spotMarketAccount.marketIndex;
188
447
  const oracle = spotMarketAccount.oracle;
189
448
  const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
190
- if (!this.oracleSubscribers.has(oracleId)) {
449
+ if (!this.oracleMultiSubscriber?.getAccountDataMap().has(oracleId)) {
191
450
  addOraclePromises.push(
192
451
  this.addOracle({
193
452
  publicKey: oracle,
@@ -201,7 +460,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
201
460
  await Promise.all(addOraclePromises);
202
461
  }
203
462
 
204
- override async subscribeToPerpMarketAccounts(): Promise<boolean> {
463
+ async subscribeToPerpMarketAccounts(): Promise<boolean> {
205
464
  const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
206
465
  await Promise.all(
207
466
  this.perpMarketIndexes.map(async (marketIndex) => [
@@ -263,7 +522,7 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
263
522
  return true;
264
523
  }
265
524
 
266
- override async subscribeToSpotMarketAccounts(): Promise<boolean> {
525
+ async subscribeToSpotMarketAccounts(): Promise<boolean> {
267
526
  const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
268
527
  await Promise.all(
269
528
  this.spotMarketIndexes.map(async (marketIndex) => [
@@ -325,81 +584,81 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
325
584
  return true;
326
585
  }
327
586
 
328
- override async subscribeToOracles(): Promise<boolean> {
329
- const pubkeyToSources = new Map<string, Set<OracleInfo['source']>>();
587
+ async subscribeToOracles(): Promise<boolean> {
588
+ const oraclePubkeyToInfosMap = new Map<string, OracleInfo[]>();
330
589
  for (const info of this.oracleInfos) {
331
- if (info.publicKey.equals((PublicKey as any).default)) {
332
- continue;
590
+ const pubkey = info.publicKey.toBase58();
591
+ if (!oraclePubkeyToInfosMap.has(pubkey)) {
592
+ oraclePubkeyToInfosMap.set(pubkey, []);
333
593
  }
334
- const key = info.publicKey.toBase58();
335
- let sources = pubkeyToSources.get(key);
336
- if (!sources) {
337
- sources = new Set<OracleInfo['source']>();
338
- pubkeyToSources.set(key, sources);
339
- }
340
- sources.add(info.source);
594
+ oraclePubkeyToInfosMap.get(pubkey).push(info);
341
595
  }
342
596
 
343
- const oraclePubkeys = Array.from(pubkeyToSources.keys()).map(
344
- (k) => new PublicKey(k)
597
+ const oraclePubkeys = Array.from(
598
+ new Set(this.oracleInfos.map((info) => info.publicKey))
345
599
  );
346
600
 
347
- this.oracleMultiSubscriber =
348
- await grpcMultiAccountSubscriber.create<OraclePriceData>(
349
- this.grpcConfigs,
350
- 'oracle',
351
- this.program,
352
- (buffer: Buffer, pubkey?: string) => {
353
- if (!pubkey) {
354
- throw new Error('Oracle pubkey missing in decode');
355
- }
356
- const sources = pubkeyToSources.get(pubkey);
357
- if (!sources || sources.size === 0) {
358
- throw new Error('Oracle sources missing for pubkey in decode');
359
- }
360
- const primarySource = sources.values().next().value;
361
- const client = this.oracleClientCache.get(
362
- primarySource,
363
- this.program.provider.connection,
364
- this.program
365
- );
366
- return client.getOraclePriceDataFromBuffer(buffer);
367
- },
368
- this.resubOpts,
369
- undefined,
370
- async () => {
371
- try {
372
- if (this.resubOpts?.logResubMessages) {
373
- console.log(
374
- '[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
375
- );
376
- }
377
- await this.subscribeToOracles();
378
- } catch (e) {
379
- console.error('Oracle resubscribe failed:', e);
601
+ this.oracleMultiSubscriber = await grpcMultiAccountSubscriber.create<
602
+ OraclePriceData,
603
+ OracleInfo
604
+ >(
605
+ this.grpcConfigs,
606
+ 'oracle',
607
+ this.program,
608
+ (buffer: Buffer, pubkey?: string, accountProps?: OracleInfo) => {
609
+ if (!pubkey) {
610
+ throw new Error('Oracle pubkey missing in decode');
611
+ }
612
+
613
+ const client = this.oracleClientCache.get(
614
+ accountProps.source,
615
+ this.program.provider.connection,
616
+ this.program
617
+ );
618
+ const price = client.getOraclePriceDataFromBuffer(buffer);
619
+ return price;
620
+ },
621
+ this.resubOpts,
622
+ undefined,
623
+ async () => {
624
+ try {
625
+ if (this.resubOpts?.logResubMessages) {
626
+ console.log(
627
+ '[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing'
628
+ );
380
629
  }
630
+ await this.subscribeToOracles();
631
+ } catch (e) {
632
+ console.error('Oracle resubscribe failed:', e);
381
633
  }
382
- );
634
+ },
635
+ oraclePubkeyToInfosMap
636
+ );
383
637
 
384
638
  for (const data of this.initialOraclePriceData.entries()) {
385
639
  const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
386
640
  this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
641
+ this.oracleIdToOracleDataMap.set(data[0], {
642
+ data: data[1],
643
+ slot: 0,
644
+ });
387
645
  }
388
646
 
389
647
  await this.oracleMultiSubscriber.subscribe(
390
648
  oraclePubkeys,
391
- (accountId, data) => {
392
- const sources = pubkeyToSources.get(accountId.toBase58());
393
- if (sources) {
394
- for (const source of sources.values()) {
395
- this.eventEmitter.emit(
396
- 'oraclePriceUpdate',
397
- accountId,
398
- source,
399
- data
400
- );
401
- }
402
- }
649
+ (accountId, data, context, _b, accountProps) => {
650
+ const oracleId = getOracleId(accountId, accountProps.source);
651
+ this.oracleIdToOracleDataMap.set(oracleId, {
652
+ data,
653
+ slot: context.slot,
654
+ });
655
+ this.eventEmitter.emit(
656
+ 'oraclePriceUpdate',
657
+ accountId,
658
+ accountProps.source,
659
+ data
660
+ );
661
+
403
662
  this.eventEmitter.emit('update');
404
663
  }
405
664
  );
@@ -407,20 +666,56 @@ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAcco
407
666
  return true;
408
667
  }
409
668
 
669
+ async handleDelistedMarkets(): Promise<void> {
670
+ if (this.delistedMarketSetting === DelistedMarketSetting.Subscribe) {
671
+ return;
672
+ }
673
+
674
+ const { perpMarketIndexes, oracles } = findDelistedPerpMarketsAndOracles(
675
+ Array.from(
676
+ this.perpMarketsSubscriber?.getAccountDataMap().values() || []
677
+ ),
678
+ Array.from(this.spotMarketsSubscriber?.getAccountDataMap().values() || [])
679
+ );
680
+
681
+ for (const perpMarketIndex of perpMarketIndexes) {
682
+ await this.perpMarketsSubscriber.removeAccounts([
683
+ new PublicKey(
684
+ this.perpMarketIndexToAccountPubkeyMap.get(perpMarketIndex) || ''
685
+ ),
686
+ ]);
687
+ if (this.delistedMarketSetting === DelistedMarketSetting.Discard) {
688
+ this.perpMarketIndexToAccountPubkeyMap.delete(perpMarketIndex);
689
+ }
690
+ }
691
+
692
+ for (const oracle of oracles) {
693
+ await this.oracleMultiSubscriber.removeAccounts([oracle.publicKey]);
694
+ }
695
+ }
696
+
697
+ removeInitialData() {
698
+ this.initialPerpMarketAccountData = new Map();
699
+ this.initialSpotMarketAccountData = new Map();
700
+ this.initialOraclePriceData = new Map();
701
+ }
702
+
410
703
  async unsubscribeFromOracles(): Promise<void> {
411
704
  if (this.oracleMultiSubscriber) {
412
705
  await this.oracleMultiSubscriber.unsubscribe();
413
706
  this.oracleMultiSubscriber = undefined;
414
707
  return;
415
708
  }
416
- await super.unsubscribeFromOracles();
417
709
  }
418
710
 
419
- override async unsubscribe(): Promise<void> {
711
+ async unsubscribe(): Promise<void> {
420
712
  if (this.isSubscribed) {
421
713
  return;
422
714
  }
423
715
 
424
716
  await this.stateAccountSubscriber.unsubscribe();
717
+ await this.unsubscribeFromOracles();
718
+ await this.perpMarketsSubscriber?.unsubscribe();
719
+ await this.spotMarketsSubscriber?.unsubscribe();
425
720
  }
426
721
  }