@drift-labs/sdk 2.49.0-beta.1 → 2.49.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 (51) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/{mockUserAccountSubscriber.d.ts → basicUserAccountSubscriber.d.ts} +2 -2
  3. package/lib/accounts/{mockUserAccountSubscriber.js → basicUserAccountSubscriber.js} +9 -6
  4. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.d.ts +29 -0
  5. package/lib/accounts/pollingInsuranceFundStakeAccountSubscriber.js +110 -0
  6. package/lib/accounts/types.d.ts +14 -1
  7. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.d.ts +23 -0
  8. package/lib/accounts/webSocketInsuranceFundStakeAccountSubscriber.js +65 -0
  9. package/lib/dlob/DLOB.d.ts +6 -2
  10. package/lib/dlob/DLOB.js +37 -12
  11. package/lib/driftClient.d.ts +66 -66
  12. package/lib/driftClient.js +208 -194
  13. package/lib/events/eventSubscriber.js +2 -1
  14. package/lib/events/sort.d.ts +2 -2
  15. package/lib/events/sort.js +6 -23
  16. package/lib/examples/loadDlob.js +10 -5
  17. package/lib/index.d.ts +3 -1
  18. package/lib/index.js +3 -1
  19. package/lib/orderSubscriber/OrderSubscriber.js +4 -0
  20. package/lib/orderSubscriber/WebsocketSubscription.d.ts +1 -1
  21. package/lib/orderSubscriber/WebsocketSubscription.js +8 -6
  22. package/lib/types.d.ts +0 -2
  23. package/lib/userMap/PollingSubscription.d.ts +15 -0
  24. package/lib/userMap/PollingSubscription.js +26 -0
  25. package/lib/userMap/WebsocketSubscription.d.ts +19 -0
  26. package/lib/userMap/WebsocketSubscription.js +40 -0
  27. package/lib/userMap/userMap.d.ts +15 -18
  28. package/lib/userMap/userMap.js +62 -31
  29. package/lib/userMap/userMapConfig.d.ts +20 -0
  30. package/lib/userMap/userMapConfig.js +2 -0
  31. package/package.json +1 -1
  32. package/src/accounts/{mockUserAccountSubscriber.ts → basicUserAccountSubscriber.ts} +8 -6
  33. package/src/accounts/pollingInsuranceFundStakeAccountSubscriber.ts +185 -0
  34. package/src/accounts/types.ts +21 -0
  35. package/src/accounts/webSocketInsuranceFundStakeAccountSubscriber.ts +127 -0
  36. package/src/dlob/DLOB.ts +55 -15
  37. package/src/driftClient.ts +429 -272
  38. package/src/events/eventSubscriber.ts +2 -1
  39. package/src/events/sort.ts +7 -29
  40. package/src/examples/loadDlob.ts +11 -6
  41. package/src/index.ts +3 -1
  42. package/src/orderSubscriber/OrderSubscriber.ts +4 -0
  43. package/src/orderSubscriber/WebsocketSubscription.ts +19 -16
  44. package/src/types.ts +0 -2
  45. package/src/userMap/PollingSubscription.ts +46 -0
  46. package/src/userMap/WebsocketSubscription.ts +74 -0
  47. package/src/userMap/userMap.ts +88 -60
  48. package/src/userMap/userMapConfig.ts +31 -0
  49. package/tests/amm/test.ts +6 -3
  50. package/tests/dlob/helpers.ts +2 -6
  51. package/tests/dlob/test.ts +194 -0
