@drift-labs/sdk 2.96.0-beta.1 → 2.96.0-beta.3

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 (66) hide show
  1. package/VERSION +1 -1
  2. package/lib/accounts/types.d.ts +0 -8
  3. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  4. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
  5. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  6. package/lib/driftClient.js +14 -35
  7. package/lib/driftClientConfig.d.ts +0 -6
  8. package/lib/events/eventSubscriber.d.ts +7 -0
  9. package/lib/events/eventSubscriber.js +69 -32
  10. package/lib/events/eventsServerLogProvider.d.ts +21 -0
  11. package/lib/events/eventsServerLogProvider.js +121 -0
  12. package/lib/events/pollingLogProvider.js +1 -1
  13. package/lib/events/types.d.ts +12 -5
  14. package/lib/events/types.js +5 -1
  15. package/lib/events/webSocketLogProvider.js +2 -2
  16. package/lib/orderSubscriber/OrderSubscriber.d.ts +1 -2
  17. package/lib/orderSubscriber/OrderSubscriber.js +4 -19
  18. package/lib/orderSubscriber/types.d.ts +0 -9
  19. package/lib/user.js +4 -11
  20. package/lib/userConfig.d.ts +1 -6
  21. package/lib/userMap/userMap.js +0 -14
  22. package/lib/userMap/userMapConfig.d.ts +0 -7
  23. package/lib/userStatsConfig.d.ts +0 -6
  24. package/package.json +1 -3
  25. package/src/accounts/types.ts +0 -9
  26. package/src/accounts/webSocketAccountSubscriber.ts +1 -1
  27. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +3 -3
  28. package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
  29. package/src/driftClient.ts +0 -28
  30. package/src/driftClientConfig.ts +0 -7
  31. package/src/events/eventSubscriber.ts +125 -54
  32. package/src/events/eventsServerLogProvider.ts +152 -0
  33. package/src/events/pollingLogProvider.ts +1 -1
  34. package/src/events/types.ts +29 -6
  35. package/src/events/webSocketLogProvider.ts +4 -4
  36. package/src/orderSubscriber/OrderSubscriber.ts +1 -15
  37. package/src/orderSubscriber/types.ts +0 -10
  38. package/src/user.ts +0 -11
  39. package/src/userConfig.ts +1 -7
  40. package/src/userMap/userMap.ts +1 -17
  41. package/src/userMap/userMapConfig.ts +0 -8
  42. package/src/userStatsConfig.ts +0 -7
  43. package/lib/accounts/grpcAccountSubscriber.d.ts +0 -16
  44. package/lib/accounts/grpcAccountSubscriber.js +0 -155
  45. package/lib/accounts/grpcDriftClientAccountSubscriber.d.ts +0 -13
  46. package/lib/accounts/grpcDriftClientAccountSubscriber.js +0 -96
  47. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +0 -10
  48. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.js +0 -30
  49. package/lib/accounts/grpcProgramAccountSubscriber.d.ts +0 -19
  50. package/lib/accounts/grpcProgramAccountSubscriber.js +0 -161
  51. package/lib/accounts/grpcUserAccountSubscriber.d.ts +0 -10
  52. package/lib/accounts/grpcUserAccountSubscriber.js +0 -28
  53. package/lib/accounts/grpcUserStatsAccountSubscriber.d.ts +0 -10
  54. package/lib/accounts/grpcUserStatsAccountSubscriber.js +0 -28
  55. package/lib/orderSubscriber/grpcSubscription.d.ts +0 -25
  56. package/lib/orderSubscriber/grpcSubscription.js +0 -68
  57. package/lib/userMap/grpcSubscription.d.ts +0 -26
  58. package/lib/userMap/grpcSubscription.js +0 -42
  59. package/src/accounts/grpcAccountSubscriber.ts +0 -158
  60. package/src/accounts/grpcDriftClientAccountSubscriber.ts +0 -196
  61. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +0 -62
  62. package/src/accounts/grpcProgramAccountSubscriber.ts +0 -181
  63. package/src/accounts/grpcUserAccountSubscriber.ts +0 -48
  64. package/src/accounts/grpcUserStatsAccountSubscriber.ts +0 -51
  65. package/src/orderSubscriber/grpcSubscription.ts +0 -126
  66. package/src/userMap/grpcSubscription.ts +0 -83
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.96.0-beta.1
1
+ 2.96.0-beta.3
@@ -6,8 +6,6 @@ import { EventEmitter } from 'events';
6
6
  import { Context, PublicKey } from '@solana/web3.js';
7
7
  import { Account } from '@solana/spl-token';
8
8
  import { OracleInfo, OraclePriceData } from '..';
9
- import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
10
- import { ChannelOptions } from '@grpc/grpc-js';
11
9
  export interface AccountSubscriber<T> {
12
10
  dataAndSlot?: DataAndSlot<T>;
13
11
  subscribe(onChange: (data: T) => void): Promise<void>;
@@ -142,9 +140,3 @@ export interface UserStatsAccountSubscriber {
142
140
  unsubscribe(): Promise<void>;
143
141
  getUserStatsAccountAndSlot(): DataAndSlot<UserStatsAccount>;
144
142
  }
145
- export type GrpcConfigs = {
146
- endpoint: string;
147
- token: string;
148
- commitmentLevel?: CommitmentLevel;
149
- channelOptions?: ChannelOptions;
150
- };
@@ -20,7 +20,7 @@ export declare class WebSocketAccountSubscriber<T> implements AccountSubscriber<
20
20
  constructor(accountName: string, program: Program, accountPublicKey: PublicKey, decodeBuffer?: (buffer: Buffer) => T, resubOpts?: ResubOpts, commitment?: Commitment);
21
21
  subscribe(onChange: (data: T) => void): Promise<void>;
22
22
  setData(data: T, slot?: number): void;
23
- protected setTimeout(): void;
23
+ private setTimeout;
24
24
  fetch(): Promise<void>;
25
25
  handleRpcResponse(context: Context, accountInfo?: AccountInfo<Buffer>): void;
26
26
  decodeBuffer(buffer: Buffer): T;
@@ -30,9 +30,9 @@ export declare class WebSocketDriftClientAccountSubscriber implements DriftClien
30
30
  initialPerpMarketAccountData: Map<number, PerpMarketAccount>;
31
31
  initialSpotMarketAccountData: Map<number, SpotMarketAccount>;
32
32
  initialOraclePriceData: Map<string, OraclePriceData>;
33
- protected isSubscribing: boolean;
34
- protected subscriptionPromise: Promise<boolean>;
35
- protected subscriptionPromiseResolver: (val: boolean) => void;
33
+ private isSubscribing;
34
+ private subscriptionPromise;
35
+ private subscriptionPromiseResolver;
36
36
  constructor(program: Program, perpMarketIndexes: number[], spotMarketIndexes: number[], oracleInfos: OracleInfo[], shouldFindAllMarketsAndOracles: boolean, resubOpts?: ResubOpts, commitment?: Commitment);
37
37
  subscribe(): Promise<boolean>;
38
38
  setInitialData(): Promise<void>;
@@ -27,7 +27,7 @@ export declare class WebSocketProgramAccountSubscriber<T> implements ProgramAcco
27
27
  commitment?: Commitment;
28
28
  }, resubOpts?: ResubOpts);
29
29
  subscribe(onChange: (accountId: PublicKey, data: T, context: Context, buffer: Buffer) => void): Promise<void>;
30
- protected setTimeout(): void;
30
+ private setTimeout;
31
31
  handleRpcResponse(context: Context, keyedAccountInfo: KeyedAccountInfo): void;
32
32
  unsubscribe(onResub?: boolean): Promise<void>;
33
33
  }
@@ -68,7 +68,6 @@ const utils_2 = require("./tx/utils");
68
68
  const pyth_solana_receiver_json_1 = __importDefault(require("./idl/pyth_solana_receiver.json"));
69
69
  const on_demand_1 = require("@switchboard-xyz/on-demand");
70
70
  const switchboard_on_demand_30_json_1 = __importDefault(require("./idl/switchboard_on_demand_30.json"));
71
- const grpcDriftClientAccountSubscriber_1 = require("./accounts/grpcDriftClientAccountSubscriber");
72
71
  /**
73
72
  * # DriftClient
74
73
  * This class is the main way to interact with Drift Protocol. It allows you to subscribe to the various accounts where the Market's state is stored, as well as: opening positions, liquidating, settling funding, depositing & withdrawing, and more.
@@ -81,7 +80,7 @@ class DriftClient {
81
80
  this._isSubscribed = val;
82
81
  }
83
82
  constructor(config) {
84
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19;
83
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6;
85
84
  this.users = new Map();
86
85
  this._isSubscribed = false;
87
86
  this.perpMarketLastSlotCache = new Map();
@@ -144,32 +143,18 @@ class DriftClient {
144
143
  accountLoader: config.accountSubscription.accountLoader,
145
144
  };
146
145
  }
147
- else if (((_q = config.accountSubscription) === null || _q === void 0 ? void 0 : _q.type) === 'grpc') {
148
- this.userAccountSubscriptionConfig = {
149
- type: 'grpc',
150
- resubTimeoutMs: (_r = config.accountSubscription) === null || _r === void 0 ? void 0 : _r.resubTimeoutMs,
151
- logResubMessages: (_s = config.accountSubscription) === null || _s === void 0 ? void 0 : _s.logResubMessages,
152
- configs: (_t = config.accountSubscription) === null || _t === void 0 ? void 0 : _t.configs,
153
- };
154
- this.userStatsAccountSubscriptionConfig = {
155
- type: 'grpc',
156
- resubTimeoutMs: (_u = config.accountSubscription) === null || _u === void 0 ? void 0 : _u.resubTimeoutMs,
157
- logResubMessages: (_v = config.accountSubscription) === null || _v === void 0 ? void 0 : _v.logResubMessages,
158
- configs: (_w = config.accountSubscription) === null || _w === void 0 ? void 0 : _w.configs,
159
- };
160
- }
161
146
  else {
162
147
  this.userAccountSubscriptionConfig = {
163
148
  type: 'websocket',
164
- resubTimeoutMs: (_x = config.accountSubscription) === null || _x === void 0 ? void 0 : _x.resubTimeoutMs,
165
- logResubMessages: (_y = config.accountSubscription) === null || _y === void 0 ? void 0 : _y.logResubMessages,
166
- commitment: (_z = config.accountSubscription) === null || _z === void 0 ? void 0 : _z.commitment,
149
+ resubTimeoutMs: (_q = config.accountSubscription) === null || _q === void 0 ? void 0 : _q.resubTimeoutMs,
150
+ logResubMessages: (_r = config.accountSubscription) === null || _r === void 0 ? void 0 : _r.logResubMessages,
151
+ commitment: (_s = config.accountSubscription) === null || _s === void 0 ? void 0 : _s.commitment,
167
152
  };
168
153
  this.userStatsAccountSubscriptionConfig = {
169
154
  type: 'websocket',
170
- resubTimeoutMs: (_0 = config.accountSubscription) === null || _0 === void 0 ? void 0 : _0.resubTimeoutMs,
171
- logResubMessages: (_1 = config.accountSubscription) === null || _1 === void 0 ? void 0 : _1.logResubMessages,
172
- commitment: (_2 = config.accountSubscription) === null || _2 === void 0 ? void 0 : _2.commitment,
155
+ resubTimeoutMs: (_t = config.accountSubscription) === null || _t === void 0 ? void 0 : _t.resubTimeoutMs,
156
+ logResubMessages: (_u = config.accountSubscription) === null || _u === void 0 ? void 0 : _u.logResubMessages,
157
+ commitment: (_v = config.accountSubscription) === null || _v === void 0 ? void 0 : _v.commitment,
173
158
  };
174
159
  }
175
160
  if (config.userStats) {
@@ -186,20 +171,14 @@ class DriftClient {
186
171
  const noMarketsAndOraclesSpecified = config.perpMarketIndexes === undefined &&
187
172
  config.spotMarketIndexes === undefined &&
188
173
  config.oracleInfos === undefined;
189
- if (((_3 = config.accountSubscription) === null || _3 === void 0 ? void 0 : _3.type) === 'polling') {
190
- this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_4 = config.perpMarketIndexes) !== null && _4 !== void 0 ? _4 : [], (_5 = config.spotMarketIndexes) !== null && _5 !== void 0 ? _5 : [], (_6 = config.oracleInfos) !== null && _6 !== void 0 ? _6 : [], noMarketsAndOraclesSpecified);
191
- }
192
- else if (((_7 = config.accountSubscription) === null || _7 === void 0 ? void 0 : _7.type) === 'grpc') {
193
- this.accountSubscriber = new grpcDriftClientAccountSubscriber_1.gprcDriftClientAccountSubscriber(config.accountSubscription.configs, this.program, (_8 = config.perpMarketIndexes) !== null && _8 !== void 0 ? _8 : [], (_9 = config.spotMarketIndexes) !== null && _9 !== void 0 ? _9 : [], (_10 = config.oracleInfos) !== null && _10 !== void 0 ? _10 : [], noMarketsAndOraclesSpecified, {
194
- resubTimeoutMs: (_11 = config.accountSubscription) === null || _11 === void 0 ? void 0 : _11.resubTimeoutMs,
195
- logResubMessages: (_12 = config.accountSubscription) === null || _12 === void 0 ? void 0 : _12.logResubMessages,
196
- });
174
+ if (((_w = config.accountSubscription) === null || _w === void 0 ? void 0 : _w.type) === 'polling') {
175
+ this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (_x = config.perpMarketIndexes) !== null && _x !== void 0 ? _x : [], (_y = config.spotMarketIndexes) !== null && _y !== void 0 ? _y : [], (_z = config.oracleInfos) !== null && _z !== void 0 ? _z : [], noMarketsAndOraclesSpecified);
197
176
  }
198
177
  else {
199
- this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_13 = config.perpMarketIndexes) !== null && _13 !== void 0 ? _13 : [], (_14 = config.spotMarketIndexes) !== null && _14 !== void 0 ? _14 : [], (_15 = config.oracleInfos) !== null && _15 !== void 0 ? _15 : [], noMarketsAndOraclesSpecified, {
200
- resubTimeoutMs: (_16 = config.accountSubscription) === null || _16 === void 0 ? void 0 : _16.resubTimeoutMs,
201
- logResubMessages: (_17 = config.accountSubscription) === null || _17 === void 0 ? void 0 : _17.logResubMessages,
202
- }, (_18 = config.accountSubscription) === null || _18 === void 0 ? void 0 : _18.commitment);
178
+ this.accountSubscriber = new webSocketDriftClientAccountSubscriber_1.WebSocketDriftClientAccountSubscriber(this.program, (_0 = config.perpMarketIndexes) !== null && _0 !== void 0 ? _0 : [], (_1 = config.spotMarketIndexes) !== null && _1 !== void 0 ? _1 : [], (_2 = config.oracleInfos) !== null && _2 !== void 0 ? _2 : [], noMarketsAndOraclesSpecified, {
179
+ resubTimeoutMs: (_3 = config.accountSubscription) === null || _3 === void 0 ? void 0 : _3.resubTimeoutMs,
180
+ logResubMessages: (_4 = config.accountSubscription) === null || _4 === void 0 ? void 0 : _4.logResubMessages,
181
+ }, (_5 = config.accountSubscription) === null || _5 === void 0 ? void 0 : _5.commitment);
203
182
  }
204
183
  this.eventEmitter = this.accountSubscriber.eventEmitter;
205
184
  this.metricsEventEmitter = new events_1.EventEmitter();
@@ -207,7 +186,7 @@ class DriftClient {
207
186
  this.enableMetricsEvents = true;
208
187
  }
209
188
  this.txSender =
210
- (_19 = config.txSender) !== null && _19 !== void 0 ? _19 : new retryTxSender_1.RetryTxSender({
189
+ (_6 = config.txSender) !== null && _6 !== void 0 ? _6 : new retryTxSender_1.RetryTxSender({
211
190
  connection: this.connection,
212
191
  wallet: this.wallet,
213
192
  opts: this.opts,
@@ -5,7 +5,6 @@ import { BulkAccountLoader } from './accounts/bulkAccountLoader';
5
5
  import { DriftEnv } from './config';
6
6
  import { TxSender } from './tx/types';
7
7
  import { TxHandler, TxHandlerConfig } from './tx/txHandler';
8
- import { GrpcConfigs } from './accounts/types';
9
8
  export type DriftClientConfig = {
10
9
  connection: Connection;
11
10
  wallet: IWallet;
@@ -39,9 +38,4 @@ export type DriftClientSubscriptionConfig = {
39
38
  } | {
40
39
  type: 'polling';
41
40
  accountLoader: BulkAccountLoader;
42
- } | {
43
- type: 'grpc';
44
- configs: GrpcConfigs;
45
- resubTimeoutMs?: number;
46
- logResubMessages?: boolean;
47
41
  };
@@ -15,12 +15,19 @@ export declare class EventSubscriber {
15
15
  private awaitTxPromises;
16
16
  private awaitTxResolver;
17
17
  private logProvider;
18
+ private currentProviderType;
18
19
  eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
19
20
  private lastSeenSlot;
20
21
  private lastSeenBlockTime;
21
22
  lastSeenTxSig: string;
22
23
  constructor(connection: Connection, program: Program, options?: EventSubscriptionOptions);
24
+ private initializeLogProvider;
23
25
  private populateInitialEventListMap;
26
+ /**
27
+ * Implements fallback logic for reconnecting to LogProvider. Currently terminates at polling,
28
+ * could be improved to try the original type again after some cooldown.
29
+ */
30
+ private updateFallbackProviderType;
24
31
  subscribe(): Promise<boolean>;
25
32
  private handleTxLogs;
26
33
  fetchPreviousTx(fetchMax?: boolean): Promise<void>;
@@ -10,6 +10,7 @@ const webSocketLogProvider_1 = require("./webSocketLogProvider");
10
10
  const events_1 = require("events");
11
11
  const sort_1 = require("./sort");
12
12
  const parse_1 = require("./parse");
13
+ const eventsServerLogProvider_1 = require("./eventsServerLogProvider");
13
14
  class EventSubscriber {
14
15
  constructor(connection, program, options = types_1.DefaultEventSubscriptionOptions) {
15
16
  var _a;
@@ -23,15 +24,36 @@ class EventSubscriber {
23
24
  this.txEventCache = new txEventCache_1.TxEventCache(this.options.maxTx);
24
25
  this.eventListMap = new Map();
25
26
  this.eventEmitter = new events_1.EventEmitter();
26
- if (this.options.logProviderConfig.type === 'websocket') {
27
+ this.currentProviderType = this.options.logProviderConfig.type;
28
+ this.initializeLogProvider();
29
+ }
30
+ initializeLogProvider(subscribe = false) {
31
+ if (this.currentProviderType === 'websocket') {
32
+ const logProviderConfig = this.options
33
+ .logProviderConfig;
27
34
  this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(
28
35
  // @ts-ignore
29
- this.connection, this.address, this.options.commitment, this.options.logProviderConfig.resubTimeoutMs);
36
+ this.connection, this.address, this.options.commitment, logProviderConfig.resubTimeoutMs);
30
37
  }
31
- else {
38
+ else if (this.currentProviderType === 'polling') {
39
+ const logProviderConfig = this.options
40
+ .logProviderConfig;
32
41
  this.logProvider = new pollingLogProvider_1.PollingLogProvider(
33
42
  // @ts-ignore
34
- this.connection, this.address, options.commitment, this.options.logProviderConfig.frequency, this.options.logProviderConfig.batchSize);
43
+ this.connection, this.address, this.options.commitment, logProviderConfig.frequency, logProviderConfig.batchSize);
44
+ }
45
+ else if (this.currentProviderType === 'events-server') {
46
+ const logProviderConfig = this.options
47
+ .logProviderConfig;
48
+ this.logProvider = new eventsServerLogProvider_1.EventsServerLogProvider(logProviderConfig.url, this.options.eventTypes, this.options.address ? this.options.address.toString() : undefined);
49
+ }
50
+ else {
51
+ throw new Error(`Invalid log provider type: ${this.currentProviderType}`);
52
+ }
53
+ if (subscribe) {
54
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
55
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime, this.currentProviderType === 'events-server', txSigIndex);
56
+ }, true);
35
57
  }
