@drift-labs/sdk 2.131.0-beta.0 → 2.131.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 (86) hide show
  1. package/VERSION +1 -1
  2. package/bun.lock +96 -5
  3. package/lib/browser/accounts/grpcAccountSubscriber.d.ts +0 -1
  4. package/lib/browser/accounts/grpcAccountSubscriber.js +2 -18
  5. package/lib/browser/accounts/grpcProgramAccountSubscriber.d.ts +0 -1
  6. package/lib/browser/accounts/grpcProgramAccountSubscriber.js +2 -18
  7. package/lib/browser/accounts/webSocketAccountSubscriberV2.d.ts +36 -0
  8. package/lib/browser/accounts/webSocketAccountSubscriberV2.js +223 -0
  9. package/lib/browser/accounts/webSocketDriftClientAccountSubscriber.d.ts +6 -1
  10. package/lib/browser/accounts/webSocketDriftClientAccountSubscriber.js +4 -2
  11. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.d.ts +53 -0
  12. package/lib/browser/accounts/webSocketProgramAccountSubscriberV2.js +453 -0
  13. package/lib/browser/constants/index.d.ts +5 -0
  14. package/lib/browser/constants/index.js +21 -0
  15. package/lib/browser/constants/insuranceFund.d.ts +5 -0
  16. package/lib/browser/constants/insuranceFund.js +9 -0
  17. package/lib/browser/driftClient.js +3 -3
  18. package/lib/browser/driftClientConfig.d.ts +7 -2
  19. package/lib/browser/events/types.d.ts +1 -1
  20. package/lib/browser/idl/drift.json +29 -5
  21. package/lib/browser/index.d.ts +2 -4
  22. package/lib/browser/index.js +4 -5
  23. package/lib/browser/math/insurance.d.ts +2 -1
  24. package/lib/browser/math/insurance.js +7 -6
  25. package/lib/browser/math/oracles.d.ts +2 -2
  26. package/lib/browser/math/oracles.js +32 -32
  27. package/lib/browser/orderSubscriber/OrderSubscriber.js +4 -3
  28. package/lib/node/accounts/grpcAccountSubscriber.d.ts +0 -1
  29. package/lib/node/accounts/grpcAccountSubscriber.d.ts.map +1 -1
  30. package/lib/node/accounts/grpcAccountSubscriber.js +2 -18
  31. package/lib/node/accounts/grpcProgramAccountSubscriber.d.ts +0 -1
  32. package/lib/node/accounts/grpcProgramAccountSubscriber.d.ts.map +1 -1
  33. package/lib/node/accounts/grpcProgramAccountSubscriber.js +2 -18
  34. package/lib/node/accounts/webSocketAccountSubscriberV2.d.ts +37 -0
  35. package/lib/node/accounts/webSocketAccountSubscriberV2.d.ts.map +1 -0
  36. package/lib/node/accounts/webSocketAccountSubscriberV2.js +223 -0
  37. package/lib/node/accounts/webSocketDriftClientAccountSubscriber.d.ts +6 -1
  38. package/lib/node/accounts/webSocketDriftClientAccountSubscriber.d.ts.map +1 -1
  39. package/lib/node/accounts/webSocketDriftClientAccountSubscriber.js +4 -2
  40. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts +54 -0
  41. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.d.ts.map +1 -0
  42. package/lib/node/accounts/webSocketProgramAccountSubscriberV2.js +453 -0
  43. package/lib/node/constants/index.d.ts +6 -0
  44. package/lib/node/constants/index.d.ts.map +1 -0
  45. package/lib/node/constants/index.js +21 -0
  46. package/lib/node/constants/insuranceFund.d.ts +6 -0
  47. package/lib/node/constants/insuranceFund.d.ts.map +1 -0
  48. package/lib/node/constants/insuranceFund.js +9 -0
  49. package/lib/node/driftClient.d.ts.map +1 -1
  50. package/lib/node/driftClient.js +3 -3
  51. package/lib/node/driftClientConfig.d.ts +7 -2
  52. package/lib/node/driftClientConfig.d.ts.map +1 -1
  53. package/lib/node/events/types.d.ts +1 -1
  54. package/lib/node/idl/drift.json +29 -5
  55. package/lib/node/index.d.ts +2 -4
  56. package/lib/node/index.d.ts.map +1 -1
  57. package/lib/node/index.js +4 -5
  58. package/lib/node/math/insurance.d.ts +2 -1
  59. package/lib/node/math/insurance.d.ts.map +1 -1
  60. package/lib/node/math/insurance.js +7 -6
  61. package/lib/node/math/oracles.d.ts +2 -2
  62. package/lib/node/math/oracles.d.ts.map +1 -1
  63. package/lib/node/math/oracles.js +32 -32
  64. package/lib/node/orderSubscriber/OrderSubscriber.d.ts.map +1 -1
  65. package/lib/node/orderSubscriber/OrderSubscriber.js +4 -3
  66. package/lib/node/orderSubscriber/WebsocketSubscription.d.ts.map +1 -1
  67. package/package.json +2 -1
  68. package/src/accounts/README_WebSocketAccountSubscriberV2.md +54 -0
  69. package/src/accounts/README_WebSocketProgramAccountSubscriberV2.md +135 -0
  70. package/src/accounts/grpcAccountSubscriber.ts +2 -28
  71. package/src/accounts/grpcProgramAccountSubscriber.ts +2 -28
  72. package/src/accounts/webSocketAccountSubscriberV2.ts +315 -0
  73. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +22 -2
  74. package/src/accounts/webSocketProgramAccountSubscriberV2.ts +596 -0
  75. package/src/constants/index.ts +5 -0
  76. package/src/constants/insuranceFund.ts +8 -0
  77. package/src/driftClient.ts +2 -1
  78. package/src/driftClientConfig.ts +16 -2
  79. package/src/events/types.ts +1 -1
  80. package/src/idl/drift.json +30 -6
  81. package/src/index.ts +2 -4
  82. package/src/math/insurance.ts +2 -1
  83. package/src/math/oracles.ts +9 -7
  84. package/src/orderSubscriber/OrderSubscriber.ts +2 -1
  85. package/src/orderSubscriber/WebsocketSubscription.ts +1 -1
  86. package/tests/bn/test.ts +2 -1
@@ -0,0 +1,315 @@
1
+ import {
2
+ DataAndSlot,
3
+ AccountSubscriber,
4
+ ResubOpts,
5
+ BufferAndSlot,
6
+ } from './types';
7
+ import { AnchorProvider, Program } from '@coral-xyz/anchor';
8
+ import { capitalize } from './utils';
9
+ import {
10
+ AccountInfoBase,
11
+ AccountInfoWithBase58EncodedData,
12
+ AccountInfoWithBase64EncodedData,
13
+ createSolanaClient,
14
+ isAddress,
15
+ type Address,
16
+ type Commitment,
17
+ } from 'gill';
18
+ import { PublicKey } from '@solana/web3.js';
19
+ import bs58 from 'bs58';
20
+
21
+ export class WebSocketAccountSubscriberV2<T> implements AccountSubscriber<T> {
22
+ dataAndSlot?: DataAndSlot<T>;
23
+ bufferAndSlot?: BufferAndSlot;
24
+ accountName: string;
25
+ logAccountName: string;
26
+ program: Program;
27
+ accountPublicKey: PublicKey;
28
+ decodeBufferFn: (buffer: Buffer) => T;
29
+ onChange: (data: T) => void;
30
+ listenerId?: number;
31
+
32
+ resubOpts?: ResubOpts;
33
+
34
+ commitment?: Commitment;
35
+ isUnsubscribing = false;
36
+
37
+ timeoutId?: ReturnType<typeof setTimeout>;
38
+
39
+ receivingData: boolean;
40
+
41
+ // Gill client components
42
+ private rpc: ReturnType<typeof createSolanaClient>['rpc'];
43
+ private rpcSubscriptions: ReturnType<
44
+ typeof createSolanaClient
45
+ >['rpcSubscriptions'];
46
+ private abortController?: AbortController;
47
+
48
+ public constructor(
49
+ accountName: string,
50
+ program: Program,
51
+ accountPublicKey: PublicKey,
52
+ decodeBuffer?: (buffer: Buffer) => T,
53
+ resubOpts?: ResubOpts,
54
+ commitment?: Commitment
55
+ ) {
56
+ this.accountName = accountName;
57
+ this.logAccountName = `${accountName}-${accountPublicKey.toBase58()}-ws-acct-subscriber-v2`;
58
+ this.program = program;
59
+ this.accountPublicKey = accountPublicKey;
60
+ this.decodeBufferFn = decodeBuffer;
61
+ this.resubOpts = resubOpts;
62
+ if (this.resubOpts?.resubTimeoutMs < 1000) {
63
+ console.log(
64
+ `resubTimeoutMs should be at least 1000ms to avoid spamming resub ${this.logAccountName}`
65
+ );
66
+ }
67
+ this.receivingData = false;
68
+ if (
69
+ ['recent', 'single', 'singleGossip', 'root', 'max'].includes(
70
+ (this.program.provider as AnchorProvider).opts.commitment
71
+ )
72
+ ) {
73
+ console.warn(
74
+ `using commitment ${
75
+ (this.program.provider as AnchorProvider).opts.commitment
76
+ } that is not supported by gill, this may cause issues`
77
+ );
78
+ }
79
+ this.commitment =
80
+ commitment ??
81
+ ((this.program.provider as AnchorProvider).opts.commitment as Commitment);
82
+
83
+ // Initialize gill client using the same RPC URL as the program provider
84
+ const rpcUrl = (this.program.provider as AnchorProvider).connection
85
+ .rpcEndpoint;
86
+ const { rpc, rpcSubscriptions } = createSolanaClient({
87
+ urlOrMoniker: rpcUrl,
88
+ });
89
+ this.rpc = rpc;
90
+ this.rpcSubscriptions = rpcSubscriptions;
91
+ }
92
+
93
+ private async handleNotificationLoop(subscription: AsyncIterable<any>) {
94
+ for await (const notification of subscription) {
95
+ if (this.resubOpts?.resubTimeoutMs) {
96
+ this.receivingData = true;
97
+ clearTimeout(this.timeoutId);
98
+ this.handleRpcResponse(notification.context, notification.value);
99
+ this.setTimeout();
100
+ } else {
101
+ this.handleRpcResponse(notification.context, notification.value);
102
+ }
103
+ }
104
+ }
105
+
106
+ async subscribe(onChange: (data: T) => void): Promise<void> {
107
+ if (this.listenerId != null || this.isUnsubscribing) {
108
+ if (this.resubOpts?.logResubMessages) {
109
+ console.log(
110
+ `[${this.logAccountName}] Subscribe returning early - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
111
+ );
112
+ }
113
+ return;
114
+ }
115
+
116
+ this.onChange = onChange;
117
+ if (!this.dataAndSlot) {
118
+ await this.fetch();
119
+ }
120
+
121
+ // Create abort controller for proper cleanup
122
+ const abortController = new AbortController();
123
+ this.abortController = abortController;
124
+
125
+ this.listenerId = Math.random(); // Unique ID for logging purposes
126
+
127
+ if (this.resubOpts?.resubTimeoutMs) {
128
+ this.receivingData = true;
129
+ this.setTimeout();
130
+ }
131
+
132
+ // Subscribe to account changes using gill's rpcSubscriptions
133
+ const pubkey = this.accountPublicKey.toBase58();
134
+ if (isAddress(pubkey)) {
135
+ const subscription = await this.rpcSubscriptions
136
+ .accountNotifications(pubkey, {
137
+ commitment: this.commitment,
138
+ encoding: 'base64',
139
+ })
140
+ .subscribe({
141
+ abortSignal: abortController.signal,
142
+ });
143
+
144
+ // Start notification loop without awaiting
145
+ this.handleNotificationLoop(subscription);
146
+ }
147
+ }
148
+
149
+ setData(data: T, slot?: number): void {
150
+ const newSlot = slot || 0;
151
+ if (this.dataAndSlot && this.dataAndSlot.slot > newSlot) {
152
+ return;
153
+ }
154
+
155
+ this.dataAndSlot = {
156
+ data,
157
+ slot,
158
+ };
159
+ }
160
+
161
+ protected setTimeout(): void {
162
+ if (!this.onChange) {
163
+ throw new Error('onChange callback function must be set');
164
+ }
165
+ this.timeoutId = setTimeout(
166
+ async () => {
167
+ if (this.isUnsubscribing) {
168
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
169
+ if (this.resubOpts?.logResubMessages) {
170
+ console.log(
171
+ `[${this.logAccountName}] Timeout fired but isUnsubscribing=true, skipping resubscribe`
172
+ );
173
+ }
174
+ return;
175
+ }
176
+
177
+ if (this.receivingData) {
178
+ if (this.resubOpts?.logResubMessages) {
179
+ console.log(
180
+ `No ws data from ${this.logAccountName} in ${this.resubOpts.resubTimeoutMs}ms, resubscribing - listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
181
+ );
182
+ }
183
+ await this.unsubscribe(true);
184
+ this.receivingData = false;
185
+ await this.subscribe(this.onChange);
186
+ if (this.resubOpts?.logResubMessages) {
187
+ console.log(
188
+ `[${this.logAccountName}] Resubscribe completed - receivingData=${this.receivingData}, listenerId=${this.listenerId}, isUnsubscribing=${this.isUnsubscribing}`
189
+ );
190
+ }
191
+ } else {
192
+ if (this.resubOpts?.logResubMessages) {
193
+ console.log(
194
+ `[${this.logAccountName}] Timeout fired but receivingData=false, skipping resubscribe`
195
+ );
196
+ }
197
+ }
198
+ },
199
+ this.resubOpts?.resubTimeoutMs
200
+ );
201
+ }
202
+
203
+ async fetch(): Promise<void> {
204
+ // Use gill's rpc for fetching account info
205
+ const accountAddress = this.accountPublicKey.toBase58() as Address;
206
+ const rpcResponse = await this.rpc
207
+ .getAccountInfo(accountAddress, {
208
+ commitment: this.commitment,
209
+ encoding: 'base64',
210
+ })
211
+ .send();
212
+
213
+ // Convert gill response to match the expected format
214
+ const context = {
215
+ slot: Number(rpcResponse.context.slot),
216
+ };
217
+
218
+ const accountInfo = rpcResponse.value;
219
+
220
+ this.handleRpcResponse({ slot: BigInt(context.slot) }, accountInfo);
221
+ }
222
+
223
+ handleRpcResponse(
224
+ context: { slot: bigint },
225
+ accountInfo?: AccountInfoBase &
226
+ (AccountInfoWithBase58EncodedData | AccountInfoWithBase64EncodedData)
227
+ ): void {
228
+ const newSlot = context.slot;
229
+ let newBuffer: Buffer | undefined = undefined;
230
+
231
+ if (accountInfo) {
232
+ // Extract data from gill response
233
+ if (accountInfo.data) {
234
+ // Handle different data formats from gill
235
+ if (Array.isArray(accountInfo.data)) {
236
+ // If it's a tuple [data, encoding]
237
+ const [data, encoding] = accountInfo.data;
238
+
239
+ if (encoding === 'base58') {
240
+ // we know encoding will be base58
241
+ // Convert base58 to buffer using bs58
242
+ newBuffer = Buffer.from(bs58.decode(data));
243
+ } else {
244
+ newBuffer = Buffer.from(data, 'base64');
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ if (!this.bufferAndSlot) {
251
+ this.bufferAndSlot = {
252
+ buffer: newBuffer,
253
+ slot: Number(newSlot),
254
+ };
255
+ if (newBuffer) {
256
+ const account = this.decodeBuffer(newBuffer);
257
+ this.dataAndSlot = {
258
+ data: account,
259
+ slot: Number(newSlot),
260
+ };
261
+ this.onChange(account);
262
+ }
263
+ return;
264
+ }
265
+
266
+ if (Number(newSlot) < this.bufferAndSlot.slot) {
267
+ return;
268
+ }
269
+
270
+ const oldBuffer = this.bufferAndSlot.buffer;
271
+ if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
272
+ this.bufferAndSlot = {
273
+ buffer: newBuffer,
274
+ slot: Number(newSlot),
275
+ };
276
+ const account = this.decodeBuffer(newBuffer);
277
+ this.dataAndSlot = {
278
+ data: account,
279
+ slot: Number(newSlot),
280
+ };
281
+ this.onChange(account);
282
+ }
283
+ }
284
+
285
+ decodeBuffer(buffer: Buffer): T {
286
+ if (this.decodeBufferFn) {
287
+ return this.decodeBufferFn(buffer);
288
+ } else {
289
+ return this.program.account[this.accountName].coder.accounts.decode(
290
+ capitalize(this.accountName),
291
+ buffer
292
+ );
293
+ }
294
+ }
295
+
296
+ unsubscribe(onResub = false): Promise<void> {
297
+ if (!onResub && this.resubOpts) {
298
+ this.resubOpts.resubTimeoutMs = undefined;
299
+ }
300
+ this.isUnsubscribing = true;
301
+ clearTimeout(this.timeoutId);
302
+ this.timeoutId = undefined;
303
+
304
+ // Abort the WebSocket subscription
305
+ if (this.abortController) {
306
+ this.abortController.abort('unsubscribing');
307
+ this.abortController = undefined;
308
+ }
309
+
310
+ this.listenerId = undefined;
311
+ this.isUnsubscribing = false;
312
+
313
+ return Promise.resolve();
314
+ }
315
+ }
@@ -28,6 +28,7 @@ import { findAllMarketAndOracles } from '../config';
28
28
  import { findDelistedPerpMarketsAndOracles } from './utils';
29
29
  import { getOracleId } from '../oracles/oracleId';
30
30
  import { OracleSource } from '../types';
31
+ import { WebSocketAccountSubscriberV2 } from './webSocketAccountSubscriberV2';
31
32
 
32
33
  const ORACLE_DEFAULT_ID = getOracleId(
33
34
  PublicKey.default,
@@ -68,6 +69,14 @@ export class WebSocketDriftClientAccountSubscriber
68
69
  initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
69
70
  initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
70
71
  initialOraclePriceData: Map<string, OraclePriceData>;
72
+ customPerpMarketAccountSubscriber?: new (
73
+ accountName: string,
74
+ program: Program,
75
+ accountPublicKey: PublicKey,
76
+ decodeBuffer?: (buffer: Buffer) => any,
77
+ resubOpts?: ResubOpts,
78
+ commitment?: Commitment
79
+ ) => AccountSubscriber<any>;
71
80
 
72
81
  protected isSubscribing = false;
73
82
  protected subscriptionPromise: Promise<boolean>;
@@ -81,7 +90,15 @@ export class WebSocketDriftClientAccountSubscriber
81
90
  shouldFindAllMarketsAndOracles: boolean,
82
91
  delistedMarketSetting: DelistedMarketSetting,
83
92
  resubOpts?: ResubOpts,
84
- commitment?: Commitment
93
+ commitment?: Commitment,
94
+ customPerpMarketAccountSubscriber?: new (
95
+ accountName: string,
96
+ program: Program,
97
+ accountPublicKey: PublicKey,
98
+ decodeBuffer?: (buffer: Buffer) => any,
99
+ resubOpts?: ResubOpts,
100
+ commitment?: Commitment
101
+ ) => WebSocketAccountSubscriberV2<any> | WebSocketAccountSubscriber<any>
85
102
  ) {
86
103
  this.isSubscribed = false;
87
104
  this.program = program;
@@ -93,6 +110,7 @@ export class WebSocketDriftClientAccountSubscriber
93
110
  this.delistedMarketSetting = delistedMarketSetting;
94
111
  this.resubOpts = resubOpts;
95
112
  this.commitment = commitment;
113
+ this.customPerpMarketAccountSubscriber = customPerpMarketAccountSubscriber;
96
114
  }
97
115
 
98
116
  public async subscribe(): Promise<boolean> {
@@ -292,7 +310,9 @@ export class WebSocketDriftClientAccountSubscriber
292
310
  this.program.programId,
293
311
  marketIndex
294
312
  );
295
- const accountSubscriber = new WebSocketAccountSubscriber<PerpMarketAccount>(
313
+ const AccountSubscriberClass =
314
+ this.customPerpMarketAccountSubscriber || WebSocketAccountSubscriber;
315
+ const accountSubscriber = new AccountSubscriberClass<PerpMarketAccount>(
296
316
  'perpMarket',
297
317
  this.program,
298
318
  perpMarketPublicKey,