"""
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"]))