36
58
  }
37
59
  populateInitialEventListMap() {
@@ -39,37 +61,51 @@ class EventSubscriber {
39
61
  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));
40
62
  }
41
63
  }
64
+ /**
65
+ * Implements fallback logic for reconnecting to LogProvider. Currently terminates at polling,
66
+ * could be improved to try the original type again after some cooldown.
67
+ */
68
+ updateFallbackProviderType(reconnectAttempts, maxReconnectAttempts) {
69
+ if (reconnectAttempts < maxReconnectAttempts) {
70
+ return;
71
+ }
72
+ let nextProviderType = this.currentProviderType;
73
+ if (this.currentProviderType === 'events-server') {
74
+ nextProviderType = 'websocket';
75
+ }
76
+ else if (this.currentProviderType === 'websocket') {
77
+ nextProviderType = 'polling';
78
+ }
79
+ else if (this.currentProviderType === 'polling') {
80
+ nextProviderType = 'polling';
81
+ }
82
+ console.log(`EventSubscriber: Failing over providerType ${this.currentProviderType} to ${nextProviderType}`);
83
+ this.currentProviderType = nextProviderType;
84
+ }
42
85
  async subscribe() {
43
86
  try {
44
87
  if (this.logProvider.isSubscribed()) {
45
88
  return true;
46
89
  }
47
90
  this.populateInitialEventListMap();
48
- if (this.options.logProviderConfig.type === 'websocket') {
49
- if (this.options.logProviderConfig.resubTimeoutMs) {
50
- if (this.options.logProviderConfig.maxReconnectAttempts &&
51
- this.options.logProviderConfig.maxReconnectAttempts > 0) {
52
- const logProviderConfig = this.options
53
- .logProviderConfig;
54
- this.logProvider.eventEmitter.on('reconnect', (reconnectAttempts) => {
55
- if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
56
- console.log('Failing over to polling');
57
- this.logProvider.eventEmitter.removeAllListeners('reconnect');
58
- this.unsubscribe().then(() => {
59
- this.logProvider = new pollingLogProvider_1.PollingLogProvider(
60
- // @ts-ignore
61
- this.connection, this.address, this.options.commitment, logProviderConfig.fallbackFrequency, logProviderConfig.fallbackBatchSize);
62
- this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
63
- this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
64
- }, true);
65
- });
66
- }
67
- });
68
- }
91
+ if (this.options.logProviderConfig.type === 'websocket' ||
92
+ this.options.logProviderConfig.type === 'events-server') {
93
+ const logProviderConfig = this.options
94
+ .logProviderConfig;
95
+ if (this.logProvider.eventEmitter) {
96
+ this.logProvider.eventEmitter.on('reconnect', async (reconnectAttempts) => {
97
+ if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
98
+ console.log(`EventSubscriber: Reconnect attempts ${reconnectAttempts}/${logProviderConfig.maxReconnectAttempts}, reconnecting...`);
99
+ this.logProvider.eventEmitter.removeAllListeners('reconnect');
100
+ await this.unsubscribe();
101
+ this.updateFallbackProviderType(reconnectAttempts, logProviderConfig.maxReconnectAttempts);
102
+ this.initializeLogProvider(true);
103
+ }
104
+ });
69
105
  }
70
106
  }
71
- this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
72
- this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
107
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
108
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime, this.currentProviderType === 'events-server', txSigIndex);
73
109
  }, true);
74
110
  return true;
75
111
  }
@@ -79,11 +115,11 @@ class EventSubscriber {
79
115
  return false;
80
116
  }
