@drift-labs/sdk 2.41.0-beta.0 → 2.41.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 (81) hide show
  1. package/README.md +0 -1
  2. package/VERSION +1 -1
  3. package/bun.lockb +0 -0
  4. package/lib/accounts/types.d.ts +5 -1
  5. package/lib/accounts/webSocketAccountSubscriber.d.ts +6 -1
  6. package/lib/accounts/webSocketAccountSubscriber.js +28 -2
  7. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +2 -1
  8. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
  9. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +32 -0
  10. package/lib/accounts/webSocketProgramAccountSubscriber.js +99 -0
  11. package/lib/accounts/webSocketUserAccountSubscriber.d.ts +2 -1
  12. package/lib/accounts/webSocketUserAccountSubscriber.js +3 -2
  13. package/lib/accounts/webSocketUserStatsAccountSubsriber.d.ts +2 -1
  14. package/lib/accounts/webSocketUserStatsAccountSubsriber.js +3 -2
  15. package/lib/addresses/pda.d.ts +1 -0
  16. package/lib/addresses/pda.js +5 -1
  17. package/lib/adminClient.d.ts +2 -0
  18. package/lib/adminClient.js +20 -0
  19. package/lib/auctionSubscriber/auctionSubscriber.d.ts +3 -2
  20. package/lib/auctionSubscriber/auctionSubscriber.js +15 -7
  21. package/lib/auctionSubscriber/types.d.ts +1 -0
  22. package/lib/dlob/DLOB.d.ts +2 -6
  23. package/lib/dlob/DLOB.js +9 -11
  24. package/lib/dlob/DLOBSubscriber.js +4 -7
  25. package/lib/dlob/orderBookLevels.d.ts +4 -2
  26. package/lib/dlob/orderBookLevels.js +79 -16
  27. package/lib/driftClient.d.ts +2 -1
  28. package/lib/driftClient.js +5 -4
  29. package/lib/driftClientConfig.d.ts +1 -0
  30. package/lib/factory/bigNum.js +4 -2
  31. package/lib/idl/drift.json +222 -2
  32. package/lib/jupiter/jupiterClient.d.ts +4 -1
  33. package/lib/jupiter/jupiterClient.js +4 -1
  34. package/lib/math/amm.js +7 -2
  35. package/lib/math/auction.d.ts +12 -1
  36. package/lib/math/auction.js +22 -1
  37. package/lib/math/market.js +2 -4
  38. package/lib/math/superStake.d.ts +51 -4
  39. package/lib/math/superStake.js +173 -21
  40. package/lib/math/trade.js +2 -4
  41. package/lib/math/utils.d.ts +1 -0
  42. package/lib/math/utils.js +10 -1
  43. package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -3
  44. package/lib/orderSubscriber/OrderSubscriber.js +4 -3
  45. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -2
  46. package/lib/orderSubscriber/WebsocketSubscription.js +15 -12
  47. package/lib/orderSubscriber/types.d.ts +1 -0
  48. package/lib/user.d.ts +2 -2
  49. package/lib/user.js +5 -5
  50. package/package.json +2 -1
  51. package/src/accounts/types.ts +8 -1
  52. package/src/accounts/webSocketAccountSubscriber.ts +36 -2
  53. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +15 -5
  54. package/src/accounts/webSocketProgramAccountSubscriber.ts +152 -0
  55. package/src/accounts/webSocketUserAccountSubscriber.ts +10 -2
  56. package/src/accounts/webSocketUserStatsAccountSubsriber.ts +10 -2
  57. package/src/addresses/pda.ts +9 -0
  58. package/src/adminClient.ts +32 -0
  59. package/src/auctionSubscriber/auctionSubscriber.ts +30 -21
  60. package/src/auctionSubscriber/types.ts +1 -0
  61. package/src/dlob/DLOB.ts +9 -32
  62. package/src/dlob/DLOBSubscriber.ts +8 -7
  63. package/src/dlob/orderBookLevels.ts +133 -32
  64. package/src/driftClient.ts +5 -1
  65. package/src/driftClientConfig.ts +1 -0
  66. package/src/factory/bigNum.ts +2 -0
  67. package/src/idl/drift.json +222 -2
  68. package/src/jupiter/jupiterClient.ts +6 -0
  69. package/src/math/amm.ts +9 -2
  70. package/src/math/auction.ts +36 -2
  71. package/src/math/market.ts +4 -9
  72. package/src/math/superStake.ts +247 -23
  73. package/src/math/trade.ts +3 -11
  74. package/src/math/utils.ts +12 -0
  75. package/src/orderSubscriber/OrderSubscriber.ts +12 -7
  76. package/src/orderSubscriber/WebsocketSubscription.ts +34 -23
  77. package/src/orderSubscriber/types.ts +1 -0
  78. package/src/user.ts +7 -5
  79. package/tests/amm/test.ts +402 -0
  80. package/tests/auctions/test.ts +66 -0
  81. package/tests/dlob/test.ts +1 -73
@@ -13,17 +13,24 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
13
13
  decodeBufferFn: (buffer: Buffer) => T;
14
14
  onChange: (data: T) => void;
15
15
  listenerId?: number;
16
+ resubTimeoutMs?: number;
17
+ timeoutId?: NodeJS.Timeout;
18
+
19
+ receivingData: boolean;
16
20
 
17
21
  public constructor(
18
22
  accountName: string,
19
23
  program: Program,
20
24
  accountPublicKey: PublicKey,
21
- decodeBuffer?: (buffer: Buffer) => T
25
+ decodeBuffer?: (buffer: Buffer) => T,
26
+ resubTimeoutMs?: number
22
27
  ) {
23
28
  this.accountName = accountName;
24
29
  this.program = program;
25
30
  this.accountPublicKey = accountPublicKey;
26
31
  this.decodeBufferFn = decodeBuffer;
32
+ this.resubTimeoutMs = resubTimeoutMs;
33
+ this.receivingData = false;
27
34
  }
28
35
 
29
36
  async subscribe(onChange: (data: T) => void): Promise<void> {
@@ -39,10 +46,21 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
39
46
  this.listenerId = this.program.provider.connection.onAccountChange(
40
47
  this.accountPublicKey,
41
48
  (accountInfo, context) => {
42
- this.handleRpcResponse(context, accountInfo);
49
+ if (this.resubTimeoutMs) {
50
+ this.receivingData = true;
51
+ clearTimeout(this.timeoutId);
52
+ this.handleRpcResponse(context, accountInfo);
53
+ this.setTimeout();
54
+ } else {
55
+ this.handleRpcResponse(context, accountInfo);
56
+ }
43
57
  },
44
58
  (this.program.provider as AnchorProvider).opts.commitment
45
59
  );
60
+
61
+ if (this.resubTimeoutMs) {
62
+ this.setTimeout();
63
+ }
46
64
  }
47
65
 
48
66
  setData(data: T, slot?: number): void {
@@ -57,6 +75,22 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
57
75
  };
58
76
  }
59
77
 
