@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.
- package/VERSION +1 -1
- package/lib/accounts/grpcAccountSubscriber.d.ts +16 -0
- package/lib/accounts/grpcAccountSubscriber.js +155 -0
- package/lib/accounts/grpcDriftClientAccountSubscriber.d.ts +13 -0
- package/lib/accounts/grpcDriftClientAccountSubscriber.js +96 -0
- package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.d.ts +10 -0
- package/lib/accounts/grpcInsuranceFundStakeAccountSubscriber.js +30 -0
- package/lib/accounts/grpcProgramAccountSubscriber.d.ts +19 -0
- package/lib/accounts/grpcProgramAccountSubscriber.js +161 -0
- package/lib/accounts/grpcUserAccountSubscriber.d.ts +10 -0
- package/lib/accounts/grpcUserAccountSubscriber.js +28 -0
- package/lib/accounts/grpcUserStatsAccountSubscriber.d.ts +10 -0
- package/lib/accounts/grpcUserStatsAccountSubscriber.js +28 -0
- package/lib/accounts/types.d.ts +8 -0
- package/lib/accounts/webSocketAccountSubscriber.d.ts +1 -1
- package/lib/accounts/webSocketDriftClientAccountSubscriber.d.ts +3 -3
- package/lib/accounts/webSocketProgramAccountSubscriber.d.ts +1 -1
- package/lib/driftClient.js +35 -14
- package/lib/driftClientConfig.d.ts +6 -0
- package/lib/events/eventSubscriber.d.ts +7 -0
- package/lib/events/eventSubscriber.js +69 -32
- package/lib/events/eventsServerLogProvider.d.ts +21 -0
- package/lib/events/eventsServerLogProvider.js +121 -0
- package/lib/events/pollingLogProvider.js +1 -1
- package/lib/events/types.d.ts +12 -5
- package/lib/events/types.js +5 -1
- package/lib/events/webSocketLogProvider.js +2 -2
- package/lib/orderSubscriber/OrderSubscriber.d.ts +2 -1
- package/lib/orderSubscriber/OrderSubscriber.js +19 -4
- package/lib/orderSubscriber/grpcSubscription.d.ts +25 -0
- package/lib/orderSubscriber/grpcSubscription.js +68 -0
- package/lib/orderSubscriber/types.d.ts +9 -0
- package/lib/user.js +11 -4
- package/lib/userConfig.d.ts +6 -1
- package/lib/userMap/grpcSubscription.d.ts +26 -0
- package/lib/userMap/grpcSubscription.js +42 -0
- package/lib/userMap/userMap.js +14 -0
- package/lib/userMap/userMapConfig.d.ts +7 -0
- package/lib/userStatsConfig.d.ts +6 -0
- package/package.json +3 -1
- package/src/accounts/grpcAccountSubscriber.ts +158 -0
- package/src/accounts/grpcDriftClientAccountSubscriber.ts +196 -0
- package/src/accounts/grpcInsuranceFundStakeAccountSubscriber.ts +62 -0
- package/src/accounts/grpcProgramAccountSubscriber.ts +181 -0
- package/src/accounts/grpcUserAccountSubscriber.ts +48 -0
- package/src/accounts/grpcUserStatsAccountSubscriber.ts +51 -0
- package/src/accounts/types.ts +9 -0
- package/src/accounts/webSocketAccountSubscriber.ts +1 -1
- package/src/accounts/webSocketDriftClientAccountSubscriber.ts +3 -3
- package/src/accounts/webSocketProgramAccountSubscriber.ts +1 -1
- package/src/driftClient.ts +28 -0
- package/src/driftClientConfig.ts +7 -0
- package/src/events/eventSubscriber.ts +125 -54
- package/src/events/eventsServerLogProvider.ts +152 -0
- package/src/events/pollingLogProvider.ts +1 -1
- package/src/events/types.ts +29 -6
- package/src/events/webSocketLogProvider.ts +4 -4
- package/src/orderSubscriber/OrderSubscriber.ts +15 -1
- package/src/orderSubscriber/grpcSubscription.ts +126 -0
- package/src/orderSubscriber/types.ts +10 -0
- package/src/user.ts +11 -0
- package/src/userConfig.ts +7 -1
- package/src/userMap/grpcSubscription.ts +83 -0
- package/src/userMap/userMap.ts +17 -1
- package/src/userMap/userMapConfig.ts +8 -0
- package/src/userStatsConfig.ts +7 -0
package/lib/driftClient.js
CHANGED
|
@@ -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: (
|
|
150
|
-
logResubMessages: (
|
|
151
|
-
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: (
|
|
156
|
-
logResubMessages: (
|
|
157
|
-
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 (((
|
|
175
|
-
this.accountSubscriber = new pollingDriftClientAccountSubscriber_1.PollingDriftClientAccountSubscriber(this.program, config.accountSubscription.accountLoader, (
|
|
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, (
|
|
179
|
-
resubTimeoutMs: (
|
|
180
|
-
logResubMessages: (
|
|
181
|
-
}, (
|
|
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
|
-
(
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 =
|
|
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
|
}
|
package/lib/events/types.d.ts
CHANGED
|
@@ -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
|
|
59
|
-
|
|
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
|
|
73
|
+
export type EventsServerLogProviderConfig = StreamingLogProviderConfig & {
|
|
74
|
+
type: 'events-server';
|
|
75
|
+
url: string;
|
|
76
|
+
};
|
|
77
|
+
export type LogProviderConfig = WebSocketLogProviderConfig | PollingLogProviderConfig | EventsServerLogProviderConfig;
|
package/lib/events/types.js
CHANGED
|
@@ -25,6 +25,10 @@ exports.DefaultEventSubscriptionOptions = {
|
|
|
25
25
|
commitment: 'confirmed',
|
|
26
26
|
maxTx: 4096,
|
|
27
27
|
logProviderConfig: {
|
|
28
|
-
type: '
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|