81
117
  }
82
- handleTxLogs(txSig, slot, logs, mostRecentBlockTime) {
83
- if (this.txEventCache.has(txSig)) {
118
+ handleTxLogs(txSig, slot, logs, mostRecentBlockTime, fromEventsServer = false, txSigIndex = undefined) {
119
+ if (!fromEventsServer && this.txEventCache.has(txSig)) {
84
120
  return;
85
121
  }
86
- const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs);
122
+ const wrappedEvents = this.parseEventsFromLogs(txSig, slot, logs, txSigIndex);
87
123
  for (const wrappedEvent of wrappedEvents) {
88
124
  this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
89
125
  }
@@ -134,7 +170,7 @@ class EventSubscriber {
134
170
  this.awaitTxResolver.clear();
135
171
  return await this.logProvider.unsubscribe(true);
136
172
  }
137
- parseEventsFromLogs(txSig, slot, logs) {
173
+ parseEventsFromLogs(txSig, slot, logs, txSigIndex) {
138
174
  const records = [];
139
175
  // @ts-ignore
140
176
  const events = (0, parse_1.parseLogs)(this.program, logs);
@@ -146,7 +182,8 @@ class EventSubscriber {
146
182
  event.data.txSig = txSig;
147
183
  event.data.slot = slot;
148
184
  event.data.eventType = event.name;
149
- event.data.txSigIndex = runningEventIndex;
185
+ event.data.txSigIndex =
186
+ txSigIndex !== undefined ? txSigIndex : runningEventIndex;
150
187
  records.push(event.data);
151
188
  }
152
189
  runningEventIndex++;
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ import { logProviderCallback, EventType, LogProvider } from './types';
3
+ import { EventEmitter } from 'events';
4
+ export declare class EventsServerLogProvider implements LogProvider {
5
+ private readonly url;
6
+ private readonly eventTypes;
7
+ private readonly userAccount?;
8
+ private ws?;
9
+ private callback?;
10
+ private isUnsubscribing;
11
+ private externalUnsubscribe;
12
+ private lastHeartbeat;
13
+ private timeoutId?;
14
+ private reconnectAttempts;
15
+ eventEmitter?: EventEmitter;
16
+ constructor(url: string, eventTypes: EventType[], userAccount?: string);
17
+ isSubscribed(): boolean;
18
+ subscribe(callback: logProviderCallback): Promise<boolean>;
19
+ unsubscribe(external?: boolean): Promise<boolean>;
20
+ private setTimeout;
21
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventsServerLogProvider = void 0;
4
+ const events_1 = require("events");
5
+ // browser support
6
+ let WebSocketImpl;
7
+ if (typeof window !== 'undefined' && window.WebSocket) {
8
+ WebSocketImpl = window.WebSocket;
9
+ }
10
+ else {
11
+ WebSocketImpl = require('ws');
12
+ }
13
+ const EVENT_SERVER_HEARTBEAT_INTERVAL_MS = 5000;
14
+ const ALLOWED_MISSED_HEARTBEATS = 3;
15
+ class EventsServerLogProvider {
16
+ constructor(url, eventTypes, userAccount) {
17
+ this.url = url;
18
+ this.eventTypes = eventTypes;
19
+ this.userAccount = userAccount;
20
+ this.isUnsubscribing = false;
21
+ this.externalUnsubscribe = false;
22
+ this.lastHeartbeat = 0;
23
+ this.reconnectAttempts = 0;
24
+ this.eventEmitter = new events_1.EventEmitter();
25
+ }
26
+ isSubscribed() {
27
+ return this.ws !== undefined;
28
+ }
29
+ async subscribe(callback) {
30
+ if (this.ws !== undefined) {
31
+ return true;
32
+ }
33
+ this.ws = new WebSocketImpl(this.url);
34
+ this.callback = callback;
35
+ this.ws.addEventListener('open', () => {
36
+ for (const channel of this.eventTypes) {
37
+ const subscribeMessage = {
38
+ type: 'subscribe',
39
+ channel: channel,
40
+ };
41
+ if (this.userAccount) {
42
+ subscribeMessage['user'] = this.userAccount;
43
+ }
44
+ this.ws.send(JSON.stringify(subscribeMessage));
45
+ }
46
+ this.reconnectAttempts = 0;
47
+ });
48
+ this.ws.addEventListener('message', (data) => {
49
+ try {
50
+ if (!this.isUnsubscribing) {
51
+ clearTimeout(this.timeoutId);
52
+ this.setTimeout();
53
+ if (this.reconnectAttempts > 0) {
54
+ console.log('eventsServerLogProvider: Resetting reconnect attempts to 0');
55
+ }
56
+ this.reconnectAttempts = 0;
57
+ }
58
+ const parsedData = JSON.parse(data.data.toString());
59
+ if (parsedData.channel === 'heartbeat') {
60
+ this.lastHeartbeat = Date.now();
61
+ return;
62
+ }
63
+ if (parsedData.message !== undefined) {
64
+ return;
65
+ }
66
+ const event = JSON.parse(parsedData.data);
67
+ this.callback(event.txSig, event.slot, [
68
+ 'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH invoke [1]',
69
+ event.rawLog,
70
+ 'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH success',
71
+ ], undefined, event.txSigIndex);
72
+ }
73
+ catch (error) {
74
+ console.error('Error parsing message:', error);
75
+ }
76
+ });
77
+ this.ws.addEventListener('close', () => {
78
+ console.log('eventsServerLogProvider: WebSocket closed');
79
+ });
80
+ this.ws.addEventListener('error', (error) => {
81
+ console.error('eventsServerLogProvider: WebSocket error:', error);
82
+ });
83
+ this.setTimeout();
84
+ return true;
85
+ }
86
+ async unsubscribe(external = false) {
87
+ this.isUnsubscribing = true;
88
+ this.externalUnsubscribe = external;
89
+ if (this.timeoutId) {
90
+ clearInterval(this.timeoutId);
91
+ this.timeoutId = undefined;
92
+ }
93
+ if (this.ws !== undefined) {
94
+ this.ws.close();
95
+ this.ws = undefined;
96
+ return true;
97
+ }
98
+ else {
99
+ this.isUnsubscribing = false;
100
+ return true;
101
+ }
102
+ }
103
+ setTimeout() {
104
+ this.timeoutId = setTimeout(async () => {
105
+ if (this.isUnsubscribing || this.externalUnsubscribe) {
106
+ // If we are in the process of unsubscribing, do not attempt to resubscribe
107
+ return;
108
+ }
109
+ const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeat;
110
+ if (timeSinceLastHeartbeat >
111
+ EVENT_SERVER_HEARTBEAT_INTERVAL_MS * ALLOWED_MISSED_HEARTBEATS) {
112
+ console.log(`eventServerLogProvider: No heartbeat in ${timeSinceLastHeartbeat}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`);
113
+ await this.unsubscribe();
114
+ this.reconnectAttempts++;
115
+ this.eventEmitter.emit('reconnect', this.reconnectAttempts);
116
+ this.subscribe(this.callback);
117
+ }
118
+ }, EVENT_SERVER_HEARTBEAT_INTERVAL_MS * 2);
119
+ }
120
+ }
121
+ exports.EventsServerLogProvider = EventsServerLogProvider;
@@ -30,7 +30,7 @@ class PollingLogProvider {
30
30
  this.firstFetch = false;
31
31
  const { mostRecentTx, transactionLogs } = response;
32
32
  for (const { txSig, slot, logs } of transactionLogs) {
33
- callback(txSig, slot, logs, response.mostRecentBlockTime);
33
+ callback(txSig, slot, logs, response.mostRecentBlockTime, undefined);
34
34
  }
35
35
  this.mostRecentSeenTx = mostRecentTx;
36
36
  }
@@ -48,23 +48,30 @@ export interface EventSubscriberEvents {
48
48
  newEvent: (event: WrappedEvent<EventType>) => void;
49
49
  }
50
50
  export type SortFn = (currentRecord: EventMap[EventType], newRecord: EventMap[EventType]) => 'less than' | 'greater than';
51
- export type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[], mostRecentBlockTime: number | undefined) => void;
51
+ export type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[], mostRecentBlockTime: number | undefined, txSigIndex: number | undefined) => void;
52
52
  export interface LogProvider {
53
53
  isSubscribed(): boolean;
54
54
  subscribe(callback: logProviderCallback, skipHistory?: boolean): Promise<boolean>;
55
55
  unsubscribe(external?: boolean): Promise<boolean>;
56
56
  eventEmitter?: EventEmitter;
57
57
  }
58
- export type WebSocketLogProviderConfig = {
59
- type: 'websocket';
60
- resubTimeoutMs?: number;
58
+ export type LogProviderType = 'websocket' | 'polling' | 'events-server';
59
+ export type StreamingLogProviderConfig = {
61
60
  maxReconnectAttempts?: number;
62
61
  fallbackFrequency?: number;
63
62
  fallbackBatchSize?: number;
64
63
  };
64
+ export type WebSocketLogProviderConfig = StreamingLogProviderConfig & {
65
+ type: 'websocket';
66
+ resubTimeoutMs?: number;
67
+ };
65
68
  export type PollingLogProviderConfig = {
66
69
  type: 'polling';
67
70
  frequency: number;
68
71
  batchSize?: number;
69
72
  };
70
- export type LogProviderConfig = WebSocketLogProviderConfig | PollingLogProviderConfig;
73
+ export type EventsServerLogProviderConfig = StreamingLogProviderConfig & {
74
+ type: 'events-server';
75
+ url: string;
76
+ };
77
+ export type LogProviderConfig = WebSocketLogProviderConfig | PollingLogProviderConfig | EventsServerLogProviderConfig;
@@ -25,6 +25,10 @@ exports.DefaultEventSubscriptionOptions = {
25
25
  commitment: 'confirmed',
26
26
  maxTx: 4096,
27
27
  logProviderConfig: {
28
- type: 'websocket',
28
+ type: 'events-server',
29
+ url: 'wss://events.drift.trade/ws',
30
+ maxReconnectAttempts: 5,
31
+ fallbackFrequency: 1000,
32
+ fallbackBatchSize: 100,
29
33
  },
30
34
  };
@@ -47,7 +47,7 @@ class WebSocketLogProvider {
47
47
  if (logs.err !== null) {
48
48
  return;
49
49
  }
50
- callback(logs.signature, ctx.slot, logs.logs, undefined);
50
+ callback(logs.signature, ctx.slot, logs.logs, undefined, undefined);
51
51
  }, this.commitment);
52
52
  }
53
53
  isSubscribed() {
@@ -83,7 +83,7 @@ class WebSocketLogProvider {
83
83
  return;
84
84
  }
85
85
  if (this.receivingData) {
86
- console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`);
86
+ console.log(`webSocketLogProvider: No log data in ${this.resubTimeoutMs}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`);
87
87
  await this.unsubscribe();
88
88
  this.receivingData = false;
89
89
  this.reconnectAttempts++;
@@ -10,14 +10,13 @@ import { PollingSubscription } from './PollingSubscription';
10
10
  import { WebsocketSubscription } from './WebsocketSubscription';
11
11
  import StrictEventEmitter from 'strict-event-emitter-types';
12
12
  import { EventEmitter } from 'events';
13
- import { grpcSubscription } from './grpcSubscription';
14
13
  export declare class OrderSubscriber {
15
14
  driftClient: DriftClient;
16
15
  usersAccounts: Map<string, {
17
16
  slot: number;
18
17
  userAccount: UserAccount;
19
18
  }>;
20
- subscription: PollingSubscription | WebsocketSubscription | grpcSubscription;
19
+ subscription: PollingSubscription | WebsocketSubscription;
21
20
  commitment: Commitment;
22
21
  eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
23
22
  fetchPromise?: Promise<void>;
@@ -10,10 +10,9 @@ const WebsocketSubscription_1 = require("./WebsocketSubscription");
10
10
  const events_1 = require("events");
11
11
  const index_1 = require("../index");
12
12
  const user_1 = require("../decode/user");
13
- const grpcSubscription_1 = require("./grpcSubscription");
14
13
  class OrderSubscriber {
15
14
  constructor(config) {
16
- var _a, _b, _c, _d, _e;
15
+ var _a, _b, _c;
17
16
  this.usersAccounts = new Map();
18
17
  this.driftClient = config.driftClient;
19
18
  this.commitment = config.subscriptionConfig.commitment || 'processed';
@@ -23,34 +22,20 @@ class OrderSubscriber {
23
22
  frequency: config.subscriptionConfig.frequency,
24
23
  });
25
24
  }
26
- else if (config.subscriptionConfig.type === 'grpc') {
27
- this.subscription = new grpcSubscription_1.grpcSubscription({
28
- grpcConfigs: config.subscriptionConfig.configs,
29
- orderSubscriber: this,
30
- commitment: this.commitment,
31
- skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
32
- resubOpts: {
33
- resubTimeoutMs: (_a = config.subscriptionConfig) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs,
34
- logResubMessages: (_b = config.subscriptionConfig) === null || _b === void 0 ? void 0 : _b.logResubMessages,
35
- },
36
- resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
37
- decoded: config.decodeData,
38
- });
39
- }
40
25
  else {
41
26
  this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
42
27
  orderSubscriber: this,
43
28
  commitment: this.commitment,
44
29
  skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
45
30
  resubOpts: {
46
- resubTimeoutMs: (_c = config.subscriptionConfig) === null || _c === void 0 ? void 0 : _c.resubTimeoutMs,
47
- logResubMessages: (_d = config.subscriptionConfig) === null || _d === void 0 ? void 0 : _d.logResubMessages,
31
+ resubTimeoutMs: (_a = config.subscriptionConfig) === null || _a === void 0 ? void 0 : _a.resubTimeoutMs,
32
+ logResubMessages: (_b = config.subscriptionConfig) === null || _b === void 0 ? void 0 : _b.logResubMessages,
48
33
  },
49
34
  resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
50
35
  decoded: config.decodeData,
51
36
  });
52
37
  }
53
- if ((_e = config.fastDecode) !== null && _e !== void 0 ? _e : true) {
38
+ if ((_c = config.fastDecode) !== null && _c !== void 0 ? _c : true) {
54
39
  this.decodeFn = (name, data) => (0, user_1.decodeUser)(data);
55
40
  }
56
41
  else {