@drift-labs/sdk 2.96.0-beta.0 → 2.96.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 (53) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/grpcAccountSubscriber.d.ts +16 -0
  3. package/lib/accounts/grpcAccountSubscriber.js +155 -0
  4. package/lib/accounts/grpcDriftClientAccountSubscriber.d.ts +13 -0
  5. package/lib/accounts/grpcDriftClientAccountSubscriber.js +96 -0
  6. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +10 -0
  7. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.js +30 -0
  8. package/lib/accounts/grpcProgramAccountSubscriber.d.ts +19 -0
  9. package/lib/accounts/grpcProgramAccountSubscriber.js +161 -0
  10. package/lib/accounts/grpcUserAccountSubscriber.d.ts +10 -0
  11. package/lib/accounts/grpcUserAccountSubscriber.js +28 -0
  12. package/lib/accounts/grpcUserStatsAccountSubscriber.d.ts +10 -0
  13. package/lib/accounts/grpcUserStatsAccountSubscriber.js +28 -0
  14. package/lib/accounts/types.d.ts +8 -0
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  16. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
  17. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  18. package/lib/driftClient.js +35 -14
  19. package/lib/driftClientConfig.d.ts +6 -0
  20. package/lib/orderSubscriber/OrderSubscriber.d.ts +2 -1
  21. package/lib/orderSubscriber/OrderSubscriber.js +19 -4
  22. package/lib/orderSubscriber/grpcSubscription.d.ts +25 -0
  23. package/lib/orderSubscriber/grpcSubscription.js +68 -0
  24. package/lib/orderSubscriber/types.d.ts +9 -0
  25. package/lib/user.js +11 -4
  26. package/lib/userConfig.d.ts +6 -1
  27. package/lib/userMap/grpcSubscription.d.ts +26 -0
  28. package/lib/userMap/grpcSubscription.js +42 -0
  29. package/lib/userMap/userMap.js +14 -0
  30. package/lib/userMap/userMapConfig.d.ts +7 -0
  31. package/lib/userStatsConfig.d.ts +6 -0
  32. package/package.json +3 -1
  33. package/src/accounts/grpcAccountSubscriber.ts +158 -0
  34. package/src/accounts/grpcDriftClientAccountSubscriber.ts +196 -0
  35. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +62 -0
  36. package/src/accounts/grpcProgramAccountSubscriber.ts +181 -0
  37. package/src/accounts/grpcUserAccountSubscriber.ts +48 -0
  38. package/src/accounts/grpcUserStatsAccountSubscriber.ts +51 -0
  39. package/src/accounts/types.ts +9 -0
  40. package/src/accounts/webSocketAccountSubscriber.ts +1 -1
  41. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +3 -3
  42. package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
  43. package/src/driftClient.ts +28 -0
  44. package/src/driftClientConfig.ts +7 -0
  45. package/src/orderSubscriber/OrderSubscriber.ts +15 -1
  46. package/src/orderSubscriber/grpcSubscription.ts +126 -0
  47. package/src/orderSubscriber/types.ts +10 -0
  48. package/src/user.ts +11 -0
  49. package/src/userConfig.ts +7 -1
  50. package/src/userMap/grpcSubscription.ts +83 -0
  51. package/src/userMap/userMap.ts +17 -1
  52. package/src/userMap/userMapConfig.ts +8 -0
  53. package/src/userStatsConfig.ts +7 -0
@@ -94,7 +94,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
94
94
  };
95
95
  }
96
96
 
