@drift-labs/sdk 2.40.0-beta.7 → 2.40.0-beta.9

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 (44) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/types.d.ts +5 -1
  3. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -1
  4. package/lib/accounts/webSocketAccountSubscriber.js +28 -2
  5. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +2 -1
  6. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
  7. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
  8. package/lib/accounts/webSocketProgramAccountSubscriber.js +99 -0
  9. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +2 -1
  10. package/lib/accounts/webSocketUserAccountSubscriber.js +3 -2
  11. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +2 -1
  12. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -2
  13. package/lib/auctionSubscriber/auctionSubscriber.d.ts +3 -2
  14. package/lib/auctionSubscriber/auctionSubscriber.js +15 -7
  15. package/lib/auctionSubscriber/types.d.ts +1 -0
  16. package/lib/constants/spotMarkets.js +1 -1
  17. package/lib/driftClient.js +3 -3
  18. package/lib/driftClientConfig.d.ts +1 -0
  19. package/lib/math/superStake.d.ts +51 -4
  20. package/lib/math/superStake.js +173 -21
  21. package/lib/math/utils.d.ts +1 -0
  22. package/lib/math/utils.js +10 -1
  23. package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -3
  24. package/lib/orderSubscriber/OrderSubscriber.js +4 -3
  25. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -2
  26. package/lib/orderSubscriber/WebsocketSubscription.js +13 -12
  27. package/lib/orderSubscriber/types.d.ts +1 -0
  28. package/package.json +1 -1
  29. package/src/accounts/types.ts +8 -1
  30. package/src/accounts/webSocketAccountSubscriber.ts +36 -2
  31. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +15 -5
  32. package/src/accounts/webSocketProgramAccountSubscriber.ts +152 -0
  33. package/src/accounts/webSocketUserAccountSubscriber.ts +10 -2
  34. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +10 -2
  35. package/src/auctionSubscriber/auctionSubscriber.ts +30 -21
  36. package/src/auctionSubscriber/types.ts +1 -0
  37. package/src/constants/spotMarkets.ts +1 -1
  38. package/src/driftClient.ts +2 -1
  39. package/src/driftClientConfig.ts +1 -0
  40. package/src/math/superStake.ts +248 -24
  41. package/src/math/utils.ts +12 -0
  42. package/src/orderSubscriber/OrderSubscriber.ts +12 -7
  43. package/src/orderSubscriber/WebsocketSubscription.ts +33 -23
  44. package/src/orderSubscriber/types.ts +1 -0
@@ -0,0 +1,152 @@
1
+ import { DataAndSlot, BufferAndSlot, ProgramAccountSubscriber } from './types';
2
+ import { AnchorProvider, Program } from '@coral-xyz/anchor';
3
+ import {
4
+ Commitment,
5
+ Context,
6
+ KeyedAccountInfo,
7
+ MemcmpFilter,
8
+ PublicKey,
9
+ } from '@solana/web3.js';
10
+ import * as Buffer from 'buffer';
11
+
12
+ export class WebSocketProgramAccountSubscriber<T>
13
+ implements ProgramAccountSubscriber<T>
14
+ {
15
+ subscriptionName: string;
16
+ accountDiscriminator: string;
17
+ dataAndSlot?: DataAndSlot<T> & { accountId: PublicKey };
18
+ bufferAndSlot?: BufferAndSlot;
19
+ program: Program;
20
+ decodeBuffer: (accountName: string, ix: Buffer) => T;
21
+ onChange: (accountId: PublicKey, data: T, context: Context) => void;
22
+ listenerId?: number;
23
+ resubTimeoutMs?: number;
24
+ timeoutId?: NodeJS.Timeout;
25
+ options: { filters: MemcmpFilter[]; commitment?: Commitment };
26
+
27
+ receivingData = false;
28
+
29
+ public constructor(
30
+ subscriptionName: string,
31
+ accountDiscriminator: string,
32
+ program: Program,
33
+ decodeBufferFn: (accountName: string, ix: Buffer) => T,
34
+ options: { filters: MemcmpFilter[]; commitment?: Commitment } = {
35
+ filters: [],
36
+ },
37
+ resubTimeoutMs?: number
38
+ ) {
39
+ this.subscriptionName = subscriptionName;
40
+ this.accountDiscriminator = accountDiscriminator;
41
+ this.program = program;
42
+ this.decodeBuffer = decodeBufferFn;
43
+ this.resubTimeoutMs = resubTimeoutMs;
44
+ this.options = options;
45
+ this.receivingData = false;
46
+ }
47
+
48
+ async subscribe(
49
+ onChange: (accountId: PublicKey, data: T, context: Context) => void
50
+ ): Promise<void> {
51
+ if (this.listenerId) {
52
+ return;
53
+ }
54
+
55
+ this.onChange = onChange;
56
+
57
+ this.listenerId = this.program.provider.connection.onProgramAccountChange(
58
+ this.program.programId,
59
+ (keyedAccountInfo, context) => {
60
+ if (this.resubTimeoutMs) {
61
+ this.receivingData = true;
62
+ clearTimeout(this.timeoutId);
63
+ this.handleRpcResponse(context, keyedAccountInfo);
64
+ this.setTimeout();
65
+ } else {
66
+ this.handleRpcResponse(context, keyedAccountInfo);
67
+ }
68
+ },
69
+ this.options.commitment ??
70
+ (this.program.provider as AnchorProvider).opts.commitment,
71
+ this.options.filters
72
+ );
73
+
74
+ if (this.resubTimeoutMs) {
75
+ this.setTimeout();
76
+ }
77
+ }
78
+
79
+ private setTimeout(): void {
80
+ if (!this.onChange) {
81
+ throw new Error('onChange callback function must be set');
82
+ }
83
+ this.timeoutId = setTimeout(async () => {
84
+ if (this.receivingData) {
85
+ console.log(
86
+ `No ws data from ${this.subscriptionName} in ${this.resubTimeoutMs}ms, resubscribing`
87
+ );
88
+ await this.unsubscribe();
89
+ this.receivingData = false;
90
+ await this.subscribe(this.onChange);
91
+ }
92
+ }, this.resubTimeoutMs);
93
+ }
94
+
95
+ handleRpcResponse(
96
+ context: Context,
97
+ keyedAccountInfo: KeyedAccountInfo
98
+ ): void {
99
+ const newSlot = context.slot;
100
+ let newBuffer: Buffer | undefined = undefined;
101
+ if (keyedAccountInfo) {
102
+ newBuffer = keyedAccountInfo.accountInfo.data;
103
+ }
104
+
105
+ if (!this.bufferAndSlot) {
106
+ this.bufferAndSlot = {
107
+ buffer: newBuffer,
108
+ slot: newSlot,
109
+ };
110
+ if (newBuffer) {
111
+ const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
112
+ this.dataAndSlot = {
113
+ data: account,
114
+ slot: newSlot,
115
+ accountId: keyedAccountInfo.accountId,
116
+ };
117
+ this.onChange(keyedAccountInfo.accountId, account, context);
118
+ }
119
+ return;
120
+ }
121
+
122
+ if (newSlot <= this.bufferAndSlot.slot) {
123
+ return;
124
+ }
125
+
126
+ const oldBuffer = this.bufferAndSlot.buffer;
127
+ if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
128
+ this.bufferAndSlot = {
129
+ buffer: newBuffer,
130
+ slot: newSlot,
131
+ };
132
+ const account = this.decodeBuffer(this.accountDiscriminator, newBuffer);
133
+ this.dataAndSlot = {
134
+ data: account,
135
+ slot: newSlot,
136
+ accountId: keyedAccountInfo.accountId,
137
+ };
138
+ this.onChange(keyedAccountInfo.accountId, account, context);
139
+ }
140
+ }
141
+
142
+ unsubscribe(): Promise<void> {
143
+ if (this.listenerId) {
144
+ const promise =
145
+ this.program.provider.connection.removeAccountChangeListener(
146
+ this.listenerId
147
+ );
148
+ this.listenerId = undefined;
149
+ return promise;
150
+ }
151
+ }
152
+ }
@@ -14,17 +14,23 @@ import { UserAccount } from '../types';
14
14
 
15
15
  export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
16
16
  isSubscribed: boolean;
17
+ reconnectTimeoutMs?: number;
17
18
  program: Program;
18
19
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
19
20
  userAccountPublicKey: PublicKey;
20
21
 
21
22
  userDataAccountSubscriber: AccountSubscriber<UserAccount>;
22
23
 
23
- public constructor(program: Program, userAccountPublicKey: PublicKey) {
24
+ public constructor(
25
+ program: Program,
26
+ userAccountPublicKey: PublicKey,
27
+ reconnectTimeoutMs?: number
28
+ ) {
24
29
  this.isSubscribed = false;
25
30
  this.program = program;
26
31
  this.userAccountPublicKey = userAccountPublicKey;
27
32
  this.eventEmitter = new EventEmitter();
33
+ this.reconnectTimeoutMs = reconnectTimeoutMs;
28
34
  }
29
35
 
30
36
  async subscribe(userAccount?: UserAccount): Promise<boolean> {
@@ -35,7 +41,9 @@ export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
35
41
  this.userDataAccountSubscriber = new WebSocketAccountSubscriber(
36
42
  'user',
37
43
  this.program,
38
- this.userAccountPublicKey
44
+ this.userAccountPublicKey,
45
+ undefined,
46
+ this.reconnectTimeoutMs
39
47
  );
40
48
 
41
49
  if (userAccount) {
@@ -16,17 +16,23 @@ export class WebSocketUserStatsAccountSubscriber
16
16
  implements UserStatsAccountSubscriber
17
17
  {
18
18
  isSubscribed: boolean;
19
+ reconnectTimeoutMs?: number;
19
20
  program: Program;
20
21
  eventEmitter: StrictEventEmitter<EventEmitter, UserStatsAccountEvents>;
21
22
  userStatsAccountPublicKey: PublicKey;
22
23
 
23
24
  userStatsAccountSubscriber: AccountSubscriber<UserStatsAccount>;
24
25
 
25
- public constructor(program: Program, userStatsAccountPublicKey: PublicKey) {
26
+ public constructor(
27
+ program: Program,
28
+ userStatsAccountPublicKey: PublicKey,
29
+ reconnectTimeoutMs?: number
30
+ ) {
26
31
  this.isSubscribed = false;
27
32
  this.program = program;
28
33
  this.userStatsAccountPublicKey = userStatsAccountPublicKey;
29
34
  this.eventEmitter = new EventEmitter();
35
+ this.reconnectTimeoutMs = reconnectTimeoutMs;
30
36
  }
31
37
 
32
38
  async subscribe(userStatsAccount?: UserStatsAccount): Promise<boolean> {
@@ -37,7 +43,9 @@ export class WebSocketUserStatsAccountSubscriber
37
43
  this.userStatsAccountSubscriber = new WebSocketAccountSubscriber(
38
44
  'userStats',
39
45
  this.program,
40
- this.userStatsAccountPublicKey
46
+ this.userStatsAccountPublicKey,
47
+ undefined,
48
+ this.reconnectTimeoutMs
41
49
  );
42
50
 
43
51
  if (userStatsAccount) {
@@ -4,48 +4,57 @@ import { getUserFilter, getUserWithAuctionFilter } from '../memcmp';
4
4
  import StrictEventEmitter from 'strict-event-emitter-types';
5
5
  import { EventEmitter } from 'events';
6
6
  import { UserAccount } from '../types';
7
- import { ConfirmOptions } from '@solana/web3.js';
7
+ import { ConfirmOptions, Context, PublicKey } from '@solana/web3.js';
8
+ import { WebSocketProgramAccountSubscriber } from '../accounts/webSocketProgramAccountSubscriber';
8
9
 
9
10
  export class AuctionSubscriber {
10
11
  private driftClient: DriftClient;
11
12
  private opts: ConfirmOptions;
13
+ private resubTimeoutMs?: number;
12
14
 
13
15
  eventEmitter: StrictEventEmitter<EventEmitter, AuctionSubscriberEvents>;
16
+ private subscriber: WebSocketProgramAccountSubscriber<UserAccount>;
14
17
 
15
- private websocketId: number;
16
-
17
- constructor({ driftClient, opts }: AuctionSubscriberConfig) {
18
+ constructor({ driftClient, opts, resubTimeoutMs }: AuctionSubscriberConfig) {
18
19
  this.driftClient = driftClient;
19
20
  this.opts = opts || this.driftClient.opts;
20
21
  this.eventEmitter = new EventEmitter();
22
+ this.resubTimeoutMs = resubTimeoutMs;
21
23
  }
22
24
 
23
25
  public async subscribe() {
24
- this.websocketId = this.driftClient.connection.onProgramAccountChange(
25
- this.driftClient.program.programId,
26
- (keyAccountInfo, context) => {
27
- const userAccount =
28
- this.driftClient.program.account.user.coder.accounts.decode(
29
- 'User',
30
- keyAccountInfo.accountInfo.data
31
- ) as UserAccount;
26
+ if (!this.subscriber) {
27
+ this.subscriber = new WebSocketProgramAccountSubscriber<UserAccount>(
28
+ 'AuctionSubscriber',
29
+ 'User',
30
+ this.driftClient.program,
31
+ this.driftClient.program.account.user.coder.accounts.decode.bind(
32
+ this.driftClient.program.account.user.coder.accounts
33
+ ),
34
+ {
35
+ filters: [getUserFilter(), getUserWithAuctionFilter()],
36
+ commitment: this.driftClient.opts.commitment,
37
+ },
38
+ this.resubTimeoutMs
39
+ );
40
+ }
41
+
42
+ await this.subscriber.subscribe(
43
+ (accountId: PublicKey, data: UserAccount, context: Context) => {
32
44
  this.eventEmitter.emit(
33
45
  'onAccountUpdate',
34
- userAccount,
35
- keyAccountInfo.accountId,
46
+ data,
47
+ accountId,
36
48
  context.slot
37
49
  );
38
- },
39
- this.driftClient.opts.commitment,
40
- [getUserFilter(), getUserWithAuctionFilter()]
50
+ }
41
51
  );
42
52
  }
43
53
 
44
54
  public async unsubscribe() {
45
- if (this.websocketId) {
46
- await this.driftClient.connection.removeProgramAccountChangeListener(
47
- this.websocketId
48
- );
55
+ if (!this.subscriber) {
56
+ return;
49
57
  }
58
+ this.subscriber.unsubscribe();
50
59
  }
51
60
  }
@@ -5,6 +5,7 @@ import { ConfirmOptions, PublicKey } from '@solana/web3.js';
5
5
  export type AuctionSubscriberConfig = {
6
6
  driftClient: DriftClient;
7
7
  opts?: ConfirmOptions;
8
+ resubTimeoutMs?: number;
8
9
  };
9
10
 
10
11
  export interface AuctionSubscriberEvents {
@@ -128,7 +128,7 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
128
128
  serumMarket: new PublicKey('B2na8Awyd7cpC59iEU43FagJAPLigr3AP3s38KM982bu'),
129
129
  },
130
130
  {
131
- symbol: 'jitoSOL',
131
+ symbol: 'JitoSOL',
132
132
  marketIndex: 6,
133
133
  oracle: new PublicKey('7yyaeuJ1GGtVBLT2z2xub5ZWYKaNhF28mj1RdV4VDFVk'),
134
134
  oracleSource: OracleSource.PYTH,
@@ -278,7 +278,8 @@ export class DriftClient {
278
278
  config.perpMarketIndexes ?? [],
279
279
  config.spotMarketIndexes ?? [],
280
280
  config.oracleInfos ?? [],
281
- noMarketsAndOraclesSpecified
281
+ noMarketsAndOraclesSpecified,
282
+ config.accountSubscription?.resubTimeoutMs
282
283
  );
283
284
  }
284
285
  this.eventEmitter = this.accountSubscriber.eventEmitter;
@@ -36,6 +36,7 @@ export type DriftClientConfig = {
36
36
  export type DriftClientSubscriptionConfig =
37
37
  | {
38
38
  type: 'websocket';
39
+ resubTimeoutMs?: number;
39
40
  }
40
41
  | {
41
42
  type: 'polling';