Created
August 2, 2023 21:05
-
-
Save ahmadina/d18c991ff07b67ec789face7f13eb141 to your computer and use it in GitHub Desktop.
Ether.js Reconnecting WebSocket Provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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