import R from 'ramda'; import chainIdMap from './chainIdMap.json'; import { EthereumNetwork, ExtendedNetworkInfo, PartialRecord } from './types'; export const getEvmNetworkToChainIdMap = (): Record => { const lookupMap: Record = chainIdMap as Record; const invertKeyAndValue = (updatedMap: Record, chainId: string): Record => ({ ...updatedMap, [lookupMap[chainId]]: parseInt(chainId), }); return R.reduce(invertKeyAndValue, {}, Object.keys(lookupMap)); }; export const lookupEvmChainIdByNetwork = (network: string): string | null => { const lookupMap: Record = chainIdMap as Record; const chainEntry = R.toPairs(lookupMap).find( ([, value]: [string, EthereumNetwork]) => value === (network as EthereumNetwork) ); return chainEntry ? chainEntry[0] : null; }; export const lookupEvmChainNetworkById = (chainId: number): EthereumNetwork | null => { const lookupMap: Record = chainIdMap as Record; return lookupMap[chainId.toString()] || null; }; /** * For a given network name, return the possible aliases it could have (including the original passed name). * @param network */ export const lookupAliasesByNetworkName = (network: EthereumNetwork): EthereumNetwork[] => { // Find the first network entry where either: // a) The top-level key matches the given network, or // b) One of the aliases matches the given network. // // If one is found, return the top-level key and all aliases in a single array. const isMatch = ([networkKey, networkSettings]: [string, ExtendedNetworkInfo]): boolean => networkKey.toLowerCase() === network.toLowerCase() || !!networkSettings.aliases?.map(R.toLower).includes(network.toLowerCase()); const [networkKeyFound, networkSettingsFound] = Object.entries(networks).find(isMatch) || []; if (networkKeyFound) { const nameList: EthereumNetwork[] = [ networkKeyFound as EthereumNetwork, ...((networkSettingsFound?.aliases as EthereumNetwork[]) || []), ]; return R.uniq(nameList); } // No matches found. return []; }; const evmChainIdMap = getEvmNetworkToChainIdMap(); // One network can be an alias to another, e.g. 'matic' points to 'polygonMainnet', so we allow either config or another EthereumNetwork as the value. export const networks: PartialRecord = { localhost: { url: 'http://localhost:8545/', chainId: evmChainIdMap.localhost, network: 'Localhost', }, mainnet: { url: 'https://mainnet.infura.io/v3/', chainId: evmChainIdMap.mainnet, aliases: ['homestead', 'ethereum'], isMainnet: true, symbol: 'ETH', network: 'Ethereum', }, sepolia: { url: 'https://sepolia.infura.io/v3/', chainId: evmChainIdMap.sepolia, symbol: 'ETH', network: 'Sepolia', }, goerli: { url: 'https://goerli.infura.io/v3/', chainId: evmChainIdMap.goerli, symbol: 'ETH', network: 'Goerli', }, polygonMumbai: { url: 'https://polygon-mumbai.infura.io/v3/', chainId: evmChainIdMap.polygonMumbai, aliases: ['maticmum'], symbol: 'MATIC', network: 'Polygon Mumbai', }, polygonMainnet: { url: 'https://polygon-mainnet.infura.io/v3/', chainId: evmChainIdMap.polygonMainnet, aliases: ['matic', 'polygon'], isMainnet: true, symbol: 'MATIC', network: 'Polygon', }, polygonZKEVMTestnet: { url: `https://polygonzkevm-testnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY_GATEKEEPER_ZKEVM_TESTNET}`, chainId: evmChainIdMap.polygonZKEVMTestnet, isMainnet: false, symbol: 'ETH', network: 'Polygon ZKEVM Testnet', }, polygonZKEVM: { url: `https://polygonzkevm-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_API_KEY_GATEKEEPER_ZKEVM}`, chainId: evmChainIdMap.polygonZKEVM, isMainnet: true, symbol: 'ETH', network: 'Polygon ZKEVM', }, auroraTestnet: { url: 'https://aurora-testnet.infura.io/v3/', chainId: evmChainIdMap.auroraTestnet, symbol: 'AURORA', network: 'Aurora Testnet', }, auroraMainnet: { url: 'https://aurora-mainnet.infura.io/v3/', chainId: evmChainIdMap.auroraMainnet, isMainnet: true, symbol: 'AURORA', network: 'Aurora', aliases: ['aurora'], }, optimismGoerli: { url: 'https://optimism-goerli.infura.io/v3/', chainId: evmChainIdMap.optimismGoerli, symbol: 'ETH', network: 'Optimism Goerli', }, optimismMainnet: { url: 'https://optimism-mainnet.infura.io/v3/', chainId: evmChainIdMap.optimismMainnet, isMainnet: true, symbol: 'ETH', network: 'Optimism', aliases: ['optimism'], }, palmTestnet: { url: 'https://palm-testnet.infura.io/v3/', chainId: evmChainIdMap.palmTestnet, symbol: 'PALM', network: 'Palm Testnet', }, palmMainnet: { url: 'https://palm-mainnet.infura.io/v3/', chainId: evmChainIdMap.palmMainnet, isMainnet: true, symbol: 'PALM', network: 'Palm', aliases: ['palm'], }, arbitrumGoerli: { url: 'https://arbitrum-goerli.infura.io/v3/', chainId: evmChainIdMap.arbitrumGoerli, symbol: 'ETH', network: 'Arbitrum Goerli', }, arbitrumSepolia: { url: 'https://arbitrum-sepolia.infura.io/v3/', chainId: evmChainIdMap.arbitrumSepolia, symbol: 'ETH', network: 'Arbitrum Sepolia', }, arbitrumMainnet: { url: 'https://arbitrum-mainnet.infura.io/v3/', chainId: evmChainIdMap.arbitrumMainnet, isMainnet: true, symbol: 'ETH', network: 'Arbitrum', aliases: ['arbitrum'], }, celoMainnet: { url: 'https://celo-mainnet.infura.io/v3/', chainId: evmChainIdMap.celoMainnet, isMainnet: true, network: 'Celo', aliases: ['celo'], }, celoAlfajores: { url: 'https://celo-alfajores.infura.io/v3/', chainId: evmChainIdMap.celoAlfajores, symbol: 'CELO', network: 'Celo Algajores', }, avalancheCChain: { url: 'https://avalanche-mainnet.infura.io/v3/', chainId: evmChainIdMap.avalancheCChain, isMainnet: true, symbol: 'AVAX', network: 'Avalanche', aliases: ['avalancheCChain'], }, avalancheCChainFuji: { url: 'https://avalanche-fuji.infura.io/v3/', chainId: evmChainIdMap.avalancheCChainFuji, symbol: 'AVAX', network: 'Avalanche Fuji', }, starknetMainnet: { url: 'https://starknet-mainnet.infura.io/v3/', chainId: 0, isMainnet: true, symbol: 'STRK', network: 'Starknet', aliases: ['starknet'], }, starknetGoerli: { url: 'https://starknet-goerli.infura.io/v3/', chainId: 0, symbol: 'STRK', network: 'Starknet Goerli', }, xdcMainnet: { url: 'https://rpc.xinfin.network', chainId: evmChainIdMap.xdcMainnet, isMainnet: true, symbol: 'XDC', network: 'XDC', aliases: ['xdc'], }, xdcApothem: { url: 'https://erpc.apothem.network', chainId: evmChainIdMap.xdcApothem, symbol: 'XDC', network: 'XDC Apothem', }, fantomMainnet: { url: `${process.env.FANTOM_MAINNET_URL || 'https://fantom.publicnode.com'}`, chainId: evmChainIdMap.fantomMainnet, isMainnet: true, symbol: 'FTM', network: 'Fantom', aliases: ['fantom'], }, fantomTestnet: { url: 'https://rpc.testnet.fantom.network', chainId: evmChainIdMap.fantomTestnet, symbol: 'FTM', network: 'Fantom Testnet', }, baseSepolia: { url: 'https://sepolia.base.org', chainId: evmChainIdMap.baseSepolia, symbol: 'ETH', network: 'Base Sepolia', }, baseMainnet: { url: 'https://base.llamarpc.com', chainId: evmChainIdMap.baseMainnet, symbol: 'ETH', isMainnet: true, network: 'Base', aliases: ['base'], }, // bscMainnet: { // url: 'https://bsc.rpc.blxrbdn.com', // chainId: evnChainIdMap.bscMainnet, // isMainnet: true, // aliases: ['bsc'], // }, // cronos: { // url: 'https://cronos.blockpi.network/v1/rpc/public', // chainId: evnChainIdMap.cronos, // }, // gnosis: { // url: 'https://rpc.gnosischain.com', // chainId: evnChainIdMap.gnosis, // }, // moonbeam: { // url: 'https://1rpc.io/glmr', // chainId: evnChainIdMap.moonbeam, // }, // moonriver: { // url: 'https://moonriver.public.blastapi.io', // chainId: evnChainIdMap.moonriver, // }, }; /** * Given an ethereum chainId, return the network name * @param {string} network: name or chainId * @returns {string} network name */ export const getNetworkName = (network: string): string => { // if not nan lookup network name const id = parseInt(network); if (isNaN(id)) { return network; } return lookupEvmChainNetworkById(id) ?? network; }; /** * given an ethereum chainId, return the network info * @param {string} network - name or chainId of the ethereum network * @returns {ExtendedNetworkInfo|undefinedå} */ export const getNetwork = (network: string): ExtendedNetworkInfo | undefined => { const name = getNetworkName(network) as EthereumNetwork; return networks[name]; };