Skip to content
This repository was archived by the owner on Mar 5, 2025. It is now read-only.

Commit 4ca66af

Browse files
authored
Add rpc provider public node (#7322)
* init * finished * add unit test * add changelog * Update CHANGELOG.md * Update web3_provider_publicnode.ts * fix * Update web3_provider_publicnode.ts
1 parent 3687070 commit 4ca66af

File tree

8 files changed

+287
-6
lines changed

8 files changed

+287
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,3 +2765,9 @@ If there are any bugs, improvements, optimizations or any new feature proposal f
27652765
- fix `padRight` validation failure on large `uint` (#7265)
27662766

27672767
## [Unreleased]
2768+
2769+
### Added
2770+
2771+
#### web3-rpc-providers
2772+
2773+
- PublicNodeProvider was added (#7322)

packages/web3-rpc-providers/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5959
- Added optional `HttpProviderOptions | SocketOptions` in `Web3ExternalProvider` and `QuickNodeProvider` for provider configs
6060

6161
## [Unreleased]
62+
63+
### Added
64+
65+
- PublicNodeProvider was added (#7322)

packages/web3-rpc-providers/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { QuickNodeProvider } from './web3_provider_quicknode.js';
1919

2020
export * from './types.js';
2121
export * from './web3_provider_quicknode.js';
22+
export * from './web3_provider_publicnode.js';
2223
export * from './web3_provider.js';
2324
export * from './errors.js';
2425

packages/web3-rpc-providers/src/types.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ export enum Transport {
2525

2626
export enum Network {
2727
ETH_MAINNET = 'eth_mainnet',
28-
ETH_GOERLI = 'eth_goerli',
2928
ETH_SEPOLIA = 'eth_sepolia',
3029
ETH_HOLESKY = 'eth_holesky',
3130

3231
POLYGON_MAINNET = 'polygon_mainnet',
33-
POLYGON_MUMBAI = 'polygon_mumbai',
34-
POLYGON_AMONY = 'polygon_amony',
32+
33+
POLYGON_AMOY = 'polygon_amoy',
34+
AVALANCHE_C_MAINNET = 'avalanche_c_mainnet',
35+
AVALANCHE_P_MAINNET = 'avalanche_p_mainnet',
36+
AVALANCHE_X_MAINNET = 'avalanche_x_mainnet',
3537

3638
ARBITRUM_MAINNET = 'arbitrum_mainnet',
3739
ARBITRUM_SEPOLIA = 'arbitrum_sepolia',
@@ -42,8 +44,51 @@ export enum Network {
4244
OPTIMISM_MAINNET = 'optimism_mainnet',
4345
OPTIMISM_SEPOLIA = 'optimism_sepolia',
4446

47+
FANTOM_MAINNET = 'fantom_mainnet',
48+
FANTOM_TESTNET = 'fantom_testnet',
49+
50+
DYMENSION_MAINNET = 'dymension_mainnet',
51+
DYMENSION_TESTNET = 'dymension_testnet',
52+
4553
BNB_MAINNET = 'bnb_mainnet',
4654
BNB_TESTNET = 'bnb_testnet',
55+
56+
BSC_MAINNET = 'bsc_mainnet',
57+
BSC_TESTNET = 'bsc_testnet',
58+
59+
ARBITRUM_ONE = 'arbitrum_one',
60+
ARBITRUM_NOVA = 'arbitrum_nova',
61+
AVALANCHE_FUJI_C = 'avalanche_fuji_c',
62+
AVALANCHE_FUJI_P = 'avalanche_fuji_p',
63+
AVALANCHE_FUJI_X = 'avalanche_fuji_x',
64+
BLAST_MAINNET = 'blast_mainnet',
65+
OPBNB_MAINNET = 'opbnb_mainnet',
66+
OPBNB_TESTNET = 'opbnb_testnet',
67+
GNOSIS_MAINNET = 'gnosis_mainnet',
68+
GNOSIS_CHIADO = 'gnosis_chiado',
69+
PULSECHAIN_MAINNET = 'pulsechain_mainnet',
70+
PULSECHAIN_TESTNET = 'pulsechain_testnet',
71+
KAVA_MAINNET = 'kava_mainnet',
72+
CRONOS_MAINNET = 'cronos_mainnet',
73+
MANTLE_MAINNET = 'mantle_mainnet',
74+
CHILIZ_MAINNET = 'chiliz_mainnet',
75+
CHILIZ_SPICY = 'chiliz_spicy',
76+
MOONBEAM_MAINNET = 'moonbeam_mainnet',
77+
TAIKO_MAINNET = 'taiko_mainnet',
78+
TAIKO_HEKLA = 'taiko_hekla',
79+
LINEA_MAINNET = 'linea_mainnet',
80+
LINEA_SEPOLIA = 'linea_sepolia',
81+
BAHAMUT_MAINNET = 'bahamut_mainnet',
82+
SCROLL_MAINNET = 'scroll_mainnet',
83+
SCROLL_SEPOLIA = 'scroll_sepolia',
84+
TRON_MAINNET = 'tron_mainnet',
85+
SYSCOIN_MAINNET = 'syscoin_mainnet',
86+
SYSCOIN_TANENBAUM = 'syscoin_tanenbaum',
87+
MOONRIVER_MAINNET = 'moonriver_mainnet',
88+
HAQQ_MAINNET = 'haqq_mainnet',
89+
EVMOS_MAINNET = 'evmos_mainnet',
90+
EVMOS_TESTNET = 'evmos_testnet',
91+
BERACHAIN_TESTNET = 'berachain_testnet',
4792
}
4893

4994
// Combining the ws types
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*
2+
This file is part of web3.js.
3+
4+
web3.js is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU Lesser General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
web3.js is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU Lesser General Public License for more details.
13+
14+
You should have received a copy of the GNU Lesser General Public License
15+
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import { EthExecutionAPI, Web3APISpec } from 'web3-types';
19+
import { HttpProviderOptions } from 'web3-providers-http';
20+
import { Network, SocketOptions, Transport } from './types.js';
21+
import { Web3ExternalProvider } from './web3_provider.js';
22+
23+
const isValid = (str: string) => str !== undefined && str.trim().length > 0;
24+
25+
const websocketExclusions = [
26+
Network.DYMENSION_MAINNET,
27+
Network.DYMENSION_TESTNET,
28+
Network.KAVA_MAINNET,
29+
Network.CRONOS_MAINNET,
30+
// deprecated
31+
Network.POLYGON_MAINNET,
32+
];
33+
34+
export class PublicNodeProvider<
35+
API extends Web3APISpec = EthExecutionAPI,
36+
> extends Web3ExternalProvider<API> {
37+
// eslint-disable-next-line default-param-last
38+
public constructor(
39+
network: Network = Network.ETH_MAINNET,
40+
transport: Transport = Transport.HTTPS,
41+
host = '',
42+
providerConfigOptions?: HttpProviderOptions | SocketOptions,
43+
) {
44+
super(network, transport, '', host, providerConfigOptions);
45+
}
46+
public static readonly networkHostMap: { [key: string]: string } = {
47+
[Network.POLYGON_AMOY]: 'polygon-amoy-bor-rpc',
48+
[Network.DYMENSION_MAINNET]: 'dymension-evm-rpc',
49+
[Network.DYMENSION_TESTNET]: 'dymension-testnet-evm-rpc',
50+
[Network.BLAST_MAINNET]: 'blast-rpc',
51+
[Network.GNOSIS_MAINNET]: 'gnosis-rpc',
52+
[Network.PULSECHAIN_MAINNET]: 'pulsechain-rpc',
53+
[Network.PULSECHAIN_TESTNET]: 'pulsechain-testnet-rpc',
54+
[Network.KAVA_MAINNET]: 'kava-evm-rpc',
55+
[Network.CRONOS_MAINNET]: 'cronos-evm-rpc',
56+
[Network.MANTLE_MAINNET]: 'mantle-rpc',
57+
[Network.TAIKO_MAINNET]: 'taiko-rpc',
58+
[Network.TAIKO_HEKLA]: 'taiko-hekla-rpc',
59+
[Network.LINEA_MAINNET]: 'linea-rpc',
60+
[Network.LINEA_SEPOLIA]: 'linea-sepolia-rpc',
61+
[Network.SCROLL_MAINNET]: 'scroll-rpc',
62+
[Network.SCROLL_SEPOLIA]: 'scroll-sepolia-rpc',
63+
[Network.SYSCOIN_MAINNET]: 'syscoin-evm-rpc',
64+
[Network.SYSCOIN_TANENBAUM]: 'syscoin-tanenbaum-evm-rpc',
65+
[Network.HAQQ_MAINNET]: 'haqq-evm-rpc',
66+
[Network.EVMOS_MAINNET]: 'evmos-evm-rpc',
67+
[Network.EVMOS_TESTNET]: 'evmos-testnet-evm-rpc',
68+
[Network.BERACHAIN_TESTNET]: 'berachain-testnet-evm-rpc',
69+
[Network.ETH_MAINNET]: 'ethereum-rpc',
70+
[Network.ETH_SEPOLIA]: 'ethereum-sepolia-rpc',
71+
[Network.ETH_HOLESKY]: 'ethereum-holesky-rpc',
72+
[Network.BSC_MAINNET]: 'bsc-rpc',
73+
[Network.BSC_TESTNET]: 'bsc-testnet-rpc',
74+
[Network.POLYGON_MAINNET]: 'polygon-bor-rpc',
75+
[Network.BASE_MAINNET]: 'base-rpc',
76+
[Network.BASE_SEPOLIA]: 'base-sepolia-rpc',
77+
[Network.ARBITRUM_ONE]: 'arbitrum-one-rpc',
78+
[Network.ARBITRUM_NOVA]: 'arbitrum-nova-rpc',
79+
[Network.ARBITRUM_SEPOLIA]: 'arbitrum-sepolia-rpc',
80+
[Network.AVALANCHE_C_MAINNET]: 'avalanche-c-chain-rpc',
81+
[Network.AVALANCHE_P_MAINNET]: 'avalanche-p-chain-rpc',
82+
[Network.AVALANCHE_X_MAINNET]: 'avalanche-x-chain-rpc',
83+
[Network.AVALANCHE_FUJI_C]: 'avalanche-fuji-c-chain-rpc',
84+
[Network.AVALANCHE_FUJI_P]: 'avalanche-fuji-p-chain-rpc',
85+
[Network.AVALANCHE_FUJI_X]: 'avalanche-fuji-x-chain-rpc',
86+
[Network.OPTIMISM_MAINNET]: 'optimism-rpc',
87+
[Network.OPTIMISM_SEPOLIA]: 'optimism-sepolia-rpc',
88+
[Network.FANTOM_MAINNET]: 'fantom-rpc',
89+
[Network.FANTOM_TESTNET]: 'fantom-testnet-rpc',
90+
[Network.OPBNB_MAINNET]: 'opbnb-rpc',
91+
[Network.OPBNB_TESTNET]: 'opbnb-testnet-rpc',
92+
[Network.GNOSIS_CHIADO]: 'gnosis-chiado-rpc',
93+
[Network.CHILIZ_MAINNET]: 'chiliz-rpc',
94+
[Network.CHILIZ_SPICY]: 'chiliz-spicy-rpc',
95+
[Network.MOONBEAM_MAINNET]: 'moonbeam-rpc',
96+
[Network.BAHAMUT_MAINNET]: 'bahamut-rpc',
97+
[Network.TRON_MAINNET]: 'tron-evm-rpc',
98+
[Network.MOONRIVER_MAINNET]: 'moonriver-rpc',
99+
};
100+
// eslint-disable-next-line class-methods-use-this
101+
public getRPCURL(network: Network, transport: Transport, _: string, _host: string) {
102+
if (!PublicNodeProvider.networkHostMap[network]) {
103+
throw new Error('Network info not avalible.');
104+
}
105+
const defaultHost = `${PublicNodeProvider.networkHostMap[network]}.publicnode.com`;
106+
const host = isValid(_host) ? _host : defaultHost;
107+
if (websocketExclusions.includes(network) && transport === Transport.WebSocket) {
108+
return `${transport}://${host}/websocket`;
109+
}
110+
return `${transport}://${host}`;
111+
}
112+
}

packages/web3-rpc-providers/src/web3_provider_quicknode.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export class QuickNodeProvider<
107107
host = isValid(_host) ? _host : 'small-chaotic-moon.matic.quiknode.pro';
108108
token = isValid(_token) ? _token : '847569f8a017e84d985e10d0f44365d965a951f1';
109109
break;
110-
case Network.POLYGON_AMONY:
110+
case Network.POLYGON_AMOY:
111111
host = isValid(_host) ? _host : 'prettiest-side-shape.matic-amoy.quiknode.pro';
112112
token = isValid(_token) ? _token : '79a9476eea661d4f82de614db1d8a895b14b881c';
113113
break;

packages/web3-rpc-providers/test/unit/request.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Network, Transport } from '../../src/types';
2020
import { Web3ExternalProvider } from '../../src/web3_provider';
2121
import { QuickNodeRateLimitError } from '../../src/errors';
2222
import { QuickNodeProvider } from '../../src/web3_provider_quicknode';
23+
import { PublicNodeProvider } from '../../src/web3_provider_publicnode';
2324

2425
jest.mock('web3-providers-ws', () => {
2526
return {
@@ -122,4 +123,27 @@ describe('Web3ExternalProvider', () => {
122123
};
123124
await expect(provider.request(payload)).rejects.toThrow(QuickNodeRateLimitError);
124125
});
126+
127+
it('should make a request using the PublicNodeProvider provider', async () => {
128+
const network: Network = Network.ETH_MAINNET;
129+
const transport: Transport = Transport.HTTPS;
130+
131+
const mockHttpProvider = {
132+
request: jest.fn(),
133+
};
134+
135+
const mockResponse = { result: 'mock-result' };
136+
mockHttpProvider.request.mockResolvedValue(mockResponse);
137+
138+
const provider = new PublicNodeProvider(network, transport);
139+
(provider as any).provider = mockHttpProvider;
140+
141+
const payload: Web3APIPayload<EthExecutionAPI, Web3APIMethod<EthExecutionAPI>> = {
142+
method: 'eth_getBalance',
143+
params: ['0x0123456789012345678901234567890123456789', 'latest'],
144+
};
145+
146+
const result = await provider.request(payload);
147+
expect(result).toEqual(mockResponse);
148+
});
125149
});

packages/web3/test/integration/web3RPCProviders.test.ts

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ You should have received a copy of the GNU Lesser General Public License
1515
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
1616
*/
1717

18-
import { mainnet, Network, QuickNodeProvider, Transport } from 'web3-rpc-providers';
18+
import {
19+
mainnet,
20+
Network,
21+
QuickNodeProvider,
22+
Transport,
23+
PublicNodeProvider,
24+
} from 'web3-rpc-providers';
1925
import { Web3 } from '../../src/index';
2026

2127
describe('Web3 RPC Provider Integration tests', () => {
@@ -29,7 +35,7 @@ describe('Web3 RPC Provider Integration tests', () => {
2935
Network.BNB_MAINNET,
3036
Network.BNB_TESTNET,
3137
Network.POLYGON_MAINNET,
32-
Network.POLYGON_AMONY,
38+
Network.POLYGON_AMOY,
3339
];
3440

3541
transports.forEach(transport => {
@@ -48,6 +54,89 @@ describe('Web3 RPC Provider Integration tests', () => {
4854
});
4955
});
5056
});
57+
58+
const publicNodeNetworks = [
59+
Network.POLYGON_AMOY,
60+
Network.POLYGON_MAINNET,
61+
Network.DYMENSION_MAINNET,
62+
Network.DYMENSION_TESTNET,
63+
Network.BLAST_MAINNET,
64+
Network.GNOSIS_MAINNET,
65+
Network.PULSECHAIN_MAINNET,
66+
Network.PULSECHAIN_TESTNET,
67+
Network.KAVA_MAINNET,
68+
Network.CRONOS_MAINNET,
69+
Network.MANTLE_MAINNET,
70+
Network.HAQQ_MAINNET,
71+
Network.TAIKO_MAINNET,
72+
Network.TAIKO_HEKLA,
73+
Network.EVMOS_MAINNET,
74+
Network.EVMOS_TESTNET,
75+
Network.BERACHAIN_TESTNET,
76+
Network.LINEA_MAINNET,
77+
Network.LINEA_SEPOLIA,
78+
Network.SCROLL_MAINNET,
79+
Network.SCROLL_SEPOLIA,
80+
Network.SYSCOIN_MAINNET,
81+
Network.SYSCOIN_TANENBAUM,
82+
Network.ETH_MAINNET,
83+
Network.ETH_SEPOLIA,
84+
Network.ETH_HOLESKY,
85+
Network.BSC_MAINNET,
86+
Network.BSC_TESTNET,
87+
Network.BASE_MAINNET,
88+
Network.BASE_SEPOLIA,
89+
Network.ARBITRUM_ONE,
90+
Network.ARBITRUM_NOVA,
91+
Network.ARBITRUM_SEPOLIA,
92+
Network.AVALANCHE_C_MAINNET,
93+
Network.AVALANCHE_P_MAINNET,
94+
Network.AVALANCHE_X_MAINNET,
95+
Network.AVALANCHE_FUJI_C,
96+
Network.AVALANCHE_FUJI_P,
97+
Network.AVALANCHE_FUJI_X,
98+
Network.OPTIMISM_MAINNET,
99+
Network.OPTIMISM_SEPOLIA,
100+
Network.FANTOM_MAINNET,
101+
Network.FANTOM_TESTNET,
102+
Network.OPBNB_MAINNET,
103+
Network.OPBNB_TESTNET,
104+
Network.GNOSIS_CHIADO,
105+
Network.CHILIZ_MAINNET,
106+
Network.CHILIZ_SPICY,
107+
Network.MOONBEAM_MAINNET,
108+
Network.BAHAMUT_MAINNET,
109+
Network.TRON_MAINNET,
110+
Network.MOONRIVER_MAINNET,
111+
];
112+
transports.forEach(transport => {
113+
publicNodeNetworks.forEach(network => {
114+
if (
115+
!(
116+
[
117+
Network.AVALANCHE_P_MAINNET,
118+
Network.AVALANCHE_X_MAINNET,
119+
Network.AVALANCHE_FUJI_X,
120+
Network.AVALANCHE_FUJI_P,
121+
].includes(network) && transport === Transport.WebSocket
122+
)
123+
) {
124+
it(`PublicNodeProvider should work with ${transport} transport and ${network} network`, async () => {
125+
const provider = new PublicNodeProvider(network, transport);
126+
const web3 = new Web3(provider);
127+
const result = await web3.eth.getBlockNumber();
128+
129+
expect(typeof result).toBe('bigint');
130+
expect(result > 0).toBe(true);
131+
132+
if (transport === Transport.WebSocket) {
133+
web3.provider?.disconnect();
134+
}
135+
});
136+
}
137+
});
138+
});
139+
51140
it(`should work with mainnet provider`, async () => {
52141
const web3 = new Web3(mainnet);
53142
const result = await web3.eth.getBlockNumber();

0 commit comments

Comments
 (0)