diff --git a/packages/web3-rpc-providers/src/index.ts b/packages/web3-rpc-providers/src/index.ts index f7c0e6c2613..83a04a55b71 100644 --- a/packages/web3-rpc-providers/src/index.ts +++ b/packages/web3-rpc-providers/src/index.ts @@ -16,12 +16,15 @@ along with web3.js. If not, see . */ import { QuickNodeProvider } from './web3_provider_quicknode.js'; +import { AlchemyProvider } from './web3_provider_alchemy.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'; +export * from './web3_provider_alchemy.js'; // default providers export const mainnet = new QuickNodeProvider(); +export const alchemy = new AlchemyProvider(); diff --git a/packages/web3-rpc-providers/src/types.ts b/packages/web3-rpc-providers/src/types.ts index 258d6411c57..dad9ef0c3b3 100644 --- a/packages/web3-rpc-providers/src/types.ts +++ b/packages/web3-rpc-providers/src/types.ts @@ -29,6 +29,11 @@ export enum Network { ETH_HOLESKY = 'eth_holesky', POLYGON_MAINNET = 'polygon_mainnet', + POLYGON_MUMBAI = 'polygon_mumbai', + POLYGON_AMONY = 'polygon_amony', + POLYGON_POS_MAINNET = 'polygon_pos_mainnet', + POLYGON_ZKEVM_MAINNET = 'polygon_zkevm_mainnet', + POLYGON_ZKEVM_CARDONA = 'polygon_zkevm_cardona', POLYGON_AMOY = 'polygon_amoy', AVALANCHE_C_MAINNET = 'avalanche_c_mainnet', @@ -37,6 +42,7 @@ export enum Network { ARBITRUM_MAINNET = 'arbitrum_mainnet', ARBITRUM_SEPOLIA = 'arbitrum_sepolia', + ARBITRUM_NOVA_MAINNET = 'arbitrum_nova_mainnet', BASE_MAINNET = 'base_mainnet', BASE_SEPOLIA = 'base_sepolia', @@ -53,6 +59,55 @@ export enum Network { BNB_MAINNET = 'bnb_mainnet', BNB_TESTNET = 'bnb_testnet', + WORLD_CHAIN_MAINNET = 'world_chain_mainnet', + WORLD_CHAIN_SEPOLIA = 'world_chain_sepolia', + + SHAPE_MAINNET = 'shape_mainnet', + SHAPE_SEPOLIA = 'shape_sepolia', + + ZKSYNC_MAINNET = 'zksync_mainnet', + ZKSYNC_SEPOLIA = 'zksync_sepolia', + + STARKNET_MAINNET = 'starknet_mainnet', + STARKNET_SEPOLIA = 'starknet_sepolia', + + ZETACHAIN_MAINNET = 'zetachain_mainnet', + ZETACHAIN_TESTNET = 'zetachain_testnet', + + FANTOM_OPERA_MAINNET = 'fantom_opera_mainnet', + FANTOM_OPERA_TESTNET = 'fantom_opera_testnet', + + BERACHAIN_ARTIO = 'berachain_artio', + + BLAST_SEPOLIA = 'blast_sepolia', + + ZORA_MAINNET = 'zora_mainnet', + ZORA_SEPOLIA = 'zora_sepolia', + + POLYNOMIAL_MAINNET = 'polynomial_mainnet', + POLYNOMIAL_SEPOLIA = 'polynomial_sepolia', + + FRAX_MAINNET = 'frax_mainnet', + FRAX_SEPOLIA = 'frax_sepolia', + + SOLANA_MAINNET = 'solana_mainnet', + SOLANA_DEVNET = 'solana_devnet', + + CROSSFI_TESTNET = 'crossfi_testnet', + + ASTAR_MAINNET = 'astar_mainnet', + + FLOW_EVM_TESTNET = 'flow_evm_testnet', + + SONEIUM_MINATO = 'soneium_minato', + + GEIST_POLTER = 'geist_polter', + + ROOTSTOCK_MAINNET = 'rootstock_mainnet', + ROOTSTOCK_TESTNET = 'rootstock_testnet', + + UNICHAIN_SEPOLIA = 'unichain_sepolia', + BSC_MAINNET = 'bsc_mainnet', BSC_TESTNET = 'bsc_testnet', diff --git a/packages/web3-rpc-providers/src/web3_provider_alchemy.ts b/packages/web3-rpc-providers/src/web3_provider_alchemy.ts new file mode 100644 index 00000000000..750bb348d0b --- /dev/null +++ b/packages/web3-rpc-providers/src/web3_provider_alchemy.ts @@ -0,0 +1,107 @@ +/* +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 { HttpProviderOptions } from 'web3-providers-http'; +import { Web3ExternalProvider } from './web3_provider.js'; +import { Network, Transport, SocketOptions } from './types.js'; + +function isValid(value: string): boolean { + return !!(value && value.trim().length > 0); +} + +export class AlchemyProvider extends Web3ExternalProvider { + public constructor( + network: Network = Network.ETH_MAINNET, + transport: Transport = Transport.HTTPS, + token = '', + host = '', + providerConfigOptions?: HttpProviderOptions | SocketOptions, + ) { + super(network, transport, token, host, providerConfigOptions); + } + + public static readonly networkStringMap: { [key: string]: string } = { + [Network.ETH_MAINNET]: 'eth-mainnet.g.alchemy.com', + [Network.ETH_SEPOLIA]: 'eth-sepolia.g.alchemy.com', + [Network.ETH_HOLESKY]: 'eth-holesky.g.alchemy.com', + [Network.ARBITRUM_MAINNET]: 'arb-mainnet.g.alchemy.com', + [Network.ARBITRUM_SEPOLIA]: 'arb-sepolia.g.alchemy.com', + [Network.ARBITRUM_NOVA_MAINNET]: 'arb-nova.g.alchemy.com', + [Network.BASE_MAINNET]: 'base-mainnet.g.alchemy.com', + [Network.BASE_SEPOLIA]: 'base-sepolia.g.alchemy.com', + [Network.POLYGON_MAINNET]: 'polygon-mainnet.g.alchemy.com', + [Network.POLYGON_MUMBAI]: 'polygon-mumbai.g.alchemy.com', + [Network.POLYGON_AMOY]: 'polygon-amoy.g.alchemy.com', + [Network.POLYGON_POS_MAINNET]: 'polygon-mainnet.g.alchemy.com', + [Network.POLYGON_ZKEVM_MAINNET]: 'polygonzkevm-mainnet.g.alchemy.com', + [Network.POLYGON_ZKEVM_CARDONA]: 'polygonzkevm-cardona.g.alchemy.com', + [Network.OPTIMISM_MAINNET]: 'opt-mainnet.g.alchemy.com', + [Network.OPTIMISM_SEPOLIA]: 'opt-sepolia.g.alchemy.com', + [Network.ASTAR_MAINNET]: 'astar-mainnet.g.alchemy.com', + [Network.WORLD_CHAIN_MAINNET]: 'worldchain-mainnet.g.alchemy.com', + [Network.WORLD_CHAIN_SEPOLIA]: 'worldchain-sepolia.g.alchemy.com', + [Network.SHAPE_MAINNET]: 'shape-mainnet.g.alchemy.com', + [Network.SHAPE_SEPOLIA]: 'shape-sepolia.g.alchemy.com', + [Network.ZKSYNC_MAINNET]: 'zksync-mainnet.g.alchemy.com', + [Network.ZKSYNC_SEPOLIA]: 'zksync-sepolia.g.alchemy.com', + [Network.STARKNET_MAINNET]: 'starknet-mainnet.g.alchemy.com', + [Network.STARKNET_SEPOLIA]: 'starknet-sepolia.g.alchemy.com', + [Network.ZETACHAIN_MAINNET]: 'zetachain-mainnet.g.alchemy.com', + [Network.ZETACHAIN_TESTNET]: 'zetachain-testnet.g.alchemy.com', + [Network.FANTOM_OPERA_MAINNET]: 'fantom-mainnet.g.alchemy.com', + [Network.FANTOM_OPERA_TESTNET]: 'fantom-testnet.g.alchemy.com', + [Network.MANTLE_MAINNET]: 'mantle-mainnet.g.alchemy.com', + [Network.BERACHAIN_ARTIO]: 'berachain-artio.g.alchemy.com', + [Network.BLAST_MAINNET]: 'blast-mainnet.g.alchemy.com', + [Network.BLAST_SEPOLIA]: 'blast-sepolia.g.alchemy.com', + [Network.LINEA_MAINNET]: 'linea-mainnet.g.alchemy.com', + [Network.LINEA_SEPOLIA]: 'linea-sepolia.g.alchemy.com', + [Network.ZORA_MAINNET]: 'zora-mainnet.g.alchemy.com', + [Network.ZORA_SEPOLIA]: 'zora-sepolia.g.alchemy.com', + [Network.POLYNOMIAL_MAINNET]: 'polynomial-mainnet.g.alchemy.com', + [Network.POLYNOMIAL_SEPOLIA]: 'polynomial-sepolia.g.alchemy.com', + [Network.SCROLL_MAINNET]: 'scroll-mainnet.g.alchemy.com', + [Network.SCROLL_SEPOLIA]: 'scroll-sepolia.g.alchemy.com', + [Network.FRAX_MAINNET]: 'frax-mainnet.g.alchemy.com', + [Network.FRAX_SEPOLIA]: 'frax-sepolia.g.alchemy.com', + [Network.SOLANA_MAINNET]: 'solana-mainnet.g.alchemy.com', + [Network.SOLANA_DEVNET]: 'solana-devnet.g.alchemy.com', + [Network.CROSSFI_TESTNET]: 'crossfi-testnet.g.alchemy.com', + [Network.FLOW_EVM_TESTNET]: 'flow-testnet.g.alchemy.com', + [Network.SONEIUM_MINATO]: 'soneium-minato.g.alchemy.com', + [Network.GEIST_POLTER]: 'geist-polter.g.alchemy.com', + [Network.ROOTSTOCK_MAINNET]: 'rootstock-mainnet.g.alchemy.com', + [Network.ROOTSTOCK_TESTNET]: 'rootstock-testnet.g.alchemy.com', + [Network.UNICHAIN_SEPOLIA]: 'unichain-sepolia.g.alchemy.com', + [Network.GNOSIS_MAINNET]: 'gnosis-mainnet.g.alchemy.com', + [Network.BNB_MAINNET]: 'bnb-mainnet.g.alchemy.com', + [Network.BNB_TESTNET]: 'bnb-testnet.g.alchemy.com', + [Network.OPBNB_MAINNET]: 'opbnb-mainnet.g.alchemy.com', + [Network.OPBNB_TESTNET]: 'opbnb-testnet.g.alchemy.com', + }; + + // eslint-disable-next-line class-methods-use-this + public getRPCURL(network: Network, transport: Transport, _token: string, _host: string) { + const host = AlchemyProvider.networkStringMap[network] || ''; + const token = isValid(_token) ? _token : `alchemy-${network.toLowerCase()}-token`; + + if (!host) { + throw new Error('Network info not available.'); + } + + return `${transport}://${host}/v2/${token}`; + } +} diff --git a/packages/web3/test/integration/web3RPCProviders.test.ts b/packages/web3/test/integration/web3RPCProviders.test.ts index 6e6d2994f9c..d192a54cd88 100644 --- a/packages/web3/test/integration/web3RPCProviders.test.ts +++ b/packages/web3/test/integration/web3RPCProviders.test.ts @@ -21,12 +21,13 @@ import { QuickNodeProvider, Transport, PublicNodeProvider, + AlchemyProvider, } from 'web3-rpc-providers'; import { Web3 } from '../../src/index'; describe('Web3 RPC Provider Integration tests', () => { const transports = Object.values(Transport); - const networks = [ + const quickNodeNetworks = [ Network.ETH_MAINNET, Network.ETH_HOLESKY, Network.ETH_SEPOLIA, @@ -39,7 +40,7 @@ describe('Web3 RPC Provider Integration tests', () => { ]; transports.forEach(transport => { - networks.forEach(network => { + quickNodeNetworks.forEach(network => { it(`QuickNodeProvider should work with ${transport} transport and ${network} network`, async () => { const provider = new QuickNodeProvider(network, transport); const web3 = new Web3(provider); @@ -53,6 +54,81 @@ describe('Web3 RPC Provider Integration tests', () => { } }); }); + + const alchemyNetworks = [ + Network.ETH_MAINNET, + Network.ETH_SEPOLIA, + Network.ETH_HOLESKY, + Network.ARBITRUM_MAINNET, + Network.ARBITRUM_SEPOLIA, + Network.ARBITRUM_NOVA_MAINNET, + Network.BASE_MAINNET, + Network.BASE_SEPOLIA, + Network.POLYGON_MAINNET, + Network.POLYGON_MUMBAI, + Network.POLYGON_AMOY, + Network.POLYGON_POS_MAINNET, + Network.POLYGON_ZKEVM_MAINNET, + Network.POLYGON_ZKEVM_CARDONA, + Network.OPTIMISM_MAINNET, + Network.OPTIMISM_SEPOLIA, + Network.ASTAR_MAINNET, + Network.WORLD_CHAIN_MAINNET, + Network.WORLD_CHAIN_SEPOLIA, + Network.SHAPE_MAINNET, + Network.SHAPE_SEPOLIA, + Network.ZKSYNC_MAINNET, + Network.ZKSYNC_SEPOLIA, + Network.STARKNET_MAINNET, + Network.STARKNET_SEPOLIA, + Network.ZETACHAIN_MAINNET, + Network.ZETACHAIN_TESTNET, + Network.FANTOM_OPERA_MAINNET, + Network.FANTOM_OPERA_TESTNET, + Network.MANTLE_MAINNET, + Network.BERACHAIN_ARTIO, + Network.BLAST_MAINNET, + Network.BLAST_SEPOLIA, + Network.LINEA_MAINNET, + Network.LINEA_SEPOLIA, + Network.ZORA_MAINNET, + Network.ZORA_SEPOLIA, + Network.POLYNOMIAL_MAINNET, + Network.POLYNOMIAL_SEPOLIA, + Network.SCROLL_MAINNET, + Network.SCROLL_SEPOLIA, + Network.FRAX_MAINNET, + Network.FRAX_SEPOLIA, + Network.SOLANA_MAINNET, + Network.SOLANA_DEVNET, + Network.CROSSFI_TESTNET, + Network.FLOW_EVM_TESTNET, + Network.SONEIUM_MINATO, + Network.GEIST_POLTER, + Network.ROOTSTOCK_MAINNET, + Network.ROOTSTOCK_TESTNET, + Network.UNICHAIN_SEPOLIA, + Network.GNOSIS_MAINNET, + Network.BNB_MAINNET, + Network.BNB_TESTNET, + Network.OPBNB_MAINNET, + Network.OPBNB_TESTNET, + ]; + + alchemyNetworks.forEach(network => { + it(`AlchemyProvider should work with ${transport} transport and ${network} network`, async () => { + const provider = new AlchemyProvider(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(); + } + }); + }); }); const publicNodeNetworks = [