@drift-labs/sdk 2.142.0-beta.1 → 2.142.0-beta.11

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 (48) 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 +251 -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/types.d.ts +3 -1
  15. package/lib/node/accounts/grpcAccountSubscriber.d.ts +2 -1
  16. package/lib/node/accounts/grpcAccountSubscriber.d.ts.map +1 -1
  17. package/lib/node/accounts/grpcAccountSubscriber.js +4 -2
  18. package/lib/node/accounts/grpcDriftClientAccountSubscriber.d.ts +1 -1
  19. package/lib/node/accounts/grpcDriftClientAccountSubscriber.js +3 -3
  20. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts +25 -0
  21. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.d.ts.map +1 -0
  22. package/lib/node/accounts/grpcDriftClientAccountSubscriberV2.js +251 -0
  23. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts +35 -0
  24. package/lib/node/accounts/grpcMultiAccountSubscriber.d.ts.map +1 -0
  25. package/lib/node/accounts/grpcMultiAccountSubscriber.js +284 -0
  26. package/lib/node/constants/spotMarkets.js +4 -4
  27. package/lib/node/driftClient.d.ts.map +1 -1
  28. package/lib/node/driftClient.js +11 -10
  29. package/lib/node/driftClientConfig.d.ts +3 -0
  30. package/lib/node/driftClientConfig.d.ts.map +1 -1
  31. package/lib/node/isomorphic/grpc.d.ts +5 -3
  32. package/lib/node/isomorphic/grpc.js +1 -3
  33. package/lib/node/isomorphic/grpc.node.d.ts +5 -3
  34. package/lib/node/isomorphic/grpc.node.d.ts.map +1 -1
  35. package/lib/node/isomorphic/grpc.node.js +1 -3
  36. package/lib/node/types.d.ts +3 -1
  37. package/lib/node/types.d.ts.map +1 -1
  38. package/package.json +10 -4
  39. package/scripts/client-test.ts +214 -0
  40. package/src/accounts/grpcAccountSubscriber.ts +9 -6
  41. package/src/accounts/grpcDriftClientAccountSubscriber.ts +1 -1
  42. package/src/accounts/grpcDriftClientAccountSubscriberV2.ts +426 -0
  43. package/src/accounts/grpcMultiAccountSubscriber.ts +343 -0
  44. package/src/constants/spotMarkets.ts +4 -4
  45. package/src/driftClient.ts +5 -2
  46. package/src/driftClientConfig.ts +13 -0
  47. package/src/isomorphic/grpc.node.ts +11 -7
  48. package/src/types.ts +4 -2
