From 2ef694cc5b40cf44f4ec46c60e183970d2f5c18e Mon Sep 17 00:00:00 2001
From: Junaid <86780488+jdevcs@users.noreply.github.com>
Date: Wed, 21 Aug 2024 09:11:20 +0200
Subject: [PATCH] Web3 RPC Providers support of configuration of selected
transport (#7205)
* SocketOptions type
* ProviderConfigOptionsError
* providerConfigOptions
* tests
* changelog
---
packages/web3-rpc-providers/CHANGELOG.md | 3 +-
packages/web3-rpc-providers/src/errors.ts | 14 ++-
packages/web3-rpc-providers/src/types.ts | 9 ++
.../web3-rpc-providers/src/web3_provider.ts | 34 ++++++--
.../src/web3_provider_quicknode.ts | 12 ++-
.../test/unit/constructor.test.ts | 87 ++++++++++++++++---
6 files changed, 132 insertions(+), 27 deletions(-)
diff --git a/packages/web3-rpc-providers/CHANGELOG.md b/packages/web3-rpc-providers/CHANGELOG.md
index 52a8508d082..d07ba9124a4 100644
--- a/packages/web3-rpc-providers/CHANGELOG.md
+++ b/packages/web3-rpc-providers/CHANGELOG.md
@@ -55,4 +55,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
-- Updated rate limit error of QuickNode provider for HTTP transport
\ No newline at end of file
+- Updated rate limit error of QuickNode provider for HTTP transport
+- Added optional `HttpProviderOptions | SocketOptions` in `Web3ExternalProvider` and `QuickNodeProvider` for provider configs
\ No newline at end of file
diff --git a/packages/web3-rpc-providers/src/errors.ts b/packages/web3-rpc-providers/src/errors.ts
index 54ad3094596..d698fbb66ab 100644
--- a/packages/web3-rpc-providers/src/errors.ts
+++ b/packages/web3-rpc-providers/src/errors.ts
@@ -15,6 +15,8 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
+/* eslint-disable max-classes-per-file */
+
import { BaseWeb3Error } from 'web3-errors';
const ERR_QUICK_NODE_RATE_LIMIT = 1300;
@@ -24,4 +26,14 @@ export class QuickNodeRateLimitError extends BaseWeb3Error {
public constructor(error?: Error) {
super(`You've reach the rate limit of free RPC calls from our Partner Quick Nodes. There are two options you can either create a paid Quick Nodes account and get 20% off for 2 months using WEB3JS referral code, or use Free public RPC endpoint.`, error);
}
-}
\ No newline at end of file
+}
+
+const ERR_PROVIDER_CONFIG_OPTIONS = 1301;
+export class ProviderConfigOptionsError extends BaseWeb3Error {
+ public code = ERR_PROVIDER_CONFIG_OPTIONS;
+
+ public constructor(msg: string) {
+ super(`Invalid provider config options given for ${msg}`);
+ }
+}
+/* eslint-enable max-classes-per-file */
\ No newline at end of file
diff --git a/packages/web3-rpc-providers/src/types.ts b/packages/web3-rpc-providers/src/types.ts
index 9da4c1dbfe7..e23714977c1 100644
--- a/packages/web3-rpc-providers/src/types.ts
+++ b/packages/web3-rpc-providers/src/types.ts
@@ -15,6 +15,9 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
+import {ClientOptions, ClientRequestArgs} from "web3-providers-ws";
+import { ReconnectOptions } from 'web3-utils';
+
export enum Transport {
HTTPS = "https",
WebSocket = "wss"
@@ -41,4 +44,10 @@ export enum Network {
BNB_MAINNET = "bnb_mainnet",
BNB_TESTNET = "bnb_testnet"
+};
+
+// Combining the ws types
+export type SocketOptions = {
+ socketOptions?: ClientOptions | ClientRequestArgs;
+ reconnectOptions?: Partial;
};
\ No newline at end of file
diff --git a/packages/web3-rpc-providers/src/web3_provider.ts b/packages/web3-rpc-providers/src/web3_provider.ts
index dbd50cf9e56..498dec60433 100644
--- a/packages/web3-rpc-providers/src/web3_provider.ts
+++ b/packages/web3-rpc-providers/src/web3_provider.ts
@@ -15,7 +15,7 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
-import HttpProvider from "web3-providers-http";
+import HttpProvider, { HttpProviderOptions } from "web3-providers-http";
import WebSocketProvider from "web3-providers-ws";
import {
EthExecutionAPI, JsonRpcResult, ProviderConnectInfo, ProviderMessage,
@@ -27,7 +27,8 @@ import {
JsonRpcResponseWithResult,
} from "web3-types";
import { Eip1193Provider } from "web3-utils";
-import { Transport, Network } from "./types.js";
+import { Transport, Network, SocketOptions } from "./types.js";
+import { ProviderConfigOptionsError } from "./errors.js";
/*
This class can be used to create new providers only when there is custom logic required in each Request method like
@@ -50,16 +51,36 @@ export abstract class Web3ExternalProvider<
network: Network,
transport: Transport,
token: string,
- host: string) {
+ host: string,
+ providerConfigOptions?: HttpProviderOptions | SocketOptions) {
super();
+ if(providerConfigOptions!== undefined &&
+ transport === Transport.HTTPS &&
+ !('providerOptions' in providerConfigOptions)){
+
+ throw new ProviderConfigOptionsError("HTTP Provider");
+ }
+ else if(providerConfigOptions!== undefined &&
+ transport === Transport.WebSocket &&
+ !( 'socketOptions' in providerConfigOptions ||
+ 'reconnectOptions' in providerConfigOptions
+ )){
+ throw new ProviderConfigOptionsError("Websocket Provider");
+ }
+
this.transport = transport;
if (transport === Transport.HTTPS) {
- this.provider = new HttpProvider(this.getRPCURL(network, transport, token, host));
+ this.provider = new HttpProvider(
+ this.getRPCURL(network, transport, token, host),
+ providerConfigOptions as HttpProviderOptions);
}
else if (transport === Transport.WebSocket) {
- this.provider = new WebSocketProvider(this.getRPCURL(network, transport, token, host));
+ this.provider = new WebSocketProvider(
+ this.getRPCURL(network, transport, token, host),
+ (providerConfigOptions as SocketOptions)?.socketOptions,
+ (providerConfigOptions as SocketOptions)?.reconnectOptions);
}
}
@@ -133,4 +154,5 @@ export abstract class Web3ExternalProvider<
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
this.provider.removeListener(_type as any, _listener as any);
}
-}
\ No newline at end of file
+}
+
diff --git a/packages/web3-rpc-providers/src/web3_provider_quicknode.ts b/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
index 4444110afdd..459cf5d5d38 100644
--- a/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
+++ b/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
@@ -17,7 +17,8 @@ along with web3.js. If not, see .
import { EthExecutionAPI, JsonRpcResponseWithResult, Web3APIMethod, Web3APIPayload, Web3APIReturnType, Web3APISpec } from "web3-types";
import { ResponseError } from "web3-errors";
-import { Transport, Network } from "./types.js";
+import { HttpProviderOptions } from "web3-providers-http";
+import { Transport, Network, SocketOptions } from "./types.js";
import { Web3ExternalProvider } from "./web3_provider.js";
import { QuickNodeRateLimitError } from "./errors.js";
@@ -27,13 +28,10 @@ export class QuickNodeProvider<
API extends Web3APISpec = EthExecutionAPI,
> extends Web3ExternalProvider {
- public constructor(
- network: Network = Network.ETH_MAINNET,
- transport: Transport = Transport.HTTPS,
- token = "",
- host = "") {
+ // eslint-disable-next-line default-param-last
+ public constructor( network: Network = Network.ETH_MAINNET, transport: Transport = Transport.HTTPS, token = "", host = "", providerConfigOptions?: HttpProviderOptions | SocketOptions) {
- super(network, transport, token, host);
+ super(network, transport, token, host, providerConfigOptions);
}
diff --git a/packages/web3-rpc-providers/test/unit/constructor.test.ts b/packages/web3-rpc-providers/test/unit/constructor.test.ts
index 0daedb8c77d..16133129d55 100644
--- a/packages/web3-rpc-providers/test/unit/constructor.test.ts
+++ b/packages/web3-rpc-providers/test/unit/constructor.test.ts
@@ -16,12 +16,13 @@ along with web3.js. If not, see .
*/
-import HttpProvider from 'web3-providers-http';
+import HttpProvider, { HttpProviderOptions } from 'web3-providers-http';
import WebSocketProvider from 'web3-providers-ws';
import WebSocket from 'isomorphic-ws';
import { Web3ExternalProvider } from '../../src/web3_provider';
-import { Network, Transport } from '../../src/types';
+import { Network, SocketOptions, Transport } from '../../src/types';
+import { ProviderConfigOptionsError } from '../../src/errors';
// Mock implementation so ws doesnt have openhandle after test exits as it attempts to connects at start
jest.mock('isomorphic-ws', () => {
@@ -60,11 +61,11 @@ jest.mock('isomorphic-ws', () => {
});
class MockWeb3ExternalProviderA extends Web3ExternalProvider {
- public constructor(network: Network, transport: Transport, token: string){
- super(network, transport, token, "");
+ public constructor(network: Network, transport: Transport, token: string, host?: string, providerConfigOptions?: HttpProviderOptions | SocketOptions) {
+ super(network, transport, token, host ?? "", providerConfigOptions);
}
// eslint-disable-next-line class-methods-use-this
- public getRPCURL(_network: Network, _transport: Transport, _token: string, _host=""): string {
+ public getRPCURL(_network: Network, _transport: Transport, _token: string, _host = ""): string {
let transport = "";
if (_transport === Transport.HTTPS)
transport = "http://";
@@ -76,10 +77,12 @@ class MockWeb3ExternalProviderA extends Web3ExternalProvider {
}
describe('Web3ExternalProvider', () => {
+ const network: Network = Network.ETH_MAINNET;
+ const transport: Transport = Transport.HTTPS;
+ const token = 'test-token';
+ const host = 'test-host';
+
it('should initialize the provider correctly', () => {
- const network: Network = Network.ETH_MAINNET;
- const transport: Transport = Transport.HTTPS;
- const token = 'your-token';
const provider = new MockWeb3ExternalProviderA(network, transport, token);
@@ -87,12 +90,72 @@ describe('Web3ExternalProvider', () => {
});
it('should initialize the provider with WebSocketProvider for WebSocket transport', () => {
- const network: Network = Network.ETH_MAINNET;
- const transport: Transport = Transport.WebSocket;
- const token = 'your-token';
+ const transport1: Transport = Transport.WebSocket;
- const provider = new MockWeb3ExternalProviderA(network, transport, token);
+ const provider = new MockWeb3ExternalProviderA(network, transport1, token);
expect(provider.provider).toBeInstanceOf(WebSocketProvider);
});
+ it('should throw ProviderConfigOptionsError for HTTP provider with missing providerOptions', () => {
+ const providerConfigOptions: HttpProviderOptions | SocketOptions = { /* missing providerOptions */ };
+ expect(() => new MockWeb3ExternalProviderA(network, transport, token, host, providerConfigOptions)).toThrow(ProviderConfigOptionsError);
+ });
+
+ it('should throw ProviderConfigOptionsError for HTTP provider with WS providerOptions', () => {
+ const providerConfigOptions: SocketOptions = {
+ socketOptions: { /* options */ },
+ reconnectOptions: { /* options */ },
+ };
+ expect(() => new MockWeb3ExternalProviderA(network, transport, token, host, providerConfigOptions)).toThrow(ProviderConfigOptionsError);
+ });
+
+ it('should throw ProviderConfigOptionsError for WebSocket provider with missing socketOptions and reconnectOptions', () => {
+ const providerConfigOptions: HttpProviderOptions | SocketOptions = { /* missing socketOptions and reconnectOptions */ };
+ expect(() => new MockWeb3ExternalProviderA(network, Transport.WebSocket, token, host, providerConfigOptions)).toThrow(ProviderConfigOptionsError);
+ });
+
+ it('should throw ProviderConfigOptionsError for WebSocket provider with HTTP options', () => {
+ const providerConfigOptions: HttpProviderOptions = { providerOptions: { /* options */ } };
+ expect(() => new MockWeb3ExternalProviderA(network, Transport.WebSocket, token, host, providerConfigOptions)).toThrow(ProviderConfigOptionsError);
+ });
+
+ it('should create provider instance and not throw ProviderConfigOptionsError for WebSocket provider with missing reconnectOptions', () => {
+ const providerConfigOptions: SocketOptions = {
+ socketOptions: { /* options */ },
+ };
+
+ // Create an instance of the MockWeb3ExternalProviderA
+ const provider = new MockWeb3ExternalProviderA(network, Transport.WebSocket, token, host, providerConfigOptions);
+
+ // Expect that the provider is created successfully
+ expect(provider).toBeInstanceOf(MockWeb3ExternalProviderA);
+ });
+
+ it('should create provider instance and not throw ProviderConfigOptionsError for WebSocket provider with missing socketOptions', () => {
+ const providerConfigOptions: SocketOptions = {
+ reconnectOptions: { /* options */ },
+ };
+
+ // Create an instance of the MockWeb3ExternalProviderA
+ const provider = new MockWeb3ExternalProviderA(network, Transport.WebSocket, token, host, providerConfigOptions);
+
+ // Expect that the provider is created successfully
+ expect(provider).toBeInstanceOf(MockWeb3ExternalProviderA);
+ });
+
+ it('should create an HttpProvider with providerOptions', () => {
+ const providerConfigOptions: HttpProviderOptions = { providerOptions: { /* options */ } };
+ const provider = new MockWeb3ExternalProviderA(network, transport, token, host, providerConfigOptions);
+ expect(provider.provider).toBeInstanceOf(HttpProvider);
+ });
+
+ it('should create a WebSocketProvider with socketOptions and reconnectOptions', () => {
+ const providerConfigOptions: SocketOptions = {
+ socketOptions: { /* options */ },
+ reconnectOptions: { /* options */ },
+ };
+ const provider = new MockWeb3ExternalProviderA(network, Transport.WebSocket, token, host, providerConfigOptions);
+ expect(provider.provider).toBeInstanceOf(WebSocketProvider);
+ });
});
+