@@ -23,7 +23,7 @@ class EventSubscriber {
23
23
  this.txEventCache = new txEventCache_1.TxEventCache(this.options.maxTx);
24
24
  this.eventListMap = new Map();
25
25
  for (const eventType of this.options.eventTypes) {
26
- this.eventListMap.set(eventType, new eventList_1.EventList(eventType, this.options.maxEventsPerType, (0, sort_1.getSortFn)(this.options.orderBy, this.options.orderDir, eventType), this.options.orderDir));
26
+ this.eventListMap.set(eventType, new eventList_1.EventList(eventType, this.options.maxEventsPerType, (0, sort_1.getSortFn)(this.options.orderBy, this.options.orderDir), this.options.orderDir));
27
27
  }
28
28
  this.eventEmitter = new events_1.EventEmitter();
29
29
  if (this.options.logProviderConfig.type === 'websocket') {
@@ -89,6 +89,7 @@ class EventSubscriber {
89
89
  }
90
90
  if (!this.lastSeenSlot || slot > this.lastSeenSlot) {
91
91
  this.lastSeenTxSig = txSig;
92
+ this.lastSeenSlot = slot;
92
93
  }
93
94
  if (this.lastSeenBlockTime === undefined ||
94
95
  mostRecentBlockTime > this.lastSeenBlockTime) {
@@ -1,2 +1,2 @@
1
- import { EventSubscriptionOrderBy, EventSubscriptionOrderDirection, EventType, SortFn } from './types';
2
- export declare function getSortFn(orderBy: EventSubscriptionOrderBy, orderDir: EventSubscriptionOrderDirection, eventType: EventType): SortFn;
1
+ import { EventSubscriptionOrderBy, EventSubscriptionOrderDirection, SortFn } from './types';
2
+ export declare function getSortFn(orderBy: EventSubscriptionOrderBy, orderDir: EventSubscriptionOrderDirection): SortFn;
@@ -1,41 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getSortFn = void 0;
4
- const index_1 = require("../index");
5
4
  function clientSortAscFn() {
6
5
  return 'less than';
7
6
  }
8
7
  function clientSortDescFn() {
9
8
  return 'greater than';
10
9
  }
11
- function defaultBlockchainSortFn(currentEvent, newEvent) {
12
- return currentEvent.slot <= newEvent.slot ? 'less than' : 'greater than';
13
- }
14
- function orderActionRecordSortFn(currentEvent, newEvent) {
15
- var _a, _b;
16
- const currentEventMarketIndex = currentEvent.marketIndex;
17
- const newEventMarketIndex = newEvent.marketIndex;
18
- if (currentEventMarketIndex !== newEventMarketIndex) {
19
- return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
20
- }
21
- if (((_a = currentEvent.fillRecordId) === null || _a === void 0 ? void 0 : _a.gt(index_1.ZERO)) && ((_b = newEvent.fillRecordId) === null || _b === void 0 ? void 0 : _b.gt(index_1.ZERO))) {
22
- return currentEvent.fillRecordId.lte(newEvent.fillRecordId)
10
+ function blockchainSortFn(currentEvent, newEvent) {
11
+ if (currentEvent.slot == newEvent.slot) {
12
+ return currentEvent.txSigIndex < newEvent.txSigIndex
23
13
  ? 'less than'
24
14
  : 'greater than';
25
15
  }
26
- else {
27
- return currentEvent.ts.lte(newEvent.ts) ? 'less than' : 'greater than';
28
- }
16
+ return currentEvent.slot < newEvent.slot ? 'less than' : 'greater than';
29
17
  }
30
- function getSortFn(orderBy, orderDir, eventType) {
18
+ function getSortFn(orderBy, orderDir) {
31
19
  if (orderBy === 'client') {
32
20
  return orderDir === 'asc' ? clientSortAscFn : clientSortDescFn;
33
21
  }
34
- switch (eventType) {
35
- case 'OrderActionRecord':
36
- return orderActionRecordSortFn;
37
- default:
38
- return defaultBlockchainSortFn;
39
- }
22
+ return blockchainSortFn;
40
23
  }
41
24
  exports.getSortFn = getSortFn;
@@ -35,15 +35,20 @@ const main = async () => {
35
35
  console.log('Subscribing drift client...');
36
36
  await driftClient.subscribe();
37
37
  console.log('Loading user map...');
38
- const userMap = new __1.UserMap(driftClient, {
39
- type: 'polling',
40
- accountLoader: bulkAccountLoader,
38
+ const userMap = new __1.UserMap({
39
+ driftClient,
40
+ subscriptionConfig: {
41
+ type: 'websocket',
42
+ commitment: 'processed',
43
+ },
44
+ skipInitialLoad: false,
45
+ includeIdle: false,
41
46
  });
42
47
  // fetches all users and subscribes for updates
43
48
  await userMap.subscribe();
44
49
  console.log('Loading dlob from user map...');
45
- const dlob = new __1.DLOB();
46
- await dlob.initFromUserMap(userMap, bulkAccountLoader.mostRecentSlot);
50
+ const slot = await driftClient.connection.getSlot();
51
+ const dlob = await userMap.getDLOB(slot);
47
52
  console.log('number of orders', dlob.getDLOBOrders().length);
48
53
  dlob.clear();
49
54
  console.log('Unsubscribing users...');
package/lib/index.d.ts CHANGED
@@ -9,6 +9,7 @@ export * from './types';
9
9
  export * from './constants/perpMarkets';
10
10
  export * from './accounts/fetch';
11
11
  export * from './accounts/webSocketDriftClientAccountSubscriber';
12
+ export * from './accounts/webSocketInsuranceFundStakeAccountSubscriber';
12
13
  export * from './accounts/bulkAccountLoader';
13
14
  export * from './accounts/bulkUserSubscription';
14
15
  export * from './accounts/bulkUserStatsSubscription';
@@ -17,7 +18,8 @@ export * from './accounts/pollingOracleAccountSubscriber';
17
18
  export * from './accounts/pollingTokenAccountSubscriber';
18
19
  export * from './accounts/pollingUserAccountSubscriber';
19
20
  export * from './accounts/pollingUserStatsAccountSubscriber';
20
- export * from './accounts/mockUserAccountSubscriber';
21
+ export * from './accounts/pollingInsuranceFundStakeAccountSubscriber';
22
+ export * from './accounts/basicUserAccountSubscriber';
21
23
  export * from './accounts/types';
22
24
  export * from './addresses/pda';
23
25
  export * from './adminClient';
package/lib/index.js CHANGED
@@ -32,6 +32,7 @@ __exportStar(require("./types"), exports);
32
32
  __exportStar(require("./constants/perpMarkets"), exports);
33
33
  __exportStar(require("./accounts/fetch"), exports);
34
34
  __exportStar(require("./accounts/webSocketDriftClientAccountSubscriber"), exports);
35
+ __exportStar(require("./accounts/webSocketInsuranceFundStakeAccountSubscriber"), exports);
35
36
  __exportStar(require("./accounts/bulkAccountLoader"), exports);
36
37
  __exportStar(require("./accounts/bulkUserSubscription"), exports);
37
38
  __exportStar(require("./accounts/bulkUserStatsSubscription"), exports);
@@ -40,7 +41,8 @@ __exportStar(require("./accounts/pollingOracleAccountSubscriber"), exports);
40
41
  __exportStar(require("./accounts/pollingTokenAccountSubscriber"), exports);
41
42
  __exportStar(require("./accounts/pollingUserAccountSubscriber"), exports);
42
43
  __exportStar(require("./accounts/pollingUserStatsAccountSubscriber"), exports);
43
- __exportStar(require("./accounts/mockUserAccountSubscriber"), exports);
44
+ __exportStar(require("./accounts/pollingInsuranceFundStakeAccountSubscriber"), exports);
45
+ __exportStar(require("./accounts/basicUserAccountSubscriber"), exports);
44
46
  __exportStar(require("./accounts/types"), exports);
45
47
  __exportStar(require("./addresses/pda"), exports);
46
48
  __exportStar(require("./adminClient"), exports);
@@ -60,11 +60,15 @@ class OrderSubscriber {
60
60
  const key = programAccount.pubkey.toString();
61
61
  programAccountSet.add(key);
62
62
  this.tryUpdateUserAccount(key, 'raw', programAccount.account.data, slot);
63
+ // give event loop a chance to breathe
64
+ await new Promise((resolve) => setTimeout(resolve, 0));
63
65
  }
64
66
  for (const key of this.usersAccounts.keys()) {
65
67
  if (!programAccountSet.has(key)) {
66
68
  this.usersAccounts.delete(key);
67
69
  }
70
+ // give event loop a chance to breathe
71
+ await new Promise((resolve) => setTimeout(resolve, 0));
68
72
  }
69
73
  }
70
74
  catch (e) {
@@ -5,7 +5,7 @@ export declare class WebsocketSubscription {
5
5
  private commitment;
6
6
  private skipInitialLoad;
7
7
  private resubTimeoutMs?;
8
- private subscriber;
8
+ private subscriber?;
9
9
  constructor({ orderSubscriber, commitment, skipInitialLoad, resubTimeoutMs, }: {
10
10
  orderSubscriber: OrderSubscriber;
11
11
  commitment: Commitment;
@@ -11,12 +11,13 @@ class WebsocketSubscription {
11
11
  this.resubTimeoutMs = resubTimeoutMs;
12
12
  }
13
13
  async subscribe() {
14
- if (!this.subscriber) {
15
- this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, this.orderSubscriber.driftClient.program.account.user.coder.accounts.decode.bind(this.orderSubscriber.driftClient.program.account.user.coder.accounts), {
16
- filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
17
- commitment: this.commitment,
18
- }, this.resubTimeoutMs);
14
+ if (this.subscriber) {
15
+ return;
19
16
  }
17
+ this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('OrderSubscriber', 'User', this.orderSubscriber.driftClient.program, this.orderSubscriber.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.orderSubscriber.driftClient.program.account.user.coder.accounts), {
18
+ filters: [(0, memcmp_1.getUserFilter)(), (0, memcmp_1.getNonIdleUserFilter)()],
19
+ commitment: this.commitment,
20
+ }, this.resubTimeoutMs);
20
21
  await this.subscriber.subscribe((accountId, account, context) => {
21
22
  const userKey = accountId.toBase58();
22
23
  this.orderSubscriber.tryUpdateUserAccount(userKey, 'decoded', account, context.slot);
@@ -28,7 +29,8 @@ class WebsocketSubscription {
28
29
  async unsubscribe() {
29
30
  if (!this.subscriber)
30
31
  return;
31
- this.subscriber.unsubscribe();
32
+ await this.subscriber.unsubscribe();
33
+ this.subscriber = undefined;
32
34
  }
33
35
  }
34
36
  exports.WebsocketSubscription = WebsocketSubscription;
package/lib/types.d.ts CHANGED
@@ -1025,8 +1025,6 @@ export interface IVersionedWallet {
1025
1025
  }
1026
1026
  export type FeeStructure = {
1027
1027
  feeTiers: FeeTier[];
1028
- makerRebateNumerator: BN;
1029
- makerRebateDenominator: BN;
1030
1028
  fillerRewardStructure: OrderFillerRewardStructure;
1031
1029
  flatFillerFee: BN;
1032
1030
  referrerRewardEpochUpperBound: BN;
@@ -0,0 +1,15 @@
1
+ import { UserMap } from './userMap';
2
+ export declare class PollingSubscription {
3
+ private userMap;
4
+ private frequency;
5
+ private skipInitialLoad;
6
+ intervalId?: ReturnType<typeof setTimeout>;
7
+ constructor({ userMap, frequency, skipInitialLoad, }: {
8
+ userMap: UserMap;
9
+ frequency: number;
10
+ skipInitialLoad?: boolean;
11
+ includeIdle?: boolean;
12
+ });
13
+ subscribe(): Promise<void>;
14
+ unsubscribe(): Promise<void>;
15
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PollingSubscription = void 0;
4
+ class PollingSubscription {
5
+ constructor({ userMap, frequency, skipInitialLoad = false, }) {
6
+ this.userMap = userMap;
7
+ this.frequency = frequency;
8
+ this.skipInitialLoad = skipInitialLoad;
9
+ }
10
+ async subscribe() {
11
+ if (this.intervalId) {
12
+ return;
13
+ }
14
+ this.intervalId = setInterval(this.userMap.sync.bind(this.userMap), this.frequency);
15
+ if (!this.skipInitialLoad) {
16
+ await this.userMap.sync();
17
+ }
18
+ }
19
+ async unsubscribe() {
20
+ if (this.intervalId) {
21
+ clearInterval(this.intervalId);
22
+ this.intervalId = undefined;
23
+ }
24
+ }
25
+ }
26
+ exports.PollingSubscription = PollingSubscription;
@@ -0,0 +1,19 @@
1
+ import { UserMap } from './userMap';
2
+ import { Commitment } from '@solana/web3.js';
3
+ export declare class WebsocketSubscription {
4
+ private userMap;
5
+ private commitment;
6
+ private skipInitialLoad;
7
+ private resubTimeoutMs?;
8
+ private includeIdle?;
9
+ private subscriber;
10
+ constructor({ userMap, commitment, skipInitialLoad, resubTimeoutMs, includeIdle, }: {
11
+ userMap: UserMap;
12
+ commitment: Commitment;
13
+ skipInitialLoad?: boolean;
14
+ resubTimeoutMs?: number;
15
+ includeIdle?: boolean;
16
+ });
17
+ subscribe(): Promise<void>;
18
+ unsubscribe(): Promise<void>;
19
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebsocketSubscription = void 0;
4
+ const memcmp_1 = require("../memcmp");
5
+ const webSocketProgramAccountSubscriber_1 = require("../accounts/webSocketProgramAccountSubscriber");
6
+ class WebsocketSubscription {
7
+ constructor({ userMap, commitment, skipInitialLoad = false, resubTimeoutMs, includeIdle = false, }) {
8
+ this.userMap = userMap;
9
+ this.commitment = commitment;
10
+ this.skipInitialLoad = skipInitialLoad;
11
+ this.resubTimeoutMs = resubTimeoutMs;
12
+ this.includeIdle = includeIdle || false;
13
+ }
14
+ async subscribe() {
15
+ if (!this.subscriber) {
16
+ const filters = [(0, memcmp_1.getUserFilter)()];
17
+ if (!this.includeIdle) {
18
+ filters.push((0, memcmp_1.getNonIdleUserFilter)());
19
+ }
20
+ this.subscriber = new webSocketProgramAccountSubscriber_1.WebSocketProgramAccountSubscriber('UserMap', 'User', this.userMap.driftClient.program, this.userMap.driftClient.program.account.user.coder.accounts.decodeUnchecked.bind(this.userMap.driftClient.program.account.user.coder.accounts), {
21
+ filters,
22
+ commitment: this.commitment,
23
+ }, this.resubTimeoutMs);
24
+ }
25
+ await this.subscriber.subscribe((accountId, account, context) => {
26
+ const userKey = accountId.toBase58();
27
+ this.userMap.updateUserAccount(userKey, account, context.slot);
28
+ });
29
+ if (!this.skipInitialLoad) {
30
+ await this.userMap.sync();
31
+ }
32
+ }
33
+ async unsubscribe() {
34
+ if (!this.subscriber)
35
+ return;
36
+ await this.subscriber.unsubscribe();
37
+ this.subscriber = undefined;
38
+ }
39
+ }
40
+ exports.WebsocketSubscription = WebsocketSubscription;
@@ -1,5 +1,6 @@
1
- import { User, DriftClient, UserAccount, OrderRecord, UserSubscriptionConfig, WrappedEvent, DLOB } from '..';
1
+ import { User, DriftClient, UserAccount, OrderRecord, WrappedEvent, DLOB } from '..';
2
2
  import { PublicKey } from '@solana/web3.js';
3
+ import { UserAccountFilterCriteria as UserFilterCriteria, UserMapConfig } from './userMapConfig';
3
4
  export interface UserMapInterface {
4
5
  subscribe(): Promise<void>;
5
6
  unsubscribe(): Promise<void>;
@@ -11,33 +12,23 @@ export interface UserMapInterface {
11
12
  updateWithOrderRecord(record: OrderRecord): Promise<void>;
12
13
  values(): IterableIterator<User>;
13
14
  }
14
- export type SyncCallbackCriteria = {
15
- hasOpenOrders: boolean;
16
- };
17
15
  export declare class UserMap implements UserMapInterface {
18
16
  private userMap;
19
- private driftClient;
20
- private accountSubscription;
17
+ driftClient: DriftClient;
18
+ private connection;
19
+ private commitment;
21
20
  private includeIdle;
22
21
  private lastNumberOfSubAccounts;
22
+ private subscription;
23
23
  private stateAccountUpdateCallback;
24
- private syncCallback;
25
- private syncCallbackCriteria;
26
24
  private syncPromise?;
27
25
  private syncPromiseResolver;
28
26
  /**
29
27
  * Constructs a new UserMap instance.
30
- *
31
- * @param {DriftClient} driftClient - The DriftClient instance.
32
- * @param {UserSubscriptionConfig} accountSubscription - The UserSubscriptionConfig instance.
33
- * @param {boolean} includeIdle - Whether idle users are subscribed to. Defaults to false to decrease # of user subscriptions.
34
- * @param {(authorities: PublicKey[]) => Promise<void>} syncCallback - Called after `sync` completes, will pas in unique list of authorities. Useful for using it to sync UserStatsMap.
35
- * @param {SyncCallbackCriteria} syncCallbackCriteria - The criteria for the sync callback. Defaults to having no filters
36
28
  */
37
- constructor(driftClient: DriftClient, accountSubscription: UserSubscriptionConfig, includeIdle?: boolean, syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria);
38
- addSyncCallback(syncCallback?: (authorities: PublicKey[]) => Promise<void>, syncCallbackCriteria?: SyncCallbackCriteria): void;
29
+ constructor(config: UserMapConfig);
39
30
  subscribe(): Promise<void>;
40
- addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount): Promise<void>;
31
+ addPubkey(userAccountPublicKey: PublicKey, userAccount?: UserAccount, slot?: number): Promise<void>;
41
32
  has(key: string): boolean;
42
33
  /**
43
34
  * gets the User for a particular userAccountPublicKey, if no User exists, undefined is returned
@@ -67,7 +58,13 @@ export declare class UserMap implements UserMapInterface {
67
58
  updateWithEventRecord(record: WrappedEvent<any>): Promise<void>;
68
59
  values(): IterableIterator<User>;
69
60
  size(): number;
70
- getUniqueAuthorities(useSyncCallbackCriteria?: boolean): PublicKey[];
61
+ /**
62
+ * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
63
+ * @param filterCriteria: Users must meet these criteria to be included
64
+ * @returns
65
+ */
66
+ getUniqueAuthorities(filterCriteria?: UserFilterCriteria): PublicKey[];
71
67
  sync(): Promise<void>;
72
68
  unsubscribe(): Promise<void>;
69
+ updateUserAccount(key: string, userAccount: UserAccount, slot: number): Promise<void>;
73
70
  }
@@ -5,33 +5,46 @@ const __1 = require("..");
5
5
  const web3_js_1 = require("@solana/web3.js");
6
6
  const buffer_1 = require("buffer");
7
7
  const memcmp_1 = require("../memcmp");
8
+ const WebsocketSubscription_1 = require("./WebsocketSubscription");
9
+ const PollingSubscription_1 = require("./PollingSubscription");
8
10
  class UserMap {
9
11
  /**
10
12
  * Constructs a new UserMap instance.
11
- *
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
17
13
  */
18
- constructor(driftClient, accountSubscription, includeIdle = false, syncCallback, syncCallbackCriteria = { hasOpenOrders: false }) {
14
+ constructor(config) {
15
+ var _a, _b;
19
16
  this.userMap = new Map();
20
17
  this.stateAccountUpdateCallback = async (state) => {
21
- if (state.numberOfSubAccounts !== this.lastNumberOfSubAccounts) {
18
+ if (!state.numberOfSubAccounts.eq(this.lastNumberOfSubAccounts)) {
22
19
  await this.sync();
23
20
  this.lastNumberOfSubAccounts = state.numberOfSubAccounts;
24
21
  }
25
22
  };
26
- this.driftClient = driftClient;
27
- this.accountSubscription = accountSubscription;
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;
23
+ this.driftClient = config.driftClient;
24
+ if (config.connection) {
25
+ this.connection = config.connection;
26
+ }
27
+ else {
28
+ this.connection = this.driftClient.connection;
29
+ }
30
+ this.commitment =
31
+ (_a = config.subscriptionConfig.commitment) !== null && _a !== void 0 ? _a : this.driftClient.opts.commitment;
32
+ this.includeIdle = (_b = config.includeIdle) !== null && _b !== void 0 ? _b : false;
33
+ if (config.subscriptionConfig.type === 'polling') {
34
+ this.subscription = new PollingSubscription_1.PollingSubscription({
35
+ userMap: this,
36
+ frequency: config.subscriptionConfig.frequency,
37
+ skipInitialLoad: config.skipInitialLoad,
38
+ });
39
+ }
40
+ else {
41
+ this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
42
+ userMap: this,
43
+ commitment: this.commitment,
44
+ resubTimeoutMs: config.subscriptionConfig.resubTimeoutMs,
45
+ skipInitialLoad: config.skipInitialLoad,
46
+ });
47
+ }
35
48
  }
36
49
  async subscribe() {
37
50
  if (this.size() > 0) {
@@ -41,13 +54,16 @@ class UserMap {
41
54
  this.lastNumberOfSubAccounts =
42
55
  this.driftClient.getStateAccount().numberOfSubAccounts;
43
56
  this.driftClient.eventEmitter.on('stateAccountUpdate', this.stateAccountUpdateCallback);
44
- await this.sync();
57
+ await this.subscription.subscribe();
45
58
  }
46
- async addPubkey(userAccountPublicKey, userAccount) {
59
+ async addPubkey(userAccountPublicKey, userAccount, slot) {
47
60
  const user = new __1.User({
48
61
  driftClient: this.driftClient,
49
62
  userAccountPublicKey,
50
- accountSubscription: this.accountSubscription,
63
+ accountSubscription: {
64
+ type: 'custom',
65
+ userAccountSubscriber: new __1.BasicUserAccountSubscriber(userAccountPublicKey, userAccount, slot),
66
+ },
51
67
  });
52
68
  await user.subscribe(userAccount);
53
69
  this.userMap.set(userAccountPublicKey.toString(), user);
@@ -148,11 +164,15 @@ class UserMap {
148
164
  size() {
149
165
  return this.userMap.size;
150
166
  }
151
- getUniqueAuthorities(useSyncCallbackCriteria = true) {
167
+ /**
168
+ * Returns a unique list of authorities for all users in the UserMap that meet the filter criteria
169
+ * @param filterCriteria: Users must meet these criteria to be included
170
+ * @returns
171
+ */
172
+ getUniqueAuthorities(filterCriteria) {
152
173
  const usersMeetingCriteria = Array.from(this.userMap.values()).filter((user) => {
153
174
  let pass = true;
154
- if (useSyncCallbackCriteria &&
155
- this.syncCallbackCriteria.hasOpenOrders) {
175
+ if (filterCriteria && filterCriteria.hasOpenOrders) {
156
176
  pass = pass && user.getUserAccount().hasOpenOrder;
157
177
  }
158
178
  return pass;
@@ -176,7 +196,7 @@ class UserMap {
176
196
  const rpcRequestArgs = [
177
197
  this.driftClient.program.programId.toBase58(),
178
198
  {
179
- commitment: this.driftClient.connection.commitment,
199
+ commitment: this.commitment,
180
200
  filters,
181
201
  encoding: 'base64',
182
202
  withContext: true,
@@ -184,7 +204,7 @@ class UserMap {
184
204
  ];
185
205
  const rpcJSONResponse =
186
206
  // @ts-ignore
187
- await this.driftClient.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
207
+ await this.connection._rpcRequest('getProgramAccounts', rpcRequestArgs);
188
208
  const rpcResponseAndContext = rpcJSONResponse.result;
189
209
  const slot = rpcResponseAndContext.context.slot;
190
210
  const programAccountBufferMap = new Map();
@@ -195,9 +215,11 @@ class UserMap {
195
215
  }
196
216
  for (const [key, buffer] of programAccountBufferMap.entries()) {
197
217
  if (!this.has(key)) {
198
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', buffer);
218
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decodeUnchecked('User', buffer);
199
219
  await this.addPubkey(new web3_js_1.PublicKey(key), userAccount);
200
220
  }
221
+ // give event loop a chance to breathe
222
+ await new Promise((resolve) => setTimeout(resolve, 0));
201
223
  }
202
224
  for (const [key, user] of this.userMap.entries()) {
203
225
  if (!programAccountBufferMap.has(key)) {
@@ -205,16 +227,15 @@ class UserMap {
205
227
  this.userMap.delete(key);
206
228
  }
207
229
  else {
208
- const userAccount = this.driftClient.program.account.user.coder.accounts.decode('User', programAccountBufferMap.get(key));
230
+ const userAccount = this.driftClient.program.account.user.coder.accounts.decodeUnchecked('User', programAccountBufferMap.get(key));
209
231
  user.accountSubscriber.updateData(userAccount, slot);
210
232
  }
211
- }
212
- if (this.syncCallback) {
213
- await this.syncCallback(this.getUniqueAuthorities());
233
+ // give event loop a chance to breathe
234
+ await new Promise((resolve) => setTimeout(resolve, 0));
214
235
  }
215
236
  }
216
237
  catch (e) {
217
- console.error(`Error in UserMap.sync()`);
238
+ console.error(`Error in UserMap.sync():`);
218
239
  console.error(e);
219
240
  }
220
241
  finally {
@@ -223,6 +244,7 @@ class UserMap {
223
244
  }
224
245
  }
225
246
  async unsubscribe() {
247
+ await this.subscription.unsubscribe();
226
248
  for (const [key, user] of this.userMap.entries()) {
227
249
  await user.unsubscribe();
228
250
  this.userMap.delete(key);
@@ -232,5 +254,14 @@ class UserMap {
232
254
  this.lastNumberOfSubAccounts = undefined;
233
255
  }
234
256
  }
257
+ async updateUserAccount(key, userAccount, slot) {
258
+ if (!this.userMap.has(key)) {
259
+ this.addPubkey(new web3_js_1.PublicKey(key), userAccount, slot);
260
+ }
261
+ else {
262
+ const user = this.userMap.get(key);
263
+ user.accountSubscriber.updateData(userAccount, slot);
264
+ }
265
+ }
235
266
  }
236
267
  exports.UserMap = UserMap;
@@ -0,0 +1,20 @@
1
+ import { Commitment, Connection } from '@solana/web3.js';
2
+ import { DriftClient } from '../driftClient';
3
+ export type UserAccountFilterCriteria = {
4
+ hasOpenOrders: boolean;
5
+ };
6
+ export type UserMapConfig = {
7
+ driftClient: DriftClient;
8
+ connection?: Connection;
9
+ subscriptionConfig: {
10
+ type: 'polling';
11
+ frequency: number;
12
+ commitment?: Commitment;
13
+ } | {
14
+ type: 'websocket';
15
+ resubTimeoutMs?: number;
16
+ commitment?: Commitment;
17
+ };
18
+ skipInitialLoad?: boolean;
19
+ includeIdle?: boolean;
20
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.49.0-beta.1",
3
+ "version": "2.49.0-beta.10",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -4,7 +4,7 @@ import StrictEventEmitter from 'strict-event-emitter-types';
4
4
  import { EventEmitter } from 'events';
5
5
  import { UserAccount } from '../types';
6
6
 
7
- export class MockUserAccountSubscriber implements UserAccountSubscriber {
7
+ export class BasicUserAccountSubscriber implements UserAccountSubscriber {
8
8
  isSubscribed: boolean;
9
9
  eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
10
10
  userAccountPublicKey: PublicKey;
@@ -16,8 +16,8 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
16
16
 
17
17
  public constructor(
18
18
  userAccountPublicKey: PublicKey,
19
- data: UserAccount,
20
- slot: number
19
+ data?: UserAccount,
20
+ slot?: number
21
21
  ) {
22
22
  this.isSubscribed = true;
23
23
  this.eventEmitter = new EventEmitter();
@@ -46,8 +46,10 @@ export class MockUserAccountSubscriber implements UserAccountSubscriber {
46
46
  }
47
47
 
48
48
  public updateData(userAccount: UserAccount, slot: number): void {
49
- this.user = { data: userAccount, slot };
50
- this.eventEmitter.emit('userAccountUpdate', userAccount);
51
- this.eventEmitter.emit('update');
49
+ if (!this.user || slot >= (this.user.slot ?? 0)) {
50
+ this.user = { data: userAccount, slot };
51
+ this.eventEmitter.emit('userAccountUpdate', userAccount);
52
+ this.eventEmitter.emit('update');
53
+ }
52
54
  }
53
55
  }