@drift-labs/sdk 2.96.0-beta.1 → 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/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/package.json +1 -1
- 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/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.96.0-beta.
|
|
1
|
+
2.96.0-beta.2
|
|
@@ -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++;
|
package/package.json
CHANGED
|
@@ -9,6 +9,10 @@ import {
|
|
|
9
9
|
LogProvider,
|
|
10
10
|
EventSubscriberEvents,
|
|
11
11
|
WebSocketLogProviderConfig,
|
|
12
|
+
PollingLogProviderConfig,
|
|
13
|
+
EventsServerLogProviderConfig,
|
|
14
|
+
LogProviderType,
|
|
15
|
+
StreamingLogProviderConfig,
|
|
12
16
|
} from './types';
|
|
13
17
|
import { TxEventCache } from './txEventCache';
|
|
14
18
|
import { EventList } from './eventList';
|
|
@@ -19,6 +23,7 @@ import { EventEmitter } from 'events';
|
|
|
19
23
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
20
24
|
import { getSortFn } from './sort';
|
|
21
25
|
import { parseLogs } from './parse';
|
|
26
|
+
import { EventsServerLogProvider } from './eventsServerLogProvider';
|
|
22
27
|
|
|
23
28
|
export class EventSubscriber {
|
|
24
29
|
private address: PublicKey;
|
|
@@ -27,6 +32,7 @@ export class EventSubscriber {
|
|
|
27
32
|
private awaitTxPromises = new Map<string, Promise<void>>();
|
|
28
33
|
private awaitTxResolver = new Map<string, () => void>();
|
|
29
34
|
private logProvider: LogProvider;
|
|
35
|
+
private currentProviderType: LogProviderType;
|
|
30
36
|
public eventEmitter: StrictEventEmitter<EventEmitter, EventSubscriberEvents>;
|
|
31
37
|
private lastSeenSlot: number;
|
|
32
38
|
private lastSeenBlockTime: number | undefined;
|
|
@@ -43,22 +49,57 @@ export class EventSubscriber {
|
|
|
43
49
|
this.eventListMap = new Map<EventType, EventList<EventType>>();
|
|
44
50
|
this.eventEmitter = new EventEmitter();
|
|
45
51
|
|
|
46
|
-
|
|
52
|
+
this.currentProviderType = this.options.logProviderConfig.type;
|
|
53
|
+
this.initializeLogProvider();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private initializeLogProvider(subscribe = false) {
|
|
57
|
+
if (this.currentProviderType === 'websocket') {
|
|
58
|
+
const logProviderConfig = this.options
|
|
59
|
+
.logProviderConfig as WebSocketLogProviderConfig;
|
|
47
60
|
this.logProvider = new WebSocketLogProvider(
|
|
48
61
|
// @ts-ignore
|
|
49
62
|
this.connection,
|
|
50
63
|
this.address,
|
|
51
64
|
this.options.commitment,
|
|
52
|
-
|
|
65
|
+
logProviderConfig.resubTimeoutMs
|
|
53
66
|
);
|
|
54
|
-
} else {
|
|
67
|
+
} else if (this.currentProviderType === 'polling') {
|
|
68
|
+
const logProviderConfig = this.options
|
|
69
|
+
.logProviderConfig as PollingLogProviderConfig;
|
|
55
70
|
this.logProvider = new PollingLogProvider(
|
|
56
71
|
// @ts-ignore
|
|
57
72
|
this.connection,
|
|
58
73
|
this.address,
|
|
59
|
-
options.commitment,
|
|
60
|
-
|
|
61
|
-
|
|
74
|
+
this.options.commitment,
|
|
75
|
+
logProviderConfig.frequency,
|
|
76
|
+
logProviderConfig.batchSize
|
|
77
|
+
);
|
|
78
|
+
} else if (this.currentProviderType === 'events-server') {
|
|
79
|
+
const logProviderConfig = this.options
|
|
80
|
+
.logProviderConfig as EventsServerLogProviderConfig;
|
|
81
|
+
this.logProvider = new EventsServerLogProvider(
|
|
82
|
+
logProviderConfig.url,
|
|
83
|
+
this.options.eventTypes,
|
|
84
|
+
this.options.address ? this.options.address.toString() : undefined
|
|
85
|
+
);
|
|
86
|
+
} else {
|
|
87
|
+
throw new Error(`Invalid log provider type: ${this.currentProviderType}`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (subscribe) {
|
|
91
|
+
this.logProvider.subscribe(
|
|
92
|
+
(txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
|
|
93
|
+
this.handleTxLogs(
|
|
94
|
+
txSig,
|
|
95
|
+
slot,
|
|
96
|
+
logs,
|
|
97
|
+
mostRecentBlockTime,
|
|
98
|
+
this.currentProviderType === 'events-server',
|
|
99
|
+
txSigIndex
|
|
100
|
+
);
|
|
101
|
+
},
|
|
102
|
+
true
|
|
62
103
|
);
|
|
63
104
|
}
|
|
64
105
|
}
|
|
@@ -77,6 +118,33 @@ export class EventSubscriber {
|
|
|
77
118
|
}
|
|
78
119
|
}
|
|
79
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Implements fallback logic for reconnecting to LogProvider. Currently terminates at polling,
|
|
123
|
+
* could be improved to try the original type again after some cooldown.
|
|
124
|
+
*/
|
|
125
|
+
private updateFallbackProviderType(
|
|
126
|
+
reconnectAttempts: number,
|
|
127
|
+
maxReconnectAttempts: number
|
|
128
|
+
) {
|
|
129
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
let nextProviderType = this.currentProviderType;
|
|
134
|
+
if (this.currentProviderType === 'events-server') {
|
|
135
|
+
nextProviderType = 'websocket';
|
|
136
|
+
} else if (this.currentProviderType === 'websocket') {
|
|
137
|
+
nextProviderType = 'polling';
|
|
138
|
+
} else if (this.currentProviderType === 'polling') {
|
|
139
|
+
nextProviderType = 'polling';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log(
|
|
143
|
+
`EventSubscriber: Failing over providerType ${this.currentProviderType} to ${nextProviderType}`
|
|
144
|
+
);
|
|
145
|
+
this.currentProviderType = nextProviderType;
|
|
146
|
+
}
|
|
147
|
+
|
|
80
148
|
public async subscribe(): Promise<boolean> {
|
|
81
149
|
try {
|
|
82
150
|
if (this.logProvider.isSubscribed()) {
|
|
@@ -85,52 +153,46 @@ export class EventSubscriber {
|
|
|
85
153
|
|
|
86
154
|
this.populateInitialEventListMap();
|
|
87
155
|
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
logProviderConfig.fallbackFrequency,
|
|
111
|
-
logProviderConfig.fallbackBatchSize
|
|
112
|
-
);
|
|
113
|
-
this.logProvider.subscribe(
|
|
114
|
-
(txSig, slot, logs, mostRecentBlockTime) => {
|
|
115
|
-
this.handleTxLogs(
|
|
116
|
-
txSig,
|
|
117
|
-
slot,
|
|
118
|
-
logs,
|
|
119
|
-
mostRecentBlockTime
|
|
120
|
-
);
|
|
121
|
-
},
|
|
122
|
-
true
|
|
123
|
-
);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
156
|
+
if (
|
|
157
|
+
this.options.logProviderConfig.type === 'websocket' ||
|
|
158
|
+
this.options.logProviderConfig.type === 'events-server'
|
|
159
|
+
) {
|
|
160
|
+
const logProviderConfig = this.options
|
|
161
|
+
.logProviderConfig as StreamingLogProviderConfig;
|
|
162
|
+
|
|
163
|
+
if (this.logProvider.eventEmitter) {
|
|
164
|
+
this.logProvider.eventEmitter.on(
|
|
165
|
+
'reconnect',
|
|
166
|
+
async (reconnectAttempts) => {
|
|
167
|
+
if (reconnectAttempts > logProviderConfig.maxReconnectAttempts) {
|
|
168
|
+
console.log(
|
|
169
|
+
`EventSubscriber: Reconnect attempts ${reconnectAttempts}/${logProviderConfig.maxReconnectAttempts}, reconnecting...`
|
|
170
|
+
);
|
|
171
|
+
this.logProvider.eventEmitter.removeAllListeners('reconnect');
|
|
172
|
+
await this.unsubscribe();
|
|
173
|
+
this.updateFallbackProviderType(
|
|
174
|
+
reconnectAttempts,
|
|
175
|
+
logProviderConfig.maxReconnectAttempts
|
|
176
|
+
);
|
|
177
|
+
this.initializeLogProvider(true);
|
|
126
178
|
}
|
|
127
|
-
|
|
128
|
-
|
|
179
|
+
}
|
|
180
|
+
);
|
|
129
181
|
}
|
|
130
182
|
}
|
|
131
|
-
this.logProvider.subscribe(
|
|
132
|
-
|
|
133
|
-
|
|
183
|
+
this.logProvider.subscribe(
|
|
184
|
+
(txSig, slot, logs, mostRecentBlockTime, txSigIndex) => {
|
|
185
|
+
this.handleTxLogs(
|
|
186
|
+
txSig,
|
|
187
|
+
slot,
|
|
188
|
+
logs,
|
|
189
|
+
mostRecentBlockTime,
|
|
190
|
+
this.currentProviderType === 'events-server',
|
|
191
|
+
txSigIndex
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
true
|
|
195
|
+
);
|
|
134
196
|
|
|
135
197
|
return true;
|
|
136
198
|
} catch (e) {
|
|
@@ -144,13 +206,20 @@ export class EventSubscriber {
|
|
|
144
206
|
txSig: TransactionSignature,
|
|
145
207
|
slot: number,
|
|
146
208
|
logs: string[],
|
|
147
|
-
mostRecentBlockTime: number | undefined
|
|
209
|
+
mostRecentBlockTime: number | undefined,
|
|
210
|
+
fromEventsServer = false,
|
|
211
|
+
txSigIndex: number | undefined = undefined
|
|
148
212
|
): void {
|
|
149
|
-
if (this.txEventCache.has(txSig)) {
|
|
213
|
+
if (!fromEventsServer && this.txEventCache.has(txSig)) {
|
|
150
214
|
return;
|
|
151
215
|
}
|
|
152
216
|
|
|
153
|
-
const wrappedEvents = this.parseEventsFromLogs(
|
|
217
|
+
const wrappedEvents = this.parseEventsFromLogs(
|
|
218
|
+
txSig,
|
|
219
|
+
slot,
|
|
220
|
+
logs,
|
|
221
|
+
txSigIndex
|
|
222
|
+
);
|
|
154
223
|
|
|
155
224
|
for (const wrappedEvent of wrappedEvents) {
|
|
156
225
|
this.eventListMap.get(wrappedEvent.eventType).insert(wrappedEvent);
|
|
@@ -225,7 +294,8 @@ export class EventSubscriber {
|
|
|
225
294
|
private parseEventsFromLogs(
|
|
226
295
|
txSig: TransactionSignature,
|
|
227
296
|
slot: number,
|
|
228
|
-
logs: string[]
|
|
297
|
+
logs: string[],
|
|
298
|
+
txSigIndex: number | undefined
|
|
229
299
|
): WrappedEvents {
|
|
230
300
|
const records = [];
|
|
231
301
|
// @ts-ignore
|
|
@@ -238,7 +308,8 @@ export class EventSubscriber {
|
|
|
238
308
|
event.data.txSig = txSig;
|
|
239
309
|
event.data.slot = slot;
|
|
240
310
|
event.data.eventType = event.name;
|
|
241
|
-
event.data.txSigIndex =
|
|
311
|
+
event.data.txSigIndex =
|
|
312
|
+
txSigIndex !== undefined ? txSigIndex : runningEventIndex;
|
|
242
313
|
records.push(event.data);
|
|
243
314
|
}
|
|
244
315
|
runningEventIndex++;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// import WebSocket from 'ws';
|
|
2
|
+
import { logProviderCallback, EventType, LogProvider } from './types';
|
|
3
|
+
import { EventEmitter } from 'events';
|
|
4
|
+
|
|
5
|
+
// browser support
|
|
6
|
+
let WebSocketImpl: typeof WebSocket;
|
|
7
|
+
if (typeof window !== 'undefined' && window.WebSocket) {
|
|
8
|
+
WebSocketImpl = window.WebSocket;
|
|
9
|
+
} else {
|
|
10
|
+
WebSocketImpl = require('ws');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const EVENT_SERVER_HEARTBEAT_INTERVAL_MS = 5000;
|
|
14
|
+
const ALLOWED_MISSED_HEARTBEATS = 3;
|
|
15
|
+
|
|
16
|
+
export class EventsServerLogProvider implements LogProvider {
|
|
17
|
+
private ws?: WebSocket;
|
|
18
|
+
private callback?: logProviderCallback;
|
|
19
|
+
private isUnsubscribing = false;
|
|
20
|
+
private externalUnsubscribe = false;
|
|
21
|
+
private lastHeartbeat = 0;
|
|
22
|
+
private timeoutId?: NodeJS.Timeout;
|
|
23
|
+
private reconnectAttempts = 0;
|
|
24
|
+
eventEmitter?: EventEmitter;
|
|
25
|
+
|
|
26
|
+
public constructor(
|
|
27
|
+
private readonly url: string,
|
|
28
|
+
private readonly eventTypes: EventType[],
|
|
29
|
+
private readonly userAccount?: string
|
|
30
|
+
) {
|
|
31
|
+
this.eventEmitter = new EventEmitter();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public isSubscribed(): boolean {
|
|
35
|
+
return this.ws !== undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public async subscribe(callback: logProviderCallback): Promise<boolean> {
|
|
39
|
+
if (this.ws !== undefined) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
this.ws = new WebSocketImpl(this.url);
|
|
43
|
+
|
|
44
|
+
this.callback = callback;
|
|
45
|
+
this.ws.addEventListener('open', () => {
|
|
46
|
+
for (const channel of this.eventTypes) {
|
|
47
|
+
const subscribeMessage = {
|
|
48
|
+
type: 'subscribe',
|
|
49
|
+
channel: channel,
|
|
50
|
+
};
|
|
51
|
+
if (this.userAccount) {
|
|
52
|
+
subscribeMessage['user'] = this.userAccount;
|
|
53
|
+
}
|
|
54
|
+
this.ws.send(JSON.stringify(subscribeMessage));
|
|
55
|
+
}
|
|
56
|
+
this.reconnectAttempts = 0;
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.ws.addEventListener('message', (data) => {
|
|
60
|
+
try {
|
|
61
|
+
if (!this.isUnsubscribing) {
|
|
62
|
+
clearTimeout(this.timeoutId);
|
|
63
|
+
this.setTimeout();
|
|
64
|
+
if (this.reconnectAttempts > 0) {
|
|
65
|
+
console.log(
|
|
66
|
+
'eventsServerLogProvider: Resetting reconnect attempts to 0'
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
this.reconnectAttempts = 0;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const parsedData = JSON.parse(data.data.toString());
|
|
73
|
+
if (parsedData.channel === 'heartbeat') {
|
|
74
|
+
this.lastHeartbeat = Date.now();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (parsedData.message !== undefined) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const event = JSON.parse(parsedData.data);
|
|
81
|
+
this.callback(
|
|
82
|
+
event.txSig,
|
|
83
|
+
event.slot,
|
|
84
|
+
[
|
|
85
|
+
'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH invoke [1]',
|
|
86
|
+
event.rawLog,
|
|
87
|
+
'Program dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH success',
|
|
88
|
+
],
|
|
89
|
+
undefined,
|
|
90
|
+
event.txSigIndex
|
|
91
|
+
);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error parsing message:', error);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
this.ws.addEventListener('close', () => {
|
|
98
|
+
console.log('eventsServerLogProvider: WebSocket closed');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
this.ws.addEventListener('error', (error) => {
|
|
102
|
+
console.error('eventsServerLogProvider: WebSocket error:', error);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
this.setTimeout();
|
|
106
|
+
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public async unsubscribe(external = false): Promise<boolean> {
|
|
111
|
+
this.isUnsubscribing = true;
|
|
112
|
+
this.externalUnsubscribe = external;
|
|
113
|
+
if (this.timeoutId) {
|
|
114
|
+
clearInterval(this.timeoutId);
|
|
115
|
+
this.timeoutId = undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (this.ws !== undefined) {
|
|
119
|
+
this.ws.close();
|
|
120
|
+
this.ws = undefined;
|
|
121
|
+
return true;
|
|
122
|
+
} else {
|
|
123
|
+
this.isUnsubscribing = false;
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private setTimeout(): void {
|
|
129
|
+
this.timeoutId = setTimeout(async () => {
|
|
130
|
+
if (this.isUnsubscribing || this.externalUnsubscribe) {
|
|
131
|
+
// If we are in the process of unsubscribing, do not attempt to resubscribe
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const timeSinceLastHeartbeat = Date.now() - this.lastHeartbeat;
|
|
136
|
+
if (
|
|
137
|
+
timeSinceLastHeartbeat >
|
|
138
|
+
EVENT_SERVER_HEARTBEAT_INTERVAL_MS * ALLOWED_MISSED_HEARTBEATS
|
|
139
|
+
) {
|
|
140
|
+
console.log(
|
|
141
|
+
`eventServerLogProvider: No heartbeat in ${timeSinceLastHeartbeat}ms, resubscribing on attempt ${
|
|
142
|
+
this.reconnectAttempts + 1
|
|
143
|
+
}`
|
|
144
|
+
);
|
|
145
|
+
await this.unsubscribe();
|
|
146
|
+
this.reconnectAttempts++;
|
|
147
|
+
this.eventEmitter.emit('reconnect', this.reconnectAttempts);
|
|
148
|
+
this.subscribe(this.callback);
|
|
149
|
+
}
|
|
150
|
+
}, EVENT_SERVER_HEARTBEAT_INTERVAL_MS * 2);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -60,7 +60,7 @@ export class PollingLogProvider implements LogProvider {
|
|
|
60
60
|
const { mostRecentTx, transactionLogs } = response;
|
|
61
61
|
|
|
62
62
|
for (const { txSig, slot, logs } of transactionLogs) {
|
|
63
|
-
callback(txSig, slot, logs, response.mostRecentBlockTime);
|
|
63
|
+
callback(txSig, slot, logs, response.mostRecentBlockTime, undefined);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
this.mostRecentSeenTx = mostRecentTx;
|
package/src/events/types.ts
CHANGED
|
@@ -56,7 +56,11 @@ export const DefaultEventSubscriptionOptions: EventSubscriptionOptions = {
|
|
|
56
56
|
commitment: 'confirmed',
|
|
57
57
|
maxTx: 4096,
|
|
58
58
|
logProviderConfig: {
|
|
59
|
-
type: '
|
|
59
|
+
type: 'events-server',
|
|
60
|
+
url: 'wss://events.drift.trade/ws',
|
|
61
|
+
maxReconnectAttempts: 5,
|
|
62
|
+
fallbackFrequency: 1000,
|
|
63
|
+
fallbackBatchSize: 100,
|
|
60
64
|
},
|
|
61
65
|
};
|
|
62
66
|
|
|
@@ -126,7 +130,8 @@ export type logProviderCallback = (
|
|
|
126
130
|
txSig: TransactionSignature,
|
|
127
131
|
slot: number,
|
|
128
132
|
logs: string[],
|
|
129
|
-
mostRecentBlockTime: number | undefined
|
|
133
|
+
mostRecentBlockTime: number | undefined,
|
|
134
|
+
txSigIndex: number | undefined
|
|
130
135
|
) => void;
|
|
131
136
|
|
|
132
137
|
export interface LogProvider {
|
|
@@ -139,20 +144,38 @@ export interface LogProvider {
|
|
|
139
144
|
eventEmitter?: EventEmitter;
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
export type
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
export type LogProviderType = 'websocket' | 'polling' | 'events-server';
|
|
148
|
+
|
|
149
|
+
export type StreamingLogProviderConfig = {
|
|
150
|
+
/// Max number of times to try reconnecting before failing over to fallback provider
|
|
145
151
|
maxReconnectAttempts?: number;
|
|
152
|
+
/// used for PollingLogProviderConfig on fallback
|
|
146
153
|
fallbackFrequency?: number;
|
|
154
|
+
/// used for PollingLogProviderConfig on fallback
|
|
147
155
|
fallbackBatchSize?: number;
|
|
148
156
|
};
|
|
149
157
|
|
|
158
|
+
export type WebSocketLogProviderConfig = StreamingLogProviderConfig & {
|
|
159
|
+
type: 'websocket';
|
|
160
|
+
/// Max time to wait before resubscribing
|
|
161
|
+
resubTimeoutMs?: number;
|
|
162
|
+
};
|
|
163
|
+
|
|
150
164
|
export type PollingLogProviderConfig = {
|
|
151
165
|
type: 'polling';
|
|
166
|
+
/// frequency to poll for new events
|
|
152
167
|
frequency: number;
|
|
168
|
+
/// max number of events to fetch per poll
|
|
153
169
|
batchSize?: number;
|
|
154
170
|
};
|
|
155
171
|
|
|
172
|
+
export type EventsServerLogProviderConfig = StreamingLogProviderConfig & {
|
|
173
|
+
type: 'events-server';
|
|
174
|
+
/// url of the events server
|
|
175
|
+
url: string;
|
|
176
|
+
};
|
|
177
|
+
|
|
156
178
|
export type LogProviderConfig =
|
|
157
179
|
| WebSocketLogProviderConfig
|
|
158
|
-
| PollingLogProviderConfig
|
|
180
|
+
| PollingLogProviderConfig
|
|
181
|
+
| EventsServerLogProviderConfig;
|
|
@@ -64,7 +64,7 @@ export class WebSocketLogProvider implements LogProvider {
|
|
|
64
64
|
if (logs.err !== null) {
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
|
-
callback(logs.signature, ctx.slot, logs.logs, undefined);
|
|
67
|
+
callback(logs.signature, ctx.slot, logs.logs, undefined, undefined);
|
|
68
68
|
},
|
|
69
69
|
this.commitment
|
|
70
70
|
);
|
|
@@ -106,9 +106,9 @@ export class WebSocketLogProvider implements LogProvider {
|
|
|
106
106
|
|
|
107
107
|
if (this.receivingData) {
|
|
108
108
|
console.log(
|
|
109
|
-
`No log data in ${
|
|
110
|
-
this.
|
|
111
|
-
}`
|
|
109
|
+
`webSocketLogProvider: No log data in ${
|
|
110
|
+
this.resubTimeoutMs
|
|
111
|
+
}ms, resubscribing on attempt ${this.reconnectAttempts + 1}`
|
|
112
112
|
);
|
|
113
113
|
await this.unsubscribe();
|
|
114
114
|
this.receivingData = false;
|