@drift-labs/sdk 2.144.0-beta.5 → 2.144.0

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 (33) hide show
  1. package/VERSION +1 -1
  2. package/lib/browser/accounts/grpcMultiAccountSubscriber.js +1 -0
  3. package/lib/browser/accounts/grpcMultiUserAccountSubscriber.d.ts +25 -0
  4. package/lib/browser/accounts/grpcMultiUserAccountSubscriber.js +196 -0
  5. package/lib/browser/driftClient.js +26 -25
  6. package/lib/browser/driftClientConfig.d.ts +2 -0
  7. package/lib/browser/idl/drift.json +1 -1
  8. package/lib/browser/user.js +10 -4
  9. package/lib/browser/userConfig.d.ts +2 -0
  10. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -1
  11. package/lib/node/accounts/grpcMultiAccountSubscriber.js +1 -0
  12. package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts +26 -0
  13. package/lib/node/accounts/grpcMultiUserAccountSubscriber.d.ts.map +1 -0
  14. package/lib/node/accounts/grpcMultiUserAccountSubscriber.js +196 -0
  15. package/lib/node/driftClient.d.ts.map +1 -1
  16. package/lib/node/driftClient.js +26 -25
  17. package/lib/node/driftClientConfig.d.ts +2 -0
  18. package/lib/node/driftClientConfig.d.ts.map +1 -1
  19. package/lib/node/idl/drift.json +1 -1
  20. package/lib/node/user.d.ts.map +1 -1
  21. package/lib/node/user.js +10 -4
  22. package/lib/node/userConfig.d.ts +2 -0
  23. package/lib/node/userConfig.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/scripts/grpc-multiuser-client-test-comparison.ts +156 -0
  26. package/scripts/single-grpc-client-test.ts +20 -10
  27. package/src/accounts/grpcMultiAccountSubscriber.ts +1 -0
  28. package/src/accounts/grpcMultiUserAccountSubscriber.ts +274 -0
  29. package/src/driftClient.ts +2 -0
  30. package/src/driftClientConfig.ts +2 -0
  31. package/src/idl/drift.json +1 -1
  32. package/src/user.ts +16 -9
  33. package/src/userConfig.ts +2 -0
@@ -0,0 +1,156 @@
1
+ import { grpcUserAccountSubscriber } from '../src/accounts/grpcUserAccountSubscriber';
2
+ import { grpcMultiUserAccountSubscriber } from '../src/accounts/grpcMultiUserAccountSubscriber';
3
+ import { Connection, Keypair, PublicKey } from '@solana/web3.js';
4
+ import { DRIFT_PROGRAM_ID } from '../src';
5
+ import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
6
+ import { AnchorProvider, Idl, Program } from '@coral-xyz/anchor';
7
+ import driftIDL from '../src/idl/drift.json';
8
+ import assert from 'assert';
9
+ import { Wallet } from '../src';
10
+
11
+ const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
12
+ const TOKEN = process.env.TOKEN;
13
+ const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
14
+
15
+ const USER_ACCOUNT_PUBKEYS = [
16
+ // Add user account public keys here, e.g.:
17
+ // new PublicKey('...')
18
+ ];
19
+
20
+ async function testGrpcUserAccountSubscriberV1VsV2() {
21
+ console.log('🚀 Initializing User Account Subscriber V1 vs V2 Test...');
22
+
23
+ if (USER_ACCOUNT_PUBKEYS.length === 0) {
24
+ console.error('❌ No user account public keys provided. Please add some to USER_ACCOUNT_PUBKEYS array.');
25
+ process.exit(1);
26
+ }
27
+
28
+ const connection = new Connection(RPC_ENDPOINT);
29
+ const wallet = new Wallet(new Keypair());
30
+
31
+ const programId = new PublicKey(DRIFT_PROGRAM_ID);
32
+ const provider = new AnchorProvider(
33
+ connection,
34
+ // @ts-ignore
35
+ wallet,
36
+ {
37
+ commitment: 'processed',
38
+ }
39
+ );
40
+
41
+ const program = new Program(driftIDL as Idl, programId, provider);
42
+
43
+ const grpcConfigs = {
44
+ endpoint: GRPC_ENDPOINT,
45
+ token: TOKEN,
46
+ commitmentLevel: CommitmentLevel.PROCESSED,
47
+ channelOptions: {
48
+ 'grpc.keepalive_time_ms': 10_000,
49
+ 'grpc.keepalive_timeout_ms': 1_000,
50
+ 'grpc.keepalive_permit_without_calls': 1,
51
+ },
52
+ };
53
+
54
+ console.log(`📊 Testing ${USER_ACCOUNT_PUBKEYS.length} user accounts...`);
55
+
56
+ // V1: Create individual subscribers for each user account
57
+ const v1Subscribers = USER_ACCOUNT_PUBKEYS.map(
58
+ (pubkey) =>
59
+ new grpcUserAccountSubscriber(
60
+ grpcConfigs,
61
+ program,
62
+ pubkey,
63
+ { logResubMessages: true }
64
+ )
65
+ );
66
+
67
+ // V2: Create a single multi-subscriber and get per-user interfaces
68
+ const v2MultiSubscriber = new grpcMultiUserAccountSubscriber(
69
+ program,
70
+ grpcConfigs,
71
+ { logResubMessages: true }
72
+ );
73
+ const v2Subscribers = USER_ACCOUNT_PUBKEYS.map((pubkey) =>
74
+ v2MultiSubscriber.forUser(pubkey)
75
+ );
76
+
77
+ // Subscribe all V1 subscribers
78
+ console.log('🔗 Subscribing V1 subscribers...');
79
+ await Promise.all(v1Subscribers.map((sub) => sub.subscribe()));
80
+ console.log('✅ V1 subscribers ready');
81
+
82
+ // Subscribe all V2 subscribers
83
+ console.log('🔗 Subscribing V2 subscribers...');
84
+ await v2MultiSubscriber.subscribe();
85
+ console.log('✅ V2 subscribers ready');
86
+
87
+ const compare = () => {
88
+ try {
89
+ let passedTests = 0;
90
+ let totalTests = 0;
91
+
92
+ // Test each user account
93
+ for (let i = 0; i < USER_ACCOUNT_PUBKEYS.length; i++) {
94
+ const pubkey = USER_ACCOUNT_PUBKEYS[i];
95
+ const v1Sub = v1Subscribers[i];
96
+ const v2Sub = v2Subscribers[i];
97
+
98
+ totalTests++;
99
+
100
+ // 1. Test isSubscribed
101
+ assert.strictEqual(
102
+ v1Sub.isSubscribed,
103
+ v2Sub.isSubscribed,
104
+ `User ${pubkey.toBase58()}: isSubscribed should match`
105
+ );
106
+
107
+ // 2. Test getUserAccountAndSlot
108
+ const v1Data = v1Sub.getUserAccountAndSlot();
109
+ const v2Data = v2Sub.getUserAccountAndSlot();
110
+
111
+ // Compare the user account data
112
+ assert.deepStrictEqual(
113
+ v1Data.data,
114
+ v2Data.data,
115
+ `User ${pubkey.toBase58()}: account data should match`
116
+ );
117
+
118
+ // Slots might differ slightly due to timing, but let's check if they're close
119
+ const slotDiff = Math.abs(v1Data.slot - v2Data.slot);
120
+ if (slotDiff > 10) {
121
+ console.warn(
122
+ `⚠️ User ${pubkey.toBase58()}: slot difference is ${slotDiff} (v1: ${v1Data.slot}, v2: ${v2Data.slot})`
123
+ );
124
+ }
125
+
126
+ passedTests++;
127
+ }
128
+
129
+ console.log(`✅ All comparisons passed (${passedTests}/${totalTests} user accounts)`);
130
+ } catch (error) {
131
+ console.error('❌ Comparison failed:', error);
132
+ }
133
+ };
134
+
135
+ // Run initial comparison
136
+ compare();
137
+
138
+ // Run comparison every second to verify live updates
139
+ const interval = setInterval(compare, 1000);
140
+
141
+ const cleanup = async () => {
142
+ clearInterval(interval);
143
+ console.log('🧹 Cleaning up...');
144
+ await Promise.all([
145
+ ...v1Subscribers.map((sub) => sub.unsubscribe()),
146
+ ...v2Subscribers.map((sub) => sub.unsubscribe()),
147
+ ]);
148
+ console.log('✅ Cleanup complete');
149
+ process.exit(0);
150
+ };
151
+
152
+ process.on('SIGINT', cleanup);
153
+ process.on('SIGTERM', cleanup);
154
+ }
155
+
156
+ testGrpcUserAccountSubscriberV1VsV2().catch(console.error);
@@ -19,6 +19,7 @@ import {
19
19
  ProgramAccount,
20
20
  } from '@coral-xyz/anchor';