97
- private setTimeout(): void {
97
+ protected setTimeout(): void {
98
98
  if (!this.onChange) {
99
99
  throw new Error('onChange callback function must be set');
100
100
  }
@@ -60,9 +60,9 @@ export class WebSocketDriftClientAccountSubscriber
60
60
  initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
61
61
  initialOraclePriceData: Map<string, OraclePriceData>;
62
62
 
63
- private isSubscribing = false;
64
- private subscriptionPromise: Promise<boolean>;
65
- private subscriptionPromiseResolver: (val: boolean) => void;
63
+ protected isSubscribing = false;
64
+ protected subscriptionPromise: Promise<boolean>;
65
+ protected subscriptionPromiseResolver: (val: boolean) => void;
66
66
 
67
67
  public constructor(
68
68
  program: Program,
@@ -98,7 +98,7 @@ export class WebSocketProgramAccountSubscriber<T>
98
98
  }
99
99
  }
100
100
 
101
- private setTimeout(): void {
101
+ protected setTimeout(): void {
102
102
  if (!this.onChange) {
103
103
  throw new Error('onChange callback function must be set');
104
104
  }
@@ -152,6 +152,7 @@ import { isVersionedTransaction } from './tx/utils';
152
152
  import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json';
153
153
  import { asV0Tx, PullFeed } from '@switchboard-xyz/on-demand';
154
154
  import switchboardOnDemandIdl from './idl/switchboard_on_demand_30.json';
155
+ import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
155
156
 
156
157
  type RemainingAccountParams = {
157
158
  userAccounts: UserAccount[];
@@ -292,6 +293,19 @@ export class DriftClient {
292
293
  type: 'polling',
293
294
  accountLoader: config.accountSubscription.accountLoader,
294
295
  };
296
+ } else if (config.accountSubscription?.type === 'grpc') {
297
+ this.userAccountSubscriptionConfig = {
298
+ type: 'grpc',
299
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
300
+ logResubMessages: config.accountSubscription?.logResubMessages,
301
+ configs: config.accountSubscription?.configs,
302
+ };
303
+ this.userStatsAccountSubscriptionConfig = {
304
+ type: 'grpc',
305
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
306
+ logResubMessages: config.accountSubscription?.logResubMessages,
307
+ configs: config.accountSubscription?.configs,
308
+ };
295
309
  } else {
296
310
  this.userAccountSubscriptionConfig = {
297
311
  type: 'websocket',
@@ -338,6 +352,19 @@ export class DriftClient {
338
352
  config.oracleInfos ?? [],
339
353
  noMarketsAndOraclesSpecified
340
354
  );
355
+ } else if (config.accountSubscription?.type === 'grpc') {
356
+ this.accountSubscriber = new gprcDriftClientAccountSubscriber(
357
+ config.accountSubscription.configs,
358
+ this.program,
359
+ config.perpMarketIndexes ?? [],
360
+ config.spotMarketIndexes ?? [],
361
+ config.oracleInfos ?? [],
362
+ noMarketsAndOraclesSpecified,
363
+ {
364
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
365
+ logResubMessages: config.accountSubscription?.logResubMessages,
366
+ }
367
+ );
341
368
  } else {
342
369
  this.accountSubscriber = new WebSocketDriftClientAccountSubscriber(
343
370
  this.program,
@@ -352,6 +379,7 @@ export class DriftClient {
352
379
  config.accountSubscription?.commitment
353
380
  );
354
381
  }
382
+
355
383
  this.eventEmitter = this.accountSubscriber.eventEmitter;
356
384
 
357
385
  this.metricsEventEmitter = new EventEmitter();
@@ -11,6 +11,7 @@ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
11
11
  import { DriftEnv } from './config';
12
12
  import { TxSender } from './tx/types';
13
13
  import { TxHandler, TxHandlerConfig } from './tx/txHandler';
14
+ import { GrpcConfigs } from './accounts/types';
14
15
 
15
16
  export type DriftClientConfig = {
16
17
  connection: Connection;
@@ -48,4 +49,10 @@ export type DriftClientSubscriptionConfig =
48
49
  | {
49
50
  type: 'polling';
50
51
  accountLoader: BulkAccountLoader;
52
+ }
53
+ | {
54
+ type: 'grpc';
55
+ configs: GrpcConfigs;
56
+ resubTimeoutMs?: number;
57
+ logResubMessages?: boolean;
51
58
  };
@@ -11,11 +11,12 @@ import StrictEventEmitter from 'strict-event-emitter-types';
11
11
  import { EventEmitter } from 'events';
12
12
  import { BN } from '../index';
13
13
  import { decodeUser } from '../decode/user';
14
+ import { grpcSubscription } from './grpcSubscription';
14
15
 
15
16
  export class OrderSubscriber {
16
17
  driftClient: DriftClient;
17
18
  usersAccounts = new Map<string, { slot: number; userAccount: UserAccount }>();
18
- subscription: PollingSubscription | WebsocketSubscription;
19
+ subscription: PollingSubscription | WebsocketSubscription | grpcSubscription;
19
20
  commitment: Commitment;
20
21
  eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
21
22
 
@@ -34,6 +35,19 @@ export class OrderSubscriber {
34
35
  orderSubscriber: this,
35
36
  frequency: config.subscriptionConfig.frequency,
36
37
  });
38
+ } else if (config.subscriptionConfig.type === 'grpc') {
39
+ this.subscription = new grpcSubscription({
40
+ grpcConfigs: config.subscriptionConfig.configs,
41
+ orderSubscriber: this,
42
+ commitment: this.commitment,
43
+ skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
44
+ resubOpts: {
45
+ resubTimeoutMs: config.subscriptionConfig?.resubTimeoutMs,
46
+ logResubMessages: config.subscriptionConfig?.logResubMessages,
47
+ },
48
+ resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
49
+ decoded: config.decodeData,
50
+ });
37
51
  } else {
38
52
  this.subscription = new WebsocketSubscription({
39
53
  orderSubscriber: this,
@@ -0,0 +1,126 @@
1
+ import { Commitment, Context, PublicKey } from '@solana/web3.js';
2
+ import { Buffer } from 'buffer';
3
+ import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber';
4
+ import { OrderSubscriber } from './OrderSubscriber';
5
+ import { GrpcConfigs, ResubOpts } from '../accounts/types';
6
+ import { UserAccount } from '../types';
7
+ import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
8
+
9
+ export class grpcSubscription {
10
+ private orderSubscriber: OrderSubscriber;
11
+ private commitment: Commitment;
12
+ private skipInitialLoad: boolean;
13
+ private resubOpts?: ResubOpts;
14
+ private resyncIntervalMs?: number;
15
+
16
+ private subscriber?: grpcProgramAccountSubscriber<UserAccount>;
17
+ private resyncTimeoutId?: NodeJS.Timeout;
18
+
19
+ private decoded?: boolean;
20
+
21
+ private grpcConfigs: GrpcConfigs;
22
+
23
+ constructor({
24
+ grpcConfigs,
25
+ orderSubscriber,
26
+ commitment,
27
+ skipInitialLoad = false,
28
+ resubOpts,
29
+ resyncIntervalMs,
30
+ decoded = true,
31
+ }: {
32
+ grpcConfigs: GrpcConfigs;
33
+ orderSubscriber: OrderSubscriber;
34
+ commitment: Commitment;
35
+ skipInitialLoad?: boolean;
36
+ resubOpts?: ResubOpts;
37
+ resyncIntervalMs?: number;
38
+ decoded?: boolean;
39
+ }) {
40
+ this.orderSubscriber = orderSubscriber;
41
+ this.commitment = commitment;
42
+ this.skipInitialLoad = skipInitialLoad;
43
+ this.resubOpts = resubOpts;
44
+ this.resyncIntervalMs = resyncIntervalMs;
45
+ this.decoded = decoded;
46
+ this.grpcConfigs = grpcConfigs;
47
+ }
48
+
49
+ public async subscribe(): Promise<void> {
50
+ if (this.subscriber) {
51
+ return;
52
+ }
53
+
54
+ this.subscriber = new grpcProgramAccountSubscriber<UserAccount>(
55
+ this.grpcConfigs,
56
+ 'OrderSubscriber',
57
+ 'User',
58
+ this.orderSubscriber.driftClient.program,
59
+ this.orderSubscriber.decodeFn,
60
+ {
61
+ filters: [getUserFilter(), getNonIdleUserFilter()],
62
+ commitment: this.commitment,
63
+ },
64
+ this.resubOpts
65
+ );
66
+
67
+ await this.subscriber.subscribe(
68
+ (
69
+ accountId: PublicKey,
70
+ account: UserAccount,
71
+ context: Context,
72
+ buffer: Buffer
73
+ ) => {
74
+ const userKey = accountId.toBase58();
75
+ if (this.decoded ?? true) {
76
+ this.orderSubscriber.tryUpdateUserAccount(
77
+ userKey,
78
+ 'decoded',
79
+ account,
80
+ context.slot
81
+ );
82
+ } else {
83
+ this.orderSubscriber.tryUpdateUserAccount(
84
+ userKey,
85
+ 'buffer',
86
+ buffer,
87
+ context.slot
88
+ );
89
+ }
90
+ }
91
+ );
92
+
93
+ if (!this.skipInitialLoad) {
94
+ await this.orderSubscriber.fetch();
95
+ }
96
+
97
+ if (this.resyncIntervalMs) {
98
+ const recursiveResync = () => {
99
+ this.resyncTimeoutId = setTimeout(() => {
100
+ this.orderSubscriber
101
+ .fetch()
102
+ .catch((e) => {
103
+ console.error('Failed to resync in OrderSubscriber');
104
+ console.log(e);
105
+ })
106
+ .finally(() => {
107
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
+ if (!this.resyncTimeoutId) return;
109
+ recursiveResync();
110
+ });
111
+ }, this.resyncIntervalMs);
112
+ };
113
+ recursiveResync();
114
+ }
115
+ }
116
+
117
+ public async unsubscribe(): Promise<void> {
118
+ if (!this.subscriber) return;
119
+ await this.subscriber.unsubscribe();
120
+ this.subscriber = undefined;
121
+ if (this.resyncTimeoutId !== undefined) {
122
+ clearTimeout(this.resyncTimeoutId);
123
+ this.resyncTimeoutId = undefined;
124
+ }
125
+ }
126
+ }
@@ -1,6 +1,7 @@
1
1
  import { Commitment, PublicKey } from '@solana/web3.js';
2
2
  import { Order, UserAccount } from '../types';
3
3
  import { DriftClient } from '../driftClient';
4
+ import { GrpcConfigs } from '../accounts/types';
4
5
 
5
6
  export type OrderSubscriberConfig = {
6
7
  driftClient: DriftClient;
@@ -10,6 +11,15 @@ export type OrderSubscriberConfig = {
10
11
  frequency: number;
11
12
  commitment?: Commitment;
12
13
  }
14
+ | {
15
+ type: 'grpc';
16
+ skipInitialLoad?: boolean;
17
+ resubTimeoutMs?: number;
18
+ logResubMessages?: boolean;
19
+ resyncIntervalMs?: number;
20
+ configs: GrpcConfigs;
21
+ commitment?: Commitment;
22
+ }
13
23
  | {
14
24
  type: 'websocket';
15
25
  skipInitialLoad?: boolean;
package/src/user.ts CHANGED
@@ -98,6 +98,7 @@ import {
98
98
  calculatePerpFuelBonus,
99
99
  calculateInsuranceFuelBonus,
100
100
  } from './math/fuel';
101
+ import { grpcUserAccountSubscriber } from './accounts/grpcUserAccountSubscriber';
101
102
 
102
103
  export class User {
103
104
  driftClient: DriftClient;
@@ -128,6 +129,16 @@ export class User {
128
129
  );
129
130
  } else if (config.accountSubscription?.type === 'custom') {
130
131
  this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
132
+ } else if (config.accountSubscription?.type === 'grpc') {
133
+ this.accountSubscriber = new grpcUserAccountSubscriber(
134
+ config.accountSubscription.configs,
135
+ config.driftClient.program,
136
+ config.userAccountPublicKey,
137
+ {
138
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
139
+ logResubMessages: config.accountSubscription?.logResubMessages,
140
+ }
141
+ );
131
142
  } else {
132
143
  this.accountSubscriber = new WebSocketUserAccountSubscriber(
133
144
  config.driftClient.program,
package/src/userConfig.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { DriftClient } from './driftClient';
2
2
  import { Commitment, PublicKey } from '@solana/web3.js';
3
3
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
- import { UserAccountSubscriber } from './accounts/types';
4
+ import { GrpcConfigs, UserAccountSubscriber } from './accounts/types';
5
5
 
6
6
  export type UserConfig = {
7
7
  accountSubscription?: UserSubscriptionConfig;
@@ -23,4 +23,10 @@ export type UserSubscriptionConfig =
23
23
  | {
24
24
  type: 'custom';
25
25
  userAccountSubscriber: UserAccountSubscriber;
26
+ }
27
+ | {
28
+ type: 'grpc';
29
+ resubTimeoutMs?: number;
30
+ logResubMessages?: boolean;
31
+ configs: GrpcConfigs;
26
32
  };
@@ -0,0 +1,83 @@
1
+ import { UserMap } from './userMap';
2
+ import { getNonIdleUserFilter, getUserFilter } from '../memcmp';
3
+ import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
4
+ import { UserAccount } from '../types';
5
+ import { Commitment, Context, PublicKey } from '@solana/web3.js';
6
+ import { GrpcConfigs, ResubOpts } from '../accounts/types';
7
+ import { grpcProgramAccountSubscriber } from '../accounts/grpcProgramAccountSubscriber';
8
+
9
+ export class grpcSubscription {
10
+ private configs: GrpcConfigs;
11
+ private userMap: UserMap;
12
+ private commitment: Commitment;
13
+ private skipInitialLoad: boolean;
14
+ private resubOpts?: ResubOpts;
15
+ private includeIdle?: boolean;
16
+ private decodeFn: (name: string, data: Buffer) => UserAccount;
17
+
18
+ private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
19
+
20
+ constructor({
21
+ configs,
22
+ userMap,
23
+ commitment,
24
+ skipInitialLoad = false,
25
+ resubOpts,
26
+ includeIdle = false,
27
+ decodeFn,
28
+ }: {
29
+ configs: GrpcConfigs;
30
+ userMap: UserMap;
31
+ commitment: Commitment;
32
+ skipInitialLoad?: boolean;
33
+ resubOpts?: ResubOpts;
34
+ includeIdle?: boolean;
35
+ decodeFn: (name: string, data: Buffer) => UserAccount;
36
+ }) {
37
+ this.userMap = userMap;
38
+ this.commitment = commitment;
39
+ this.skipInitialLoad = skipInitialLoad;
40
+ this.resubOpts = resubOpts;
41
+ this.includeIdle = includeIdle || false;
42
+ this.decodeFn = decodeFn;
43
+ this.configs = configs;
44
+ }
45
+
46
+ public async subscribe(): Promise<void> {
47
+ if (!this.subscriber) {
48
+ const filters = [getUserFilter()];
49
+ if (!this.includeIdle) {
50
+ filters.push(getNonIdleUserFilter());
51
+ }
52
+ this.subscriber = new grpcProgramAccountSubscriber<UserAccount>(
53
+ this.configs,
54
+ 'UserMap',
55
+ 'User',
56
+ this.userMap.driftClient.program,
57
+ this.decodeFn,
58
+ {
59
+ filters,
60
+ commitment: this.commitment,
61
+ },
62
+ this.resubOpts
63
+ );
64
+ }
65
+
66
+ await this.subscriber.subscribe(
67
+ (accountId: PublicKey, account: UserAccount, context: Context) => {
68
+ const userKey = accountId.toBase58();
69
+ this.userMap.updateUserAccount(userKey, account, context.slot);
70
+ }
71
+ );
72
+
73
+ if (!this.skipInitialLoad) {
74
+ await this.userMap.sync();
75
+ }
76
+ }
77
+
78
+ public async unsubscribe(): Promise<void> {
79
+ if (!this.subscriber) return;
80
+ await this.subscriber.unsubscribe();
81
+ this.subscriber = undefined;
82
+ }
83
+ }
@@ -36,6 +36,7 @@ import {
36
36
  import { WebsocketSubscription } from './WebsocketSubscription';
37
37
  import { PollingSubscription } from './PollingSubscription';
38
38
  import { decodeUser } from '../decode/user';
39
+ import { grpcSubscription } from './grpcSubscription';
39
40
 
40
41
  const MAX_USER_ACCOUNT_SIZE_BYTES = 4376;
41
42
 
@@ -75,7 +76,10 @@ export class UserMap implements UserMapInterface {
75
76
  private includeIdle: boolean;
76
77
  private disableSyncOnTotalAccountsChange: boolean;
77
78
  private lastNumberOfSubAccounts: BN;
78
- private subscription: PollingSubscription | WebsocketSubscription;
79
+ private subscription:
80
+ | PollingSubscription
81
+ | WebsocketSubscription
82
+ | grpcSubscription;
79
83
  private stateAccountUpdateCallback = async (state: StateAccount) => {
80
84
  if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
81
85
  await this.sync();
@@ -122,6 +126,18 @@ export class UserMap implements UserMapInterface {
122
126
  frequency: config.subscriptionConfig.frequency,
123
127
  skipInitialLoad: config.skipInitialLoad,
124
128
  });
129
+ } else if (config.subscriptionConfig.type === 'grpc') {
130
+ this.subscription = new grpcSubscription({
131
+ configs: config.subscriptionConfig.configs,
132
+ userMap: this,
133
+ commitment: this.commitment,
134
+ resubOpts: {
135
+ resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
136
+ logResubMessages: config.subscriptionConfig.logResubMessages,
137
+ },
138
+ skipInitialLoad: config.skipInitialLoad,
139
+ decodeFn,
140
+ });
125
141
  } else {
126
142
  this.subscription = new WebsocketSubscription({
127
143
  userMap: this,
@@ -1,5 +1,6 @@
1
1
  import { Commitment, Connection } from '@solana/web3.js';
2
2
  import { DriftClient } from '../driftClient';
3
+ import { GrpcConfigs } from '../accounts/types';
3
4
 
4
5
  // passed into UserMap.getUniqueAuthorities to filter users
5
6
  export type UserAccountFilterCriteria = {
@@ -27,6 +28,13 @@ export type UserMapConfig = {
27
28
  frequency: number;
28
29
  commitment?: Commitment;
29
30
  }
31
+ | {
32
+ type: 'grpc';
33
+ configs: GrpcConfigs;
34
+ resubTimeoutMs?: number;
35
+ logResubMessages?: boolean;
36
+ commitment?: Commitment;
37
+ }
30
38
  | {
31
39
  type: 'websocket';
32
40
  resubTimeoutMs?: number;
@@ -1,6 +1,7 @@
1
1
  import { DriftClient } from './driftClient';
2
2
  import { Commitment, PublicKey } from '@solana/web3.js';
3
3
  import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
+ import { GrpcConfigs } from './accounts/types';
4
5
 
5
6
  export type UserStatsConfig = {
6
7
  accountSubscription?: UserStatsSubscriptionConfig;
@@ -21,4 +22,10 @@ export type UserStatsSubscriptionConfig =
21
22
  }
22
23
  | {
23
24
  type: 'custom';
25
+ }
26
+ | {
27
+ type: 'grpc';
28
+ resubTimeoutMs?: number;
29
+ logResubMessages?: boolean;
30
+ configs: GrpcConfigs;
24
31
  };