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

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.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.48.0-beta.20
1
+ 2.48.0-beta.22
@@ -27,7 +27,7 @@ class EventSubscriber {
27
27
  }
28
28
  this.eventEmitter = new events_1.EventEmitter();
29
29
  if (this.options.logProviderConfig.type === 'websocket') {
30
- this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(this.connection, this.address, this.options.commitment, this.options.resubTimeoutMs);
30
+ this.logProvider = new webSocketLogProvider_1.WebSocketLogProvider(this.connection, this.address, this.options.commitment, this.options.logProviderConfig.resubTimeoutMs);
31
31
  }
32
32
  else {
33
33
  this.logProvider = new pollingLogProvider_1.PollingLogProvider(this.connection, this.address, options.commitment, this.options.logProviderConfig.frequency, this.options.logProviderConfig.batchSize);
@@ -38,6 +38,27 @@ class EventSubscriber {
38
38
  if (this.logProvider.isSubscribed()) {
39
39
  return true;
40
40
  }
41
+ if (this.options.logProviderConfig.type === 'websocket') {
42
+ if (this.options.logProviderConfig.resubTimeoutMs) {
43
+ if (this.options.logProviderConfig.maxReconnectAttempts &&
44
+ this.options.logProviderConfig.maxReconnectAttempts > 0) {
45
+ const logProviderConfig = this.options
46
+ .logProviderConfig;
47
+ this.logProvider.eventEmitter.on('reconnect', (reconnectAttempts) => {
48
+ if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
49
+ console.log('Failing over to polling');
50
+ this.logProvider.eventEmitter.removeAllListeners('reconnect');
51
+ this.unsubscribe().then(() => {
52
+ this.logProvider = new pollingLogProvider_1.PollingLogProvider(this.connection, this.address, this.options.commitment, logProviderConfig.fallbackFrequency, logProviderConfig.fallbackBatchSize);
53
+ this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
54
+ this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
55
+ }, true);
56
+ });
57
+ }
58
+ });
59
+ }
60
+ }
61
+ }
41
62
  this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
42
63
  this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
43
64
  }, true);
@@ -95,7 +116,7 @@ class EventSubscriber {
95
116
  }
96
117
  }
97
118
  async unsubscribe() {
98
- return await this.logProvider.unsubscribe();
119
+ return await this.logProvider.unsubscribe(true);
99
120
  }
