@drift-labs/sdk 2.48.0-beta.2 → 2.48.0-beta.20

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 (50) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/bulkAccountLoader.js +1 -1
  3. package/lib/accounts/pollingUserAccountSubscriber.js +14 -7
  4. package/lib/accounts/pollingUserStatsAccountSubscriber.js +14 -7
  5. package/lib/accounts/webSocketAccountSubscriber.js +1 -1
  6. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -2
  7. package/lib/accounts/webSocketDriftClientAccountSubscriber.js +6 -5
  8. package/lib/accounts/webSocketProgramAccountSubscriber.js +1 -1
  9. package/lib/accounts/webSocketUserAccountSubscriber.js +1 -1
  10. package/lib/constants/spotMarkets.js +10 -0
  11. package/lib/dlob/orderBookLevels.js +1 -1
  12. package/lib/driftClient.js +4 -4
  13. package/lib/events/eventSubscriber.js +1 -1
  14. package/lib/events/types.d.ts +1 -0
  15. package/lib/events/webSocketLogProvider.d.ts +7 -1
  16. package/lib/events/webSocketLogProvider.js +45 -4
  17. package/lib/orderSubscriber/OrderSubscriber.d.ts +5 -1
  18. package/lib/orderSubscriber/OrderSubscriber.js +24 -7
  19. package/lib/orderSubscriber/WebsocketSubscription.d.ts +4 -1
  20. package/lib/orderSubscriber/WebsocketSubscription.js +4 -3
  21. package/lib/orderSubscriber/types.d.ts +3 -1
  22. package/lib/slot/SlotSubscriber.js +4 -2
  23. package/lib/user.js +3 -3
  24. package/lib/userMap/userMap.d.ts +16 -4
  25. package/lib/userMap/userMap.js +83 -41
  26. package/lib/userMap/userStatsMap.d.ts +29 -8
  27. package/lib/userMap/userStatsMap.js +46 -41
  28. package/package.json +1 -1
  29. package/src/accounts/bulkAccountLoader.ts +1 -1
  30. package/src/accounts/pollingUserAccountSubscriber.ts +19 -11
  31. package/src/accounts/pollingUserStatsAccountSubscriber.ts +20 -12
  32. package/src/accounts/webSocketAccountSubscriber.ts +1 -1
  33. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +13 -6
  34. package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
  35. package/src/accounts/webSocketUserAccountSubscriber.ts +1 -1
  36. package/src/constants/spotMarkets.ts +10 -0
  37. package/src/dlob/orderBookLevels.ts +1 -1
  38. package/src/driftClient.ts +3 -2
  39. package/src/events/eventSubscriber.ts +2 -1
  40. package/src/events/types.ts +1 -0
  41. package/src/events/webSocketLogProvider.ts +51 -4
  42. package/src/orderSubscriber/OrderSubscriber.ts +39 -15
  43. package/src/orderSubscriber/WebsocketSubscription.ts +7 -2
  44. package/src/orderSubscriber/types.ts +3 -1
  45. package/src/slot/SlotSubscriber.ts +4 -2
  46. package/src/user.ts +3 -3
  47. package/src/userMap/userMap.ts +139 -66
  48. package/src/userMap/userStatsMap.ts +64 -69
  49. package/tests/amm/test.ts +3 -2
  50. package/tests/dlob/test.ts +8 -5
@@ -7,14 +7,17 @@ const buffer_1 = require("buffer");
7
7
  const memcmp_1 = require("../memcmp");
8
8
  class UserMap {
9
9
  /**
10
+ * Constructs a new UserMap instance.
10
11
  *
11
- * @param driftClient
12
- * @param accountSubscription
13
- * @param includeIdle whether idle users are subscribed to. defaults to false to decrease # of user subscriptions
12
+ * @param {DriftClient} driftClient - The DriftClient instance.
13
+ * @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
14
+ * @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
15
+ * @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
16
+ * @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
14
17
  */
15
- constructor(driftClient, accountSubscription, includeIdle = false) {
18
+ constructor(driftClient, accountSubscription, includeIdle = false, syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
16
19
  this.userMap = new Map();
17
- this.syncCallback = async (state) => {
20
+ this.stateAccountUpdateCallback = async (state) => {
18
21
  if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
19
22
  await this.sync();
20
23
  this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
@@ -23,6 +26,12 @@ class UserMap {
23
26
  this.driftClient = driftClient;
24
27
  this.accountSubscription = accountSubscription;
25
28
  this.includeIdle = includeIdle;
29
+ this.syncCallback = syncCallback;
30
+ this.syncCallbackCriteria = syncCallbackCriteria;
31
+ }
32
+ addSyncCallback(syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
33
+ this.syncCallback = syncCallback;
34
+ this.syncCallbackCriteria = syncCallbackCriteria;
26
35
  }
27
36
  async subscribe() {
28
37
  if (this.size() > 0) {
@@ -31,7 +40,7 @@ class UserMap {
31
40
  await this.driftClient.subscribe();
32
41
  this.lastNumberOfSubAccounts =
33
42
  this.driftClient.getStateAccount().numberOfSubAccounts;
34
- this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
43
+ this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
35
44
  await this.sync();
36
45
  }
37
46
  async addPubkey(userAccountPublicKey, userAccount) {
@@ -139,45 +148,78 @@ class UserMap {
139
148
  size() {
140
149
  return this.userMap.size;
141
150
  }
151
+ getUniqueAuthorities(useSyncCallbackCriteria = true) {
152
+ const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
153
+ let pass = true;
154
+ if (useSyncCallbackCriteria &&
155
+ this.syncCallbackCriteria.hasOpenOrders) {
156
+ pass = pass && user.getUserAccount().hasOpenOrder;
157
+ }
158
+ return pass;
159
+ });
160
+ const userAuths = new Set(usersMeetingCriteria.map((user) => user.getUserAccount().authority.toBase58()));
161
+ const userAuthKeys = Array.from(userAuths).map((userAuth) => new web3_js_1.PublicKey(userAuth));
162
+ return userAuthKeys;
163
+ }
142
164
  async sync() {
143
- const filters = [(0, memcmp_1.getUserFilter)()];
144
- if (!this.includeIdle) {
145
- filters.push((0, memcmp_1.getNonIdleUserFilter)());
146
- }
147
- const rpcRequestArgs = [
148
- this.driftClient.program.programId.toBase58(),
149
- {
150
- commitment: this.driftClient.connection.commitment,
151
- filters,
152
- encoding: 'base64',
153
- withContext: true,
154
- },
155
- ];
156
- // @ts-ignore
157
- const rpcJSONResponse = await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
158
- const rpcResponseAndContext = rpcJSONResponse.result;
159
- const slot = rpcResponseAndContext.context.slot;
160
- const programAccountBufferMap = new Map();
161
- for (const programAccount of rpcResponseAndContext.value) {
162
- programAccountBufferMap.set(programAccount.pubkey.toString(),
163
- // @ts-ignore
164
- buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]));
165
+ if (this.syncPromise) {
166
+ return this.syncPromise;
165
167
  }
166
- for (const [key, buffer] of programAccountBufferMap.entries()) {
167
- if (!this.has(key)) {
168
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
169
- await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
168
+ this.syncPromise = new Promise((resolver) => {
169
+ this.syncPromiseResolver = resolver;
170
+ });
171
+ try {
172
+ const filters = [(0, memcmp_1.getUserFilter)()];
173
+ if (!this.includeIdle) {
174
+ filters.push((0, memcmp_1.getNonIdleUserFilter)());
170
175
  }
171
- }
172
- for (const [key, user] of this.userMap.entries()) {
173
- if (!programAccountBufferMap.has(key)) {
174
- await user.unsubscribe();
175
- this.userMap.delete(key);
176
+ const rpcRequestArgs = [
177
+ this.driftClient.program.programId.toBase58(),
178
+ {
179
+ commitment: this.driftClient.connection.commitment,
180
+ filters,
181
+ encoding: 'base64',
182
+ withContext: true,
183
+ },
184
+ ];
185
+ const rpcJSONResponse =
186
+ // @ts-ignore
187
+ await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
188
+ const rpcResponseAndContext = rpcJSONResponse.result;
189
+ const slot = rpcResponseAndContext.context.slot;
190
+ const programAccountBufferMap = new Map();
191
+ for (const programAccount of rpcResponseAndContext.value) {
192
+ programAccountBufferMap.set(programAccount.pubkey.toString(),
193
+ // @ts-ignore
194
+ buffer_1.Buffer.from(programAccount.account.data[0], programAccount.account.data[1]));
195
+ }
196
+ for (const [key, buffer] of programAccountBufferMap.entries()) {
197
+ if (!this.has(key)) {
198
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
199
+ await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
200
+ }
176
201
  }
177
- else {
178
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
179
- user.accountSubscriber.updateData(userAccount, slot);
202
+ for (const [key, user] of this.userMap.entries()) {
203
+ if (!programAccountBufferMap.has(key)) {
204
+ await user.unsubscribe();
205
+ this.userMap.delete(key);
206
+ }
207
+ else {
208
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
209
+ user.accountSubscriber.updateData(userAccount, slot);
210
+ }
180
211
  }
212
+ if (this.syncCallback) {
213
+ await this.syncCallback(this.getUniqueAuthorities());
214
+ }
215
+ }
216
+ catch (e) {
217
+ console.error(`Error in UserMap.sync()`);
218
+ console.error(e);
219
+ }
220
+ finally {
221
+ this.syncPromiseResolver();
222
+ this.syncPromise = undefined;
181
223
  }
182
224
  }
183
225
  async unsubscribe() {
@@ -186,7 +228,7 @@ class UserMap {
186
228
  this.userMap.delete(key);
187
229
  }
188
230
  if (this.lastNumberOfSubAccounts) {
189
- this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.syncCallback);
231
+ this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.stateAccountUpdateCallback);
190
232
  this.lastNumberOfSubAccounts = undefined;
191
233
  }
192
234
  }
@@ -1,4 +1,4 @@
1
- import { DriftClient, OrderRecord, UserStatsAccount, UserStats, UserStatsSubscriptionConfig, WrappedEvent } from '..';
1
+ import { DriftClient, OrderRecord, UserStatsAccount, UserStats, WrappedEvent, BulkAccountLoader } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
3
  import { UserMap } from './userMap';
4
4
  export declare class UserStatsMap {
@@ -7,19 +7,40 @@ export declare class UserStatsMap {
7
7
  */
8
8
  private userStatsMap;
9
9
  private driftClient;
10
- private accountSubscription;
11
- private lastNumberOfAuthorities;
12
- private syncCallback;
13
- constructor(driftClient: DriftClient, accountSubscription: UserStatsSubscriptionConfig);
14
- subscribe(): Promise<void>;
15
- addUserStat(authority: PublicKey, userStatsAccount?: UserStatsAccount): Promise<void>;
10
+ private bulkAccountLoader;
11
+ /**
12
+ * Creates a new UserStatsMap instance.
13
+ *
14
+ * @param {DriftClient} driftClient - The DriftClient instance.
15
+ * @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
16
+ */
17
+ constructor(driftClient: DriftClient, bulkAccountLoader?: BulkAccountLoader);
18
+ subscribe(authorities: PublicKey[]): Promise<void>;
19
+ /**
20
+ *
21
+ * @param authority that owns the UserStatsAccount
22
+ * @param userStatsAccount optional UserStatsAccount to subscribe to, if undefined will be fetched later
23
+ * @param skipFetch if true, will not immediately fetch the UserStatsAccount
24
+ */
25
+ addUserStat(authority: PublicKey, userStatsAccount?: UserStatsAccount, skipFetch?: boolean): Promise<void>;
16
26
  updateWithOrderRecord(record: OrderRecord, userMap: UserMap): Promise<void>;
17
27
  updateWithEventRecord(record: WrappedEvent<any>, userMap?: UserMap): Promise<void>;
18
28
  has(authorityPublicKey: string): boolean;
19
29
  get(authorityPublicKey: string): UserStats;
30
+ /**
31
+ * Enforce that a UserStats will exist for the given authorityPublicKey,
32
+ * reading one from the blockchain if necessary.
33
+ * @param authorityPublicKey
34
+ * @returns
35
+ */
20
36
  mustGet(authorityPublicKey: string): Promise<UserStats>;
21
37
  values(): IterableIterator<UserStats>;
22
38
  size(): number;
23
- sync(): Promise<void>;
39
+ /**
40
+ * Sync the UserStatsMap
41
+ * @param authorities list of authorities to derive UserStatsAccount public keys from.
42
+ * You may want to get this list from UserMap in order to filter out idle users
43
+ */
44
+ sync(authorities: PublicKey[]): Promise<void>;
24
45
  unsubscribe(): Promise<void>;
25
46
  }
@@ -4,43 +4,57 @@ exports.UserStatsMap = void 0;
4
4
  const __1 = require("..");
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  class UserStatsMap {
7
- constructor(driftClient, accountSubscription) {
7
+ /**
8
+ * Creates a new UserStatsMap instance.
9
+ *
10
+ * @param {DriftClient} driftClient - The DriftClient instance.
11
+ * @param {BulkAccountLoader} [bulkAccountLoader] - If not provided, a new BulkAccountLoader with polling disabled will be created.
12
+ */
13
+ constructor(driftClient, bulkAccountLoader) {
8
14
  /**
9
15
  * map from authority pubkey to UserStats
10
16
  */
11
17
  this.userStatsMap = new Map();
12
- this.syncCallback = async (state) => {
13
- if (state.numberOfAuthorities !== this.lastNumberOfAuthorities) {
14
- await this.sync();
15
- this.lastNumberOfAuthorities = state.numberOfAuthorities;
16
- }
17
- };
18
18
  this.driftClient = driftClient;
19
- this.accountSubscription = accountSubscription;
19
+ if (!bulkAccountLoader) {
20
+ bulkAccountLoader = new __1.BulkAccountLoader(driftClient.connection, driftClient.opts.commitment, 0);
21
+ }
22
+ this.bulkAccountLoader = bulkAccountLoader;
20
23
  }
21
- async subscribe() {
24
+ async subscribe(authorities) {
22
25
  if (this.size() > 0) {
23
26
  return;
24
27
  }
25
28
  await this.driftClient.subscribe();
26
- this.lastNumberOfAuthorities =
27
- this.driftClient.getStateAccount().numberOfAuthorities;
28
- this.driftClient.eventEmitter.on('stateAccountUpdate', this.syncCallback);
29
- await this.sync();
29
+ await this.sync(authorities);
30
30
  }
31
- async addUserStat(authority, userStatsAccount) {
31
+ /**
32
+ *
33
+ * @param authority that owns the UserStatsAccount
34
+ * @param userStatsAccount optional UserStatsAccount to subscribe to, if undefined will be fetched later
35
+ * @param skipFetch if true, will not immediately fetch the UserStatsAccount
36
+ */
37
+ async addUserStat(authority, userStatsAccount, skipFetch) {
32
38
  const userStat = new __1.UserStats({
33
39
  driftClient: this.driftClient,
34
40
  userStatsAccountPublicKey: (0, __1.getUserStatsAccountPublicKey)(this.driftClient.program.programId, authority),
35
- accountSubscription: this.accountSubscription,
41
+ accountSubscription: {
42
+ type: 'polling',
43
+ accountLoader: this.bulkAccountLoader,
44
+ },
36
45
  });
37
- await userStat.subscribe(userStatsAccount);
46
+ if (skipFetch) {
47
+ await userStat.accountSubscriber.addToAccountLoader();
48
+ }
49
+ else {
50
+ await userStat.subscribe(userStatsAccount);
51
+ }
38
52
  this.userStatsMap.set(authority.toString(), userStat);
39
53
  }
40
54
  async updateWithOrderRecord(record, userMap) {
41
55
  const user = await userMap.mustGet(record.user.toString());
42
56
  if (!this.has(user.getUserAccount().authority.toString())) {
43
- await this.addUserStat(user.getUserAccount().authority);
57
+ await this.addUserStat(user.getUserAccount().authority, undefined, false);
44
58
  }
45
59
  }
46
60
  async updateWithEventRecord(record, userMap) {
@@ -114,9 +128,15 @@ class UserStatsMap {
114
128
  get(authorityPublicKey) {
115
129
  return this.userStatsMap.get(authorityPublicKey);
116
130
  }
131
+ /**
132
+ * Enforce that a UserStats will exist for the given authorityPublicKey,
133
+ * reading one from the blockchain if necessary.
134
+ * @param authorityPublicKey
135
+ * @returns
136
+ */
117
137
  async mustGet(authorityPublicKey) {
118
138
  if (!this.has(authorityPublicKey)) {
119
- await this.addUserStat(new web3_js_1.PublicKey(authorityPublicKey));
139
+ await this.addUserStat(new web3_js_1.PublicKey(authorityPublicKey), undefined, false);
120
140
  }
121
141
  return this.get(authorityPublicKey);
122
142
  }
@@ -126,35 +146,20 @@ class UserStatsMap {
126
146
  size() {
127
147
  return this.userStatsMap.size;
128
148
  }
129
- async sync() {
130
- const programAccounts = await this.driftClient.connection.getProgramAccounts(this.driftClient.program.programId, {
131
- commitment: this.driftClient.connection.commitment,
132
- filters: [
133
- {
134
- memcmp: this.driftClient.program.coder.accounts.memcmp('UserStats'),
135
- },
136
- ],
137
- });
138
- const programAccountMap = new Map();
139
- for (const programAccount of programAccounts) {
140
- programAccountMap.set(new web3_js_1.PublicKey(programAccount.account.data.slice(8, 40)).toString(), programAccount.account);
141
- }
142
- for (const key of programAccountMap.keys()) {
143
- if (!this.has(key)) {
144
- const userStatsAccount = this.driftClient.program.account.userStats.coder.accounts.decode('UserStats', programAccountMap.get(key).data);
145
- await this.addUserStat(new web3_js_1.PublicKey(key), userStatsAccount);
146
- }
147
- }
149
+ /**
150
+ * Sync the UserStatsMap
151
+ * @param authorities list of authorities to derive UserStatsAccount public keys from.
152
+ * You may want to get this list from UserMap in order to filter out idle users
153
+ */
154
+ async sync(authorities) {
155
+ await Promise.all(authorities.map((authority) => this.addUserStat(authority, undefined, true)));
156
+ await this.bulkAccountLoader.load();
148
157
  }
149
158
  async unsubscribe() {
150
159
  for (const [key, userStats] of this.userStatsMap.entries()) {
151
160
  await userStats.unsubscribe();
152
161
  this.userStatsMap.delete(key);
153
162
  }
154
- if (this.lastNumberOfAuthorities) {
155
- this.driftClient.eventEmitter.removeListener('stateAccountUpdate', this.syncCallback);
156
- this.lastNumberOfAuthorities = undefined;
157
- }
158
163
  }
159
164
  }
160
165
  exports.UserStatsMap = UserStatsMap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.48.0-beta.2",
3
+ "version": "2.48.0-beta.20",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -193,7 +193,7 @@ export class BulkAccountLoader {
193
193
  const key = accountToLoad.publicKey.toBase58();
194
194
  const oldRPCResponse = this.bufferAndSlotMap.get(key);
195
195
 
196
- if (oldRPCResponse && newSlot <= oldRPCResponse.slot) {
196
+ if (oldRPCResponse && newSlot < oldRPCResponse.slot) {
197
197
  return;
198
198
  }
199
199
 
@@ -93,17 +93,21 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
93
93
  }
94
94
 
95
95
  async fetch(): Promise<void> {
96
- await this.accountLoader.load();
97
- const { buffer, slot } = this.accountLoader.getBufferAndSlot(
98
- this.userAccountPublicKey
99
- );
100
- const currentSlot = this.user?.slot ?? 0;
101
- if (buffer && slot > currentSlot) {
102
- const account = this.program.account.user.coder.accounts.decode(
103
- 'User',
104
- buffer
96
+ try {
97
+ const dataAndContext = await this.program.account.user.fetchAndContext(
98
+ this.userAccountPublicKey,
99
+ this.accountLoader.commitment
100
+ );
101
+ if (dataAndContext.context.slot > (this.user?.slot ?? 0)) {
102
+ this.user = {
103
+ data: dataAndContext.data as UserAccount,
104
+ slot: dataAndContext.context.slot,
105
+ };
106
+ }
107
+ } catch (e) {
108
+ console.log(
109
+ `PollingUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}`
105
110
  );
106
- this.user = { data: account, slot };
107
111
  }
108
112
  }
109
113
 
@@ -137,7 +141,11 @@ export class PollingUserAccountSubscriber implements UserAccountSubscriber {
137
141
  }
138
142
 
139
143
  public getUserAccountAndSlot(): DataAndSlot<UserAccount> {
140
- this.assertIsSubscribed();
144
+ if (!this.doesAccountExist()) {
145
+ throw new NotSubscribedError(
146
+ 'You must call `subscribe` or `fetch` before using this function'
147
+ );
148
+ }
141
149
  return this.user;
142
150
  }
143
151
 
@@ -97,18 +97,22 @@ export class PollingUserStatsAccountSubscriber
97
97
  }
98
98
 
99
99
  async fetch(): Promise<void> {
100
- await this.accountLoader.load();
101
- const { buffer, slot } = this.accountLoader.getBufferAndSlot(
102
- this.userStatsAccountPublicKey
103
- );
104
- const currentSlot = this.userStats?.slot ?? 0;
105
- if (buffer && slot > currentSlot) {
106
- const account =
107
- this.program.account.userStats.coder.accounts.decodeUnchecked(
108
- 'UserStats',
109
- buffer
100
+ try {
101
+ const dataAndContext =
102
+ await this.program.account.userStats.fetchAndContext(
103
+ this.userStatsAccountPublicKey,
104
+ this.accountLoader.commitment
110
105
  );
111
- this.userStats = { data: account, slot };
106
+ if (dataAndContext.context.slot > (this.userStats?.slot ?? 0)) {
107
+ this.userStats = {
108
+ data: dataAndContext.data as UserStatsAccount,
109
+ slot: dataAndContext.context.slot,
110
+ };
111
+ }
112
+ } catch (e) {
113
+ console.log(
114
+ `PollingUserStatsAccountSubscriber.fetch() UserStatsAccount does not exist: ${e.message}`
115
+ );
112
116
  }
113
117
  }
114
118
 
@@ -142,7 +146,11 @@ export class PollingUserStatsAccountSubscriber
142
146
  }
143
147
 
144
148
  public getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount> {
145
- this.assertIsSubscribed();
149
+ if (!this.doesAccountExist()) {
150
+ throw new NotSubscribedError(
151
+ 'You must call `subscribe` or `fetch` before using this function'
152
+ );
153
+ }
146
154
  return this.userStats;
147
155
  }
148
156
  }
@@ -134,7 +134,7 @@ export class WebSocketAccountSubscriber<T> implements AccountSubscriber<T> {
134
134
  return;
135
135
  }
136
136
 
137
- if (newSlot <= this.bufferAndSlot.slot) {
137
+ if (newSlot < this.bufferAndSlot.slot) {
138
138
  return;
139
139
  }
140
140
 
@@ -14,7 +14,7 @@ import {
14
14
  getPerpMarketPublicKey,
15
15
  } from '../addresses/pda';
16
16
  import { WebSocketAccountSubscriber } from './webSocketAccountSubscriber';
17
- import { PublicKey } from '@solana/web3.js';
17
+ import { Commitment, PublicKey } from '@solana/web3.js';
18
18
  import { OracleInfo, OraclePriceData } from '../oracles/types';
19
19
  import { OracleClientCache } from '../oracles/oracleClientCache';
20
20
  import * as Buffer from 'buffer';
@@ -26,6 +26,7 @@ export class WebSocketDriftClientAccountSubscriber
26
26
  {
27
27
  isSubscribed: boolean;
28
28
  program: Program;
29
+ commitment?: Commitment;
29
30
  perpMarketIndexes: number[];
30
31
  spotMarketIndexes: number[];
31
32
  oracleInfos: OracleInfo[];
@@ -56,7 +57,8 @@ export class WebSocketDriftClientAccountSubscriber
56
57
  spotMarketIndexes: number[],
57
58
  oracleInfos: OracleInfo[],
58
59
  shouldFindAllMarketsAndOracles: boolean,
59
- resubTimeoutMs?: number
60
+ resubTimeoutMs?: number,
61
+ commitment?: Commitment
60
62
  ) {
61
63
  this.isSubscribed = false;
62
64
  this.program = program;
@@ -66,6 +68,7 @@ export class WebSocketDriftClientAccountSubscriber
66
68
  this.oracleInfos = oracleInfos;
67
69
  this.shouldFindAllMarketsAndOracles = shouldFindAllMarketsAndOracles;
68
70
  this.resubTimeoutMs = resubTimeoutMs;
71
+ this.commitment = commitment;
69
72
  }
70
73
 
71
74
  public async subscribe(): Promise<boolean> {
@@ -101,7 +104,8 @@ export class WebSocketDriftClientAccountSubscriber
101
104
  this.program,
102
105
  statePublicKey,
103
106
  undefined,
104
- this.resubTimeoutMs
107
+ this.resubTimeoutMs,
108
+ this.commitment
105
109
  );
106
110
  await this.stateAccountSubscriber.subscribe((data: StateAccount) => {
107
111
  this.eventEmitter.emit('stateAccountUpdate', data);
@@ -143,7 +147,8 @@ export class WebSocketDriftClientAccountSubscriber
143
147
  this.program,
144
148
  perpMarketPublicKey,
145
149
  undefined,
146
- this.resubTimeoutMs
150
+ this.resubTimeoutMs,
151
+ this.commitment
147
152
  );
148
153
  await accountSubscriber.subscribe((data: PerpMarketAccount) => {
149
154
  this.eventEmitter.emit('perpMarketAccountUpdate', data);
@@ -170,7 +175,8 @@ export class WebSocketDriftClientAccountSubscriber
170
175
  this.program,
171
176
  marketPublicKey,
172
177
  undefined,
173
- this.resubTimeoutMs
178
+ this.resubTimeoutMs,
179
+ this.commitment
174
180
  );
175
181
  await accountSubscriber.subscribe((data: SpotMarketAccount) => {
176
182
  this.eventEmitter.emit('spotMarketAccountUpdate', data);
@@ -202,7 +208,8 @@ export class WebSocketDriftClientAccountSubscriber
202
208
  (buffer: Buffer) => {
203
209
  return client.getOraclePriceDataFromBuffer(buffer);
204
210
  },
205
- this.resubTimeoutMs
211
+ this.resubTimeoutMs,
212
+ this.commitment
206
213
  );
207
214
 
208
215
  await accountSubscriber.subscribe((data: OraclePriceData) => {
@@ -125,7 +125,7 @@ export class WebSocketProgramAccountSubscriber<T>
125
125
  return;
126
126
  }
127
127
 
128
- if (newSlot <= this.bufferAndSlot.slot) {
128
+ if (newSlot < this.bufferAndSlot.slot) {
129
129
  return;
130
130
  }
131
131
 
@@ -94,7 +94,7 @@ export class WebSocketUserAccountSubscriber implements UserAccountSubscriber {
94
94
  public updateData(userAccount: UserAccount, slot: number) {
95
95
  const currentDataSlot =
96
96
  this.userDataAccountSubscriber.dataAndSlot?.slot || 0;
97
- if (currentDataSlot < slot) {
97
+ if (currentDataSlot <= slot) {
98
98
  this.userDataAccountSubscriber.setData(userAccount, slot);
99
99
  this.eventEmitter.emit('userAccountUpdate', userAccount);
100
100
  this.eventEmitter.emit('update');
@@ -150,6 +150,16 @@ export const MainnetSpotMarkets: SpotMarketConfig[] = [
150
150
  '2sTMN9A1D1qeZLF95XQgJCUPiKe5DiV52jLfZGqMP46m'
151
151
  ),
152
152
  },
153
+ {
154
+ symbol: 'bSOL',
155
+ marketIndex: 8,
156
+ oracle: new PublicKey('AFrYBhb5wKQtxRS9UA9YRS4V3dwFm7SqmS6DHKq6YVgo'),
157
+ oracleSource: OracleSource.PYTH,
158
+ mint: new PublicKey('bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1'),
159
+ precision: new BN(10).pow(NINE),
160
+ precisionExp: NINE,
161
+ serumMarket: new PublicKey('ARjaHVxGCQfTvvKjLd7U7srvk6orthZSE6uqWchCczZc'),
162
+ },
153
163
  ];
154
164
 
155
165
  export const SpotMarkets: { [key in DriftEnv]: SpotMarketConfig[] } = {
@@ -173,7 +173,7 @@ export function getVammL2Generator({
173
173
  updatedAmm.orderStepSize
174
174
  );
175
175
 
176
- const minOrderSize = marketAccount.amm.orderStepSize;
176
+ const minOrderSize = marketAccount.amm.minOrderSize;
177
177
  if (openBids.lt(minOrderSize.muln(2))) {
178
178
  openBids = ZERO;
179
179
  }
@@ -283,7 +283,8 @@ export class DriftClient {
283
283
  config.spotMarketIndexes ?? [],
284
284
  config.oracleInfos ?? [],
285
285
  noMarketsAndOraclesSpecified,
286
- config.accountSubscription?.resubTimeoutMs
286
+ config.accountSubscription?.resubTimeoutMs,
287
+ config.accountSubscription?.commitment
287
288
  );
288
289
  }
289
290
  this.eventEmitter = this.accountSubscriber.eventEmitter;
@@ -2028,7 +2029,7 @@ export class DriftClient {
2028
2029
  }
2029
2030
 
2030
2031
  const tx = await this.buildTransaction(withdrawIxs, {
2031
- computeUnits: 600_000,
2032
+ computeUnits: 1_400_000,
2032
2033
  });
2033
2034
  const { txSig, slot } = await this.sendTransaction(
2034
2035
  tx,
@@ -56,7 +56,8 @@ export class EventSubscriber {
56
56
  this.logProvider = new WebSocketLogProvider(
57
57
  this.connection,
58
58
  this.address,
59
- this.options.commitment
59
+ this.options.commitment,
60
+ this.options.resubTimeoutMs
60
61
  );
61
62
  } else {
62
63
  this.logProvider = new PollingLogProvider(
@@ -28,6 +28,7 @@ export type EventSubscriptionOptions = {
28
28
  // when the subscription starts, client might want to backtrack and fetch old tx's
29
29
  // this specifies how far to backtrack
30
30
  untilTx?: TransactionSignature;
31
+ resubTimeoutMs?: number;
31
32
  };
32
33
 
33
34
  export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {