Source code for eth_networks

"""
Ethereum Network Configuration Module

Provides built-in configurations for popular EVM-compatible networks
with default public RPC endpoints. Supports custom network configuration.
"""

from typing import Dict, Any, Optional
from dataclasses import dataclass


[docs] @dataclass class NetworkConfig: """Immutable description of a single EVM-compatible chain. Bundles the connection and display metadata the wallet tooling needs to talk to one network: its human name, numeric ``chain_id``, JSON-RPC endpoint, native-currency symbol/decimals, block-explorer URL, and a testnet flag. Every entry in the module-level ``NETWORKS`` registry is one of these, and custom user-supplied chains are built into the same shape by :func:`create_custom_network`. Instances are consumed by ``tools/eth_wallet_tools.py`` (via :func:`get_network` / :func:`create_custom_network`) to resolve which RPC URL and chain id to sign and broadcast transactions against; the dataclass is plain data and performs no I/O itself. Attributes: name (str): Human-readable network name (e.g. ``Ethereum Mainnet``). chain_id (int): EIP-155 chain id used for transaction signing. rpc_url (str): Default public JSON-RPC endpoint for the network. native_symbol (str): Ticker of the gas/native token (e.g. ``ETH``). native_decimals (int): Decimal places of the native token (18 for most EVM chains). explorer_url (Optional[str]): Base URL of the block explorer, if any. is_testnet (bool): True for test networks, used to filter them out of :func:`list_networks` results. """ name: str chain_id: int rpc_url: str native_symbol: str native_decimals: int = 18 explorer_url: Optional[str] = None is_testnet: bool = False
[docs] def to_dict(self) -> Dict[str, Any]: """Flatten this config into a plain JSON-serializable dictionary. Produces a one-to-one mapping of every field so the network can be embedded in tool responses or API payloads without exposing the dataclass type. Pure data transformation with no side effects; the keys mirror the attribute names exactly. No callers were found in the repository (grep for ``.to_dict(`` on a network config returns only the definitions), so this is a convenience serializer available to tool handlers rather than something currently wired into a hot path. Returns: Dict[str, Any]: A dictionary with ``name``, ``chain_id``, ``rpc_url``, ``native_symbol``, ``native_decimals``, ``explorer_url`` and ``is_testnet`` keys. """ return { "name": self.name, "chain_id": self.chain_id, "rpc_url": self.rpc_url, "native_symbol": self.native_symbol, "native_decimals": self.native_decimals, "explorer_url": self.explorer_url, "is_testnet": self.is_testnet, }
NETWORKS: Dict[str, NetworkConfig] = { "ethereum": NetworkConfig( name="Ethereum Mainnet", chain_id=1, rpc_url="https://eth.llamarpc.com", native_symbol="ETH", explorer_url="https://etherscan.io", ), "eth": NetworkConfig( name="Ethereum Mainnet", chain_id=1, rpc_url="https://eth.llamarpc.com", native_symbol="ETH", explorer_url="https://etherscan.io", ), "pulsechain": NetworkConfig( name="PulseChain", chain_id=369, rpc_url="https://rpc.pulsechain.com", native_symbol="PLS", explorer_url="https://scan.pulsechain.com", ), "pls": NetworkConfig( name="PulseChain", chain_id=369, rpc_url="https://rpc.pulsechain.com", native_symbol="PLS", explorer_url="https://scan.pulsechain.com", ), "polygon": NetworkConfig( name="Polygon", chain_id=137, rpc_url="https://polygon-rpc.com", native_symbol="MATIC", explorer_url="https://polygonscan.com", ), "matic": NetworkConfig( name="Polygon", chain_id=137, rpc_url="https://polygon-rpc.com", native_symbol="MATIC", explorer_url="https://polygonscan.com", ), "bsc": NetworkConfig( name="BNB Smart Chain", chain_id=56, rpc_url="https://bsc-dataseed.binance.org", native_symbol="BNB", explorer_url="https://bscscan.com", ), "bnb": NetworkConfig( name="BNB Smart Chain", chain_id=56, rpc_url="https://bsc-dataseed.binance.org", native_symbol="BNB", explorer_url="https://bscscan.com", ), "arbitrum": NetworkConfig( name="Arbitrum One", chain_id=42161, rpc_url="https://arb1.arbitrum.io/rpc", native_symbol="ETH", explorer_url="https://arbiscan.io", ), "arb": NetworkConfig( name="Arbitrum One", chain_id=42161, rpc_url="https://arb1.arbitrum.io/rpc", native_symbol="ETH", explorer_url="https://arbiscan.io", ), "optimism": NetworkConfig( name="Optimism", chain_id=10, rpc_url="https://mainnet.optimism.io", native_symbol="ETH", explorer_url="https://optimistic.etherscan.io", ), "op": NetworkConfig( name="Optimism", chain_id=10, rpc_url="https://mainnet.optimism.io", native_symbol="ETH", explorer_url="https://optimistic.etherscan.io", ), "avalanche": NetworkConfig( name="Avalanche C-Chain", chain_id=43114, rpc_url="https://api.avax.network/ext/bc/C/rpc", native_symbol="AVAX", explorer_url="https://snowtrace.io", ), "avax": NetworkConfig( name="Avalanche C-Chain", chain_id=43114, rpc_url="https://api.avax.network/ext/bc/C/rpc", native_symbol="AVAX", explorer_url="https://snowtrace.io", ), "base": NetworkConfig( name="Base", chain_id=8453, rpc_url="https://mainnet.base.org", native_symbol="ETH", explorer_url="https://basescan.org", ), "fantom": NetworkConfig( name="Fantom Opera", chain_id=250, rpc_url="https://rpcapi.fantom.network", native_symbol="FTM", explorer_url="https://ftmscan.com", ), "ftm": NetworkConfig( name="Fantom Opera", chain_id=250, rpc_url="https://rpcapi.fantom.network", native_symbol="FTM", explorer_url="https://ftmscan.com", ), "gnosis": NetworkConfig( name="Gnosis Chain", chain_id=100, rpc_url="https://rpc.gnosischain.com", native_symbol="xDAI", explorer_url="https://gnosisscan.io", ), "cronos": NetworkConfig( name="Cronos", chain_id=25, rpc_url="https://evm.cronos.org", native_symbol="CRO", explorer_url="https://cronoscan.com", ), "zksync": NetworkConfig( name="zkSync Era", chain_id=324, rpc_url="https://mainnet.era.zksync.io", native_symbol="ETH", explorer_url="https://explorer.zksync.io", ), "linea": NetworkConfig( name="Linea", chain_id=59144, rpc_url="https://rpc.linea.build", native_symbol="ETH", explorer_url="https://lineascan.build", ), "scroll": NetworkConfig( name="Scroll", chain_id=534352, rpc_url="https://rpc.scroll.io", native_symbol="ETH", explorer_url="https://scrollscan.com", ), "mantle": NetworkConfig( name="Mantle", chain_id=5000, rpc_url="https://rpc.mantle.xyz", native_symbol="MNT", explorer_url="https://explorer.mantle.xyz", ), "celo": NetworkConfig( name="Celo", chain_id=42220, rpc_url="https://forno.celo.org", native_symbol="CELO", explorer_url="https://celoscan.io", ), "polygon_zkevm": NetworkConfig( name="Polygon zkEVM", chain_id=1101, rpc_url="https://zkevm-rpc.com", native_symbol="ETH", explorer_url="https://zkevm.polygonscan.com", ), "zkevm": NetworkConfig( name="Polygon zkEVM", chain_id=1101, rpc_url="https://zkevm-rpc.com", native_symbol="ETH", explorer_url="https://zkevm.polygonscan.com", ), "blast": NetworkConfig( name="Blast", chain_id=81457, rpc_url="https://rpc.blast.io", native_symbol="ETH", explorer_url="https://blastscan.io", ), "mode": NetworkConfig( name="Mode", chain_id=34443, rpc_url="https://mainnet.mode.network", native_symbol="ETH", explorer_url="https://explorer.mode.network", ), "manta": NetworkConfig( name="Manta Pacific", chain_id=169, rpc_url="https://pacific-rpc.manta.network/http", native_symbol="ETH", explorer_url="https://pacific-explorer.manta.network", ), "metis": NetworkConfig( name="Metis Andromeda", chain_id=1088, rpc_url="https://andromeda.metis.io/?owner=1088", native_symbol="METIS", explorer_url="https://andromeda-explorer.metis.io", ), "aurora": NetworkConfig( name="Aurora", chain_id=1313161554, rpc_url="https://mainnet.aurora.dev", native_symbol="ETH", explorer_url="https://explorer.aurora.dev", ), "moonbeam": NetworkConfig( name="Moonbeam", chain_id=1284, rpc_url="https://rpc.api.moonbeam.network", native_symbol="GLMR", explorer_url="https://moonscan.io", ), "moonriver": NetworkConfig( name="Moonriver", chain_id=1285, rpc_url="https://rpc.api.moonriver.moonbeam.network", native_symbol="MOVR", explorer_url="https://moonriver.moonscan.io", ), "harmony": NetworkConfig( name="Harmony One", chain_id=1666600000, rpc_url="https://api.harmony.one", native_symbol="ONE", explorer_url="https://explorer.harmony.one", ), "one": NetworkConfig( name="Harmony One", chain_id=1666600000, rpc_url="https://api.harmony.one", native_symbol="ONE", explorer_url="https://explorer.harmony.one", ), "kava": NetworkConfig( name="Kava EVM", chain_id=2222, rpc_url="https://evm.kava.io", native_symbol="KAVA", explorer_url="https://kavascan.com", ), "opbnb": NetworkConfig( name="opBNB", chain_id=204, rpc_url="https://opbnb-mainnet-rpc.bnbchain.org", native_symbol="BNB", explorer_url="https://opbnbscan.com", ), "fraxtal": NetworkConfig( name="Fraxtal", chain_id=252, rpc_url="https://rpc.frax.com", native_symbol="frxETH", explorer_url="https://fraxscan.com", ), "worldchain": NetworkConfig( name="World Chain", chain_id=480, rpc_url="https://worldchain-mainnet.g.alchemy.com/public", native_symbol="ETH", explorer_url="https://worldscan.org", ), "taiko": NetworkConfig( name="Taiko", chain_id=167000, rpc_url="https://rpc.taiko.xyz", native_symbol="ETH", explorer_url="https://taikoscan.io", ), # Testnets "sepolia": NetworkConfig( name="Sepolia Testnet", chain_id=11155111, rpc_url="https://ethereum-sepolia-rpc.publicnode.com", native_symbol="ETH", explorer_url="https://sepolia.etherscan.io", is_testnet=True, ), "holesky": NetworkConfig( name="Holesky Testnet", chain_id=17000, rpc_url="https://holesky.drpc.org", native_symbol="ETH", explorer_url="https://holesky.etherscan.io", is_testnet=True, ), "pulsechain_testnet": NetworkConfig( name="PulseChain Testnet v4", chain_id=943, rpc_url="https://rpc.v4.testnet.pulsechain.com", native_symbol="tPLS", explorer_url="https://scan.v4.testnet.pulsechain.com", is_testnet=True, ), "pls_testnet": NetworkConfig( name="PulseChain Testnet v4", chain_id=943, rpc_url="https://rpc.v4.testnet.pulsechain.com", native_symbol="tPLS", explorer_url="https://scan.v4.testnet.pulsechain.com", is_testnet=True, ), "polygon_amoy": NetworkConfig( name="Polygon Amoy Testnet", chain_id=80002, rpc_url="https://rpc-amoy.polygon.technology", native_symbol="MATIC", explorer_url="https://amoy.polygonscan.com", is_testnet=True, ), "amoy": NetworkConfig( name="Polygon Amoy Testnet", chain_id=80002, rpc_url="https://rpc-amoy.polygon.technology", native_symbol="MATIC", explorer_url="https://amoy.polygonscan.com", is_testnet=True, ), "bsc_testnet": NetworkConfig( name="BSC Testnet", chain_id=97, rpc_url="https://data-seed-prebsc-1-s1.binance.org:8545", native_symbol="tBNB", explorer_url="https://testnet.bscscan.com", is_testnet=True, ), "arbitrum_sepolia": NetworkConfig( name="Arbitrum Sepolia", chain_id=421614, rpc_url="https://sepolia-rollup.arbitrum.io/rpc", native_symbol="ETH", explorer_url="https://sepolia.arbiscan.io", is_testnet=True, ), "base_sepolia": NetworkConfig( name="Base Sepolia", chain_id=84532, rpc_url="https://sepolia.base.org", native_symbol="ETH", explorer_url="https://sepolia.basescan.org", is_testnet=True, ), } CHAIN_ID_TO_NETWORK: Dict[int, str] = { config.chain_id: name for name, config in NETWORKS.items() if name == name.lower() and not any( other_name != name and NETWORKS[other_name].chain_id == config.chain_id and len(other_name) > len(name) for other_name in NETWORKS ) }
[docs] def get_network(network_name_or_id: str) -> Optional[NetworkConfig]: """Resolve a network by its name/alias or numeric chain id. The primary lookup the wallet tooling uses to turn a user-supplied identifier into a concrete RPC target. It first tries a case-insensitive, whitespace-trimmed match against the ``NETWORKS`` registry keys (which include short aliases like ``eth`` or ``op``); failing that, it treats the argument as a decimal chain id and scans ``NETWORKS`` for the first config with a matching ``chain_id``. The registry is in-memory, so no network I/O happens here. Called by ``tools/eth_wallet_tools.py`` (``_resolve_network`` and related handlers) to pick the chain a transaction or balance query runs against; when it returns ``None`` the tool layer falls back to :func:`create_custom_network` or reports an unknown-network error. Args: network_name_or_id (str): A registry key/alias (``ethereum``, ``base``) or a decimal chain id rendered as a string (``"1"``, ``"8453"``). Returns: Optional[NetworkConfig]: The matching network config, or ``None`` if no name alias or chain id matches. """ name_lower = network_name_or_id.lower().strip() if name_lower in NETWORKS: return NETWORKS[name_lower] try: chain_id = int(network_name_or_id) for config in NETWORKS.values(): if config.chain_id == chain_id: return config except ValueError: pass return None
[docs] def get_network_by_chain_id(chain_id: int) -> Optional[NetworkConfig]: """Look up a network config by its exact numeric chain id. A narrower, integer-typed sibling of :func:`get_network` that skips the name-alias path and linearly scans the ``NETWORKS`` registry for the first config whose ``chain_id`` equals the argument. Because several aliases share a chain id, the first registry entry encountered wins. In-memory only, no I/O. No callers were found in the repository outside this module's own definition, so it is currently an unused public helper kept for symmetry with :func:`get_network` and for external/tool callers. Args: chain_id (int): The EIP-155 chain id to match. Returns: Optional[NetworkConfig]: The matching config, or ``None`` if no registered network uses that chain id. """ for config in NETWORKS.values(): if config.chain_id == chain_id: return config return None
[docs] def create_custom_network( name: str, chain_id: int, rpc_url: str, native_symbol: str = "ETH", native_decimals: int = 18, explorer_url: Optional[str] = None, is_testnet: bool = False, ) -> NetworkConfig: """Build an ad-hoc :class:`NetworkConfig` from caller-supplied parameters. Lets the wallet tooling target chains that are not in the built-in ``NETWORKS`` registry: the caller passes an explicit RPC URL and chain id and gets back a config of the same shape as the built-ins, so the rest of the signing/broadcast path is identical. This is a thin constructor wrapper that just forwards its arguments to :class:`NetworkConfig`; it does not register the result, validate the RPC, or perform any I/O. Called by ``tools/eth_wallet_tools.py`` (``_resolve_network``) when :func:`get_network` returns ``None`` for a chain id but the caller provided a custom RPC endpoint to use. Args: name (str): Human-readable network name. chain_id (int): EIP-155 chain id for transaction signing. rpc_url (str): JSON-RPC endpoint to connect to. native_symbol (str): Native-token ticker; defaults to ``ETH``. native_decimals (int): Native-token decimal places; defaults to 18. explorer_url (Optional[str]): Block-explorer base URL, if known. is_testnet (bool): Whether this should be treated as a test network. Returns: NetworkConfig: A freshly constructed config wrapping the given values. """ return NetworkConfig( name=name, chain_id=chain_id, rpc_url=rpc_url, native_symbol=native_symbol, native_decimals=native_decimals, explorer_url=explorer_url, is_testnet=is_testnet, )
[docs] def list_networks(include_testnets: bool = True) -> list: """Summarize the registered networks, one entry per unique chain. Produces a de-duplicated, display-oriented listing of ``NETWORKS``: it walks the registry, keeps only the first config seen for each ``chain_id`` (so the many name aliases collapse to a single row), optionally drops testnets, and returns lightweight summary dicts sorted with mainnets first and then alphabetically by name. Read-only over the in-memory registry. Called by ``tools/eth_wallet_tools.py`` (the ``list_eth_networks`` tool handler at line 749) to present the available chains to the model/user. Args: include_testnets (bool): When False, networks flagged ``is_testnet`` are omitted from the result. Returns: list: A list of dicts, each with ``name``, ``chain_id``, ``symbol`` and ``is_testnet`` keys, sorted mainnets-first then by name. """ seen_chain_ids = set() networks = [] for name, config in NETWORKS.items(): if config.chain_id in seen_chain_ids: continue if not include_testnets and config.is_testnet: continue seen_chain_ids.add(config.chain_id) networks.append( { "name": config.name, "chain_id": config.chain_id, "symbol": config.native_symbol, "is_testnet": config.is_testnet, } ) return sorted(networks, key=lambda n: (n["is_testnet"], n["name"]))