hardhat
hardhat copied to clipboard
Add support for websocket connection to custom networks
TL;DR
Is there a specific reason why Hardhat does not support websockets to connect to custom networks? If not, I am happy to try implementing it 🙂
Context
Hello!
There are a number of cases where it is useful to listen to blockchain events, for example:
- Token transfers
- Pairs created on DEXes
- Liquidity added
In these cases, a websocket connection would help to reduce both the latency and the number of node calls, which is an important factor for pay-as-you-go nodes.
However, Hardhat does not seem to support websocket connections to custom networks, as documented in this issue > https://github.com/nomiclabs/hardhat/issues/1354
I was wondering whether there was a fundamental reason for the lack of support for websocket; if not, I can try implementing it.
Cheers, Cocco
After further inspecting the code, implementing websockets might be more complicated than what I thought at first.
I thought it would be enough to use the ethers or web3 functions for sockets, but apparently Hardhat uses its own client for requests (see method _fetchJsonRpcResponse
), which relies on node-fetch which in turn does not support Websockets.
I am probably missing something here, as I was expecting Hardhat to use the same clients as Ethers/Web3.
Websockets is something we could support but not many people have requested it (every feature comes with a maintenance cost). Since we don't have an open issue to track this feature request we could use this one to gauge interest.
Thanks for opening the request.
Thank you for your reply @kanej !
In the meanwhile, to use Websockets with Hardhat, I have created a getProvider()
function that returns either an HTTP provider or a Websocket provider depending on the network URL being used. It is not ideal, but here it is:
import { Provider } from "@ethersproject/providers";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { URL } from "url";
/**
* Return either an HTTP Provider or a WebSocket provider
* depending on the network URL given to Hardhat.
*/
export function getProvider(hre: HardhatRuntimeEnvironment): Provider {
// @ts-ignore
const url = new URL(hre.network.config.url);
switch (url.protocol) {
case "http:":
case "https:":
return new hre.ethers.providers.JsonRpcProvider(url.href);
case "ws:":
case "wss:":
return new hre.ethers.providers.WebSocketProvider(url.href);
default:
throw new Error(`Network URL not valid: '${url.href}'`);
}
}
Hopefully it can help fellow coders trying to use Websockets with Hardhat :-) Cheers, Cocco
This issue was marked as stale because it didn't have any activity in the last 30 days. If you think it's still relevant, please leave a comment indicating so. Otherwise, it will be closed in 7 days.
Added keep-alive support, using the approach described on this ethers.js issue.
(Please note that the getProvider()
function is defined in https://github.com/NomicFoundation/hardhat/issues/2391#issuecomment-1044673909.)
import { HardhatRuntimeEnvironment } from 'hardhat/types';
import { WebSocketProvider } from '@ethersproject/providers';
/**
* Start a keep-alive WebSocket connection in Hardhat.
*
* The function will periodically check whether the connection is still
* open, and restart it if it is not.
*
* Usage:
*
* startConnection(hre, async (hre, provider) => {
* // Your code here
* }
*
* Source: https://github.com/ethers-io/ethers.js/issues/1053#issuecomment-808736570
*/
export function startConnection(
hre: HardhatRuntimeEnvironment,
onOpen: (hre: HardhatRuntimeEnvironment, p: WebSocketProvider) => void,
expectedPongBack: number = 15000,
keepAliveCheckInterval: number = 7500
): void {
const logger = new hre.ethers.utils.Logger('v1.0');
const provider: WebSocketProvider = getProvider(hre) as WebSocketProvider;
let pingTimeout: NodeJS.Timeout;
let keepAliveInterval: NodeJS.Timeout;
provider._websocket.on('open', () => {
keepAliveInterval = setInterval(() => {
logger.debug('> Checking if the connection is alive, sending a ping');
provider._websocket.ping();
// Delay should be equal to the interval at which your server
// sends out pings plus a conservative assumption of the latency.
pingTimeout = setTimeout(() => {
provider._websocket.terminate();
}, expectedPongBack);
}, keepAliveCheckInterval);
onOpen(hre, provider);
});
provider._websocket.on('close', () => {
logger.warn('> WARNING: The websocket connection was closed');
clearInterval(keepAliveInterval);
clearTimeout(pingTimeout);
startConnection(hre, onOpen);
});
provider._websocket.on('pong', () => {
logger.debug('> Received pong, so connection is alive, clearing the timeout');
clearInterval(pingTimeout);
});
}
This issue was marked as stale because it didn't have any activity in the last 30 days. If you think it's still relevant, please leave a comment indicating so. Otherwise, it will be closed in 7 days.
Would it be possible to leave the issue open?
Although not overwhelming, there has been some interest in having the feature implemented (see reactions).
Cheers, Cocco
Marked as not-stale
so the bot doesn't comment again.
Added keep-alive support, using the approach described on this ethers.js issue:
This function is great but we would need to re-subscribe to the events on the new connection, There was an example for using web3 over on the other thread but it didn't cover ethers.js
Hi all,
Here is a plugin that adds the getProvider function to hre.
In case that you use a network that it is configured to works with web sockets, returns an instance of WebSocketProvider
, in other cases, returns hre.ethers.provider.
Here is the npm package:
https://www.npmjs.com/package/@sebasgoldberg/hardhat-wsprovider
In the future I am planning to implement automatic re-subscription.
@sebasgoldberg thank you this is a huge help!!
really an important feature to be added! WS is must novadays
+1 Would really like to be able to use a mainnet fork with websockets.
+1