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

Add rpc provider public node #7322

Merged
merged 11 commits into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
4 changes: 4 additions & 0 deletions packages/web3-rpc-providers/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions packages/web3-rpc-providers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down
51 changes: 48 additions & 3 deletions packages/web3-rpc-providers/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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
Expand Down
112 changes: 112 additions & 0 deletions packages/web3-rpc-providers/src/web3_provider_publicnode.ts
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

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<API> {
// 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}`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
24 changes: 24 additions & 0 deletions packages/web3-rpc-providers/test/unit/request.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<EthExecutionAPI, Web3APIMethod<EthExecutionAPI>> = {
method: 'eth_getBalance',
params: ['0x0123456789012345678901234567890123456789', 'latest'],
};

const result = await provider.request(payload);
expect(result).toEqual(mockResponse);
});
});
93 changes: 91 additions & 2 deletions packages/web3/test/integration/web3RPCProviders.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@ You should have received a copy of the GNU Lesser General Public License
along with web3.js. If not, see <http://www.gnu.org/licenses/>.
*/

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', () => {
Expand All @@ -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 => {
Expand All @@ -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();
Expand Down
Loading