@drift-labs/sdk 2.142.0-beta.0 → 2.142.0-beta.10

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 (54) hide show
  1. package/VERSION +1 -1
  2. package/bun.lock +25 -10
  3. package/lib/browser/accounts/grpcAccountSubscriber.d.ts +2 -1
  4. package/lib/browser/accounts/grpcAccountSubscriber.js +4 -2
  5. package/lib/browser/accounts/grpcDriftClientAccountSubscriber.d.ts +1 -1
  6. package/lib/browser/accounts/grpcDriftClientAccountSubscriber.js +3 -3
  7. package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.d.ts +24 -0
  8. package/lib/browser/accounts/grpcDriftClientAccountSubscriberV2.js +243 -0
  9. package/lib/browser/accounts/grpcMultiAccountSubscriber.d.ts +34 -0
  10. package/lib/browser/accounts/grpcMultiAccountSubscriber.js +284 -0
  11. package/lib/browser/constants/spotMarkets.js +4 -4
  12. package/lib/browser/driftClient.js +11 -10
  13. package/lib/browser/driftClientConfig.d.ts +3 -0
  14. package/lib/browser/index.d.ts +1 -0
  15. package/lib/browser/index.js +1 -0
  16. package/lib/browser/types.d.ts +3 -1
  17. package/lib/node/accounts/grpcAccountSubscriber.d.ts +2 -1
  18. package/lib/node/accounts/grpcAccountSubscriber.d.ts.map +1 -1
  19. package/lib/node/accounts/grpcAccountSubscriber.js +4 -2
  20. package/lib/node/accounts/grpcDriftClientAccountSubscriber.d.ts +1 -1
  21. package/lib/node/accounts/grpcDriftClientAccountSubscriber.js +3 -3
  22. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +25 -0
  23. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -0
  24. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +243 -0
  25. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +35 -0
  26. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -0
  27. package/lib/node/accounts/grpcMultiAccountSubscriber.js +284 -0
  28. package/lib/node/constants/spotMarkets.js +4 -4
  29. package/lib/node/driftClient.d.ts.map +1 -1
  30. package/lib/node/driftClient.js +11 -10
  31. package/lib/node/driftClientConfig.d.ts +3 -0
  32. package/lib/node/driftClientConfig.d.ts.map +1 -1
  33. package/lib/node/index.d.ts +1 -0
  34. package/lib/node/index.d.ts.map +1 -1
  35. package/lib/node/index.js +1 -0
  36. package/lib/node/isomorphic/grpc.d.ts +5 -3
  37. package/lib/node/isomorphic/grpc.js +1 -3
  38. package/lib/node/isomorphic/grpc.node.d.ts +5 -3
  39. package/lib/node/isomorphic/grpc.node.d.ts.map +1 -1
  40. package/lib/node/isomorphic/grpc.node.js +1 -3
  41. package/lib/node/types.d.ts +3 -1
  42. package/lib/node/types.d.ts.map +1 -1
  43. package/package.json +10 -4
  44. package/scripts/client-test.ts +214 -0
  45. package/src/accounts/grpcAccountSubscriber.ts +9 -6
  46. package/src/accounts/grpcDriftClientAccountSubscriber.ts +1 -1
  47. package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +417 -0
  48. package/src/accounts/grpcMultiAccountSubscriber.ts +343 -0
  49. package/src/constants/spotMarkets.ts +4 -4
  50. package/src/driftClient.ts +5 -2
  51. package/src/driftClientConfig.ts +13 -0
  52. package/src/index.ts +1 -0
  53. package/src/isomorphic/grpc.node.ts +11 -7
  54. package/src/types.ts +4 -2
