diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6a7bdee5a44..d9abf6019de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2765,3 +2765,9 @@ If there are any bugs, improvements, optimizations or any new feature proposal f
- fix `padRight` validation failure on large `uint` (#7265)
## [Unreleased]
+
+### Added
+
+#### web3-rpc-providers
+
+- PublicNodeProvider was added (#7322)
diff --git a/packages/web3-rpc-providers/CHANGELOG.md b/packages/web3-rpc-providers/CHANGELOG.md
index a9e2c2a8b79..110b620707d 100644
--- a/packages/web3-rpc-providers/CHANGELOG.md
+++ b/packages/web3-rpc-providers/CHANGELOG.md
@@ -59,3 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added optional `HttpProviderOptions | SocketOptions` in `Web3ExternalProvider` and `QuickNodeProvider` for provider configs
## [Unreleased]
+
+### Added
+
+- PublicNodeProvider was added (#7322)
diff --git a/packages/web3-rpc-providers/src/index.ts b/packages/web3-rpc-providers/src/index.ts
index 577d8e6b960..f7c0e6c2613 100644
--- a/packages/web3-rpc-providers/src/index.ts
+++ b/packages/web3-rpc-providers/src/index.ts
@@ -19,6 +19,7 @@ import { QuickNodeProvider } from './web3_provider_quicknode.js';
export * from './types.js';
export * from './web3_provider_quicknode.js';
+export * from './web3_provider_publicnode.js';
export * from './web3_provider.js';
export * from './errors.js';
diff --git a/packages/web3-rpc-providers/src/types.ts b/packages/web3-rpc-providers/src/types.ts
index 9fda306ae0d..258d6411c57 100644
--- a/packages/web3-rpc-providers/src/types.ts
+++ b/packages/web3-rpc-providers/src/types.ts
@@ -25,13 +25,15 @@ export enum Transport {
export enum Network {
ETH_MAINNET = 'eth_mainnet',
- ETH_GOERLI = 'eth_goerli',
ETH_SEPOLIA = 'eth_sepolia',
ETH_HOLESKY = 'eth_holesky',
POLYGON_MAINNET = 'polygon_mainnet',
- POLYGON_MUMBAI = 'polygon_mumbai',
- POLYGON_AMONY = 'polygon_amony',
+
+ POLYGON_AMOY = 'polygon_amoy',
+ AVALANCHE_C_MAINNET = 'avalanche_c_mainnet',
+ AVALANCHE_P_MAINNET = 'avalanche_p_mainnet',
+ AVALANCHE_X_MAINNET = 'avalanche_x_mainnet',
ARBITRUM_MAINNET = 'arbitrum_mainnet',
ARBITRUM_SEPOLIA = 'arbitrum_sepolia',
@@ -42,8 +44,51 @@ export enum Network {
OPTIMISM_MAINNET = 'optimism_mainnet',
OPTIMISM_SEPOLIA = 'optimism_sepolia',
+ FANTOM_MAINNET = 'fantom_mainnet',
+ FANTOM_TESTNET = 'fantom_testnet',
+
+ DYMENSION_MAINNET = 'dymension_mainnet',
+ DYMENSION_TESTNET = 'dymension_testnet',
+
BNB_MAINNET = 'bnb_mainnet',
BNB_TESTNET = 'bnb_testnet',
+
+ BSC_MAINNET = 'bsc_mainnet',
+ BSC_TESTNET = 'bsc_testnet',
+
+ ARBITRUM_ONE = 'arbitrum_one',
+ ARBITRUM_NOVA = 'arbitrum_nova',
+ AVALANCHE_FUJI_C = 'avalanche_fuji_c',
+ AVALANCHE_FUJI_P = 'avalanche_fuji_p',
+ AVALANCHE_FUJI_X = 'avalanche_fuji_x',
+ BLAST_MAINNET = 'blast_mainnet',
+ OPBNB_MAINNET = 'opbnb_mainnet',
+ OPBNB_TESTNET = 'opbnb_testnet',
+ GNOSIS_MAINNET = 'gnosis_mainnet',
+ GNOSIS_CHIADO = 'gnosis_chiado',
+ PULSECHAIN_MAINNET = 'pulsechain_mainnet',
+ PULSECHAIN_TESTNET = 'pulsechain_testnet',
+ KAVA_MAINNET = 'kava_mainnet',
+ CRONOS_MAINNET = 'cronos_mainnet',
+ MANTLE_MAINNET = 'mantle_mainnet',
+ CHILIZ_MAINNET = 'chiliz_mainnet',
+ CHILIZ_SPICY = 'chiliz_spicy',
+ MOONBEAM_MAINNET = 'moonbeam_mainnet',
+ TAIKO_MAINNET = 'taiko_mainnet',
+ TAIKO_HEKLA = 'taiko_hekla',
+ LINEA_MAINNET = 'linea_mainnet',
+ LINEA_SEPOLIA = 'linea_sepolia',
+ BAHAMUT_MAINNET = 'bahamut_mainnet',
+ SCROLL_MAINNET = 'scroll_mainnet',
+ SCROLL_SEPOLIA = 'scroll_sepolia',
+ TRON_MAINNET = 'tron_mainnet',
+ SYSCOIN_MAINNET = 'syscoin_mainnet',
+ SYSCOIN_TANENBAUM = 'syscoin_tanenbaum',
+ MOONRIVER_MAINNET = 'moonriver_mainnet',
+ HAQQ_MAINNET = 'haqq_mainnet',
+ EVMOS_MAINNET = 'evmos_mainnet',
+ EVMOS_TESTNET = 'evmos_testnet',
+ BERACHAIN_TESTNET = 'berachain_testnet',
}
// Combining the ws types
diff --git a/packages/web3-rpc-providers/src/web3_provider_publicnode.ts b/packages/web3-rpc-providers/src/web3_provider_publicnode.ts
new file mode 100644
index 00000000000..027d7a45921
--- /dev/null
+++ b/packages/web3-rpc-providers/src/web3_provider_publicnode.ts
@@ -0,0 +1,112 @@
+/*
+This file is part of web3.js.
+
+web3.js is free software: you can redistribute it and/or modify
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+web3.js is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public License
+along with web3.js. If not, see .
+*/
+
+import { EthExecutionAPI, Web3APISpec } from 'web3-types';
+import { HttpProviderOptions } from 'web3-providers-http';
+import { Network, SocketOptions, Transport } from './types.js';
+import { Web3ExternalProvider } from './web3_provider.js';
+
+const isValid = (str: string) => str !== undefined && str.trim().length > 0;
+
+const websocketExclusions = [
+ Network.DYMENSION_MAINNET,
+ Network.DYMENSION_TESTNET,
+ Network.KAVA_MAINNET,
+ Network.CRONOS_MAINNET,
+ // deprecated
+ Network.POLYGON_MAINNET,
+];
+
+export class PublicNodeProvider<
+ API extends Web3APISpec = EthExecutionAPI,
+> extends Web3ExternalProvider {
+ // eslint-disable-next-line default-param-last
+ public constructor(
+ network: Network = Network.ETH_MAINNET,
+ transport: Transport = Transport.HTTPS,
+ host = '',
+ providerConfigOptions?: HttpProviderOptions | SocketOptions,
+ ) {
+ super(network, transport, '', host, providerConfigOptions);
+ }
+ public static readonly networkHostMap: { [key: string]: string } = {
+ [Network.POLYGON_AMOY]: 'polygon-amoy-bor-rpc',
+ [Network.DYMENSION_MAINNET]: 'dymension-evm-rpc',
+ [Network.DYMENSION_TESTNET]: 'dymension-testnet-evm-rpc',
+ [Network.BLAST_MAINNET]: 'blast-rpc',
+ [Network.GNOSIS_MAINNET]: 'gnosis-rpc',
+ [Network.PULSECHAIN_MAINNET]: 'pulsechain-rpc',
+ [Network.PULSECHAIN_TESTNET]: 'pulsechain-testnet-rpc',
+ [Network.KAVA_MAINNET]: 'kava-evm-rpc',
+ [Network.CRONOS_MAINNET]: 'cronos-evm-rpc',
+ [Network.MANTLE_MAINNET]: 'mantle-rpc',
+ [Network.TAIKO_MAINNET]: 'taiko-rpc',
+ [Network.TAIKO_HEKLA]: 'taiko-hekla-rpc',
+ [Network.LINEA_MAINNET]: 'linea-rpc',
+ [Network.LINEA_SEPOLIA]: 'linea-sepolia-rpc',
+ [Network.SCROLL_MAINNET]: 'scroll-rpc',
+ [Network.SCROLL_SEPOLIA]: 'scroll-sepolia-rpc',
+ [Network.SYSCOIN_MAINNET]: 'syscoin-evm-rpc',
+ [Network.SYSCOIN_TANENBAUM]: 'syscoin-tanenbaum-evm-rpc',
+ [Network.HAQQ_MAINNET]: 'haqq-evm-rpc',
+ [Network.EVMOS_MAINNET]: 'evmos-evm-rpc',
+ [Network.EVMOS_TESTNET]: 'evmos-testnet-evm-rpc',
+ [Network.BERACHAIN_TESTNET]: 'berachain-testnet-evm-rpc',
+ [Network.ETH_MAINNET]: 'ethereum-rpc',
+ [Network.ETH_SEPOLIA]: 'ethereum-sepolia-rpc',
+ [Network.ETH_HOLESKY]: 'ethereum-holesky-rpc',
+ [Network.BSC_MAINNET]: 'bsc-rpc',
+ [Network.BSC_TESTNET]: 'bsc-testnet-rpc',
+ [Network.POLYGON_MAINNET]: 'polygon-bor-rpc',
+ [Network.BASE_MAINNET]: 'base-rpc',
+ [Network.BASE_SEPOLIA]: 'base-sepolia-rpc',
+ [Network.ARBITRUM_ONE]: 'arbitrum-one-rpc',
+ [Network.ARBITRUM_NOVA]: 'arbitrum-nova-rpc',
+ [Network.ARBITRUM_SEPOLIA]: 'arbitrum-sepolia-rpc',
+ [Network.AVALANCHE_C_MAINNET]: 'avalanche-c-chain-rpc',
+ [Network.AVALANCHE_P_MAINNET]: 'avalanche-p-chain-rpc',
+ [Network.AVALANCHE_X_MAINNET]: 'avalanche-x-chain-rpc',
+ [Network.AVALANCHE_FUJI_C]: 'avalanche-fuji-c-chain-rpc',
+ [Network.AVALANCHE_FUJI_P]: 'avalanche-fuji-p-chain-rpc',
+ [Network.AVALANCHE_FUJI_X]: 'avalanche-fuji-x-chain-rpc',
+ [Network.OPTIMISM_MAINNET]: 'optimism-rpc',
+ [Network.OPTIMISM_SEPOLIA]: 'optimism-sepolia-rpc',
+ [Network.FANTOM_MAINNET]: 'fantom-rpc',
+ [Network.FANTOM_TESTNET]: 'fantom-testnet-rpc',
+ [Network.OPBNB_MAINNET]: 'opbnb-rpc',
+ [Network.OPBNB_TESTNET]: 'opbnb-testnet-rpc',
+ [Network.GNOSIS_CHIADO]: 'gnosis-chiado-rpc',
+ [Network.CHILIZ_MAINNET]: 'chiliz-rpc',
+ [Network.CHILIZ_SPICY]: 'chiliz-spicy-rpc',
+ [Network.MOONBEAM_MAINNET]: 'moonbeam-rpc',
+ [Network.BAHAMUT_MAINNET]: 'bahamut-rpc',
+ [Network.TRON_MAINNET]: 'tron-evm-rpc',
+ [Network.MOONRIVER_MAINNET]: 'moonriver-rpc',
+ };
+ // eslint-disable-next-line class-methods-use-this
+ public getRPCURL(network: Network, transport: Transport, _: string, _host: string) {
+ if (!PublicNodeProvider.networkHostMap[network]) {
+ throw new Error('Network info not avalible.');
+ }
+ const defaultHost = `${PublicNodeProvider.networkHostMap[network]}.publicnode.com`;
+ const host = isValid(_host) ? _host : defaultHost;
+ if (websocketExclusions.includes(network) && transport === Transport.WebSocket) {
+ return `${transport}://${host}/websocket`;
+ }
+ return `${transport}://${host}`;
+ }
+}
diff --git a/packages/web3-rpc-providers/src/web3_provider_quicknode.ts b/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
index ae74fa49860..4b6e5ae73e6 100644
--- a/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
+++ b/packages/web3-rpc-providers/src/web3_provider_quicknode.ts
@@ -107,7 +107,7 @@ export class QuickNodeProvider<
host = isValid(_host) ? _host : 'small-chaotic-moon.matic.quiknode.pro';
token = isValid(_token) ? _token : '847569f8a017e84d985e10d0f44365d965a951f1';
break;
- case Network.POLYGON_AMONY:
+ case Network.POLYGON_AMOY:
host = isValid(_host) ? _host : 'prettiest-side-shape.matic-amoy.quiknode.pro';
token = isValid(_token) ? _token : '79a9476eea661d4f82de614db1d8a895b14b881c';
break;
diff --git a/packages/web3-rpc-providers/test/unit/request.test.ts b/packages/web3-rpc-providers/test/unit/request.test.ts
index 3de14fc0513..da07c2a578c 100644
--- a/packages/web3-rpc-providers/test/unit/request.test.ts
+++ b/packages/web3-rpc-providers/test/unit/request.test.ts
@@ -20,6 +20,7 @@ import { Network, Transport } from '../../src/types';
import { Web3ExternalProvider } from '../../src/web3_provider';
import { QuickNodeRateLimitError } from '../../src/errors';
import { QuickNodeProvider } from '../../src/web3_provider_quicknode';
+import { PublicNodeProvider } from '../../src/web3_provider_publicnode';
jest.mock('web3-providers-ws', () => {
return {
@@ -122,4 +123,27 @@ describe('Web3ExternalProvider', () => {
};
await expect(provider.request(payload)).rejects.toThrow(QuickNodeRateLimitError);
});
+
+ it('should make a request using the PublicNodeProvider provider', async () => {
+ const network: Network = Network.ETH_MAINNET;
+ const transport: Transport = Transport.HTTPS;
+
+ const mockHttpProvider = {
+ request: jest.fn(),
+ };
+
+ const mockResponse = { result: 'mock-result' };
+ mockHttpProvider.request.mockResolvedValue(mockResponse);
+
+ const provider = new PublicNodeProvider(network, transport);
+ (provider as any).provider = mockHttpProvider;
+
+ const payload: Web3APIPayload> = {
+ method: 'eth_getBalance',
+ params: ['0x0123456789012345678901234567890123456789', 'latest'],
+ };
+
+ const result = await provider.request(payload);
+ expect(result).toEqual(mockResponse);
+ });
});
diff --git a/packages/web3/test/integration/web3RPCProviders.test.ts b/packages/web3/test/integration/web3RPCProviders.test.ts
index 1faeffad452..6e6d2994f9c 100644
--- a/packages/web3/test/integration/web3RPCProviders.test.ts
+++ b/packages/web3/test/integration/web3RPCProviders.test.ts
@@ -15,7 +15,13 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see .
*/
-import { mainnet, Network, QuickNodeProvider, Transport } from 'web3-rpc-providers';
+import {
+ mainnet,
+ Network,
+ QuickNodeProvider,
+ Transport,
+ PublicNodeProvider,
+} from 'web3-rpc-providers';
import { Web3 } from '../../src/index';
describe('Web3 RPC Provider Integration tests', () => {
@@ -29,7 +35,7 @@ describe('Web3 RPC Provider Integration tests', () => {
Network.BNB_MAINNET,
Network.BNB_TESTNET,
Network.POLYGON_MAINNET,
- Network.POLYGON_AMONY,
+ Network.POLYGON_AMOY,
];
transports.forEach(transport => {
@@ -48,6 +54,89 @@ describe('Web3 RPC Provider Integration tests', () => {
});
});
});
+
+ const publicNodeNetworks = [
+ Network.POLYGON_AMOY,
+ Network.POLYGON_MAINNET,
+ Network.DYMENSION_MAINNET,
+ Network.DYMENSION_TESTNET,
+ Network.BLAST_MAINNET,
+ Network.GNOSIS_MAINNET,
+ Network.PULSECHAIN_MAINNET,
+ Network.PULSECHAIN_TESTNET,
+ Network.KAVA_MAINNET,
+ Network.CRONOS_MAINNET,
+ Network.MANTLE_MAINNET,
+ Network.HAQQ_MAINNET,
+ Network.TAIKO_MAINNET,
+ Network.TAIKO_HEKLA,
+ Network.EVMOS_MAINNET,
+ Network.EVMOS_TESTNET,
+ Network.BERACHAIN_TESTNET,
+ Network.LINEA_MAINNET,
+ Network.LINEA_SEPOLIA,
+ Network.SCROLL_MAINNET,
+ Network.SCROLL_SEPOLIA,
+ Network.SYSCOIN_MAINNET,
+ Network.SYSCOIN_TANENBAUM,
+ Network.ETH_MAINNET,
+ Network.ETH_SEPOLIA,
+ Network.ETH_HOLESKY,
+ Network.BSC_MAINNET,
+ Network.BSC_TESTNET,
+ Network.BASE_MAINNET,
+ Network.BASE_SEPOLIA,
+ Network.ARBITRUM_ONE,
+ Network.ARBITRUM_NOVA,
+ Network.ARBITRUM_SEPOLIA,
+ Network.AVALANCHE_C_MAINNET,
+ Network.AVALANCHE_P_MAINNET,
+ Network.AVALANCHE_X_MAINNET,
+ Network.AVALANCHE_FUJI_C,
+ Network.AVALANCHE_FUJI_P,
+ Network.AVALANCHE_FUJI_X,
+ Network.OPTIMISM_MAINNET,
+ Network.OPTIMISM_SEPOLIA,
+ Network.FANTOM_MAINNET,
+ Network.FANTOM_TESTNET,
+ Network.OPBNB_MAINNET,
+ Network.OPBNB_TESTNET,
+ Network.GNOSIS_CHIADO,
+ Network.CHILIZ_MAINNET,
+ Network.CHILIZ_SPICY,
+ Network.MOONBEAM_MAINNET,
+ Network.BAHAMUT_MAINNET,
+ Network.TRON_MAINNET,
+ Network.MOONRIVER_MAINNET,
+ ];
+ transports.forEach(transport => {
+ publicNodeNetworks.forEach(network => {
+ if (
+ !(
+ [
+ Network.AVALANCHE_P_MAINNET,
+ Network.AVALANCHE_X_MAINNET,
+ Network.AVALANCHE_FUJI_X,
+ Network.AVALANCHE_FUJI_P,
+ ].includes(network) && transport === Transport.WebSocket
+ )
+ ) {
+ it(`PublicNodeProvider should work with ${transport} transport and ${network} network`, async () => {
+ const provider = new PublicNodeProvider(network, transport);
+ const web3 = new Web3(provider);
+ const result = await web3.eth.getBlockNumber();
+
+ expect(typeof result).toBe('bigint');
+ expect(result > 0).toBe(true);
+
+ if (transport === Transport.WebSocket) {
+ web3.provider?.disconnect();
+ }
+ });
+ }
+ });
+ });
+
it(`should work with mainnet provider`, async () => {
const web3 = new Web3(mainnet);
const result = await web3.eth.getBlockNumber();