@@ -0,0 +1,343 @@
1
+ import { Program } from '@coral-xyz/anchor';
2
+ import { Context, PublicKey } from '@solana/web3.js';
3
+ import * as Buffer from 'buffer';
4
+ import bs58 from 'bs58';
5
+
6
+ import {
7
+ Client,
8
+ ClientDuplexStream,
9
+ CommitmentLevel,
10
+ SubscribeRequest,
11
+ SubscribeUpdate,
12
+ createClient,
13
+ } from '../isomorphic/grpc';
14
+ import { DataAndSlot, GrpcConfigs, ResubOpts } from './types';
15
+
16
+ interface AccountInfoLike {
17
+ owner: PublicKey;
18
+ lamports: number;
19
+ data: Buffer;
20
+ executable: boolean;
21
+ rentEpoch: number;
22
+ }
23
+
24
+ export class grpcMultiAccountSubscriber<T> {
25
+ private client: Client;
26
+ private stream: ClientDuplexStream<SubscribeRequest, SubscribeUpdate>;
27
+ private commitmentLevel: CommitmentLevel;
28
+ private program: Program;
29
+ private accountName: string;
30
+ private decodeBufferFn?: (buffer: Buffer, pubkey?: string) => T;
31
+ private resubOpts?: ResubOpts;
32
+ private onUnsubscribe?: () => Promise<void>;
33
+
34
+ public listenerId?: number;
35
+ public isUnsubscribing = false;
36
+ private timeoutId?: ReturnType<typeof setTimeout>;
37
+ private receivingData = false;
38
+
39
+ private subscribedAccounts = new Set<string>();
40
+ private onChangeMap = new Map<
41
+ string,
42
+ (data: T, context: Context, buffer: Buffer) => void
43
+ >();
44
+
45
+ private dataMap = new Map<string, DataAndSlot<T>>();
46
+
47
+ private constructor(
48
+ client: Client,
49
+ commitmentLevel: CommitmentLevel,
50
+ accountName: string,
51
+ program: Program,
52
+ decodeBuffer?: (buffer: Buffer, pubkey?: string) => T,
53
+ resubOpts?: ResubOpts,
54
+ onUnsubscribe?: () => Promise<void>
55
+ ) {
56
+ this.client = client;
57
+ this.commitmentLevel = commitmentLevel;
58
+ this.accountName = accountName;
59
+ this.program = program;
60
+ this.decodeBufferFn = decodeBuffer;
61
+ this.resubOpts = resubOpts;
62
+ this.onUnsubscribe = onUnsubscribe;
63
+ }
64
+
65
+ public static async create<U>(
66
+ grpcConfigs: GrpcConfigs,
67
+ accountName: string,
68
+ program: Program,
69
+ decodeBuffer?: (buffer: Buffer, pubkey?: string) => U,
70
+ resubOpts?: ResubOpts,
71
+ clientProp?: Client,
72
+ onUnsubscribe?: () => Promise<void>
73
+ ): Promise<grpcMultiAccountSubscriber<U>> {
74
+ const client = clientProp
75
+ ? clientProp
76
+ : await createClient(
77
+ grpcConfigs.endpoint,
78
+ grpcConfigs.token,
79
+ grpcConfigs.channelOptions ?? {}
80
+ );
81
+ const commitmentLevel =
82
+ // @ts-ignore :: isomorphic exported enum fails typescript but will work at runtime
83
+ grpcConfigs.commitmentLevel ?? CommitmentLevel.CONFIRMED;
84
+
85
+ return new grpcMultiAccountSubscriber(
86
+ client,
87
+ commitmentLevel,
88
+ accountName,
89
+ program,
90
+ decodeBuffer,
91
+ resubOpts,
92
+ onUnsubscribe
93
+ );
94
+ }
95
+
96
+ setAccountData(accountPubkey: string, data: T, slot?: number): void {
97
+ this.dataMap.set(accountPubkey, { data, slot });
98
+ }
99
+
100
+ getAccountData(accountPubkey: string): DataAndSlot<T> | undefined {
101
+ return this.dataMap.get(accountPubkey);
102
+ }
103
+
104
+ getAccountDataMap(): Map<string, DataAndSlot<T>> {
105
+ return this.dataMap;
106
+ }
107
+
108
+ async subscribe(
109
+ accounts: PublicKey[],
110
+ onChange: (
111
+ accountId: PublicKey,
112
+ data: T,
113
+ context: Context,
114
+ buffer: Buffer
115
+ ) => void
116
+ ): Promise<void> {
117
+ if (this.listenerId != null || this.isUnsubscribing) {
118
+ return;
119
+ }
120
+
121
+ // Track accounts and single onChange for all
122
+ for (const pk of accounts) {
123
+ const key = pk.toBase58();
124
+ this.subscribedAccounts.add(key);
125
+ this.onChangeMap.set(key, (data, ctx, buffer) => {
126
+ this.setAccountData(key, data, ctx.slot);
127
+ onChange(new PublicKey(key), data, ctx, buffer);
128
+ });
129
+ }
130
+
131
+ this.stream =
132
+ (await this.client.subscribe()) as unknown as typeof this.stream;
133
+ const request: SubscribeRequest = {
134
+ slots: {},
135
+ accounts: {
136
+ account: {
137
+ account: accounts.map((a) => a.toBase58()),
138
+ owner: [],
139
+ filters: [],
140
+ },
141
+ },
142
+ transactions: {},
143
+ blocks: {},
144
+ blocksMeta: {},
145
+ accountsDataSlice: [],
146
+ commitment: this.commitmentLevel,
147
+ entry: {},
148
+ transactionsStatus: {},
149
+ };
150
+
151
+ this.stream.on('data', (chunk: SubscribeUpdate) => {
152
+ if (!chunk.account) {
153
+ return;
154
+ }
155
+ const slot = Number(chunk.account.slot);
156
+ const accountPubkeyBytes = chunk.account.account.pubkey;
157
+ const accountPubkey = bs58.encode(
158
+ accountPubkeyBytes as unknown as Uint8Array
159
+ );
160
+ if (!accountPubkey || !this.subscribedAccounts.has(accountPubkey)) {
161
+ return;
162
+ }
163
+ const accountInfo: AccountInfoLike = {
164
+ owner: new PublicKey(chunk.account.account.owner),
165
+ lamports: Number(chunk.account.account.lamports),
166
+ data: Buffer.Buffer.from(chunk.account.account.data),
167
+ executable: chunk.account.account.executable,
168
+ rentEpoch: Number(chunk.account.account.rentEpoch),
169
+ };
170
+
171
+ const context = { slot } as Context;
172
+ const buffer = accountInfo.data;
173
+ const data = this.decodeBufferFn
174
+ ? this.decodeBufferFn(buffer, accountPubkey)
175
+ : this.program.account[this.accountName].coder.accounts.decode(
176
+ this.capitalize(this.accountName),
177
+ buffer
178
+ );
179
+
180
+ const handler = this.onChangeMap.get(accountPubkey);
181
+ if (handler) {
182
+ if (this.resubOpts?.resubTimeoutMs) {
183
+ this.receivingData = true;
184
+ clearTimeout(this.timeoutId);
185
+ handler(data, context, buffer);
186
+ this.setTimeout();
187
+ } else {
188
+ handler(data, context, buffer);
189
+ }
190
+ }
191
+ });
192
+
193
+ return new Promise<void>((resolve, reject) => {
194
+ this.stream.write(request, (err) => {
195
+ if (err === null || err === undefined) {
196
+ this.listenerId = 1;
197
+ if (this.resubOpts?.resubTimeoutMs) {
198
+ this.receivingData = true;
199
+ this.setTimeout();
200
+ }
201
+ resolve();
202
+ } else {
203
+ reject(err);
204
+ }
205
+ });
206
+ }).catch((reason) => {
207
+ console.error(reason);
208
+ throw reason;
209
+ });
210
+ }
211
+
212
+ async addAccounts(accounts: PublicKey[]): Promise<void> {
213
+ for (const pk of accounts) {
214
+ this.subscribedAccounts.add(pk.toBase58());
215
+ }
216
+ const request: SubscribeRequest = {
217
+ slots: {},
218
+ accounts: {
219
+ account: {
220
+ account: Array.from(this.subscribedAccounts.values()),
221
+ owner: [],
222
+ filters: [],
223
+ },
224
+ },
225
+ transactions: {},
226
+ blocks: {},
227
+ blocksMeta: {},
228
+ accountsDataSlice: [],
229
+ commitment: this.commitmentLevel,
230
+ entry: {},
231
+ transactionsStatus: {},
232
+ };
233
+
234
+ await new Promise<void>((resolve, reject) => {
235
+ this.stream.write(request, (err) => {
236
+ if (err === null || err === undefined) {
237
+ resolve();
238
+ } else {
239
+ reject(err);
240
+ }
241
+ });
242
+ });
243
+ }
244
+
245
+ async removeAccounts(accounts: PublicKey[]): Promise<void> {
246
+ for (const pk of accounts) {
247
+ const k = pk.toBase58();
248
+ this.subscribedAccounts.delete(k);
249
+ this.onChangeMap.delete(k);
250
+ }
251
+ const request: SubscribeRequest = {
252
+ slots: {},
253
+ accounts: {
254
+ account: {
255
+ account: Array.from(this.subscribedAccounts.values()),
256
+ owner: [],
257
+ filters: [],
258
+ },
259
+ },
260
+ transactions: {},
261
+ blocks: {},
262
+ blocksMeta: {},
263
+ accountsDataSlice: [],
264
+ commitment: this.commitmentLevel,
265
+ entry: {},
266
+ transactionsStatus: {},
267
+ };
268
+
269
+ await new Promise<void>((resolve, reject) => {
270
+ this.stream.write(request, (err) => {
271
+ if (err === null || err === undefined) {
272
+ resolve();
273
+ } else {
274
+ reject(err);
275
+ }
276
+ });
277
+ });
278
+ }
279
+
280
+ async unsubscribe(): Promise<void> {
281
+ this.isUnsubscribing = true;
282
+ clearTimeout(this.timeoutId);
283
+ this.timeoutId = undefined;
284
+
285
+ if (this.listenerId != null) {
286
+ const promise = new Promise<void>((resolve, reject) => {
287
+ const request: SubscribeRequest = {
288
+ slots: {},
289
+ accounts: {},
290
+ transactions: {},
291
+ blocks: {},
292
+ blocksMeta: {},
293
+ accountsDataSlice: [],
294
+ entry: {},
295
+ transactionsStatus: {},
296
+ };
297
+ this.stream.write(request, (err) => {
298
+ if (err === null || err === undefined) {
299
+ this.listenerId = undefined;
300
+ this.isUnsubscribing = false;
301
+ resolve();
302
+ } else {
303
+ reject(err);
304
+ }
305
+ });
306
+ }).catch((reason) => {
307
+ console.error(reason);
308
+ throw reason;
309
+ });
310
+ return promise;
311
+ } else {
312
+ this.isUnsubscribing = false;
313
+ }
314
+
315
+ if (this.onUnsubscribe) {
316
+ try {
317
+ await this.onUnsubscribe();
318
+ } catch (e) {
319
+ console.error(e);
320
+ }
321
+ }
322
+ }
323
+
324
+ private setTimeout(): void {
325
+ this.timeoutId = setTimeout(
326
+ async () => {
327
+ if (this.isUnsubscribing) {
328
+ return;
329
+ }
330
+ if (this.receivingData) {
331
+ await this.unsubscribe();
332
+ this.receivingData = false;
333
+ }
334
+ },
335
+ this.resubOpts?.resubTimeoutMs
336
+ );
337
+ }
338
+
339
+ private capitalize(value: string): string {
340
+ if (!value) return value;
341
+ return value.charAt(0).toUpperCase() + value.slice(1);
342
+ }
343
+ }
@@ -39,8 +39,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
39
39
  symbol: 'USDC',
40
40
  marketIndex: 0,
41
41
  poolId: 0,
42
- oracle: new PublicKey('En8hkHLkRe9d9DraYmBTrus518BvmVH448YcvmrFM6Ce'),
43
- oracleSource: OracleSource.PYTH_STABLE_COIN_PULL,
42
+ oracle: new PublicKey('9VCioxmni2gDLv11qufWzT3RDERhQE4iY5Gf7NTfYyAV'),
43
+ oracleSource: OracleSource.PYTH_LAZER_STABLE_COIN,
44
44
  mint: new PublicKey('8zGuJQqwhZafTah7Uc7Z4tXRnguqkn5KLFAP8oV6PHe2'),
45
45
  precision: new BN(10).pow(SIX),
46
46
  precisionExp: SIX,
@@ -52,8 +52,8 @@ export const DevnetSpotMarkets: SpotMarketConfig[] = [
52
52
  symbol: 'SOL',
53
53
  marketIndex: 1,
54
54
  poolId: 0,
55
- oracle: new PublicKey('BAtFj4kQttZRVep3UZS2aZRDixkGYgWsbqTBVDbnSsPF'),
56
- oracleSource: OracleSource.PYTH_PULL,
55
+ oracle: new PublicKey('3m6i4RFWEDw2Ft4tFHPJtYgmpPe21k56M3FHeWYrgGBz'),
56
+ oracleSource: OracleSource.PYTH_LAZER,
57
57
  mint: new PublicKey(WRAPPED_SOL_MINT),
58
58
  precision: LAMPORTS_PRECISION,
59
59
  precisionExp: LAMPORTS_EXP,
@@ -189,7 +189,7 @@ import {
189
189
  } from './tx/utils';
190
190
  import pythSolanaReceiverIdl from './idl/pyth_solana_receiver.json';
191
191
  import { asV0Tx, PullFeed, AnchorUtils } from '@switchboard-xyz/on-demand';
192
- import { gprcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
192
+ import { grpcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
193
193
  import nacl from 'tweetnacl';
194
194
  import { Slothash } from './slot/SlothashSubscriber';
195
195
  import { getOracleId } from './oracles/oracleId';
@@ -434,7 +434,10 @@ export class DriftClient {
434
434
  delistedMarketSetting
435
435
  );
436
436
  } else if (config.accountSubscription?.type === 'grpc') {
437
- this.accountSubscriber = new gprcDriftClientAccountSubscriber(
437
+ const accountSubscriberClass =
438
+ config.accountSubscription?.driftClientAccountSubscriber ??
439
+ grpcDriftClientAccountSubscriber;
440
+ this.accountSubscriber = new accountSubscriberClass(
438
441
  config.accountSubscription.grpcConfigs,
439
442
  this.program,
440
443
  config.perpMarketIndexes ?? [],
@@ -22,6 +22,8 @@ import { WebSocketAccountSubscriberV2 } from './accounts/webSocketAccountSubscri
22
22
  import { WebSocketProgramAccountSubscriber } from './accounts/webSocketProgramAccountSubscriber';
23
23
  import { WebSocketDriftClientAccountSubscriberV2 } from './accounts/webSocketDriftClientAccountSubscriberV2';
24
24
  import { WebSocketDriftClientAccountSubscriber } from './accounts/webSocketDriftClientAccountSubscriber';
25
+ import { grpcDriftClientAccountSubscriberV2 } from './accounts/grpcDriftClientAccountSubscriberV2';
26
+ import { grpcDriftClientAccountSubscriber } from './accounts/grpcDriftClientAccountSubscriber';
25
27
 
26
28
  export type DriftClientConfig = {
27
29
  connection: Connection;
@@ -60,6 +62,17 @@ export type DriftClientSubscriptionConfig =
60
62
  grpcConfigs: GrpcConfigs;
61
63
  resubTimeoutMs?: number;
62
64
  logResubMessages?: boolean;
65
+ driftClientAccountSubscriber?: new (
66
+ grpcConfigs: GrpcConfigs,
67
+ program: Program,
68
+ perpMarketIndexes: number[],
69
+ spotMarketIndexes: number[],
70
+ oracleInfos: OracleInfo[],
71
+ shouldFindAllMarketsAndOracles: boolean,
72
+ delistedMarketSetting: DelistedMarketSetting
73
+ ) =>
74
+ | grpcDriftClientAccountSubscriberV2
75
+ | grpcDriftClientAccountSubscriber;
63
76
  }
64
77
  | {
65
78
  type: 'websocket';
@@ -4,30 +4,34 @@ import type {
4
4
  SubscribeUpdate,
5
5
  } from '@triton-one/yellowstone-grpc';
6
6
  import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
7
- import { ClientDuplexStream, ChannelOptions } from '@grpc/grpc-js';
7
+ import type { ClientDuplexStream, ChannelOptions } from '@grpc/grpc-js';
8
8
 
9
9
  import {
10
10
  CommitmentLevel as LaserCommitmentLevel,
11
11
  subscribe as LaserSubscribe,
12
+ CompressionAlgorithms,
13
+ } from 'helius-laserstream';
14
+ import type {
12
15
  LaserstreamConfig,
13
16
  SubscribeRequest as LaserSubscribeRequest,
14
17
  SubscribeUpdate as LaserSubscribeUpdate,
15
- CompressionAlgorithms,
16
18
  } from 'helius-laserstream';
17
19
 
18
20
  export {
19
- ClientDuplexStream,
20
- ChannelOptions,
21
- SubscribeRequest,
22
- SubscribeUpdate,
23
21
  CommitmentLevel,
24
22
  Client,
25
23
  LaserSubscribe,
26
24
  LaserCommitmentLevel,
25
+ CompressionAlgorithms,
26
+ };
27
+ export type {
28
+ ClientDuplexStream,
29
+ ChannelOptions,
30
+ SubscribeRequest,
31
+ SubscribeUpdate,
27
32
  LaserstreamConfig,
28
33
  LaserSubscribeRequest,
29
34
  LaserSubscribeUpdate,
30
- CompressionAlgorithms,
31
35
  };
32
36
 
33
37
  // Export a function to create a new Client instance
package/src/types.ts CHANGED
@@ -1669,13 +1669,15 @@ export type RevenueShareEscrowAccount = {
1669
1669
  };
1670
1670
 
1671
1671
  export type RevenueShareOrder = {
1672
- builderIdx: number;
1673
1672
  feesAccrued: BN;
1674
1673
  orderId: number;
1675
1674
  feeTenthBps: number;
1676
1675
  marketIndex: number;
1676
+ subAccountId: number;
1677
+ builderIdx: number;
1677
1678
  bitFlags: number;
1678
- marketType: MarketType; // 0: spot, 1: perp
1679
+ userOrderIndex: number;
1680
+ marketType: MarketType;
1679
1681
  padding: number[];
1680
1682
  };
1681
1683