Skip to content

Instantly share code, notes, and snippets.

@ahmadina
Created August 2, 2023 21:05
Show Gist options
  • Save ahmadina/d18c991ff07b67ec789face7f13eb141 to your computer and use it in GitHub Desktop.
Save ahmadina/d18c991ff07b67ec789face7f13eb141 to your computer and use it in GitHub Desktop.
Ether.js Reconnecting WebSocket Provider
import { providers } from 'ethers';
const EXPECTED_PONG_BACK = 15000;
const KEEP_ALIVE_CHECK_INTERVAL = 7500;
// Used to "trick" TypeScript into treating a Proxy as the intended proxied class
export const fakeBaseClass = <T>() : new() => Pick<T, keyof T> => (class {} as any);
export class ReconnectingWebSocketProvider extends fakeBaseClass<providers.WebSocketProvider>() {
private underlyingProvider: providers.WebSocketProvider;
// Define a handler that forwards all "get" access to the underlying provider
private handler = {
get(target: ReconnectingWebSocketProvider, prop: string, receiver: any) {
return Reflect.get(target.underlyingProvider, prop, receiver);
},
};
constructor(private url: string) {
super();
this.connect();
return new Proxy(this, this.handler);
}
private connect() {
// Copy old events
const events = this.underlyingProvider?._events ?? [];
// Instantiate new provider with same url
this.underlyingProvider = new providers.WebSocketProvider(this.url);
let pingTimeout: NodeJS.Timeout;
let keepAliveInterval: NodeJS.Timer;
this.underlyingProvider._websocket.on('open', () => {
// Send ping messages on an interval
keepAliveInterval = setInterval(() => {
this.underlyingProvider._websocket.ping();
// Terminate if a pong message is not received timely
pingTimeout = setTimeout(() => this.underlyingProvider._websocket.terminate(), EXPECTED_PONG_BACK);
}, KEEP_ALIVE_CHECK_INTERVAL);
// Add old events to new provider
events.forEach((event) => {
this.underlyingProvider._events.push(event);
this.underlyingProvider._startEvent(event);
});
});
// Clear timers and reconnect on close
this.underlyingProvider._websocket.on('close', () => {
clearInterval(keepAliveInterval);
clearTimeout(pingTimeout);
this.connect();
});
// Clear ping timer when pong is received
this.underlyingProvider._websocket.on('pong', () => {
clearInterval(pingTimeout);
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment