rmi-backend/app/chain_registry.py

738 lines
26 KiB
Python

"""
SENTINEL — Multi-Chain Registry & Configuration
================================================
Centralized chain definitions, RPC endpoints, explorer APIs,
and data source configurations for all 13 supported chains.
Chains: solana, ethereum, base, bsc, arbitrum, polygon, avalanche,
optimism, fantom, linea, zksync, scroll, mantle
"""
import os
from dataclasses import dataclass, field
from enum import Enum
class ChainFamily(Enum):
"""Blockchain family classification."""
SOLANA = "solana" # Solana SPL tokens
EVM = "evm" # Ethereum Virtual Machine chains
COSMOS = "cosmos" # Not yet supported
@dataclass
class ChainConfig:
"""Full configuration for a supported blockchain."""
name: str # Short name (e.g. "ethereum", "base")
chain_id: str # Chain ID ("1", "8453", "solana")
family: ChainFamily = ChainFamily.EVM
display_name: str = "" # Human name ("Ethereum", "Base")
# RPC endpoints (primary, fallback, public)
rpc_endpoints: list[str] = field(default_factory=list)
# Block explorer API
explorer_url: str = ""
explorer_api_url: str = ""
explorer_api_key_env: str = "" # Env var for API key
# Native token
native_token_symbol: str = "ETH"
native_token_decimals: int = 18
# DEX data sources (DexScreener supports all)
dexscreener_chain_id: str = "" # Chain ID as DexScreener knows it
# Known lockers / factory addresses
known_lockers: dict[str, str] = field(default_factory=dict)
# CEX hot wallets for this chain
cex_hot_wallets: dict[str, list[str]] = field(default_factory=dict)
# Block explorer API key env var
api_key_env: str = ""
def get_explorer_api_key(self) -> str:
"""Get the block explorer API key from environment."""
return os.getenv(self.api_key_env, "")
@property
def is_solana(self) -> bool:
return self.family == ChainFamily.SOLANA
@property
def is_evm(self) -> bool:
return self.family == ChainFamily.EVM
# ─── Chain Registry ────────────────────────────────────────────────
CHAINS: dict[str, ChainConfig] = {
"solana": ChainConfig(
name="solana",
chain_id="solana",
family=ChainFamily.SOLANA,
display_name="Solana",
rpc_endpoints=[
"https://solana-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY}",
"https://solana.drpc.org",
"https://solana-rpc.publicnode.com",
"https://rpc.ankr.com/solana",
],
explorer_url="https://solscan.io",
explorer_api_url="https://public-api.solscan.io",
explorer_api_key_env="SOLSCAN_API_KEY",
native_token_symbol="SOL",
native_token_decimals=9,
dexscreener_chain_id="solana",
known_lockers={},
cex_hot_wallets={
"Binance": [
"5tzF4VG5DB9R4PJJZdE3EGX6MHcY7K6uAcAFN7b7zoyF",
"DRpbCBMxVnDK7maPM4Gqt5iQJ3U1QorZ3Nz8g7Aeu9p",
"9WzDXMPQefAPQgxPaMkr2Fi8nY91fMjJY4kMN7AhN2qh",
"QC4kUxtjAy5r1LFf6kPHF7GvNACm7UV2BcGmdv7E4Jj",
"E5jRGgPnG4F7tHSyLQyazL3rbc8zP9G1NYJNx2GV5yRT",
"8fXs4GQcZGJSJiMxH1L5tNp9R2VkS6wY7Z3A1BqFcD0E",
"GJRs4FwHtemZ5ZEKxJ8x4D2gN8qFgXSP1M2wQrLmVk7h",
],
"Coinbase": [
"2AQ7xRF2Jq5k2C8RNqiP95jRskQk91nR6Nj4Q5xqG3x7",
"9x2YQfGjg1Q7Q7Q1Q1Q1Q1Q1Q1Q1Q1Q1Q1Q1Q1Q1Q1",
],
"OKX": [
"5YK5YK5YK5YK5YK5YK5YK5YK5YK5YK5YK5YK5YK5YK5Y",
"5uNpdhHDuBoXd7RUAB4SzvqP5vQGrnQJn9f7xVU5RoXL",
],
"Bybit": [
"FcnWJ9y2xV6AKP5BrVK3hLmTM82QwpE4qXq7NgTsHDBP",
"AC5RDfQFmDS1deWZos921JfqbQJzuyqqM21f7qNeYLkN",
],
"Kraken": [
"FWznbcNXWQuHTaweGHAb3k3MMw7gfX7Y9dZH3XPYkbWY",
],
"KuCoin": [
"BmFdpraXjbNqTGHeQ8QwPXy7sL9VLsKHvkSEWcYMYxVQ",
"H8sMJSCNmYGCSVbY9u6VEU8SG7Qk2rPqFn3JntVnDpTE",
],
"Gate.io": [
"u6PJ8DtNzmFZ3rG3TzZioV5EqPbJYExq1En8HVNDjxR",
],
"MEXC": [
"ASTy5S1R2dq4WHCpVhBMbb4vLgWJdPBF5kG6zMTZBQsg",
],
"Bitget": [
"Aobp2URFvJk4iCBsiJfLkCxQ89rXHMLDR3MvUtJcNYrF",
],
"Crypto.com": [
"HyCHiyEqFGt3S1MJCqQyZ2mDn7rFQ8GWLN4Xx6VPoBRk",
],
"HTX": [
"GR4NVErnTnR3dYxGpVfPm1K9JVfZwGSmujqBBFLVxRzH",
],
"Upbit": [
"6zcmsePHoGpK6WjK7VGaMhhEu1QSGEdfSWd1qzvJJF5N",
"3YfFWpQZfC1mJgGht6wWuXLgp6nQsTEtqQtEWQHJzp1d",
],
"Bithumb": [
"7BZsKJvQLcKHx8LMuN9M8rqpPmNr4qWGM9R8RcwWcTq1",
],
"Bitfinex": [
"FXxGxnmr58cJSiy4drkLeA41td4QtEby9pRZcxPYvPnN",
],
"BitMEX": [
"8ZPuSkQC9cETSWpNRuQYJHbZvbKmpPWYyduk8Ns1e9BX",
],
"Korbit": [
"DQiuRWA57ZyRsG6LEtQNkFV6JUCa6kTEKNfRvGJLQD4",
],
},
api_key_env="",
),
"ethereum": ChainConfig(
name="ethereum",
chain_id="1",
family=ChainFamily.EVM,
display_name="Ethereum",
rpc_endpoints=[
"https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://ethereum-rpc.publicnode.com",
"https://eth.llamarpc.com",
"https://rpc.ankr.com/eth",
"https://cloudflare-eth.com",
],
explorer_url="https://etherscan.io",
explorer_api_url="https://api.etherscan.io/api",
explorer_api_key_env="ETHERSCAN_API_KEY",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="ethereum",
known_lockers={
"0x663A5C229c09b049E36dCc11a9B0d4a8c9c3dBC2": "UNCX Network",
"0xe2fE530C047f2d85298b07D9333C05737f1435fB": "Team Finance",
"0x7ee058420e5937496f5a2096f04caa7721cf70cc": "PinkLock",
},
cex_hot_wallets={
"Binance": [
"0x28C6c06298d514Db089934071355E5743bf21d60",
"0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8",
"0xDFd5293D8e347dFe59E90eFd55b2956a1343963d",
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
"0x21a31Ee1afC51d94C2eFcCAa2092aD1028285549",
"0x4976A4A02f38326660D17bf34b431dC6e2eb2327",
"0x56Eddb7aa87536c09CCc2793473599fD21A8b17F",
"0x5a52E96BAcdaBb82fd05763E25335261B270Efcb",
"0x9696f59E4d72E237BE84fFD425DCaD154Bf96976",
"0x61189Da79177950A7272c88c6058B96D4Bcd6bE2",
],
"Coinbase": [
"0x503828976D22510aad0201ac7EC88293211D23Da",
"0xdD2F458a4b46251B1dB5E2ed56bDDf8DB4Ed7E8e",
"0xbf3EEfAC1073b93a2639fB1eD4aB7bC997d8732E",
"0x71660c4005BA85C37ccec55d0C4493E3Fe753dBf",
"0xA9D1e08C7793af67e9d92fe308d5697FB81d3E43",
"0x3CD751E6b0078Be39333c1fbFDeC3a2b87Ab9C82",
],
"OKX": [
"0x6cC5F688a315f3dC28A7781717a9A798a59fDA7b",
"0x236F9F97e0E62388479bf9E5BA4889E46B0273c3",
"0x8a612aB4cC1CBf5b37Bd6a0E8A0AcE5AF0e5B00F",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
"0x1Db92e2EeBC8E0c075a02BeA49a2935BcD2dFC73",
],
"Kraken": [
"0x2910543Af39abA0Cd09dBb2D50200b3E800A63D2",
"0x0A869d79a7052C7f1b55a8EbAbbEa3420f0D1E13",
"0xE853c56864A2ebe4576a807D26Fdc4A0adA51919",
"0x267be1C1D684F78cb4F6a176C4911b741E4Ffdc0",
],
"KuCoin": [
"0x2B5634C42055806a59e9107ED44D43c426E58258",
"0x679040D7223FdE9363AA2cF68B4C8A7394cc83C5",
],
"Gate.io": [
"0x0D0707963952f2fBA59dD06f2b425ace40b492Fe",
"0x7793cD85c11A924478cF358d2b5DeC8C4f41acB0",
],
"Bitfinex": [
"0x876EabF441B2EE5B5b0554Fd502a8E0600950cFa",
"0x1151314c646Ce4E0eFD76d1aF4760aE66a2Fe30e",
"0x742d35Cc6634C0532925a3b844Bc9e7596bBDEdD",
],
"Bitget": [
"0x0639556F03714A74a5fEEaF5736a4A64f70Df206",
"0x97dE9E01a1c95d3D09D9b29333dF4e5A50A1B61d",
],
"MEXC": [
"0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88",
"0x3CC936b795A188F0e246cBB2D74C5Bd190aeCF18",
],
"HTX": [
"0xeB2d2F1b8c558a40207669291Fda468E50c8a0Bb",
"0x1871e9B1C47c0FF6FeA5bAAcB3A04F8068D60E7e",
],
"Crypto.com": [
"0x6262998Ced04146fA42253a5C0AF90CA02dfd2A3",
"0x46340b20830761efd32832A74d7169B29FEB9758",
],
"Gemini": [
"0xd24400ae8BfEBb18cA49Be86258a3C749cf46853",
"0x61EDCDf5bb737ADffE5043706e7C5bb1f1a56eEA",
"0x07ee55aA48Bb72DCc597523F4A2632811C1bE3a4",
],
"Korbit": [
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
],
"Bittrex": [
"0x94b1B6eB67A7D14b2ad2Ee93FeBA5457c5451eF2",
"0x66F820a414680B5bcda5eAFc3d1F3C99b9F9B11f",
],
"Bitstamp": [
"0x00bdb5699745f5b860228c8f939abF1b9Ae374eD",
"0x1522900B6daFac587d499a862861C0869Be6E428",
],
"Upbit": [
"0x390De26d772D2e2005C6d1D24afC902baC3ad635",
"0x5e032243d507C743b061Ef021E2eC7bfA1Ec5277",
],
"OKEx": [
"0x6cC5F688a315f3dC28A7781717a9A798a59fDA7b",
],
"BinanceUS": [
"0x61189Da79177950A7272c88c6058B96D4Bcd6bE2",
],
"Poloniex": [
"0x32Be343B94f860124dC4fEe278FDCBD38C102D88",
"0x209c4784AB1E8183Cf58cA33cb740dcbF2fD3bF7",
],
"Bithumb": [
"0x44E9d0Bc5f3F4Bc54d9F6c1bd9fA09fD0a1e633c",
],
"Coinone": [
"0x167a9333BF582556f35Bd4d16a7E80a9525A9c3B",
],
"LBank": [
"0x1205E4F0D2f0225Fd4b3d8d7C1C9c7b9A1B3D5F7",
],
"BitMart": [
"0xE93381fB4c4F14bDa253907b18faD305D799241a",
],
"WooX": [
"0x0d83F81c79E8e93b30C4f5e9a4bDf4D3C5E7F9A1",
],
},
api_key_env="ETHERSCAN_API_KEY",
),
"base": ChainConfig(
name="base",
chain_id="8453",
family=ChainFamily.EVM,
display_name="Base",
rpc_endpoints=[
"https://base-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://mainnet.base.org",
"https://base-rpc.publicnode.com",
"https://rpc.ankr.com/base",
],
explorer_url="https://basescan.org",
explorer_api_url="https://api.basescan.org/api",
explorer_api_key_env="BASESCAN_API_KEY",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="base",
known_lockers={
"0x663A5C229c09b049E36dCc11a9B0d4a8c9c3dBC2": "UNCX Network",
"0x7ee058420e5937496f5a2096f04caa7721cf70cc": "PinkLock",
},
cex_hot_wallets={
"Coinbase": [
"0x3304E22DDaa22bCdC5fCa2269b418046aE7b566A",
"0x3154B747c4bFd35f2e42dF08c28c1A7c8C4f71D7",
],
"Binance": [
"0x3304E22DDaa22bCdC5fCa2269b418046aE7b566A",
],
"OKX": [
"0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
],
},
api_key_env="BASESCAN_API_KEY",
),
"bsc": ChainConfig(
name="bsc",
chain_id="56",
family=ChainFamily.EVM,
display_name="BSC",
rpc_endpoints=[
"https://bsc-dataseed.binance.org",
"https://rpc.ankr.com/bsc",
"https://bsc-rpc.publicnode.com",
"https://bsc-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
],
explorer_url="https://bscscan.com",
explorer_api_url="https://api.bscscan.com/api",
explorer_api_key_env="BSCSCAN_API_KEY",
native_token_symbol="BNB",
native_token_decimals=18,
dexscreener_chain_id="bsc",
known_lockers={
"0x663A5C229c09b049E36dCc11a9B0d4a8c9c3dBC2": "UNCX Network",
"0x7ee058420e5937496f5a2096f04caa7721cf70cc": "PinkLock",
"0x2D8E3580C6E428d4EaA069198D0B76ae0f98e43a": "Mudra",
},
cex_hot_wallets={
"Binance": [
"0xB38e8c17e38363aF6EbdCb3dAE12e0243582891D",
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"MEXC": ["0x51e6D27FA57373d8d4C256231241053a70Cb1d93"],
"OKX": [
"0x8894E0a0c962CB723c1976a4421c95949bE2D4E3",
],
"KuCoin": [
"0xD6216fc19DB775Df92A4Dba0f1E18b85a4c623F0",
],
"Gate.io": [
"0x2A4EdF32D2b0ce008FeEB8EE2dA93B2dF34c3a4D",
],
"Bybit": [
"0xE2Fc0d06Fe01466fDDb948Cf38A8Ee98f36793B0",
],
"Bitget": [
"0x6B6a3E94FEb2B5FaF40bfD71B39C4D6A4E6F8e7c",
],
},
api_key_env="BSCSCAN_API_KEY",
),
"arbitrum": ChainConfig(
name="arbitrum",
chain_id="42161",
family=ChainFamily.EVM,
display_name="Arbitrum",
rpc_endpoints=[
"https://arb1.arbitrum.io/rpc",
"https://arb-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://arbitrum-one-rpc.publicnode.com",
],
explorer_url="https://arbiscan.io",
explorer_api_url="https://api.arbiscan.io/api",
explorer_api_key_env="ARBISCAN_API_KEY",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="arbitrum",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
],
"Coinbase": [
"0x3154B747c4bFd35f2e42dF08c28c1A7c8C4f71D7",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"Kraken": [
"0x2910543Af39abA0Cd09dBb2D50200b3E800A63D2",
],
},
api_key_env="ARBISCAN_API_KEY",
),
"polygon": ChainConfig(
name="polygon",
chain_id="137",
family=ChainFamily.EVM,
display_name="Polygon",
rpc_endpoints=[
"https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://polygon-rpc.com",
"https://polygon-bor-rpc.publicnode.com",
"https://rpc.ankr.com/polygon",
],
explorer_url="https://polygonscan.com",
explorer_api_url="https://api.polygonscan.com/api",
explorer_api_key_env="POLYGONSCAN_API_KEY",
native_token_symbol="MATIC",
native_token_decimals=18,
dexscreener_chain_id="polygon",
cex_hot_wallets={
"Binance": [
"0xe7804c37c13166fF0b37F5aE0BB07A3aEbb6e245",
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
],
"Coinbase": [
"0x3154B747c4bFd35f2e42dF08c28c1A7c8C4f71D7",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
"KuCoin": [
"0xD6216fc19DB775Df92A4Dba0f1E18b85a4c623F0",
],
"Gate.io": [
"0x2A4EdF32D2b0ce008FeEB8EE2dA93B2dF34c3a4D",
],
},
api_key_env="POLYGONSCAN_API_KEY",
),
"avalanche": ChainConfig(
name="avalanche",
chain_id="43114",
family=ChainFamily.EVM,
display_name="Avalanche",
rpc_endpoints=[
"https://api.avax.network/ext/bc/C/rpc",
"https://avalanche-c-chain-rpc.publicnode.com",
"https://rpc.ankr.com/avalanche",
],
explorer_url="https://snowtrace.io",
explorer_api_url="https://api.snowtrace.io/api",
explorer_api_key_env="SNOWTRACE_API_KEY",
native_token_symbol="AVAX",
native_token_decimals=18,
dexscreener_chain_id="avalanche",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
"0xF977814e90dA44bFA03b6295A0616a897441aceC",
],
"Coinbase": [
"0x3154B747c4bFd35f2e42dF08c28c1A7c8C4f71D7",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
"KuCoin": [
"0xD6216fc19DB775Df92A4Dba0f1E18b85a4c623F0",
],
},
api_key_env="SNOWTRACE_API_KEY",
),
"optimism": ChainConfig(
name="optimism",
chain_id="10",
family=ChainFamily.EVM,
display_name="Optimism",
rpc_endpoints=[
"https://mainnet.optimism.io",
"https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}",
"https://optimism-rpc.publicnode.com",
],
explorer_url="https://optimistic.etherscan.io",
explorer_api_url="https://api-optimistic.etherscan.io/api",
explorer_api_key_env="ETHERSCAN_API_KEY", # Shares Etherscan key
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="optimism",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"Coinbase": [
"0x3154B747c4bFd35f2e42dF08c28c1A7c8C4f71D7",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
},
api_key_env="ETHERSCAN_API_KEY",
),
"fantom": ChainConfig(
name="fantom",
chain_id="250",
family=ChainFamily.EVM,
display_name="Fantom",
rpc_endpoints=[
"https://rpc.fantom.network",
"https://fantom-rpc.publicnode.com",
"https://rpc.ankr.com/fantom",
],
explorer_url="https://ftmscan.com",
explorer_api_url="https://api.ftmscan.com/api",
explorer_api_key_env="FTMSCAN_API_KEY",
native_token_symbol="FTM",
native_token_decimals=18,
dexscreener_chain_id="fantom",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"KuCoin": [
"0xD6216fc19DB775Df92A4Dba0f1E18b85a4c623F0",
],
},
api_key_env="FTMSCAN_API_KEY",
),
"linea": ChainConfig(
name="linea",
chain_id="59144",
family=ChainFamily.EVM,
display_name="Linea",
rpc_endpoints=[
"https://rpc.linea.build",
"https://linea-rpc.publicnode.com",
],
explorer_url="https://lineascan.build",
explorer_api_url="https://api.lineascan.build/api",
explorer_api_key_env="",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="linea",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
},
api_key_env="",
),
"zksync": ChainConfig(
name="zksync",
chain_id="324",
family=ChainFamily.EVM,
display_name="zkSync Era",
rpc_endpoints=[
"https://mainnet.era.zksync.io",
"https://zksync-era.blockpi.network/v1/rpc/public",
],
explorer_url="https://explorer.zksync.io",
explorer_api_url="https://block-explorer-api.mainnet.zksync.io/api",
explorer_api_key_env="",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="zksync",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
},
api_key_env="",
),
"scroll": ChainConfig(
name="scroll",
chain_id="534352",
family=ChainFamily.EVM,
display_name="Scroll",
rpc_endpoints=[
"https://rpc.scroll.io",
"https://scroll-rpc.publicnode.com",
],
explorer_url="https://scrollscan.com",
explorer_api_url="https://api.scrollscan.com/api",
explorer_api_key_env="",
native_token_symbol="ETH",
native_token_decimals=18,
dexscreener_chain_id="scroll",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"OKX": [
"0x06959153B974D0D5fDfd87D561dF1cA9aA5E7f05",
],
},
api_key_env="ETHERSCAN_API_KEY",
),
"mantle": ChainConfig(
name="mantle",
chain_id="5000",
family=ChainFamily.EVM,
display_name="Mantle",
rpc_endpoints=[
"https://rpc.mantle.xyz",
],
explorer_url="https://mantlescan.xyz",
explorer_api_url="https://api.mantlescan.xyz/api",
explorer_api_key_env="",
native_token_symbol="MNT",
native_token_decimals=18,
dexscreener_chain_id="mantle",
cex_hot_wallets={
"Binance": [
"0x3C7832B91d940a7C87e0119f60A106C6dB2CcC60",
],
"Bybit": [
"0xf89d7b9c864f589bbF53a82105107622B35EaA40",
],
},
api_key_env="",
),
}
# ─── Convenience functions ─────────────────────────────────────────
def get_chain(chain_name: str) -> ChainConfig | None:
"""Get chain config by name (case-insensitive)."""
return CHAINS.get(chain_name.lower())
def get_chain_family(chain_name: str) -> ChainFamily:
"""Determine if a chain is Solana, EVM, or other."""
cfg = CHAINS.get(chain_name.lower())
return cfg.family if cfg else ChainFamily.EVM
def is_solana(chain_name: str) -> bool:
return get_chain_family(chain_name) == ChainFamily.SOLANA
def is_evm(chain_name: str) -> bool:
return get_chain_family(chain_name) == ChainFamily.EVM
def resolve_chain_id(chain_name: str) -> str:
"""Resolve chain name to numeric or canonical ID."""
cfg = CHAINS.get(chain_name.lower())
return cfg.chain_id if cfg else chain_name
def list_chains() -> list[str]:
"""List all supported chain names."""
return list(CHAINS.keys())
def get_chain_display(chain_name: str) -> str:
"""Get human-readable chain display name."""
cfg = CHAINS.get(chain_name.lower())
return cfg.display_name if cfg else chain_name.title()
def get_explorer_url(chain_name: str, address: str, tx: str = "") -> str:
"""Build explorer URL for an address or transaction."""
cfg = CHAINS.get(chain_name.lower())
if not cfg:
return ""
if tx:
return f"{cfg.explorer_url}/tx/{tx}" if cfg.is_evm else f"{cfg.explorer_url}/tx/{tx}"
if cfg.is_solana:
return f"{cfg.explorer_url}/account/{address}"
return f"{cfg.explorer_url}/address/{address}"
def get_rpc_urls(chain_name: str) -> list[str]:
"""Get list of RPC URLs for a chain (with env vars resolved)."""
cfg = CHAINS.get(chain_name.lower())
if not cfg:
return []
urls = []
for url in cfg.rpc_endpoints:
# Resolve ${VAR} references from environment
import re
resolved = re.sub(r"\$\{(\w+)\}", lambda m: os.getenv(m.group(1), m.group(1)), url)
urls.append(resolved)
return urls
def get_explorer_api_config(chain_name: str) -> tuple[str, str]:
"""Get (api_url, api_key) for a chain's block explorer."""
cfg = CHAINS.get(chain_name.lower())
if not cfg:
return ("", "")
api_key = ""
if cfg.api_key_env:
api_key = os.getenv(cfg.api_key_env, "")
return (cfg.explorer_api_url, api_key)
def get_cex_wallets(chain_name: str) -> dict[str, list[str]]:
"""Get known CEX hot wallets for a chain."""
cfg = CHAINS.get(chain_name.lower())
return cfg.cex_hot_wallets if cfg else {}
def get_known_lockers(chain_name: str) -> dict[str, str]:
"""Get known liquidity locker contracts for a chain (keys are lowercase-normalized)."""
cfg = CHAINS.get(chain_name.lower())
if not cfg:
return {}
return {k.lower(): v for k, v in cfg.known_lockers.items()}