78
+ private setTimeout(): void {
79
+ if (!this.onChange) {
80
+ throw new Error('onChange callback function must be set');
81
+ }
82
+ this.timeoutId = setTimeout(async () => {
83
+ if (this.receivingData) {
84
+ console.log(
85
+ `No ws data from ${this.accountName} in ${this.resubTimeoutMs}ms, resubscribing`
86
+ );
87
+ await this.unsubscribe();
88
+ this.receivingData = false;
89
+ await this.subscribe(this.onChange);
90
+ }
91
+ }, this.resubTimeoutMs);
92
+ }
93
+
60
94
  async fetch(): Promise<void> {
61
95
  const rpcResponse =
62
96
  await this.program.provider.connection.getAccountInfoAndContext(
@@ -31,6 +31,7 @@ export class WebSocketDriftClientAccountSubscriber
31
31
  oracleInfos: OracleInfo[];
32
32
  oracleClientCache = new OracleClientCache();
33
33
 
34
+ resubTimeoutMs?: number;
34
35
  shouldFindAllMarketsAndOracles: boolean;
35
36
 
36
37
  eventEmitter: StrictEventEmitter<EventEmitter, DriftClientAccountEvents>;
@@ -54,7 +55,8 @@ export class WebSocketDriftClientAccountSubscriber
54
55
  perpMarketIndexes: number[],
55
56
  spotMarketIndexes: number[],
56
57
  oracleInfos: OracleInfo[],
57
- shouldFindAllMarketsAndOracles: boolean
58
+ shouldFindAllMarketsAndOracles: boolean,
59
+ resubTimeoutMs?: number
58
60
  ) {
59
61
  this.isSubscribed = false;
60
62
  this.program = program;
@@ -63,6 +65,7 @@ export class WebSocketDriftClientAccountSubscriber
63
65
  this.spotMarketIndexes = spotMarketIndexes;
64
66
  this.oracleInfos = oracleInfos;
65
67
  this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
68
+ this.resubTimeoutMs = resubTimeoutMs;
66
69
  }
67
70
 
68
71
  public async subscribe(): Promise<boolean> {
@@ -96,7 +99,9 @@ export class WebSocketDriftClientAccountSubscriber
96
99
  this.stateAccountSubscriber = new WebSocketAccountSubscriber(
97
100
  'state',
98
101
  this.program,
99
- statePublicKey
102
+ statePublicKey,
103
+ undefined,
104
+ this.resubTimeoutMs
100
105
  );
101
106
  await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
102
107
  this.eventEmitter.emit('stateAccountUpdate', data);
@@ -136,7 +141,9 @@ export class WebSocketDriftClientAccountSubscriber
136
141
  const accountSubscriber = new WebSocketAccountSubscriber<PerpMarketAccount>(
137
142
  'perpMarket',
138
143
  this.program,
139
- perpMarketPublicKey
144
+ perpMarketPublicKey,
145
+ undefined,
146
+ this.resubTimeoutMs
140
147
  );
141
148
  await accountSubscriber.subscribe((data: PerpMarketAccount) => {
142
149
  this.eventEmitter.emit('perpMarketAccountUpdate', data);
@@ -161,7 +168,9 @@ export class WebSocketDriftClientAccountSubscriber
161
168
  const accountSubscriber = new WebSocketAccountSubscriber<SpotMarketAccount>(
162
169
  'spotMarket',
163
170
  this.program,
164
- marketPublicKey
171
+ marketPublicKey,
172
+ undefined,
173
+ this.resubTimeoutMs
165
174
  );
166
175
  await accountSubscriber.subscribe((data: SpotMarketAccount) => {
167
176
  this.eventEmitter.emit('spotMarketAccountUpdate', data);
@@ -192,7 +201,8 @@ export class WebSocketDriftClientAccountSubscriber
192
201
  oracleInfo.publicKey,
193
202
  (buffer: Buffer) => {
194
203
  return client.getOraclePriceDataFromBuffer(buffer);
195
- }
204
+ },
205
+ this.resubTimeoutMs
196
206
  );
197
207
 
198
208
  await accountSubscriber.subscribe((data: OraclePriceData) => {
@@ -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) {
@@ -214,3 +214,12 @@ export function getReferrerNamePublicKeySync(
214
214
  programId
215
215
  )[0];
216
216
  }
217
+
218
+ export function getProtocolIfSharesTransferConfigPublicKey(
219
+ programId: PublicKey
220
+ ): PublicKey {
221
+ return PublicKey.findProgramAddressSync(
222
+ [Buffer.from(anchor.utils.bytes.utf8.encode('if_shares_transfer_config'))],
223
+ programId
224
+ )[0];
225
+ }
@@ -25,6 +25,7 @@ import {
25
25
  getSerumOpenOrdersPublicKey,
26
26
  getSerumFulfillmentConfigPublicKey,
27
27
  getPhoenixFulfillmentConfigPublicKey,
28
+ getProtocolIfSharesTransferConfigPublicKey,
28
29
  } from './addresses/pda';
29
30
  import { squareRootBN } from './math/utils';
30
31
  import { TOKEN_PROGRAM_ID } from '@solana/spl-token';
@@ -1516,4 +1517,35 @@ export class AdminClient extends DriftClient {
1516
1517
  }
1517
1518
  );
1518
1519
  }
1520
+
1521
+ public async initializeProtocolIfSharesTransferConfig(): Promise<TransactionSignature> {
1522
+ return await this.program.rpc.initializeProtocolIfSharesTransferConfig({
1523
+ accounts: {
1524
+ admin: this.wallet.publicKey,
1525
+ state: await this.getStatePublicKey(),
1526
+ rent: SYSVAR_RENT_PUBKEY,
1527
+ systemProgram: anchor.web3.SystemProgram.programId,
1528
+ protocolIfSharesTransferConfig:
1529
+ getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
1530
+ },
1531
+ });
1532
+ }
1533
+
1534
+ public async updateProtocolIfSharesTransferConfig(
1535
+ whitelistedSigners?: PublicKey[],
1536
+ maxTransferPerEpoch?: BN
1537
+ ): Promise<TransactionSignature> {
1538
+ return await this.program.rpc.updateProtocolIfSharesTransferConfig(
1539
+ whitelistedSigners || null,
1540
+ maxTransferPerEpoch,
1541
+ {
1542
+ accounts: {
1543
+ admin: this.wallet.publicKey,
1544
+ state: await this.getStatePublicKey(),
1545
+ protocolIfSharesTransferConfig:
1546
+ getProtocolIfSharesTransferConfigPublicKey(this.program.programId),
1547
+ },
1548
+ }
1549
+ );
1550
+ }
1519
1551
  }
@@ -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 {
package/src/dlob/DLOB.ts CHANGED
@@ -675,7 +675,7 @@ export class DLOB {
675
675
  marketType,
676
676
  oraclePriceData,
677
677
  takingOrderGenerator,
678
- this.getMakerLimitBids.bind(this),
678
+ this.getRestingLimitBids.bind(this),
679
679
  (takerPrice, makerPrice) => {
680
680
  if (isVariant(marketType, 'spot')) {
681
681
  if (takerPrice === undefined) {
@@ -687,8 +687,7 @@ export class DLOB {
687
687
  }
688
688
  }
689
689
  return takerPrice === undefined || takerPrice.lte(makerPrice);
690
- },
691
- fallbackAsk
690
+ }
692
691
  );
693
692
  for (const takingAskCrossingBid of takingAsksCrossingBids) {
694
693
  nodesToFill.push(takingAskCrossingBid);
@@ -732,7 +731,7 @@ export class DLOB {
732
731
  marketType,
733
732
  oraclePriceData,
734
733
  takingOrderGenerator,
735
- this.getMakerLimitAsks.bind(this),
734
+ this.getRestingLimitAsks.bind(this),
736
735
  (takerPrice, makerPrice) => {
737
736
  if (isVariant(marketType, 'spot')) {
738
737
  if (takerPrice === undefined) {
@@ -745,8 +744,7 @@ export class DLOB {
745
744
  }
746
745
 
747
746
  return takerPrice === undefined || takerPrice.gte(makerPrice);
748
- },
749
- fallbackBid
747
+ }
750
748
  );
751
749
 
752
750
  for (const takingBidToFill of takingBidsToFill) {
@@ -790,11 +788,9 @@ export class DLOB {
790
788
  marketIndex: number,
791
789
  slot: number,
792
790
  marketType: MarketType,
793
- oraclePriceData: OraclePriceData,
794
- fallbackPrice?: BN
791
+ oraclePriceData: OraclePriceData
795
792
  ) => Generator<DLOBNode>,
796
- doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean,
797
- fallbackPrice?: BN
793
+ doesCross: (takerPrice: BN | undefined, makerPrice: BN) => boolean
798
794
  ): NodeToFill[] {
799
795
  const nodesToFill = new Array<NodeToFill>();
800
796
 
@@ -803,8 +799,7 @@ export class DLOB {
803
799
  marketIndex,
804
800
  slot,
805
801
  marketType,
806
- oraclePriceData,
807
- fallbackPrice
802
+ oraclePriceData
808
803
  );
809
804
 
810
805
  for (const makerNode of makerNodeGenerator) {
@@ -1714,8 +1709,6 @@ export class DLOB {
1714
1709
  * @param slot
1715
1710
  * @param oraclePriceData
1716
1711
  * @param depth how many levels of the order book to return
1717
- * @param fallbackAsk best ask for fallback liquidity, only relevant for perps
1718
- * @param fallbackBid best bid for fallback liquidity, only relevant for perps
1719
1712
  * @param fallbackL2Generators L2 generators for fallback liquidity e.g. vAMM {@link getVammL2Generator}, openbook {@link SerumSubscriber}
1720
1713
  */
1721
1714
  public getL2({
@@ -1724,8 +1717,6 @@ export class DLOB {
1724
1717
  slot,
1725
1718
  oraclePriceData,
1726
1719
  depth,
1727
- fallbackAsk,
1728
- fallbackBid,
1729
1720
  fallbackL2Generators = [],
1730
1721
  }: {
1731
1722
  marketIndex: number;
@@ -1733,18 +1724,10 @@ export class DLOB {
1733
1724
  slot: number;
1734
1725
  oraclePriceData: OraclePriceData;
1735
1726
  depth: number;
1736
- fallbackAsk?: BN;
1737
- fallbackBid?: BN;
1738
1727
  fallbackL2Generators?: L2OrderBookGenerator[];
1739
1728
  }): L2OrderBook {
1740
1729
  const makerAskL2LevelGenerator = getL2GeneratorFromDLOBNodes(
1741
- this.getMakerLimitAsks(
1742
- marketIndex,
1743
- slot,
1744
- marketType,
1745
- oraclePriceData,
1746
- fallbackBid
1747
- ),
1730
+ this.getRestingLimitAsks(marketIndex, slot, marketType, oraclePriceData),
1748
1731
  oraclePriceData,
1749
1732
  slot
1750
1733
  );
@@ -1765,13 +1748,7 @@ export class DLOB {
1765
1748
  const asks = createL2Levels(askL2LevelGenerator, depth);
1766
1749
 
1767
1750
  const makerBidGenerator = getL2GeneratorFromDLOBNodes(
1768
- this.getMakerLimitBids(
1769
- marketIndex,
1770
- slot,
1771
- marketType,
1772
- oraclePriceData,
1773
- fallbackAsk
1774
- ),
1751
+ this.getRestingLimitBids(marketIndex, slot, marketType, oraclePriceData),
1775
1752
  oraclePriceData,
1776
1753
  slot
1777
1754
  );
@@ -10,12 +10,12 @@ import {
10
10
  import { DriftClient } from '../driftClient';
11
11
  import { isVariant, MarketType } from '../types';
12
12
  import {
13
+ DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
13
14
  getVammL2Generator,
14
15
  L2OrderBook,
15
16
  L2OrderBookGenerator,
16
17
  L3OrderBook,
17
18
  } from './orderBookLevels';
18
- import { calculateAskPrice, calculateBidPrice } from '../math/market';
19
19
 
20
20
  export class DLOBSubscriber {
21
21
  driftClient: DriftClient;
@@ -103,8 +103,6 @@ export class DLOBSubscriber {
103
103
  }
104
104
 
105
105
  let oraclePriceData;
106
- let fallbackBid;
107
- let fallbackAsk;
108
106
  const isPerp = isVariant(marketType, 'perp');
109
107
  if (isPerp) {
110
108
  const perpMarketAccount =
@@ -112,19 +110,24 @@ export class DLOBSubscriber {
112
110
  oraclePriceData = this.driftClient.getOracleDataForPerpMarket(
113
111
  perpMarketAccount.marketIndex
114
112
  );
115
- fallbackBid = calculateBidPrice(perpMarketAccount, oraclePriceData);
116
- fallbackAsk = calculateAskPrice(perpMarketAccount, oraclePriceData);
117
113
  } else {
118
114
  oraclePriceData =
119
115
  this.driftClient.getOracleDataForSpotMarket(marketIndex);
120
116
  }
121
117
 
122
118
  if (isPerp && includeVamm) {
119
+ if (fallbackL2Generators.length > 0) {
120
+ throw new Error(
121
+ 'includeVamm can only be used if fallbackL2Generators is empty'
122
+ );
123
+ }
124
+
123
125
  fallbackL2Generators = [
124
126
  getVammL2Generator({
125
127
  marketAccount: this.driftClient.getPerpMarketAccount(marketIndex),
126
128
  oraclePriceData,
127
129
  numOrders: numVammOrders ?? depth,
130
+ topOfBookQuoteAmounts: DEFAULT_TOP_OF_BOOK_QUOTE_AMOUNTS,
128
131
  }),
129
132
  ];
130
133
  }
@@ -135,8 +138,6 @@ export class DLOBSubscriber {
135
138
  depth,
136
139
  oraclePriceData,
137
140
  slot: this.slotSource.getSlot(),
138
- fallbackBid,
139
- fallbackAsk,
140
141
  fallbackL2Generators: fallbackL2Generators,
141
142
  });
142
143
  }