21
21
  import driftIDL from '../src/idl/drift.json';
22
+ import { grpcMultiUserAccountSubscriber } from '../src/accounts/grpcMultiUserAccountSubscriber';
22
23
 
23
24
  const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
24
25
  const TOKEN = process.env.TOKEN;
@@ -96,18 +97,27 @@ async function initializeSingleGrpcClient() {
96
97
  console.log(`📊 Markets: ${perpMarketIndexes.length} perp, ${spotMarketIndexes.length} spot`);
97
98
  console.log(`🔮 Oracles: ${oracleInfos.length}`);
98
99
 
100
+
101
+ const grpcConfigs = {
102
+ endpoint: GRPC_ENDPOINT,
103
+ token: TOKEN,
104
+ commitmentLevel: CommitmentLevel.PROCESSED,
105
+ channelOptions: {
106
+ 'grpc.keepalive_time_ms': 10_000,
107
+ 'grpc.keepalive_timeout_ms': 1_000,
108
+ 'grpc.keepalive_permit_without_calls': 1,
109
+ },
110
+ };
111
+
112
+ const multiUserSubsciber = new grpcMultiUserAccountSubscriber(
113
+ program,
114
+ grpcConfigs
115
+ );
116
+
99
117
  const baseAccountSubscription = {
100
118
  type: 'grpc' as const,
101
- grpcConfigs: {
102
- endpoint: GRPC_ENDPOINT,
103
- token: TOKEN,
104
- commitmentLevel: CommitmentLevel.PROCESSED,
105
- channelOptions: {
106
- 'grpc.keepalive_time_ms': 10_000,
107
- 'grpc.keepalive_timeout_ms': 1_000,
108
- 'grpc.keepalive_permit_without_calls': 1,
109
- },
110
- },
119
+ grpcConfigs,
120
+ grpcMultiUserAccountSubscriber: multiUserSubsciber,
111
121
  };
112
122
 
113
123
  const config: DriftClientConfig = {
@@ -372,6 +372,7 @@ export class grpcMultiAccountSubscriber<T, U = undefined> {
372
372
  }
373
373
  });
374
374
  });
375
+ await this.fetch();
375
376
  }
376
377
 
377
378
  async removeAccounts(accounts: PublicKey[]): Promise<void> {
@@ -0,0 +1,274 @@
1
+ import {
2
+ DataAndSlot,
3
+ GrpcConfigs,
4
+ NotSubscribedError,
5
+ ResubOpts,
6
+ UserAccountEvents,
7
+ UserAccountSubscriber,
8
+ } from './types';
9
+ import StrictEventEmitter from 'strict-event-emitter-types';
10
+ import { EventEmitter } from 'events';
11
+ import { Context, PublicKey } from '@solana/web3.js';
12
+ import { Program } from '@coral-xyz/anchor';
13
+ import { UserAccount } from '../types';
14
+ import { grpcMultiAccountSubscriber } from './grpcMultiAccountSubscriber';
15
+
16
+ export class grpcMultiUserAccountSubscriber {
17
+ private program: Program;
18
+ private multiSubscriber: grpcMultiAccountSubscriber<UserAccount>;
19
+
20
+ private userData = new Map<string, DataAndSlot<UserAccount>>();
21
+ private listeners = new Map<
22
+ string,
23
+ Set<StrictEventEmitter<EventEmitter, UserAccountEvents>>
24
+ >();
25
+ private keyToPk = new Map<string, PublicKey>();
26
+ private pendingAddKeys = new Set<string>();
27
+ private debounceTimer?: ReturnType<typeof setTimeout>;
28
+ private debounceMs = 20;
29
+ private isMultiSubscribed = false;
30
+ private userAccountSubscribers = new Map<string, UserAccountSubscriber>();
31
+ private grpcConfigs: GrpcConfigs;
32
+ resubOpts?: ResubOpts;
33
+
34
+ private handleAccountChange = (
35
+ accountId: PublicKey,
36
+ data: UserAccount,
37
+ context: Context,
38
+ _buffer?: unknown,
39
+ _accountProps?: unknown
40
+ ): void => {
41
+ const k = accountId.toBase58();
42
+ this.userData.set(k, { data, slot: context.slot });
43
+ const setForKey = this.listeners.get(k);
44
+ if (setForKey) {
45
+ for (const emitter of setForKey) {
46
+ emitter.emit('userAccountUpdate', data);
47
+ emitter.emit('update');
48
+ }
49
+ }
50
+ };
51
+
52
+ public constructor(
53
+ program: Program,
54
+ grpcConfigs: GrpcConfigs,
55
+ resubOpts?: ResubOpts,
56
+ multiSubscriber?: grpcMultiAccountSubscriber<UserAccount>
57
+ ) {
58
+ this.program = program;
59
+ this.multiSubscriber = multiSubscriber;
60
+ this.grpcConfigs = grpcConfigs;
61
+ this.resubOpts = resubOpts;
62
+ }
63
+
64
+ public async subscribe(): Promise<void> {
65
+ if (!this.multiSubscriber) {
66
+ this.multiSubscriber =
67
+ await grpcMultiAccountSubscriber.create<UserAccount>(
68
+ this.grpcConfigs,
69
+ 'user',
70
+ this.program,
71
+ undefined,
72
+ this.resubOpts
73
+ );
74
+ }
75
+
76
+ // Subscribe all per-user subscribers first
77
+ await Promise.all(
78
+ Array.from(this.userAccountSubscribers.values()).map((subscriber) =>
79
+ subscriber.subscribe()
80
+ )
81
+ );
82
+ // Ensure we immediately register any pending keys and kick off underlying subscription/fetch
83
+ await this.flushPending();
84
+ // Proactively fetch once to populate data for all subscribed accounts
85
+ await this.multiSubscriber.fetch();
86
+ // Wait until the underlying multi-subscriber has data for every registered user key
87
+ const targetKeys = Array.from(this.listeners.keys());
88
+ if (targetKeys.length === 0) return;
89
+ // Poll until all keys are present in dataMap
90
+ // Use debounceMs as the polling cadence to avoid introducing new magic numbers
91
+ // eslint-disable-next-line no-constant-condition
92
+ while (true) {
93
+ const map = this.multiSubscriber.getAccountDataMap();
94
+ let allPresent = true;
95
+ for (const k of targetKeys) {
96
+ if (!map.has(k)) {
97
+ allPresent = false;
98
+ break;
99
+ }
100
+ }
101
+ if (allPresent) break;
102
+ await new Promise((resolve) => setTimeout(resolve, this.debounceMs));
103
+ }
104
+ }
105
+
106
+ public forUser(userAccountPublicKey: PublicKey): UserAccountSubscriber {
107
+ if (this.userAccountSubscribers.has(userAccountPublicKey.toBase58())) {
108
+ return this.userAccountSubscribers.get(userAccountPublicKey.toBase58())!;
109
+ }
110
+ const key = userAccountPublicKey.toBase58();
111
+ const perUserEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents> =
112
+ new EventEmitter();
113
+ // eslint-disable-next-line @typescript-eslint/no-this-alias
114
+ const parent = this;
115
+ let isSubscribed = false;
116
+
117
+ const registerHandlerIfNeeded = async () => {
118
+ if (!this.listeners.has(key)) {
119
+ this.listeners.set(key, new Set());
120
+ this.keyToPk.set(key, userAccountPublicKey);
121
+ this.pendingAddKeys.add(key);
122
+ this.scheduleFlush();
123
+ }
124
+ };
125
+
126
+ const perUser: UserAccountSubscriber = {
127
+ get eventEmitter() {
128
+ return perUserEmitter;
129
+ },
130
+ set eventEmitter(_v) {},
131
+
132
+ get isSubscribed() {
133
+ return isSubscribed;
134
+ },
135
+ set isSubscribed(_v: boolean) {
136
+ isSubscribed = _v;
137
+ },
138
+
139
+ async subscribe(userAccount?: UserAccount): Promise<boolean> {
140
+ if (isSubscribed) return true;
141
+ if (userAccount) {
142
+ this.updateData(userAccount, 0);
143
+ }
144
+ await registerHandlerIfNeeded();
145
+ const setForKey = parent.listeners.get(key)!;
146
+ setForKey.add(perUserEmitter);
147
+ isSubscribed = true;
148
+ return true;
149
+ },
150
+
151
+ async fetch(): Promise<void> {
152
+ if (!isSubscribed) {
153
+ throw new NotSubscribedError(
154
+ 'Must subscribe before fetching account updates'
155
+ );
156
+ }
157
+ const account = (await parent.program.account.user.fetch(
158
+ userAccountPublicKey
159
+ )) as UserAccount;
160
+ this.updateData(account, 0);
161
+ },
162
+
163
+ updateData(userAccount: UserAccount, slot: number): void {
164
+ parent.userData.set(key, { data: userAccount, slot });
165
+ perUserEmitter.emit('userAccountUpdate', userAccount);
166
+ perUserEmitter.emit('update');
167
+ },
168
+
169
+ async unsubscribe(): Promise<void> {
170
+ if (!isSubscribed) return;
171
+ const setForKey = parent.listeners.get(key);
172
+ if (setForKey) {
173
+ setForKey.delete(perUserEmitter);
174
+ if (setForKey.size === 0) {
175
+ parent.listeners.delete(key);
176
+ await parent.multiSubscriber.removeAccounts([userAccountPublicKey]);
177
+ parent.userData.delete(key);
178
+ parent.keyToPk.delete(key);
179
+ parent.pendingAddKeys.delete(key);
180
+ }
181
+ }
182
+ isSubscribed = false;
183
+ },
184
+
185
+ getUserAccountAndSlot(): DataAndSlot<UserAccount> {
186
+ const das = parent.userData.get(key);
187
+ if (!das) {
188
+ throw new NotSubscribedError(
189
+ 'Must subscribe before getting user account data'
190
+ );
191
+ }
192
+ return das;
193
+ },
194
+ };
195
+
196
+ this.userAccountSubscribers.set(userAccountPublicKey.toBase58(), perUser);
197
+ return perUser;
198
+ }
199
+
200
+ private scheduleFlush(): void {
201
+ if (this.debounceTimer) return;
202
+ this.debounceTimer = setTimeout(() => {
203
+ void this.flushPending();
204
+ }, this.debounceMs);
205
+ }
206
+
207
+ private async flushPending(): Promise<void> {
208
+ const hasPending = this.pendingAddKeys.size > 0;
209
+ if (!hasPending) {
210
+ this.debounceTimer = undefined;
211
+ return;
212
+ }
213
+
214
+ const allPks: PublicKey[] = [];
215
+ for (const k of this.listeners.keys()) {
216
+ const pk = this.keyToPk.get(k);
217
+ if (pk) allPks.push(pk);
218
+ }
219
+ if (allPks.length === 0) {
220
+ this.pendingAddKeys.clear();
221
+ this.debounceTimer = undefined;
222
+ return;
223
+ }
224
+
225
+ if (!this.isMultiSubscribed) {
226
+ await this.multiSubscriber.subscribe(allPks, this.handleAccountChange);
227
+ this.isMultiSubscribed = true;
228
+ await this.multiSubscriber.fetch();
229
+ for (const k of this.pendingAddKeys) {
230
+ const pk = this.keyToPk.get(k);
231
+ if (pk) {
232
+ const data = this.multiSubscriber.getAccountData(k);
233
+ if (data) {
234
+ this.handleAccountChange(
235
+ pk,
236
+ data.data,
237
+ { slot: data.slot },
238
+ undefined,
239
+ undefined
240
+ );
241
+ }
242
+ }
243
+ }
244
+ } else {
245
+ const ms = this.multiSubscriber as unknown as {
246
+ onChangeMap: Map<
247
+ string,
248
+ (
249
+ data: UserAccount,
250
+ context: Context,
251
+ buffer: unknown,
252
+ accountProps: unknown
253
+ ) => void
254
+ >;
255
+ };
256
+ for (const k of this.pendingAddKeys) {
257
+ ms.onChangeMap.set(k, (data, ctx, buffer, accountProps) => {
258
+ this.multiSubscriber.setAccountData(k, data, ctx.slot);
259
+ this.handleAccountChange(
260
+ new PublicKey(k),
261
+ data,
262
+ ctx,
263
+ buffer,
264
+ accountProps
265
+ );
266
+ });
267
+ }
268
+ await this.multiSubscriber.addAccounts(allPks);
269
+ }
270
+
271
+ this.pendingAddKeys.clear();
272
+ this.debounceTimer = undefined;
273
+ }
274
+ }
@@ -379,6 +379,8 @@ export class DriftClient {
379
379
  resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
380
380
  logResubMessages: config.accountSubscription?.logResubMessages,
381
381
  grpcConfigs: config.accountSubscription?.grpcConfigs,
382
+ grpcMultiUserAccountSubscriber:
383
+ config.accountSubscription?.grpcMultiUserAccountSubscriber,
382
384
  };
383
385
  this.userStatsAccountSubscriptionConfig = {
384
386
  type: 'grpc',
@@ -24,6 +24,7 @@ import { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDri
24
24
  import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber';
25
25
  import { grpcDriftClientAccountSubscriberV2 } from './accounts/grpcDriftClientAccountSubscriberV2';
26
26
  import { grpcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
27
+ import { grpcMultiUserAccountSubscriber } from './accounts/grpcMultiUserAccountSubscriber';
27
28
 
28
29
  export type DriftClientConfig = {
29
30
  connection: Connection;
@@ -73,6 +74,7 @@ export type DriftClientSubscriptionConfig =
73
74
  ) =>
74
75
  | grpcDriftClientAccountSubscriberV2
75
76
  | grpcDriftClientAccountSubscriber;
77
+ grpcMultiUserAccountSubscriber?: grpcMultiUserAccountSubscriber;
76
78
  }
77
79
  | {
78
80
  type: 'websocket';
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.143.0",
2
+ "version": "2.144.0",
3
3
  "name": "drift",
4
4
  "instructions": [
5
5
  {
package/src/user.ts CHANGED
@@ -137,15 +137,22 @@ export class User {
137
137
  } else if (config.accountSubscription?.type === 'custom') {
138
138
  this.accountSubscriber = config.accountSubscription.userAccountSubscriber;
139
139
  } else if (config.accountSubscription?.type === 'grpc') {
140
- this.accountSubscriber = new grpcUserAccountSubscriber(
141
- config.accountSubscription.grpcConfigs,
142
- config.driftClient.program,
143
- config.userAccountPublicKey,
144
- {
145
- resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
146
- logResubMessages: config.accountSubscription?.logResubMessages,
147
- }
148
- );
140
+ if (config.accountSubscription.grpcMultiUserAccountSubscriber) {
141
+ this.accountSubscriber =
142
+ config.accountSubscription.grpcMultiUserAccountSubscriber.forUser(
143
+ config.userAccountPublicKey
144
+ );
145
+ } else {
146
+ this.accountSubscriber = new grpcUserAccountSubscriber(
147
+ config.accountSubscription.grpcConfigs,
148
+ config.driftClient.program,
149
+ config.userAccountPublicKey,
150
+ {
151
+ resubTimeoutMs: config.accountSubscription?.resubTimeoutMs,
152
+ logResubMessages: config.accountSubscription?.logResubMessages,
153
+ }
154
+ );
155
+ }
149
156
  } else {
150
157
  if (
151
158
  config.accountSubscription?.type === 'websocket' &&
package/src/userConfig.ts CHANGED
@@ -4,6 +4,7 @@ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
4
4
  import { GrpcConfigs, UserAccountSubscriber } from './accounts/types';
5
5
  import { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber';
6
6
  import { UserAccount } from './types';
7
+ import { grpcMultiUserAccountSubscriber } from './accounts/grpcMultiUserAccountSubscriber';
7
8
 
8
9
  export type UserConfig = {
9
10
  accountSubscription?: UserSubscriptionConfig;
@@ -17,6 +18,7 @@ export type UserSubscriptionConfig =
17
18
  resubTimeoutMs?: number;
18
19
  logResubMessages?: boolean;
19
20
  grpcConfigs: GrpcConfigs;
21
+ grpcMultiUserAccountSubscriber?: grpcMultiUserAccountSubscriber;
20
22
  }
21
23
  | {
22
24
  type: 'websocket';