100
121
  parseEventsFromLogs(txSig, slot, logs) {
101
122
  const records = [];
@@ -11,7 +11,7 @@ export declare class PollingLogProvider implements LogProvider {
11
11
  private mutex;
12
12
  private firstFetch;
13
13
  constructor(connection: Connection, address: PublicKey, commitment: Commitment, frequency?: number, batchSize?: number);
14
- subscribe(callback: logProviderCallback, skipHistory?: boolean): boolean;
14
+ subscribe(callback: logProviderCallback, skipHistory?: boolean): Promise<boolean>;
15
15
  isSubscribed(): boolean;
16
16
  unsubscribe(): Promise<boolean>;
17
17
  }
@@ -11,7 +11,7 @@ class PollingLogProvider {
11
11
  this.firstFetch = true;
12
12
  this.finality = commitment === 'finalized' ? 'finalized' : 'confirmed';
13
13
  }
14
- subscribe(callback, skipHistory) {
14
+ async subscribe(callback, skipHistory) {
15
15
  if (this.intervalId) {
16
16
  return true;
17
17
  }
@@ -1,5 +1,7 @@
1
+ /// <reference types="node" />
1
2
  import { Commitment, PublicKey, TransactionSignature } from '@solana/web3.js';
2
3
  import { DepositRecord, FundingPaymentRecord, FundingRateRecord, LiquidationRecord, NewUserRecord, OrderActionRecord, OrderRecord, SettlePnlRecord, LPRecord, InsuranceFundRecord, SpotInterestRecord, InsuranceFundStakeRecord, CurveRecord, SwapRecord } from '../index';
4
+ import { EventEmitter } from 'events';
3
5
  export type EventSubscriptionOptions = {
4
6
  address?: PublicKey;
5
7
  eventTypes?: EventType[];
@@ -10,7 +12,6 @@ export type EventSubscriptionOptions = {
10
12
  maxTx?: number;
11
13
  logProviderConfig?: LogProviderConfig;
12
14
  untilTx?: TransactionSignature;
13
- resubTimeoutMs?: number;
14
15
  };
15
16
  export declare const DefaultEventSubscriptionOptions: EventSubscriptionOptions;
16
17
  export type EventSubscriptionOrderBy = 'blockchain' | 'client';
@@ -49,11 +50,16 @@ export type SortFn = (currentRecord: EventMap[EventType], newRecord: EventMap[Ev
49
50
  export type logProviderCallback = (txSig: TransactionSignature, slot: number, logs: string[], mostRecentBlockTime: number | undefined) => void;
50
51
  export interface LogProvider {
51
52
  isSubscribed(): boolean;
52
- subscribe(callback: logProviderCallback, skipHistory?: boolean): boolean;
53
- unsubscribe(): Promise<boolean>;
53
+ subscribe(callback: logProviderCallback, skipHistory?: boolean): Promise<boolean>;
54
+ unsubscribe(external?: boolean): Promise<boolean>;
55
+ eventEmitter?: EventEmitter;
54
56
  }
55
57
  export type WebSocketLogProviderConfig = {
56
58
  type: 'websocket';
59
+ resubTimeoutMs?: number;
60
+ maxReconnectAttempts?: number;
61
+ fallbackFrequency?: number;
62
+ fallbackBatchSize?: number;
57
63
  };
58
64
  export type PollingLogProviderConfig = {
59
65
  type: 'polling';
@@ -1,5 +1,7 @@
1
+ /// <reference types="node" />
1
2
  import { LogProvider, logProviderCallback } from './types';
2
3
  import { Commitment, Connection, PublicKey } from '@solana/web3.js';
4
+ import { EventEmitter } from 'events';
3
5
  export declare class WebSocketLogProvider implements LogProvider {
4
6
  private connection;
5
7
  private address;
@@ -7,12 +9,16 @@ export declare class WebSocketLogProvider implements LogProvider {
7
9
  private resubTimeoutMs?;
8
10
  private subscriptionId;
9
11
  private isUnsubscribing;
12
+ private externalUnsubscribe;
10
13
  private receivingData;
11
14
  private timeoutId?;
15
+ private reconnectAttempts;
16
+ eventEmitter?: EventEmitter;
12
17
  private callback?;
13
18
  constructor(connection: Connection, address: PublicKey, commitment: Commitment, resubTimeoutMs?: number);
14
- subscribe(callback: logProviderCallback): boolean;
19
+ subscribe(callback: logProviderCallback): Promise<boolean>;
20
+ setSubscription(callback: logProviderCallback): void;
15
21
  isSubscribed(): boolean;
16
- unsubscribe(): Promise<boolean>;
22
+ unsubscribe(external?: boolean): Promise<boolean>;
17
23
  private setTimeout;
18
24
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebSocketLogProvider = void 0;
4
+ const events_1 = require("events");
4
5
  class WebSocketLogProvider {
5
6
  constructor(connection, address, commitment, resubTimeoutMs) {
6
7
  this.connection = connection;
@@ -8,13 +9,31 @@ class WebSocketLogProvider {
8
9
  this.commitment = commitment;
9
10
  this.resubTimeoutMs = resubTimeoutMs;
10
11
  this.isUnsubscribing = false;
12
+ this.externalUnsubscribe = false;
11
13
  this.receivingData = false;
14
+ this.reconnectAttempts = 0;
15
+ if (this.resubTimeoutMs) {
16
+ this.eventEmitter = new events_1.EventEmitter();
17
+ }
12
18
  }
13
- subscribe(callback) {
19
+ async subscribe(callback) {
14
20
  if (this.subscriptionId) {
15
21
  return true;
16
22
  }
17
23
  this.callback = callback;
24
+ try {
25
+ this.setSubscription(callback);
26
+ }
27
+ catch (error) {
28
+ // Sometimes ws connection isn't ready, give it a few secs
29
+ setTimeout(() => this.setSubscription(callback), 2000);
30
+ }
31
+ if (this.resubTimeoutMs) {
32
+ this.setTimeout();
33
+ }
34
+ return true;
35
+ }
36
+ setSubscription(callback) {
18
37
  this.subscriptionId = this.connection.onLogs(this.address, (logs, ctx) => {
19
38
  if (this.resubTimeoutMs && !this.isUnsubscribing) {
20
39
  this.receivingData = true;
@@ -23,30 +42,27 @@ class WebSocketLogProvider {
23
42
  }
24
43
  callback(logs.signature, ctx.slot, logs.logs, undefined);
25
44
  }, this.commitment);
26
- if (this.resubTimeoutMs) {
27
- this.setTimeout();
28
- }
29
- return true;
30
45
  }
31
46
  isSubscribed() {
32
47
  return this.subscriptionId !== undefined;
33
48
  }
34
- async unsubscribe() {
49
+ async unsubscribe(external = false) {
35
50
  this.isUnsubscribing = true;
51
+ this.externalUnsubscribe = external;
36
52
  clearTimeout(this.timeoutId);
53
+ this.timeoutId = undefined;
37
54
  if (this.subscriptionId !== undefined) {
38
- this.connection
39
- .removeOnLogsListener(this.subscriptionId)
40
- .then(() => {
55
+ try {
56
+ await this.connection.removeOnLogsListener(this.subscriptionId);
41
57
  this.subscriptionId = undefined;
42
58
  this.isUnsubscribing = false;
43
59
  return true;
44
- })
45
- .catch((err) => {
60
+ }
61
+ catch (err) {
46
62
  console.log('Error unsubscribing from logs: ', err);
47
63
  this.isUnsubscribing = false;
48
64
  return false;
49
- });
65
+ }
50
66
  }
51
67
  else {
52
68
  this.isUnsubscribing = false;
@@ -55,14 +71,16 @@ class WebSocketLogProvider {
55
71
  }
56
72
  setTimeout() {
57
73
  this.timeoutId = setTimeout(async () => {
58
- if (this.isUnsubscribing) {
74
+ if (this.isUnsubscribing || this.externalUnsubscribe) {
59
75
  // If we are in the process of unsubscribing, do not attempt to resubscribe
60
76
  return;
61
77
  }
62
78
  if (this.receivingData) {
63
- console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing`);
79
+ console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`);
64
80
  await this.unsubscribe();
65
81
  this.receivingData = false;
82
+ this.reconnectAttempts++;
83
+ this.eventEmitter.emit('reconnect', this.reconnectAttempts);
66
84
  this.subscribe(this.callback);
67
85
  }
68
86
  }, this.resubTimeoutMs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@drift-labs/sdk",
3
- "version": "2.48.0-beta.20",
3
+ "version": "2.48.0-beta.22",
4
4
  "main": "lib/index.js",
5
5
  "types": "lib/index.d.ts",
6
6
  "author": "crispheaney",
@@ -8,6 +8,7 @@ import {
8
8
  EventMap,
9
9
  LogProvider,
10
10
  EventSubscriberEvents,
11
+ WebSocketLogProviderConfig,
11
12
  } from './types';
12
13
  import { TxEventCache } from './txEventCache';
13
14
  import { EventList } from './eventList';
@@ -57,7 +58,7 @@ export class EventSubscriber {
57
58
  this.connection,
58
59
  this.address,
59
60
  this.options.commitment,
60
- this.options.resubTimeoutMs
61
+ this.options.logProviderConfig.resubTimeoutMs
61
62
  );
62
63
  } else {
63
64
  this.logProvider = new PollingLogProvider(
@@ -76,6 +77,48 @@ export class EventSubscriber {
76
77
  return true;
77
78
  }
78
79
 
80
+ if (this.options.logProviderConfig.type === 'websocket') {
81
+ if (this.options.logProviderConfig.resubTimeoutMs) {
82
+ if (
83
+ this.options.logProviderConfig.maxReconnectAttempts &&
84
+ this.options.logProviderConfig.maxReconnectAttempts > 0
85
+ ) {
86
+ const logProviderConfig = this.options
87
+ .logProviderConfig as WebSocketLogProviderConfig;
88
+ this.logProvider.eventEmitter.on(
89
+ 'reconnect',
90
+ (reconnectAttempts) => {
91
+ if (
92
+ reconnectAttempts > logProviderConfig.maxReconnectAttempts
93
+ ) {
94
+ console.log('Failing over to polling');
95
+ this.logProvider.eventEmitter.removeAllListeners('reconnect');
96
+ this.unsubscribe().then(() => {
97
+ this.logProvider = new PollingLogProvider(
98
+ this.connection,
99
+ this.address,
100
+ this.options.commitment,
101
+ logProviderConfig.fallbackFrequency,
102
+ logProviderConfig.fallbackBatchSize
103
+ );
104
+ this.logProvider.subscribe(
105
+ (txSig, slot, logs, mostRecentBlockTime) => {
106
+ this.handleTxLogs(
107
+ txSig,
108
+ slot,
109
+ logs,
110
+ mostRecentBlockTime
111
+ );
112
+ },
113
+ true
114
+ );
115
+ });
116
+ }
117
+ }
118
+ );
119
+ }
120
+ }
121
+ }
79
122
  this.logProvider.subscribe((txSig, slot, logs, mostRecentBlockTime) => {
80
123
  this.handleTxLogs(txSig, slot, logs, mostRecentBlockTime);
81
124
  }, true);
@@ -159,7 +202,7 @@ export class EventSubscriber {
159
202
  }
160
203
 
161
204
  public async unsubscribe(): Promise<boolean> {
162
- return await this.logProvider.unsubscribe();
205
+ return await this.logProvider.unsubscribe(true);
163
206
  }
164
207
 
165
208
  private parseEventsFromLogs(
@@ -25,10 +25,10 @@ export class PollingLogProvider implements LogProvider {
25
25
  this.finality = commitment === 'finalized' ? 'finalized' : 'confirmed';
26
26
  }
27
27
 
28
- public subscribe(
28
+ public async subscribe(
29
29
  callback: logProviderCallback,
30
30
  skipHistory?: boolean
31
- ): boolean {
31
+ ): Promise<boolean> {
32
32
  if (this.intervalId) {
33
33
  return true;
34
34
  }
@@ -15,6 +15,7 @@ import {
15
15
  CurveRecord,
16
16
  SwapRecord,
17
17
  } from '../index';
18
+ import { EventEmitter } from 'events';
18
19
 
19
20
  export type EventSubscriptionOptions = {
20
21
  address?: PublicKey;
@@ -28,7 +29,6 @@ export type EventSubscriptionOptions = {
28
29
  // when the subscription starts, client might want to backtrack and fetch old tx's
29
30
  // this specifies how far to backtrack
30
31
  untilTx?: TransactionSignature;
31
- resubTimeoutMs?: number;
32
32
  };
33
33
 
34
34
  export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {
@@ -127,12 +127,20 @@ export type logProviderCallback = (
127
127
 
128
128
  export interface LogProvider {
129
129
  isSubscribed(): boolean;
130
- subscribe(callback: logProviderCallback, skipHistory?: boolean): boolean;
131
- unsubscribe(): Promise<boolean>;
130
+ subscribe(
131
+ callback: logProviderCallback,
132
+ skipHistory?: boolean
133
+ ): Promise<boolean>;
134
+ unsubscribe(external?: boolean): Promise<boolean>;
135
+ eventEmitter?: EventEmitter;
132
136
  }
133
137
 
134
138
  export type WebSocketLogProviderConfig = {
135
139
  type: 'websocket';
140
+ resubTimeoutMs?: number;
141
+ maxReconnectAttempts?: number;
142
+ fallbackFrequency?: number;
143
+ fallbackBatchSize?: number;
136
144
  };
137
145
 
138
146
  export type PollingLogProviderConfig = {
@@ -1,25 +1,48 @@
1
1
  import { LogProvider, logProviderCallback } from './types';
2
2
  import { Commitment, Connection, PublicKey } from '@solana/web3.js';
3
+ import { EventEmitter } from 'events';
3
4
 
4
5
  export class WebSocketLogProvider implements LogProvider {
5
6
  private subscriptionId: number;
6
7
  private isUnsubscribing = false;
8
+ private externalUnsubscribe = false;
7
9
  private receivingData = false;
8
10
  private timeoutId?: NodeJS.Timeout;
11
+ private reconnectAttempts = 0;
12
+ eventEmitter?: EventEmitter;
9
13
  private callback?: logProviderCallback;
10
14
  public constructor(
11
15
  private connection: Connection,
12
16
  private address: PublicKey,
13
17
  private commitment: Commitment,
14
18
  private resubTimeoutMs?: number
15
- ) {}
19
+ ) {
20
+ if (this.resubTimeoutMs) {
21
+ this.eventEmitter = new EventEmitter();
22
+ }
23
+ }
16
24
 
17
- public subscribe(callback: logProviderCallback): boolean {
25
+ public async subscribe(callback: logProviderCallback): Promise<boolean> {
18
26
  if (this.subscriptionId) {
19
27
  return true;
20
28
  }
21
29
 
22
30
  this.callback = callback;
31
+ try {
32
+ this.setSubscription(callback);
33
+ } catch (error) {
34
+ // Sometimes ws connection isn't ready, give it a few secs
35
+ setTimeout(() => this.setSubscription(callback), 2000);
36
+ }
37
+
38
+ if (this.resubTimeoutMs) {
39
+ this.setTimeout();
40
+ }
41
+
42
+ return true;
43
+ }
44
+
45
+ public setSubscription(callback: logProviderCallback): void {
23
46
  this.subscriptionId = this.connection.onLogs(
24
47
  this.address,
25
48
  (logs, ctx) => {
@@ -32,35 +55,29 @@ export class WebSocketLogProvider implements LogProvider {
32
55
  },
33
56
  this.commitment
34
57
  );
35
-
36
- if (this.resubTimeoutMs) {
37
- this.setTimeout();
38
- }
39
-
40
- return true;
41
58
  }
42
59
 
43
60
  public isSubscribed(): boolean {
44
61
  return this.subscriptionId !== undefined;
45
62
  }
46
63
 
47
- public async unsubscribe(): Promise<boolean> {
64
+ public async unsubscribe(external = false): Promise<boolean> {
48
65
  this.isUnsubscribing = true;
66
+ this.externalUnsubscribe = external;
49
67
  clearTimeout(this.timeoutId);
68
+ this.timeoutId = undefined;
50
69
 
51
70
  if (this.subscriptionId !== undefined) {
52
- this.connection
53
- .removeOnLogsListener(this.subscriptionId)
54
- .then(() => {
55
- this.subscriptionId = undefined;
56
- this.isUnsubscribing = false;
57
- return true;
58
- })
59
- .catch((err) => {
60
- console.log('Error unsubscribing from logs: ', err);
61
- this.isUnsubscribing = false;
62
- return false;
63
- });
71
+ try {
72
+ await this.connection.removeOnLogsListener(this.subscriptionId);
73
+ this.subscriptionId = undefined;
74
+ this.isUnsubscribing = false;
75
+ return true;
76
+ } catch (err) {
77
+ console.log('Error unsubscribing from logs: ', err);
78
+ this.isUnsubscribing = false;
79
+ return false;
80
+ }
64
81
  } else {
65
82
  this.isUnsubscribing = false;
66
83
  return true;
@@ -69,15 +86,21 @@ export class WebSocketLogProvider implements LogProvider {
69
86
 
70
87
  private setTimeout(): void {
71
88
  this.timeoutId = setTimeout(async () => {
72
- if (this.isUnsubscribing) {
89
+ if (this.isUnsubscribing || this.externalUnsubscribe) {
73
90
  // If we are in the process of unsubscribing, do not attempt to resubscribe
74
91
  return;
75
92
  }
76
93
 
77
94
  if (this.receivingData) {
78
- console.log(`No log data in ${this.resubTimeoutMs}ms, resubscribing`);
95
+ console.log(
96
+ `No log data in ${this.resubTimeoutMs}ms, resubscribing on attempt ${
97
+ this.reconnectAttempts + 1
98
+ }`
99
+ );
79
100
  await this.unsubscribe();
80
101
  this.receivingData = false;
102
+ this.reconnectAttempts++;
103
+ this.eventEmitter.emit('reconnect', this.reconnectAttempts);
81
104
  this.subscribe(this.callback);
82
105
  }
83
106
  }, this.resubTimeoutMs);