Skip to content

Commit

Permalink
Add rpc provider public node (#7322)
Browse files Browse the repository at this point in the history
* init

* finished

* add unit test

* add changelog

* Update CHANGELOG.md

* Update web3_provider_publicnode.ts

* fix

* Update web3_provider_publicnode.ts
  • Loading branch information
avkos authored Oct 23, 2024
1 parent 3687070 commit 4ca66af
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 6 deletions.
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}`;
}
}
2 changes: 1 addition & 1 deletion packages/web3-rpc-providers/src/web3_provider_quicknode.ts
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

1 comment on commit 4ca66af

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: 4ca66af Previous: 3687070 Ratio
processingTx 22612 ops/sec (±6.67%) 22856 ops/sec (±6.62%) 1.01
processingContractDeploy 37950 ops/sec (±8.62%) 37573 ops/sec (±8.57%) 0.99
processingContractMethodSend 15758 ops/sec (±8.57%) 15453 ops/sec (±7.92%) 0.98
processingContractMethodCall 27523 ops/sec (±7.75%) 27091 ops/sec (±7.83%) 0.98
abiEncode 44232 ops/sec (±7.00%) 42669 ops/sec (±7.08%) 0.96
abiDecode 29126 ops/sec (±7.29%) 30350 ops/sec (±6.23%) 1.04
sign 1529 ops/sec (±1.12%) 1520 ops/sec (±3.42%) 0.99
verify 356 ops/sec (±1.00%) 362 ops/sec (±0.69%) 1.02

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.