@drift-labs/sdk 2.96.0-beta.0 → 2.96.0-beta.2

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/grpcAccountSubscriber.d.ts +16 -0
  3. package/lib/accounts/grpcAccountSubscriber.js +155 -0
  4. package/lib/accounts/grpcDriftClientAccountSubscriber.d.ts +13 -0
  5. package/lib/accounts/grpcDriftClientAccountSubscriber.js +96 -0
  6. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +10 -0
  7. package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.js +30 -0
  8. package/lib/accounts/grpcProgramAccountSubscriber.d.ts +19 -0
  9. package/lib/accounts/grpcProgramAccountSubscriber.js +161 -0
  10. package/lib/accounts/grpcUserAccountSubscriber.d.ts +10 -0
  11. package/lib/accounts/grpcUserAccountSubscriber.js +28 -0
  12. package/lib/accounts/grpcUserStatsAccountSubscriber.d.ts +10 -0
  13. package/lib/accounts/grpcUserStatsAccountSubscriber.js +28 -0
  14. package/lib/accounts/types.d.ts +8 -0
  15. package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
  16. package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
  17. package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
  18. package/lib/driftClient.js +35 -14
  19. package/lib/driftClientConfig.d.ts +6 -0
  20. package/lib/events/eventSubscriber.d.ts +7 -0
  21. package/lib/events/eventSubscriber.js +69 -32
  22. package/lib/events/eventsServerLogProvider.d.ts +21 -0
  23. package/lib/events/eventsServerLogProvider.js +121 -0
  24. package/lib/events/pollingLogProvider.js +1 -1
  25. package/lib/events/types.d.ts +12 -5
  26. package/lib/events/types.js +5 -1
  27. package/lib/events/webSocketLogProvider.js +2 -2
  28. package/lib/orderSubscriber/OrderSubscriber.d.ts +2 -1
  29. package/lib/orderSubscriber/OrderSubscriber.js +19 -4
  30. package/lib/orderSubscriber/grpcSubscription.d.ts +25 -0
  31. package/lib/orderSubscriber/grpcSubscription.js +68 -0
  32. package/lib/orderSubscriber/types.d.ts +9 -0
  33. package/lib/user.js +11 -4
  34. package/lib/userConfig.d.ts +6 -1
  35. package/lib/userMap/grpcSubscription.d.ts +26 -0
  36. package/lib/userMap/grpcSubscription.js +42 -0
  37. package/lib/userMap/userMap.js +14 -0
  38. package/lib/userMap/userMapConfig.d.ts +7 -0
  39. package/lib/userStatsConfig.d.ts +6 -0
  40. package/package.json +3 -1
  41. package/src/accounts/grpcAccountSubscriber.ts +158 -0
  42. package/src/accounts/grpcDriftClientAccountSubscriber.ts +196 -0
  43. package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +62 -0
  44. package/src/accounts/grpcProgramAccountSubscriber.ts +181 -0
  45. package/src/accounts/grpcUserAccountSubscriber.ts +48 -0
  46. package/src/accounts/grpcUserStatsAccountSubscriber.ts +51 -0
  47. package/src/accounts/types.ts +9 -0
  48. package/src/accounts/webSocketAccountSubscriber.ts +1 -1
  49. package/src/accounts/webSocketDriftClientAccountSubscriber.ts +3 -3
  50. package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
  51. package/src/driftClient.ts +28 -0
  52. package/src/driftClientConfig.ts +7 -0
  53. package/src/events/eventSubscriber.ts +125 -54
  54. package/src/events/eventsServerLogProvider.ts +152 -0
  55. package/src/events/pollingLogProvider.ts +1 -1
  56. package/src/events/types.ts +29 -6
  57. package/src/events/webSocketLogProvider.ts +4 -4
  58. package/src/orderSubscriber/OrderSubscriber.ts +15 -1
  59. package/src/orderSubscriber/grpcSubscription.ts +126 -0
  60. package/src/orderSubscriber/types.ts +10 -0
  61. package/src/user.ts +11 -0
  62. package/src/userConfig.ts +7 -1
  63. package/src/userMap/grpcSubscription.ts +83 -0
  64. package/src/userMap/userMap.ts +17 -1
  65. package/src/userMap/userMapConfig.ts +8 -0
  66. package/src/userStatsConfig.ts +7 -0
@@ -68,6 +68,7 @@ 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");
71
72
  /**
72
73
  * # DriftClient
73
74
  * 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.
@@ -80,7 +81,7 @@ class DriftClient {
80
81
  this._isSubscribed = val;
81
82
  }
82
83
  constructor(config) {
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;
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;
84
85
  this.users = new Map();
85
86
  this._isSubscribed = false;
86
87
  this.perpMarketLastSlotCache = new Map();
@@ -143,18 +144,32 @@ class DriftClient {
143
144
  accountLoader: config.accountSubscription.accountLoader,
144
145
  };
145
146
  }
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
+ }
146
161
  else {
147
162
  this.userAccountSubscriptionConfig = {
148
163
  type: 'websocket',
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,
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,
152
167
  };
153
168
  this.userStatsAccountSubscriptionConfig = {
154
169
  type: 'websocket',
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,
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,
158
173
  };
159
174
  }
160
175
  if (config.userStats) {
@@ -171,14 +186,20 @@ class DriftClient {
171
186
  const noMarketsAndOraclesSpecified = config.perpMarketIndexes === undefined &&
172
187
  config.spotMarketIndexes === undefined &&
173
188
  config.oracleInfos === undefined;
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);
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
+ });
176
197
  }
177
198
  else {
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);
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);
182
203
  }
183
204
  this.eventEmitter = this.accountSubscriber.eventEmitter;
184
205
  this.metricsEventEmitter = new events_1.EventEmitter();
@@ -186,7 +207,7 @@ class DriftClient {
186
207
  this.enableMetricsEvents = true;
187
208
  }
188
209
  this.txSender =
189
- (_6 = config.txSender) !== null && _6 !== void 0 ? _6 : new retryTxSender_1.RetryTxSender({
210
+ (_19 = config.txSender) !== null && _19 !== void 0 ? _19 : new retryTxSender_1.RetryTxSender({
190
211
  connection: this.connection,
191
212
  wallet: this.wallet,
192
213
  opts: this.opts,
@@ -5,6 +5,7 @@ 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';
8
9
  export type DriftClientConfig = {
9
10
  connection: Connection;
10
11
  wallet: IWallet;
@@ -38,4 +39,9 @@ export type DriftClientSubscriptionConfig = {
38
39
  } | {
39
40
  type: 'polling';
40
41
  accountLoader: BulkAccountLoader;
42
+ } | {
43
+ type: 'grpc';
44
+ configs: GrpcConfigs;
45
+ resubTimeoutMs?: number;
46
+ logResubMessages?: boolean;
41
47
  };
@@ -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,13 +10,14 @@ 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';
13
14
  export declare class OrderSubscriber {
14
15
  driftClient: DriftClient;
15
16
  usersAccounts: Map<string, {
16
17
  slot: number;
17
18
  userAccount: UserAccount;
18
19
  }>;
19
- subscription: PollingSubscription | WebsocketSubscription;
20
+ subscription: PollingSubscription | WebsocketSubscription | grpcSubscription;
20
21
  commitment: Commitment;
21
22
  eventEmitter: StrictEventEmitter<EventEmitter, OrderSubscriberEvents>;
22
23
  fetchPromise?: Promise<void>;
@@ -10,9 +10,10 @@ 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");
13
14
  class OrderSubscriber {
14
15
  constructor(config) {
15
- var _a, _b, _c;
16
+ var _a, _b, _c, _d, _e;
16
17
  this.usersAccounts = new Map();
17
18
  this.driftClient = config.driftClient;
18
19
  this.commitment = config.subscriptionConfig.commitment || 'processed';
@@ -22,8 +23,9 @@ class OrderSubscriber {
22
23
  frequency: config.subscriptionConfig.frequency,
23
24
  });
24
25
  }
25
- else {
26
- this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
26
+ else if (config.subscriptionConfig.type === 'grpc') {
27
+ this.subscription = new grpcSubscription_1.grpcSubscription({
28
+ grpcConfigs: config.subscriptionConfig.configs,
27
29
  orderSubscriber: this,
28
30
  commitment: this.commitment,
29
31
  skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
@@ -35,7 +37,20 @@ class OrderSubscriber {
35
37
  decoded: config.decodeData,
36
38
  });
37
39
  }
38
- if ((_c = config.fastDecode) !== null && _c !== void 0 ? _c : true) {
40
+ else {
41
+ this.subscription = new WebsocketSubscription_1.WebsocketSubscription({
42
+ orderSubscriber: this,
43
+ commitment: this.commitment,
44
+ skipInitialLoad: config.subscriptionConfig.skipInitialLoad,
45
+ 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,
48
+ },
49
+ resyncIntervalMs: config.subscriptionConfig.resyncIntervalMs,
50
+ decoded: config.decodeData,
51
+ });
52
+ }
53
+ if ((_e = config.fastDecode) !== null && _e !== void 0 ? _e : true) {
39
54
  this.decodeFn = (name, data) => (0, user_1.decodeUser)(data);
40
55
  }
41
56
  else {
@@ -0,0 +1,25 @@
1
+ import { Commitment } from '@solana/web3.js';
2
+ import { OrderSubscriber } from './OrderSubscriber';
3
+ import { GrpcConfigs, ResubOpts } from '../accounts/types';
4
+ export declare class grpcSubscription {
5
+ private orderSubscriber;
6
+ private commitment;
7
+ private skipInitialLoad;
8
+ private resubOpts?;
9
+ private resyncIntervalMs?;
10
+ private subscriber?;
11
+ private resyncTimeoutId?;
12
+ private decoded?;
13
+ private grpcConfigs;
14
+ constructor({ grpcConfigs, orderSubscriber, commitment, skipInitialLoad, resubOpts, resyncIntervalMs, decoded, }: {
15
+ grpcConfigs: GrpcConfigs;
16
+ orderSubscriber: OrderSubscriber;
17
+ commitment: Commitment;
18
+ skipInitialLoad?: boolean;
19
+ resubOpts?: ResubOpts;
20
+ resyncIntervalMs?: number;
21
+ decoded?: boolean;
22
+ });
23
+ subscribe(): Promise<void>;
24
+ unsubscribe(): Promise<void>;
25
+ }