@drift-labs/sdk-browser 2.142.0-beta.9 → 2.143.0-beta.1

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.
Files changed (60) hide show
  1. package/VERSION +1 -1
  2. package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.d.ts +46 -5
  3. package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +276 -43
  4. package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +6 -3
  5. package/lib/browser/accounts/grpcMultiAccountSubscriber.js +112 -19
  6. package/lib/browser/adminClient.d.ts +4 -0
  7. package/lib/browser/adminClient.js +34 -0
  8. package/lib/browser/constants/perpMarkets.js +35 -0
  9. package/lib/browser/constants/spotMarkets.js +4 -4
  10. package/lib/browser/driftClient.d.ts +35 -5
  11. package/lib/browser/driftClient.js +41 -14
  12. package/lib/browser/events/parse.d.ts +2 -0
  13. package/lib/browser/events/parse.js +94 -1
  14. package/lib/browser/events/types.d.ts +22 -3
  15. package/lib/browser/idl/drift.json +105 -6
  16. package/lib/browser/math/amm.d.ts +1 -0
  17. package/lib/browser/math/amm.js +28 -4
  18. package/lib/browser/types.d.ts +20 -0
  19. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +46 -5
  20. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -1
  21. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +276 -43
  22. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +6 -3
  23. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
  24. package/lib/node/accounts/grpcMultiAccountSubscriber.js +112 -19
  25. package/lib/node/adminClient.d.ts +4 -0
  26. package/lib/node/adminClient.d.ts.map +1 -1
  27. package/lib/node/adminClient.js +34 -0
  28. package/lib/node/constants/perpMarkets.d.ts.map +1 -1
  29. package/lib/node/constants/perpMarkets.js +35 -0
  30. package/lib/node/constants/spotMarkets.js +4 -4
  31. package/lib/node/driftClient.d.ts +35 -5
  32. package/lib/node/driftClient.d.ts.map +1 -1
  33. package/lib/node/driftClient.js +41 -14
  34. package/lib/node/events/parse.d.ts +2 -0
  35. package/lib/node/events/parse.d.ts.map +1 -1
  36. package/lib/node/events/parse.js +94 -1
  37. package/lib/node/events/types.d.ts +22 -3
  38. package/lib/node/events/types.d.ts.map +1 -1
  39. package/lib/node/idl/drift.json +105 -6
  40. package/lib/node/math/amm.d.ts +1 -0
  41. package/lib/node/math/amm.d.ts.map +1 -1
  42. package/lib/node/math/amm.js +28 -4
  43. package/lib/node/types.d.ts +20 -0
  44. package/lib/node/types.d.ts.map +1 -1
  45. package/package.json +2 -1
  46. package/scripts/grpc-client-test-comparison.ts +373 -0
  47. package/scripts/single-grpc-client-test.ts +226 -0
  48. package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +439 -74
  49. package/src/accounts/grpcMultiAccountSubscriber.ts +163 -31
  50. package/src/adminClient.ts +74 -0
  51. package/src/constants/perpMarkets.ts +37 -0
  52. package/src/constants/spotMarkets.ts +4 -4
  53. package/src/driftClient.ts +65 -14
  54. package/src/events/parse.ts +115 -0
  55. package/src/events/types.ts +26 -2
  56. package/src/idl/drift.json +105 -6
  57. package/src/math/amm.ts +52 -8
  58. package/src/types.ts +22 -0
  59. package/tests/events/parseLogsForCuUsage.ts +139 -0
  60. package/scripts/client-test.ts +0 -214
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.142.0-beta.9
1
+ 2.143.0-beta.1
@@ -1,24 +1,65 @@
1
- import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber';
2
- import { OracleInfo } from '../oracles/types';
1
+ /// <reference types="node" />
2
+ import StrictEventEmitter from 'strict-event-emitter-types';
3
+ import { EventEmitter } from 'events';
4
+ import { OracleInfo, OraclePriceData } from '../oracles/types';
3
5
  import { Program } from '@coral-xyz/anchor';
4
- import { DataAndSlot, DelistedMarketSetting, GrpcConfigs, ResubOpts } from './types';
5
- import { PerpMarketAccount, SpotMarketAccount } from '../types';
6
- export declare class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAccountSubscriber {
6
+ import { PublicKey } from '@solana/web3.js';
7
+ import { AccountSubscriber, DataAndSlot, DelistedMarketSetting, DriftClientAccountEvents, DriftClientAccountSubscriber, GrpcConfigs, ResubOpts } from './types';
8
+ import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
9
+ import { OracleClientCache } from '../oracles/oracleClientCache';
10
+ export declare class grpcDriftClientAccountSubscriberV2 implements DriftClientAccountSubscriber {
7
11
  private grpcConfigs;
8
12
  private perpMarketsSubscriber?;
9
13
  private spotMarketsSubscriber?;
10
14
  private oracleMultiSubscriber?;
11
15
  private perpMarketIndexToAccountPubkeyMap;
12
16
  private spotMarketIndexToAccountPubkeyMap;
17
+ private delistedMarketSetting;
18
+ eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
19
+ isSubscribed: boolean;
20
+ isSubscribing: boolean;
21
+ program: Program;
22
+ perpMarketIndexes: number[];
23
+ spotMarketIndexes: number[];
24
+ shouldFindAllMarketsAndOracles: boolean;
25
+ oracleInfos: OracleInfo[];
26
+ initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
27
+ initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
28
+ initialOraclePriceData: Map<string, OraclePriceData>;
29
+ perpOracleMap: Map<number, PublicKey>;
30
+ perpOracleStringMap: Map<number, string>;
31
+ spotOracleMap: Map<number, PublicKey>;
32
+ spotOracleStringMap: Map<number, string>;
33
+ private oracleIdToOracleDataMap;
34
+ stateAccountSubscriber?: AccountSubscriber<StateAccount>;
35
+ oracleClientCache: OracleClientCache;
36
+ private resubOpts?;
37
+ private subscriptionPromise;
38
+ protected subscriptionPromiseResolver: (val: boolean) => void;
13
39
  constructor(grpcConfigs: GrpcConfigs, program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean, delistedMarketSetting: DelistedMarketSetting, resubOpts?: ResubOpts);
40
+ chunks: <T>(array: readonly T[], size: number) => T[][];
41
+ setInitialData(): Promise<void>;
42
+ addPerpMarket(_marketIndex: number): Promise<boolean>;
43
+ addSpotMarket(_marketIndex: number): Promise<boolean>;
44
+ addOracle(oracleInfo: OracleInfo): Promise<boolean>;
14
45
  subscribe(): Promise<boolean>;
46
+ fetch(): Promise<void>;
47
+ private assertIsSubscribed;
48
+ getStateAccountAndSlot(): DataAndSlot<StateAccount>;
49
+ getMarketAccountsAndSlots(): DataAndSlot<PerpMarketAccount>[];
50
+ getSpotMarketAccountsAndSlots(): DataAndSlot<SpotMarketAccount>[];
15
51
  getMarketAccountAndSlot(marketIndex: number): DataAndSlot<PerpMarketAccount> | undefined;
16
52
  getSpotMarketAccountAndSlot(marketIndex: number): DataAndSlot<SpotMarketAccount> | undefined;
53
+ getOraclePriceDataAndSlot(oracleId: string): DataAndSlot<OraclePriceData> | undefined;
54
+ getOraclePriceDataAndSlotForPerpMarket(marketIndex: number): DataAndSlot<OraclePriceData> | undefined;
55
+ getOraclePriceDataAndSlotForSpotMarket(marketIndex: number): DataAndSlot<OraclePriceData> | undefined;
17
56
  setPerpOracleMap(): Promise<void>;
18
57
  setSpotOracleMap(): Promise<void>;
19
58
  subscribeToPerpMarketAccounts(): Promise<boolean>;
20
59
  subscribeToSpotMarketAccounts(): Promise<boolean>;
21
60
  subscribeToOracles(): Promise<boolean>;
61
+ handleDelistedMarkets(): Promise<void>;
62
+ removeInitialData(): void;
22
63
  unsubscribeFromOracles(): Promise<void>;
23
64
  unsubscribe(): Promise<void>;
24
65
  }
@@ -1,19 +1,117 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.grpcDriftClientAccountSubscriberV2 = void 0;
4
- const webSocketDriftClientAccountSubscriber_1 = require("./webSocketDriftClientAccountSubscriber");
4
+ const events_1 = require("events");
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  const config_1 = require("../config");
7
7
  const pda_1 = require("../addresses/pda");
8
+ const types_1 = require("./types");
8
9
  const grpcAccountSubscriber_1 = require("./grpcAccountSubscriber");
9
10
  const grpcMultiAccountSubscriber_1 = require("./grpcMultiAccountSubscriber");
10
11
  const oracleId_1 = require("../oracles/oracleId");
11
- class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber {
12
+ const oracleClientCache_1 = require("../oracles/oracleClientCache");
13
+ const utils_1 = require("./utils");
14
+ class grpcDriftClientAccountSubscriberV2 {
12
15
  constructor(grpcConfigs, program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts) {
13
- super(program, perpMarketIndexes, spotMarketIndexes, oracleInfos, shouldFindAllMarketsAndOracles, delistedMarketSetting, resubOpts);
14
16
  this.perpMarketIndexToAccountPubkeyMap = new Map();
15
17
  this.spotMarketIndexToAccountPubkeyMap = new Map();
18
+ this.perpOracleMap = new Map();
19
+ this.perpOracleStringMap = new Map();
20
+ this.spotOracleMap = new Map();
21
+ this.spotOracleStringMap = new Map();
22
+ this.oracleIdToOracleDataMap = new Map();
23
+ this.oracleClientCache = new oracleClientCache_1.OracleClientCache();
24
+ this.chunks = (array, size) => {
25
+ return new Array(Math.ceil(array.length / size))
26
+ .fill(null)
27
+ .map((_, index) => index * size)
28
+ .map((begin) => array.slice(begin, begin + size));
29
+ };
30
+ this.eventEmitter = new events_1.EventEmitter();
31
+ this.isSubscribed = false;
32
+ this.isSubscribing = false;
33
+ this.program = program;
34
+ this.perpMarketIndexes = perpMarketIndexes;
35
+ this.spotMarketIndexes = spotMarketIndexes;
36
+ this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
37
+ this.oracleInfos = oracleInfos;
38
+ this.initialPerpMarketAccountData = new Map();
39
+ this.initialSpotMarketAccountData = new Map();
40
+ this.initialOraclePriceData = new Map();
41
+ this.perpOracleMap = new Map();
42
+ this.perpOracleStringMap = new Map();
43
+ this.spotOracleMap = new Map();
44
+ this.spotOracleStringMap = new Map();
16
45
  this.grpcConfigs = grpcConfigs;
46
+ this.resubOpts = resubOpts;
47
+ this.delistedMarketSetting = delistedMarketSetting;
48
+ }
49
+ async setInitialData() {
50
+ const connection = this.program.provider.connection;
51
+ if (!this.initialPerpMarketAccountData ||
52
+ this.initialPerpMarketAccountData.size === 0) {
53
+ const perpMarketPublicKeys = this.perpMarketIndexes.map((marketIndex) => (0, pda_1.getPerpMarketPublicKeySync)(this.program.programId, marketIndex));
54
+ const perpMarketPublicKeysChunks = this.chunks(perpMarketPublicKeys, 75);
55
+ const perpMarketAccountInfos = (await Promise.all(perpMarketPublicKeysChunks.map((perpMarketPublicKeysChunk) => connection.getMultipleAccountsInfo(perpMarketPublicKeysChunk)))).flat();
56
+ this.initialPerpMarketAccountData = new Map(perpMarketAccountInfos
57
+ .filter((accountInfo) => !!accountInfo)
58
+ .map((accountInfo) => {
59
+ const perpMarket = this.program.coder.accounts.decode('PerpMarket', accountInfo.data);
60
+ return [perpMarket.marketIndex, perpMarket];
61
+ }));
62
+ }
63
+ if (!this.initialSpotMarketAccountData ||
64
+ this.initialSpotMarketAccountData.size === 0) {
65
+ const spotMarketPublicKeys = this.spotMarketIndexes.map((marketIndex) => (0, pda_1.getSpotMarketPublicKeySync)(this.program.programId, marketIndex));
66
+ const spotMarketPublicKeysChunks = this.chunks(spotMarketPublicKeys, 75);
67
+ const spotMarketAccountInfos = (await Promise.all(spotMarketPublicKeysChunks.map((spotMarketPublicKeysChunk) => connection.getMultipleAccountsInfo(spotMarketPublicKeysChunk)))).flat();
68
+ this.initialSpotMarketAccountData = new Map(spotMarketAccountInfos
69
+ .filter((accountInfo) => !!accountInfo)
70
+ .map((accountInfo) => {
71
+ const spotMarket = this.program.coder.accounts.decode('SpotMarket', accountInfo.data);
72
+ return [spotMarket.marketIndex, spotMarket];
73
+ }));
74
+ }
75
+ const oracleAccountPubkeyChunks = this.chunks(this.oracleInfos.map((oracleInfo) => oracleInfo.publicKey), 75);
76
+ const oracleAccountInfos = (await Promise.all(oracleAccountPubkeyChunks.map((oracleAccountPublicKeysChunk) => connection.getMultipleAccountsInfo(oracleAccountPublicKeysChunk)))).flat();
77
+ this.initialOraclePriceData = new Map(this.oracleInfos.reduce((result, oracleInfo, i) => {
78
+ if (!oracleAccountInfos[i]) {
79
+ return result;
80
+ }
81
+ const oracleClient = this.oracleClientCache.get(oracleInfo.source, connection, this.program);
82
+ const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(oracleAccountInfos[i].data);
83
+ result.push([
84
+ (0, oracleId_1.getOracleId)(oracleInfo.publicKey, oracleInfo.source),
85
+ oraclePriceData,
86
+ ]);
87
+ return result;
88
+ }, []));
89
+ }
90
+ async addPerpMarket(_marketIndex) {
91
+ if (!this.perpMarketIndexes.includes(_marketIndex)) {
92
+ this.perpMarketIndexes = this.perpMarketIndexes.concat(_marketIndex);
93
+ }
94
+ return true;
95
+ }
96
+ async addSpotMarket(_marketIndex) {
97
+ return true;
98
+ }
99
+ async addOracle(oracleInfo) {
100
+ var _a, _c;
101
+ if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
102
+ console.log('[grpcDriftClientAccountSubscriberV2] addOracle');
103
+ }
104
+ if (oracleInfo.publicKey.equals(web3_js_1.PublicKey.default)) {
105
+ return true;
106
+ }
107
+ const exists = this.oracleInfos.some((o) => o.source === oracleInfo.source &&
108
+ o.publicKey.equals(oracleInfo.publicKey));
109
+ if (exists) {
110
+ return true; // Already exists, don't add duplicate
111
+ }
112
+ this.oracleInfos = this.oracleInfos.concat(oracleInfo);
113
+ (_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.addAccounts([oracleInfo.publicKey]);
114
+ return true;
17
115
  }
18
116
  async subscribe() {
19
117
  if (this.isSubscribed) {
@@ -61,6 +159,32 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
61
159
  this.removeInitialData();
62
160
  return true;
63
161
  }
162
+ async fetch() {
163
+ var _a, _c, _d, _e;
164
+ await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.fetch());
165
+ await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.fetch());
166
+ await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.fetch());
167
+ await ((_e = this.oracleMultiSubscriber) === null || _e === void 0 ? void 0 : _e.fetch());
168
+ }
169
+ assertIsSubscribed() {
170
+ if (!this.isSubscribed) {
171
+ throw new types_1.NotSubscribedError('You must call `subscribe` before using this function');
172
+ }
173
+ }
174
+ getStateAccountAndSlot() {
175
+ this.assertIsSubscribed();
176
+ return this.stateAccountSubscriber.dataAndSlot;
177
+ }
178
+ getMarketAccountsAndSlots() {
179
+ var _a, _c;
180
+ const map = (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
181
+ return Array.from((_c = map === null || map === void 0 ? void 0 : map.values()) !== null && _c !== void 0 ? _c : []);
182
+ }
183
+ getSpotMarketAccountsAndSlots() {
184
+ var _a, _c;
185
+ const map = (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
186
+ return Array.from((_c = map === null || map === void 0 ? void 0 : map.values()) !== null && _c !== void 0 ? _c : []);
187
+ }
64
188
  getMarketAccountAndSlot(marketIndex) {
65
189
  var _a;
66
190
  return (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.perpMarketIndexToAccountPubkeyMap.get(marketIndex));
@@ -69,8 +193,40 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
69
193
  var _a;
70
194
  return (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountData(this.spotMarketIndexToAccountPubkeyMap.get(marketIndex));
71
195
  }
196
+ getOraclePriceDataAndSlot(oracleId) {
197
+ this.assertIsSubscribed();
198
+ // we need to rely on a map we store in this class because the grpcMultiAccountSubscriber does not track a mapping or oracle ID.
199
+ // 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).
200
+ return this.oracleIdToOracleDataMap.get(oracleId);
201
+ }
202
+ getOraclePriceDataAndSlotForPerpMarket(marketIndex) {
203
+ const perpMarketAccount = this.getMarketAccountAndSlot(marketIndex);
204
+ const oracle = this.perpOracleMap.get(marketIndex);
205
+ const oracleId = this.perpOracleStringMap.get(marketIndex);
206
+ if (!perpMarketAccount || !oracleId) {
207
+ return undefined;
208
+ }
209
+ if (!perpMarketAccount.data.amm.oracle.equals(oracle)) {
210
+ // If the oracle has changed, we need to update the oracle map in background
211
+ this.setPerpOracleMap();
212
+ }
213
+ return this.getOraclePriceDataAndSlot(oracleId);
214
+ }
215
+ getOraclePriceDataAndSlotForSpotMarket(marketIndex) {
216
+ const spotMarketAccount = this.getSpotMarketAccountAndSlot(marketIndex);
217
+ const oracle = this.spotOracleMap.get(marketIndex);
218
+ const oracleId = this.spotOracleStringMap.get(marketIndex);
219
+ if (!spotMarketAccount || !oracleId) {
220
+ return undefined;
221
+ }
222
+ if (!spotMarketAccount.data.oracle.equals(oracle)) {
223
+ // If the oracle has changed, we need to update the oracle map in background
224
+ this.setSpotOracleMap();
225
+ }
226
+ return this.getOraclePriceDataAndSlot(oracleId);
227
+ }
72
228
  async setPerpOracleMap() {
73
- var _a;
229
+ var _a, _c;
74
230
  const perpMarketsMap = (_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
75
231
  const perpMarkets = Array.from(perpMarketsMap.values());
76
232
  const addOraclePromises = [];
@@ -82,7 +238,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
82
238
  const perpMarketIndex = perpMarketAccount.marketIndex;
83
239
  const oracle = perpMarketAccount.amm.oracle;
84
240
  const oracleId = (0, oracleId_1.getOracleId)(oracle, perpMarket.data.amm.oracleSource);
85
- if (!this.oracleSubscribers.has(oracleId)) {
241
+ if (!((_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().has(oracleId))) {
86
242
  addOraclePromises.push(this.addOracle({
87
243
  publicKey: oracle,
88
244
  source: perpMarket.data.amm.oracleSource,
@@ -94,7 +250,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
94
250
  await Promise.all(addOraclePromises);
95
251
  }
96
252
  async setSpotOracleMap() {
97
- var _a;
253
+ var _a, _c;
98
254
  const spotMarketsMap = (_a = this.spotMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap();
99
255
  const spotMarkets = Array.from(spotMarketsMap.values());
100
256
  const addOraclePromises = [];
@@ -106,7 +262,7 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
106
262
  const spotMarketIndex = spotMarketAccount.marketIndex;
107
263
  const oracle = spotMarketAccount.oracle;
108
264
  const oracleId = (0, oracleId_1.getOracleId)(oracle, spotMarketAccount.oracleSource);
109
- if (!this.oracleSubscribers.has(oracleId)) {
265
+ if (!((_c = this.oracleMultiSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().has(oracleId))) {
110
266
  addOraclePromises.push(this.addOracle({
111
267
  publicKey: oracle,
112
268
  source: spotMarketAccount.oracleSource,
@@ -118,6 +274,10 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
118
274
  await Promise.all(addOraclePromises);
119
275
  }
120
276
  async subscribeToPerpMarketAccounts() {
277
+ var _a;
278
+ if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
279
+ console.log('[grpcDriftClientAccountSubscriberV2] subscribeToPerpMarketAccounts');
280
+ }
121
281
  const perpMarketIndexToAccountPubkeys = await Promise.all(this.perpMarketIndexes.map(async (marketIndex) => [
122
282
  marketIndex,
123
283
  await (0, pda_1.getPerpMarketPublicKey)(this.program.programId, marketIndex),
@@ -149,6 +309,10 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
149
309
  return true;
150
310
  }
151
311
  async subscribeToSpotMarketAccounts() {
312
+ var _a;
313
+ if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
314
+ console.log('[grpcDriftClientAccountSubscriberV2] subscribeToSpotMarketAccounts');
315
+ }
152
316
  const spotMarketIndexToAccountPubkeys = await Promise.all(this.spotMarketIndexes.map(async (marketIndex) => [
153
317
  marketIndex,
154
318
  await (0, pda_1.getSpotMarketPublicKey)(this.program.programId, marketIndex),
@@ -180,64 +344,133 @@ class grpcDriftClientAccountSubscriberV2 extends webSocketDriftClientAccountSubs
180
344
  return true;
181
345
  }
182
346
  async subscribeToOracles() {
183
- // Build list of unique oracle pubkeys and a lookup for sources
184
- const uniqueOraclePubkeys = new Map();
347
+ var _a;
348
+ if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
349
+ console.log('grpcDriftClientAccountSubscriberV2 subscribeToOracles');
350
+ }
351
+ const oraclePubkeyToInfosMap = new Map();
185
352
  for (const info of this.oracleInfos) {
186
- const id = (0, oracleId_1.getOracleId)(info.publicKey, info.source);
187
- if (!uniqueOraclePubkeys.has(id) &&
188
- !info.publicKey.equals(web3_js_1.PublicKey.default)) {
189
- uniqueOraclePubkeys.set(id, info);
353
+ const pubkey = info.publicKey.toBase58();
354
+ if (!oraclePubkeyToInfosMap.has(pubkey)) {
355
+ oraclePubkeyToInfosMap.set(pubkey, []);
190
356
  }
357
+ oraclePubkeyToInfosMap.get(pubkey).push(info);
191
358
  }
192
- const oraclePubkeys = Array.from(uniqueOraclePubkeys.values()).map((i) => i.publicKey);
193
- const pubkeyToSource = new Map(Array.from(uniqueOraclePubkeys.values()).map((i) => [
194
- i.publicKey.toBase58(),
195
- i.source,
196
- ]));
197
- this.oracleMultiSubscriber =
198
- await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'oracle', this.program, (buffer, pubkey) => {
199
- if (!pubkey) {
200
- throw new Error('Oracle pubkey missing in decode');
201
- }
202
- const source = pubkeyToSource.get(pubkey);
203
- const client = this.oracleClientCache.get(source, this.program.provider.connection, this.program);
204
- return client.getOraclePriceDataFromBuffer(buffer);
205
- }, this.resubOpts, undefined, async () => {
206
- var _a;
207
- try {
208
- if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
209
- console.log('[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing');
210
- }
211
- await this.subscribeToOracles();
212
- }
213
- catch (e) {
214
- console.error('Oracle resubscribe failed:', e);
359
+ const oraclePubkeys = Array.from(new Set(this.oracleInfos.map((info) => info.publicKey)));
360
+ this.oracleMultiSubscriber = await grpcMultiAccountSubscriber_1.grpcMultiAccountSubscriber.create(this.grpcConfigs, 'oracle', this.program, (buffer, pubkey, accountProps) => {
361
+ if (!pubkey) {
362
+ throw new Error('Oracle pubkey missing in decode');
363
+ }
364
+ const client = this.oracleClientCache.get(accountProps.source, this.program.provider.connection, this.program);
365
+ const price = client.getOraclePriceDataFromBuffer(buffer);
366
+ return price;
367
+ }, this.resubOpts, undefined, async () => {
368
+ var _a;
369
+ try {
370
+ if ((_a = this.resubOpts) === null || _a === void 0 ? void 0 : _a.logResubMessages) {
371
+ console.log('[grpcDriftClientAccountSubscriberV2] oracle subscriber unsubscribed; resubscribing');
215
372
  }
216
- });
373
+ await this.subscribeToOracles();
374
+ }
375
+ catch (e) {
376
+ console.error('Oracle resubscribe failed:', e);
377
+ }
378
+ }, oraclePubkeyToInfosMap);
217
379
  for (const data of this.initialOraclePriceData.entries()) {
218
380
  const { publicKey } = (0, oracleId_1.getPublicKeyAndSourceFromOracleId)(data[0]);
219
381
  this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
382
+ this.oracleIdToOracleDataMap.set(data[0], {
383
+ data: data[1],
384
+ slot: 0,
385
+ });
220
386
  }
221
- await this.oracleMultiSubscriber.subscribe(oraclePubkeys, (accountId, data) => {
222
- const source = pubkeyToSource.get(accountId.toBase58());
223
- this.eventEmitter.emit('oraclePriceUpdate', accountId, source, data);
387
+ await this.oracleMultiSubscriber.subscribe(oraclePubkeys, (accountId, data, context, _b, accountProps) => {
388
+ const oracleId = (0, oracleId_1.getOracleId)(accountId, accountProps.source);
389
+ this.oracleIdToOracleDataMap.set(oracleId, {
390
+ data,
391
+ slot: context.slot,
392
+ });
393
+ this.eventEmitter.emit('oraclePriceUpdate', accountId, accountProps.source, data);
224
394
  this.eventEmitter.emit('update');
225
395
  });
226
396
  return true;
227
397
  }
398
+ async handleDelistedMarkets() {
399
+ var _a, _c;
400
+ if (this.delistedMarketSetting === types_1.DelistedMarketSetting.Subscribe) {
401
+ return;
402
+ }
403
+ const { perpMarketIndexes, oracles } = (0, utils_1.findDelistedPerpMarketsAndOracles)(Array.from(((_a = this.perpMarketsSubscriber) === null || _a === void 0 ? void 0 : _a.getAccountDataMap().values()) || []), Array.from(((_c = this.spotMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.getAccountDataMap().values()) || []));
404
+ // Build array of perp market pubkeys to remove
405
+ const perpMarketPubkeysToRemove = perpMarketIndexes
406
+ .map((marketIndex) => {
407
+ const pubkeyString = this.perpMarketIndexToAccountPubkeyMap.get(marketIndex);
408
+ return pubkeyString ? new web3_js_1.PublicKey(pubkeyString) : null;
409
+ })
410
+ .filter((pubkey) => pubkey !== null);
411
+ // Build array of oracle pubkeys to remove
412
+ const oraclePubkeysToRemove = oracles.map((oracle) => oracle.publicKey);
413
+ // Remove accounts in batches - perp markets
414
+ if (perpMarketPubkeysToRemove.length > 0) {
415
+ await this.perpMarketsSubscriber.removeAccounts(perpMarketPubkeysToRemove);
416
+ // Clean up the mapping for removed perp markets
417
+ for (const pubkey of perpMarketPubkeysToRemove) {
418
+ const pubkeyString = pubkey.toBase58();
419
+ for (const [marketIndex, accountPubkey,] of this.perpMarketIndexToAccountPubkeyMap.entries()) {
420
+ if (accountPubkey === pubkeyString) {
421
+ this.perpMarketIndexToAccountPubkeyMap.delete(marketIndex);
422
+ this.perpOracleMap.delete(marketIndex);
423
+ this.perpOracleStringMap.delete(marketIndex);
424
+ break;
425
+ }
426
+ }
427
+ }
428
+ }
429
+ // Remove accounts in batches - oracles
430
+ if (oraclePubkeysToRemove.length > 0) {
431
+ await this.oracleMultiSubscriber.removeAccounts(oraclePubkeysToRemove);
432
+ // Clean up oracle data for removed oracles by finding their sources
433
+ for (const pubkey of oraclePubkeysToRemove) {
434
+ // Find the oracle source by checking oracleInfos
435
+ const oracleInfo = this.oracleInfos.find((info) => info.publicKey.equals(pubkey));
436
+ if (oracleInfo) {
437
+ const oracleId = (0, oracleId_1.getOracleId)(pubkey, oracleInfo.source);
438
+ this.oracleIdToOracleDataMap.delete(oracleId);
439
+ }
440
+ }
441
+ }
442
+ }
443
+ removeInitialData() {
444
+ this.initialPerpMarketAccountData = new Map();
445
+ this.initialSpotMarketAccountData = new Map();
446
+ this.initialOraclePriceData = new Map();
447
+ }
228
448
  async unsubscribeFromOracles() {
229
449
  if (this.oracleMultiSubscriber) {
230
450
  await this.oracleMultiSubscriber.unsubscribe();
231
451
  this.oracleMultiSubscriber = undefined;
232
452
  return;
233
453
  }
234
- await super.unsubscribeFromOracles();
235
454
  }
236
455
  async unsubscribe() {
237
- if (this.isSubscribed) {
456
+ var _a, _c, _d;
457
+ if (!this.isSubscribed) {
238
458
  return;
239
459
  }
240
- await this.stateAccountSubscriber.unsubscribe();
460
+ this.isSubscribed = false;
461
+ this.isSubscribing = false;
462
+ await ((_a = this.stateAccountSubscriber) === null || _a === void 0 ? void 0 : _a.unsubscribe());
463
+ await this.unsubscribeFromOracles();
464
+ await ((_c = this.perpMarketsSubscriber) === null || _c === void 0 ? void 0 : _c.unsubscribe());
465
+ await ((_d = this.spotMarketsSubscriber) === null || _d === void 0 ? void 0 : _d.unsubscribe());
466
+ // Clean up all maps to prevent memory leaks
467
+ this.perpMarketIndexToAccountPubkeyMap.clear();
468
+ this.spotMarketIndexToAccountPubkeyMap.clear();
469
+ this.oracleIdToOracleDataMap.clear();
470
+ this.perpOracleMap.clear();
471
+ this.perpOracleStringMap.clear();
472
+ this.spotOracleMap.clear();
473
+ this.spotOracleStringMap.clear();
241
474
  }
242
475
  }
243
476
  exports.grpcDriftClientAccountSubscriberV2 = grpcDriftClientAccountSubscriberV2;
@@ -4,7 +4,7 @@ import { Program } from '@coral-xyz/anchor';
4
4
  import { Context, PublicKey } from '@solana/web3.js';
5
5
  import { Client } from '../isomorphic/grpc';
6
6
  import { DataAndSlot, GrpcConfigs, ResubOpts } from './types';
7
- export declare class grpcMultiAccountSubscriber<T> {
7
+ export declare class grpcMultiAccountSubscriber<T, U = undefined> {
8
8
  private client;
9
9
  private stream;
10
10
  private commitmentLevel;
@@ -20,12 +20,15 @@ export declare class grpcMultiAccountSubscriber<T> {
20
20
  private subscribedAccounts;
21
21
  private onChangeMap;
22
22
  private dataMap;
23
+ private accountPropsMap;
24
+ private bufferMap;
23
25
  private constructor();
24
- static create<U>(grpcConfigs: GrpcConfigs, accountName: string, program: Program, decodeBuffer?: (buffer: Buffer, pubkey?: string) => U, resubOpts?: ResubOpts, clientProp?: Client, onUnsubscribe?: () => Promise<void>): Promise<grpcMultiAccountSubscriber<U>>;
26
+ static create<T, U = undefined>(grpcConfigs: GrpcConfigs, accountName: string, program: Program, decodeBuffer?: (buffer: Buffer, pubkey?: string, accountProps?: U) => T, resubOpts?: ResubOpts, clientProp?: Client, onUnsubscribe?: () => Promise<void>, accountPropsMap?: Map<string, U | Array<U>>): Promise<grpcMultiAccountSubscriber<T, U>>;
25
27
  setAccountData(accountPubkey: string, data: T, slot?: number): void;
26
28
  getAccountData(accountPubkey: string): DataAndSlot<T> | undefined;
27
29
  getAccountDataMap(): Map<string, DataAndSlot<T>>;
28
- subscribe(accounts: PublicKey[], onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void): Promise<void>;
30
+ fetch(): Promise<void>;
31
+ subscribe(accounts: PublicKey[], onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer, accountProps: U) => void): Promise<void>;
29
32
  addAccounts(accounts: PublicKey[]): Promise<void>;
30
33
  removeAccounts(accounts: PublicKey[]): Promise<void>;
31
34
  unsubscribe(): Promise<void>;