@@ -0,0 +1,417 @@
1
+ import { WebSocketDriftClientAccountSubscriber } from './webSocketDriftClientAccountSubscriber';
2
+ import { OracleInfo, OraclePriceData } from '../oracles/types';
3
+ import { Program } from '@coral-xyz/anchor';
4
+ import { PublicKey } from '@solana/web3.js';
5
+ import { findAllMarketAndOracles } from '../config';
6
+ import {
7
+ getDriftStateAccountPublicKey,
8
+ getPerpMarketPublicKey,
9
+ getSpotMarketPublicKey,
10
+ } from '../addresses/pda';
11
+ import {
12
+ DataAndSlot,
13
+ DelistedMarketSetting,
14
+ GrpcConfigs,
15
+ ResubOpts,
16
+ } from './types';
17
+ import { grpcAccountSubscriber } from './grpcAccountSubscriber';
18
+ import { grpcMultiAccountSubscriber } from './grpcMultiAccountSubscriber';
19
+ import { PerpMarketAccount, SpotMarketAccount, StateAccount } from '../types';
20
+ import {
21
+ getOracleId,
22
+ getPublicKeyAndSourceFromOracleId,
23
+ } from '../oracles/oracleId';
24
+
25
+ export class grpcDriftClientAccountSubscriberV2 extends WebSocketDriftClientAccountSubscriber {
26
+ private grpcConfigs: GrpcConfigs;
27
+ private perpMarketsSubscriber?: grpcMultiAccountSubscriber<PerpMarketAccount>;
28
+ private spotMarketsSubscriber?: grpcMultiAccountSubscriber<SpotMarketAccount>;
29
+ private oracleMultiSubscriber?: grpcMultiAccountSubscriber<OraclePriceData>;
30
+ private perpMarketIndexToAccountPubkeyMap = new Map<number, string>();
31
+ private spotMarketIndexToAccountPubkeyMap = new Map<number, string>();
32
+
33
+ constructor(
34
+ grpcConfigs: GrpcConfigs,
35
+ program: Program,
36
+ perpMarketIndexes: number[],
37
+ spotMarketIndexes: number[],
38
+ oracleInfos: OracleInfo[],
39
+ shouldFindAllMarketsAndOracles: boolean,
40
+ delistedMarketSetting: DelistedMarketSetting,
41
+ resubOpts?: ResubOpts
42
+ ) {
43
+ super(
44
+ program,
45
+ perpMarketIndexes,
46
+ spotMarketIndexes,
47
+ oracleInfos,
48
+ shouldFindAllMarketsAndOracles,
49
+ delistedMarketSetting,
50
+ resubOpts
51
+ );
52
+ this.grpcConfigs = grpcConfigs;
53
+ }
54
+
55
+ public async subscribe(): Promise<boolean> {
56
+ if (this.isSubscribed) {
57
+ return true;
58
+ }
59
+
60
+ if (this.isSubscribing) {
61
+ return await this.subscriptionPromise;
62
+ }
63
+
64
+ this.isSubscribing = true;
65
+
66
+ this.subscriptionPromise = new Promise((res) => {
67
+ this.subscriptionPromiseResolver = res;
68
+ });
69
+
70
+ if (this.shouldFindAllMarketsAndOracles) {
71
+ const {
72
+ perpMarketIndexes,
73
+ perpMarketAccounts,
74
+ spotMarketIndexes,
75
+ spotMarketAccounts,
76
+ oracleInfos,
77
+ } = await findAllMarketAndOracles(this.program);
78
+ this.perpMarketIndexes = perpMarketIndexes;
79
+ this.spotMarketIndexes = spotMarketIndexes;
80
+ this.oracleInfos = oracleInfos;
81
+ // front run and set the initial data here to save extra gma call in set initial data
82
+ this.initialPerpMarketAccountData = new Map(
83
+ perpMarketAccounts.map((market) => [market.marketIndex, market])
84
+ );
85
+ this.initialSpotMarketAccountData = new Map(
86
+ spotMarketAccounts.map((market) => [market.marketIndex, market])
87
+ );
88
+ }
89
+
90
+ const statePublicKey = await getDriftStateAccountPublicKey(
91
+ this.program.programId
92
+ );
93
+
94
+ // create and activate main state account subscription
95
+ this.stateAccountSubscriber =
96
+ await grpcAccountSubscriber.create<StateAccount>(
97
+ this.grpcConfigs,
98
+ 'state',
99
+ this.program,
100
+ statePublicKey,
101
+ undefined,
102
+ undefined
103
+ );
104
+ await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
105
+ this.eventEmitter.emit('stateAccountUpdate', data);
106
+ this.eventEmitter.emit('update');
107
+ });
108
+
109
+ // set initial data to avoid spamming getAccountInfo calls in webSocketAccountSubscriber
110
+ await this.setInitialData();
111
+
112
+ // subscribe to perp + spot markets (separate) and oracles
113
+ await Promise.all([
114
+ this.subscribeToPerpMarketAccounts(),
115
+ this.subscribeToSpotMarketAccounts(),
116
+ this.subscribeToOracles(),
117
+ ]);
118
+
119
+ this.eventEmitter.emit('update');
120
+
121
+ await this.handleDelistedMarkets();
122
+
123
+ await Promise.all([this.setPerpOracleMap(), this.setSpotOracleMap()]);
124
+
125
+ this.subscriptionPromiseResolver(true);
126
+
127
+ this.isSubscribing = false;
128
+ this.isSubscribed = true;
129
+
130
+ // delete initial data
131
+ this.removeInitialData();
132
+
133
+ return true;
134
+ }
135
+
136
+ override getMarketAccountAndSlot(
137
+ marketIndex: number
138
+ ): DataAndSlot<PerpMarketAccount> | undefined {
139
+ return this.perpMarketsSubscriber?.getAccountData(
140
+ this.perpMarketIndexToAccountPubkeyMap.get(marketIndex)
141
+ );
142
+ }
143
+
144
+ override getSpotMarketAccountAndSlot(
145
+ marketIndex: number
146
+ ): DataAndSlot<SpotMarketAccount> | undefined {
147
+ return this.spotMarketsSubscriber?.getAccountData(
148
+ this.spotMarketIndexToAccountPubkeyMap.get(marketIndex)
149
+ );
150
+ }
151
+
152
+ override async setPerpOracleMap() {
153
+ const perpMarketsMap = this.perpMarketsSubscriber?.getAccountDataMap();
154
+ const perpMarkets = Array.from(perpMarketsMap.values());
155
+ const addOraclePromises = [];
156
+ for (const perpMarket of perpMarkets) {
157
+ if (!perpMarket || !perpMarket.data) {
158
+ continue;
159
+ }
160
+ const perpMarketAccount = perpMarket.data;
161
+ const perpMarketIndex = perpMarketAccount.marketIndex;
162
+ const oracle = perpMarketAccount.amm.oracle;
163
+ const oracleId = getOracleId(oracle, perpMarket.data.amm.oracleSource);
164
+ if (!this.oracleSubscribers.has(oracleId)) {
165
+ addOraclePromises.push(
166
+ this.addOracle({
167
+ publicKey: oracle,
168
+ source: perpMarket.data.amm.oracleSource,
169
+ })
170
+ );
171
+ }
172
+ this.perpOracleMap.set(perpMarketIndex, oracle);
173
+ this.perpOracleStringMap.set(perpMarketIndex, oracleId);
174
+ }
175
+ await Promise.all(addOraclePromises);
176
+ }
177
+
178
+ override async setSpotOracleMap() {
179
+ const spotMarketsMap = this.spotMarketsSubscriber?.getAccountDataMap();
180
+ const spotMarkets = Array.from(spotMarketsMap.values());
181
+ const addOraclePromises = [];
182
+ for (const spotMarket of spotMarkets) {
183
+ if (!spotMarket || !spotMarket.data) {
184
+ continue;
185
+ }
186
+ const spotMarketAccount = spotMarket.data;
187
+ const spotMarketIndex = spotMarketAccount.marketIndex;
188
+ const oracle = spotMarketAccount.oracle;
189
+ const oracleId = getOracleId(oracle, spotMarketAccount.oracleSource);
190
+ if (!this.oracleSubscribers.has(oracleId)) {
191
+ addOraclePromises.push(
192
+ this.addOracle({
193
+ publicKey: oracle,
194
+ source: spotMarketAccount.oracleSource,
195
+ })
196
+ );
197
+ }
198
+ this.spotOracleMap.set(spotMarketIndex, oracle);
199
+ this.spotOracleStringMap.set(spotMarketIndex, oracleId);
200
+ }
201
+ await Promise.all(addOraclePromises);
202
+ }
203
+
204
+ override async subscribeToPerpMarketAccounts(): Promise<boolean> {
205
+ const perpMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
206
+ await Promise.all(
207
+ this.perpMarketIndexes.map(async (marketIndex) => [
208
+ marketIndex,
209
+ await getPerpMarketPublicKey(this.program.programId, marketIndex),
210
+ ])
211
+ );
212
+ for (const [
213
+ marketIndex,
214
+ accountPubkey,
215
+ ] of perpMarketIndexToAccountPubkeys) {
216
+ this.perpMarketIndexToAccountPubkeyMap.set(
217
+ marketIndex,
218
+ accountPubkey.toBase58()
219
+ );
220
+ }
221
+
222
+ const perpMarketPubkeys = perpMarketIndexToAccountPubkeys.map(
223
+ ([_, accountPubkey]) => accountPubkey
224
+ );
225
+
226
+ this.perpMarketsSubscriber =
227
+ await grpcMultiAccountSubscriber.create<PerpMarketAccount>(
228
+ this.grpcConfigs,
229
+ 'perpMarket',
230
+ this.program,
231
+ undefined,
232
+ this.resubOpts,
233
+ undefined,
234
+ async () => {
235
+ try {
236
+ if (this.resubOpts?.logResubMessages) {
237
+ console.log(
238
+ '[grpcDriftClientAccountSubscriberV2] perp markets subscriber unsubscribed; resubscribing'
239
+ );
240
+ }
241
+ await this.subscribeToPerpMarketAccounts();
242
+ } catch (e) {
243
+ console.error('Perp markets resubscribe failed:', e);
244
+ }
245
+ }
246
+ );
247
+
248
+ for (const data of this.initialPerpMarketAccountData.values()) {
249
+ this.perpMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
250
+ }
251
+
252
+ await this.perpMarketsSubscriber.subscribe(
253
+ perpMarketPubkeys,
254
+ (_accountId, data) => {
255
+ this.eventEmitter.emit(
256
+ 'perpMarketAccountUpdate',
257
+ data as PerpMarketAccount
258
+ );
259
+ this.eventEmitter.emit('update');
260
+ }
261
+ );
262
+
263
+ return true;
264
+ }
265
+
266
+ override async subscribeToSpotMarketAccounts(): Promise<boolean> {
267
+ const spotMarketIndexToAccountPubkeys: Array<[number, PublicKey]> =
268
+ await Promise.all(
269
+ this.spotMarketIndexes.map(async (marketIndex) => [
270
+ marketIndex,
271
+ await getSpotMarketPublicKey(this.program.programId, marketIndex),
272
+ ])
273
+ );
274
+ for (const [
275
+ marketIndex,
276
+ accountPubkey,
277
+ ] of spotMarketIndexToAccountPubkeys) {
278
+ this.spotMarketIndexToAccountPubkeyMap.set(
279
+ marketIndex,
280
+ accountPubkey.toBase58()
281
+ );
282
+ }
283
+
284
+ const spotMarketPubkeys = spotMarketIndexToAccountPubkeys.map(
285
+ ([_, accountPubkey]) => accountPubkey
286
+ );
287
+
288
+ this.spotMarketsSubscriber =
289
+ await grpcMultiAccountSubscriber.create<SpotMarketAccount>(
290
+ this.grpcConfigs,
291
+ 'spotMarket',
292
+ this.program,
293
+ undefined,
294
+ this.resubOpts,
295
+ undefined,
296
+ async () => {
297
+ try {
298
+ if (this.resubOpts?.logResubMessages) {
299
+ console.log(
300
+ '[grpcDriftClientAccountSubscriberV2] spot markets subscriber unsubscribed; resubscribing'
301
+ );
302
+ }
303
+ await this.subscribeToSpotMarketAccounts();
304
+ } catch (e) {
305
+ console.error('Spot markets resubscribe failed:', e);
306
+ }
307
+ }
308
+ );
309
+
310
+ for (const data of this.initialSpotMarketAccountData.values()) {
311
+ this.spotMarketsSubscriber.setAccountData(data.pubkey.toBase58(), data);
312
+ }
313
+
314
+ await this.spotMarketsSubscriber.subscribe(
315
+ spotMarketPubkeys,
316
+ (_accountId, data) => {
317
+ this.eventEmitter.emit(
318
+ 'spotMarketAccountUpdate',
319
+ data as SpotMarketAccount
320
+ );
321
+ this.eventEmitter.emit('update');
322
+ }
323
+ );
324
+
325
+ return true;
326
+ }
327
+
328
+ override async subscribeToOracles(): Promise<boolean> {
329
+ // Build list of unique oracle pubkeys and a lookup for sources
330
+ const uniqueOraclePubkeys = new Map<string, OracleInfo>();
331
+ for (const info of this.oracleInfos) {
332
+ const id = getOracleId(info.publicKey, info.source);
333
+ if (
334
+ !uniqueOraclePubkeys.has(id) &&
335
+ !info.publicKey.equals((PublicKey as any).default)
336
+ ) {
337
+ uniqueOraclePubkeys.set(id, info);
338
+ }
339
+ }
340
+
341
+ const oraclePubkeys = Array.from(uniqueOraclePubkeys.values()).map(
342
+ (i) => i.publicKey
343
+ );
344
+ const pubkeyToSource = new Map<string, OracleInfo['source']>(
345
+ Array.from(uniqueOraclePubkeys.values()).map((i) => [
346
+ i.publicKey.toBase58(),
347
+ i.source,
348
+ ])
349
+ );
350
+
351
+ this.oracleMultiSubscriber =
352
+ await grpcMultiAccountSubscriber.create<OraclePriceData>(
353
+ this.grpcConfigs,
354
+ 'oracle',
355
+ this.program,
356
+ (buffer: Buffer, pubkey?: string) => {
357
+ if (!pubkey) {
358
+ throw new Error('Oracle pubkey missing in decode');
359
+ }
360
+ const source = pubkeyToSource.get(pubkey);
361
+ const client = this.oracleClientCache.get(
362
+ source,
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);
380
+ }
381
+ }
382
+ );
383
+
384
+ for (const data of this.initialOraclePriceData.entries()) {
385
+ const { publicKey } = getPublicKeyAndSourceFromOracleId(data[0]);
386
+ this.oracleMultiSubscriber.setAccountData(publicKey.toBase58(), data[1]);
387
+ }
388
+
389
+ await this.oracleMultiSubscriber.subscribe(
390
+ oraclePubkeys,
391
+ (accountId, data) => {
392
+ const source = pubkeyToSource.get(accountId.toBase58());
393
+ this.eventEmitter.emit('oraclePriceUpdate', accountId, source, data);
394
+ this.eventEmitter.emit('update');
395
+ }
396
+ );
397
+
398
+ return true;
399
+ }
400
+
401
+ async unsubscribeFromOracles(): Promise<void> {
402
+ if (this.oracleMultiSubscriber) {
403
+ await this.oracleMultiSubscriber.unsubscribe();
404
+ this.oracleMultiSubscriber = undefined;
405
+ return;
406
+ }
407
+ await super.unsubscribeFromOracles();
408
+ }
409
+
410
+ override async unsubscribe(): Promise<void> {
411
+ if (this.isSubscribed) {
412
+ return;
413
+ }
414
+
415
+ await this.stateAccountSubscriber.unsubscribe();
416
+ }
417
+ }