nautilus_model/defi/
chain.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Basic structures for representing on-chain blocks and transactions in DeFi integrations.
17
18use std::{
19    fmt::{Display, Formatter},
20    str::FromStr,
21    sync::Arc,
22};
23
24use serde::{Deserialize, Serialize};
25use strum::{Display, EnumIter, EnumString};
26
27/// Represents different blockchain networks.
28#[derive(
29    Debug,
30    Clone,
31    Copy,
32    Hash,
33    PartialOrd,
34    PartialEq,
35    Ord,
36    Eq,
37    Display,
38    EnumIter,
39    EnumString,
40    Serialize,
41    Deserialize,
42)]
43#[non_exhaustive]
44#[strum(ascii_case_insensitive)]
45#[cfg_attr(feature = "python", pyo3::pyclass(module = "nautilus_trader.model"))]
46#[cfg_attr(feature = "python", pyo3_stub_gen::derive::gen_stub_pyclass_enum)]
47pub enum Blockchain {
48    Abstract,
49    Arbitrum,
50    ArbitrumNova,
51    ArbitrumSepolia,
52    Aurora,
53    Avalanche,
54    Base,
55    BaseSepolia,
56    Berachain,
57    BerachainBartio,
58    Blast,
59    BlastSepolia,
60    Boba,
61    Bsc,
62    BscTestnet,
63    Celo,
64    Chiliz,
65    CitreaTestnet,
66    Curtis,
67    Cyber,
68    Darwinia,
69    Ethereum,
70    Fantom,
71    Flare,
72    Fraxtal,
73    Fuji,
74    GaladrielDevnet,
75    Gnosis,
76    GnosisChiado,
77    GnosisTraces,
78    HarmonyShard0,
79    Holesky,
80    HoleskyTokenTest,
81    Hyperliquid,
82    HyperliquidTemp,
83    Ink,
84    InternalTestChain,
85    Kroma,
86    Linea,
87    Lisk,
88    Lukso,
89    LuksoTestnet,
90    Manta,
91    Mantle,
92    MegaethTestnet,
93    Merlin,
94    Metall2,
95    Metis,
96    MevCommit,
97    Mode,
98    MonadTestnet,
99    MonadTestnetBackup,
100    MoonbaseAlpha,
101    Moonbeam,
102    Morph,
103    MorphHolesky,
104    Opbnb,
105    Optimism,
106    OptimismSepolia,
107    PharosDevnet,
108    Polygon,
109    PolygonAmoy,
110    PolygonZkEvm,
111    Rootstock,
112    Saakuru,
113    Scroll,
114    Sepolia,
115    ShimmerEvm,
116    Soneium,
117    Sophon,
118    SophonTestnet,
119    Superseed,
120    Unichain,
121    UnichainSepolia,
122    Xdc,
123    XdcTestnet,
124    Zeta,
125    Zircuit,
126    ZKsync,
127    Zora,
128}
129
130/// Defines a blockchain with its unique identifiers and connection details for network interaction.
131#[cfg_attr(feature = "python", pyo3::pyclass(module = "nautilus_pyo3.model"))]
132#[cfg_attr(feature = "python", pyo3_stub_gen::derive::gen_stub_pyclass)]
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134pub struct Chain {
135    /// The blockchain network type.
136    pub name: Blockchain,
137    /// The unique identifier for this blockchain.
138    pub chain_id: u32,
139    /// URL endpoint for HyperSync connection.
140    pub hypersync_url: String,
141    /// URL endpoint for the default RPC connection.
142    pub rpc_url: Option<String>,
143    /// The number of decimals for the native currency.
144    pub native_currency_decimals: u8,
145}
146
147/// A thread-safe shared pointer to a `Chain`, enabling efficient reuse across multiple components.
148pub type SharedChain = Arc<Chain>;
149
150impl Chain {
151    /// Creates a new [`Chain`] instance with the specified blockchain and chain ID.
152    pub fn new(name: Blockchain, chain_id: u32) -> Self {
153        Self {
154            chain_id,
155            name,
156            hypersync_url: format!("https://{chain_id}.hypersync.xyz"),
157            rpc_url: None,
158            native_currency_decimals: 18, // Default to 18 for EVM chains
159        }
160    }
161
162    /// Sets the RPC URL endpoint.
163    pub fn set_rpc_url(&mut self, rpc: String) {
164        self.rpc_url = Some(rpc);
165    }
166
167    /// Returns a reference to the `Chain` corresponding to the given `chain_id`, or `None` if it is not found.
168    pub fn from_chain_id(chain_id: u32) -> Option<&'static Chain> {
169        match chain_id {
170            2741 => Some(&chains::ABSTRACT),
171            42161 => Some(&chains::ARBITRUM),
172            42170 => Some(&chains::ARBITRUM_NOVA),
173            421614 => Some(&chains::ARBITRUM_SEPOLIA),
174            1313161554 => Some(&chains::AURORA),
175            43114 => Some(&chains::AVALANCHE),
176            8453 => Some(&chains::BASE),
177            84532 => Some(&chains::BASE_SEPOLIA),
178            80094 => Some(&chains::BERACHAIN),
179            80085 => Some(&chains::BERACHAIN_BARTIO),
180            81457 => Some(&chains::BLAST),
181            168587773 => Some(&chains::BLAST_SEPOLIA),
182            288 => Some(&chains::BOBA),
183            56 => Some(&chains::BSC),
184            97 => Some(&chains::BSC_TESTNET),
185            42220 => Some(&chains::CELO),
186            8888 => Some(&chains::CHILIZ),
187            3333 => Some(&chains::CITREA_TESTNET),
188            33111 => Some(&chains::CURTIS),
189            7560 => Some(&chains::CYBER),
190            46 => Some(&chains::DARWINIA),
191            1 => Some(&chains::ETHEREUM),
192            250 => Some(&chains::FANTOM),
193            14 => Some(&chains::FLARE),
194            252 => Some(&chains::FRAXTAL),
195            43113 => Some(&chains::FUJI),
196            696969 => Some(&chains::GALADRIEL_DEVNET),
197            100 => Some(&chains::GNOSIS),
198            10200 => Some(&chains::GNOSIS_CHIADO),
199            10300 => Some(&chains::GNOSIS_TRACES),
200            1666600000 => Some(&chains::HARMONY_SHARD_0),
201            17000 => Some(&chains::HOLESKY),
202            17001 => Some(&chains::HOLESKY_TOKEN_TEST),
203            7979 => Some(&chains::HYPERLIQUID),
204            7978 => Some(&chains::HYPERLIQUID_TEMP),
205            222 => Some(&chains::INK),
206            13337 => Some(&chains::INTERNAL_TEST_CHAIN),
207            255 => Some(&chains::KROMA),
208            59144 => Some(&chains::LINEA),
209            501 => Some(&chains::LISK),
210            42 => Some(&chains::LUKSO),
211            4201 => Some(&chains::LUKSO_TESTNET),
212            169 => Some(&chains::MANTA),
213            5000 => Some(&chains::MANTLE),
214            777 => Some(&chains::MEGAETH_TESTNET),
215            4200 => Some(&chains::MERLIN),
216            90 => Some(&chains::METALL2),
217            1088 => Some(&chains::METIS),
218            11 => Some(&chains::MEV_COMMIT),
219            34443 => Some(&chains::MODE),
220            2323 => Some(&chains::MONAD_TESTNET),
221            2358 => Some(&chains::MONAD_TESTNET_BACKUP),
222            1287 => Some(&chains::MOONBASE_ALPHA),
223            1284 => Some(&chains::MOONBEAM),
224            2710 => Some(&chains::MORPH),
225            2710111 => Some(&chains::MORPH_HOLESKY),
226            204 => Some(&chains::OPBNB),
227            10 => Some(&chains::OPTIMISM),
228            11155420 => Some(&chains::OPTIMISM_SEPOLIA),
229            1337 => Some(&chains::PHAROS_DEVNET),
230            137 => Some(&chains::POLYGON),
231            80002 => Some(&chains::POLYGON_AMOY),
232            1101 => Some(&chains::POLYGON_ZKEVM),
233            30 => Some(&chains::ROOTSTOCK),
234            1204 => Some(&chains::SAAKURU),
235            534352 => Some(&chains::SCROLL),
236            11155111 => Some(&chains::SEPOLIA),
237            148 => Some(&chains::SHIMMER_EVM),
238            109 => Some(&chains::SONEIUM),
239            138 => Some(&chains::SOPHON),
240            139 => Some(&chains::SOPHON_TESTNET),
241            10001 => Some(&chains::SUPERSEED),
242            9999 => Some(&chains::UNICHAIN),
243            9997 => Some(&chains::UNICHAIN_SEPOLIA),
244            50 => Some(&chains::XDC),
245            51 => Some(&chains::XDC_TESTNET),
246            7000 => Some(&chains::ZETA),
247            78600 => Some(&chains::ZIRCUIT),
248            324 => Some(&chains::ZKSYNC),
249            7777777 => Some(&chains::ZORA),
250            _ => None,
251        }
252    }
253
254    /// Returns a reference to the `Chain` corresponding to the given chain name, or `None` if it is not found.
255    ///
256    /// String matching is case-insensitive.
257    pub fn from_chain_name(chain_name: &str) -> Option<&'static Chain> {
258        let blockchain = Blockchain::from_str(chain_name).ok()?;
259
260        match blockchain {
261            Blockchain::Abstract => Some(&chains::ABSTRACT),
262            Blockchain::Arbitrum => Some(&chains::ARBITRUM),
263            Blockchain::ArbitrumNova => Some(&chains::ARBITRUM_NOVA),
264            Blockchain::ArbitrumSepolia => Some(&chains::ARBITRUM_SEPOLIA),
265            Blockchain::Aurora => Some(&chains::AURORA),
266            Blockchain::Avalanche => Some(&chains::AVALANCHE),
267            Blockchain::Base => Some(&chains::BASE),
268            Blockchain::BaseSepolia => Some(&chains::BASE_SEPOLIA),
269            Blockchain::Berachain => Some(&chains::BERACHAIN),
270            Blockchain::BerachainBartio => Some(&chains::BERACHAIN_BARTIO),
271            Blockchain::Blast => Some(&chains::BLAST),
272            Blockchain::BlastSepolia => Some(&chains::BLAST_SEPOLIA),
273            Blockchain::Boba => Some(&chains::BOBA),
274            Blockchain::Bsc => Some(&chains::BSC),
275            Blockchain::BscTestnet => Some(&chains::BSC_TESTNET),
276            Blockchain::Celo => Some(&chains::CELO),
277            Blockchain::Chiliz => Some(&chains::CHILIZ),
278            Blockchain::CitreaTestnet => Some(&chains::CITREA_TESTNET),
279            Blockchain::Curtis => Some(&chains::CURTIS),
280            Blockchain::Cyber => Some(&chains::CYBER),
281            Blockchain::Darwinia => Some(&chains::DARWINIA),
282            Blockchain::Ethereum => Some(&chains::ETHEREUM),
283            Blockchain::Fantom => Some(&chains::FANTOM),
284            Blockchain::Flare => Some(&chains::FLARE),
285            Blockchain::Fraxtal => Some(&chains::FRAXTAL),
286            Blockchain::Fuji => Some(&chains::FUJI),
287            Blockchain::GaladrielDevnet => Some(&chains::GALADRIEL_DEVNET),
288            Blockchain::Gnosis => Some(&chains::GNOSIS),
289            Blockchain::GnosisChiado => Some(&chains::GNOSIS_CHIADO),
290            Blockchain::GnosisTraces => Some(&chains::GNOSIS_TRACES),
291            Blockchain::HarmonyShard0 => Some(&chains::HARMONY_SHARD_0),
292            Blockchain::Holesky => Some(&chains::HOLESKY),
293            Blockchain::HoleskyTokenTest => Some(&chains::HOLESKY_TOKEN_TEST),
294            Blockchain::Hyperliquid => Some(&chains::HYPERLIQUID),
295            Blockchain::HyperliquidTemp => Some(&chains::HYPERLIQUID_TEMP),
296            Blockchain::Ink => Some(&chains::INK),
297            Blockchain::InternalTestChain => Some(&chains::INTERNAL_TEST_CHAIN),
298            Blockchain::Kroma => Some(&chains::KROMA),
299            Blockchain::Linea => Some(&chains::LINEA),
300            Blockchain::Lisk => Some(&chains::LISK),
301            Blockchain::Lukso => Some(&chains::LUKSO),
302            Blockchain::LuksoTestnet => Some(&chains::LUKSO_TESTNET),
303            Blockchain::Manta => Some(&chains::MANTA),
304            Blockchain::Mantle => Some(&chains::MANTLE),
305            Blockchain::MegaethTestnet => Some(&chains::MEGAETH_TESTNET),
306            Blockchain::Merlin => Some(&chains::MERLIN),
307            Blockchain::Metall2 => Some(&chains::METALL2),
308            Blockchain::Metis => Some(&chains::METIS),
309            Blockchain::MevCommit => Some(&chains::MEV_COMMIT),
310            Blockchain::Mode => Some(&chains::MODE),
311            Blockchain::MonadTestnet => Some(&chains::MONAD_TESTNET),
312            Blockchain::MonadTestnetBackup => Some(&chains::MONAD_TESTNET_BACKUP),
313            Blockchain::MoonbaseAlpha => Some(&chains::MOONBASE_ALPHA),
314            Blockchain::Moonbeam => Some(&chains::MOONBEAM),
315            Blockchain::Morph => Some(&chains::MORPH),
316            Blockchain::MorphHolesky => Some(&chains::MORPH_HOLESKY),
317            Blockchain::Opbnb => Some(&chains::OPBNB),
318            Blockchain::Optimism => Some(&chains::OPTIMISM),
319            Blockchain::OptimismSepolia => Some(&chains::OPTIMISM_SEPOLIA),
320            Blockchain::PharosDevnet => Some(&chains::PHAROS_DEVNET),
321            Blockchain::Polygon => Some(&chains::POLYGON),
322            Blockchain::PolygonAmoy => Some(&chains::POLYGON_AMOY),
323            Blockchain::PolygonZkEvm => Some(&chains::POLYGON_ZKEVM),
324            Blockchain::Rootstock => Some(&chains::ROOTSTOCK),
325            Blockchain::Saakuru => Some(&chains::SAAKURU),
326            Blockchain::Scroll => Some(&chains::SCROLL),
327            Blockchain::Sepolia => Some(&chains::SEPOLIA),
328            Blockchain::ShimmerEvm => Some(&chains::SHIMMER_EVM),
329            Blockchain::Soneium => Some(&chains::SONEIUM),
330            Blockchain::Sophon => Some(&chains::SOPHON),
331            Blockchain::SophonTestnet => Some(&chains::SOPHON_TESTNET),
332            Blockchain::Superseed => Some(&chains::SUPERSEED),
333            Blockchain::Unichain => Some(&chains::UNICHAIN),
334            Blockchain::UnichainSepolia => Some(&chains::UNICHAIN_SEPOLIA),
335            Blockchain::Xdc => Some(&chains::XDC),
336            Blockchain::XdcTestnet => Some(&chains::XDC_TESTNET),
337            Blockchain::Zeta => Some(&chains::ZETA),
338            Blockchain::Zircuit => Some(&chains::ZIRCUIT),
339            Blockchain::ZKsync => Some(&chains::ZKSYNC),
340            Blockchain::Zora => Some(&chains::ZORA),
341        }
342    }
343}
344
345impl Display for Chain {
346    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
347        write!(f, "Chain(name={}, id={})", self.name, self.chain_id)
348    }
349}
350
351// Define a module to contain all the chain definitions.
352pub mod chains {
353    use std::sync::LazyLock;
354
355    use crate::defi::chain::{Blockchain, Chain};
356
357    pub static ABSTRACT: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Abstract, 2741));
358    pub static ARBITRUM: LazyLock<Chain> =
359        LazyLock::new(|| Chain::new(Blockchain::Arbitrum, 42161));
360    pub static ARBITRUM_NOVA: LazyLock<Chain> =
361        LazyLock::new(|| Chain::new(Blockchain::ArbitrumNova, 42170));
362    pub static ARBITRUM_SEPOLIA: LazyLock<Chain> =
363        LazyLock::new(|| Chain::new(Blockchain::ArbitrumSepolia, 421614));
364    pub static AURORA: LazyLock<Chain> =
365        LazyLock::new(|| Chain::new(Blockchain::Aurora, 1313161554));
366    pub static AVALANCHE: LazyLock<Chain> =
367        LazyLock::new(|| Chain::new(Blockchain::Avalanche, 43114));
368    pub static BASE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Base, 8453));
369    pub static BASE_SEPOLIA: LazyLock<Chain> =
370        LazyLock::new(|| Chain::new(Blockchain::BaseSepolia, 84532));
371    pub static BERACHAIN: LazyLock<Chain> =
372        LazyLock::new(|| Chain::new(Blockchain::Berachain, 80094));
373    pub static BERACHAIN_BARTIO: LazyLock<Chain> =
374        LazyLock::new(|| Chain::new(Blockchain::BerachainBartio, 80085));
375    pub static BLAST: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Blast, 81457));
376    pub static BLAST_SEPOLIA: LazyLock<Chain> =
377        LazyLock::new(|| Chain::new(Blockchain::BlastSepolia, 168587773));
378    pub static BOBA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Boba, 288));
379    pub static BSC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Bsc, 56));
380    pub static BSC_TESTNET: LazyLock<Chain> =
381        LazyLock::new(|| Chain::new(Blockchain::BscTestnet, 97));
382    pub static CELO: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Celo, 42220));
383    pub static CHILIZ: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Chiliz, 8888));
384    pub static CITREA_TESTNET: LazyLock<Chain> =
385        LazyLock::new(|| Chain::new(Blockchain::CitreaTestnet, 3333));
386    pub static CURTIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Curtis, 33111));
387    pub static CYBER: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Cyber, 7560));
388    pub static DARWINIA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Darwinia, 46));
389    pub static ETHEREUM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Ethereum, 1));
390    pub static FANTOM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fantom, 250));
391    pub static FLARE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Flare, 14));
392    pub static FRAXTAL: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fraxtal, 252));
393    pub static FUJI: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fuji, 43113));
394    pub static GALADRIEL_DEVNET: LazyLock<Chain> =
395        LazyLock::new(|| Chain::new(Blockchain::GaladrielDevnet, 696969));
396    pub static GNOSIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Gnosis, 100));
397    pub static GNOSIS_CHIADO: LazyLock<Chain> =
398        LazyLock::new(|| Chain::new(Blockchain::GnosisChiado, 10200));
399    // Chain ID 10300 is reserved for the public *Gnosis Traces* test-network. The value was
400    // previously set to 100 (Mainnet) which caused `Chain::from_chain_id(10300)` to return a
401    // `Chain` whose `chain_id` field did not match the requested ID. This led to confusing log
402    // output and could break caching keyed by the numeric identifier. We therefore align the
403    // static definition with the mapping used in `from_chain_id` (10300).
404    pub static GNOSIS_TRACES: LazyLock<Chain> =
405        LazyLock::new(|| Chain::new(Blockchain::GnosisTraces, 10300));
406    pub static HARMONY_SHARD_0: LazyLock<Chain> =
407        LazyLock::new(|| Chain::new(Blockchain::HarmonyShard0, 1666600000));
408    pub static HOLESKY: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Holesky, 17000));
409    // The Holesky *token test* network uses a dedicated chain-ID (17001) distinct from the main
410    // Holesky devnet (17000). Align this constant with the value returned from `from_chain_id`.
411    pub static HOLESKY_TOKEN_TEST: LazyLock<Chain> =
412        LazyLock::new(|| Chain::new(Blockchain::HoleskyTokenTest, 17001));
413    // Hyperliquid main & temp test networks live on low numeric identifiers (7979 / 7978).
414    // Using the correct small IDs avoids overflow issues in certain front-ends that assume
415    // EVM-style 32-bit chain IDs.
416    pub static HYPERLIQUID: LazyLock<Chain> =
417        LazyLock::new(|| Chain::new(Blockchain::Hyperliquid, 7979));
418    pub static HYPERLIQUID_TEMP: LazyLock<Chain> =
419        LazyLock::new(|| Chain::new(Blockchain::HyperliquidTemp, 7978));
420    // Align with mapping – 222 is the well–known chain-ID for the `Ink` network.
421    pub static INK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Ink, 222));
422    // Use the `foundry`-style development chain-ID 13337 to match the lookup table above.
423    pub static INTERNAL_TEST_CHAIN: LazyLock<Chain> =
424        LazyLock::new(|| Chain::new(Blockchain::InternalTestChain, 13337));
425    pub static KROMA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Kroma, 255));
426    pub static LINEA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Linea, 59144));
427    pub static LISK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Lisk, 501));
428    pub static LUKSO: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Lukso, 42));
429    pub static LUKSO_TESTNET: LazyLock<Chain> =
430        LazyLock::new(|| Chain::new(Blockchain::LuksoTestnet, 4201));
431    pub static MANTA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Manta, 169));
432    pub static MANTLE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Mantle, 5000));
433    pub static MEGAETH_TESTNET: LazyLock<Chain> =
434        LazyLock::new(|| Chain::new(Blockchain::MegaethTestnet, 777));
435    pub static MERLIN: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Merlin, 4200));
436    pub static METALL2: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Metall2, 90));
437    pub static METIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Metis, 1088));
438    pub static MEV_COMMIT: LazyLock<Chain> =
439        LazyLock::new(|| Chain::new(Blockchain::MevCommit, 11));
440    pub static MODE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Mode, 34443));
441    pub static MONAD_TESTNET: LazyLock<Chain> =
442        LazyLock::new(|| Chain::new(Blockchain::MonadTestnet, 2323));
443    pub static MONAD_TESTNET_BACKUP: LazyLock<Chain> =
444        LazyLock::new(|| Chain::new(Blockchain::MonadTestnetBackup, 2358));
445    pub static MOONBASE_ALPHA: LazyLock<Chain> =
446        LazyLock::new(|| Chain::new(Blockchain::MoonbaseAlpha, 1287));
447    pub static MOONBEAM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Moonbeam, 1284));
448    pub static MORPH: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Morph, 2710));
449    pub static MORPH_HOLESKY: LazyLock<Chain> =
450        LazyLock::new(|| Chain::new(Blockchain::MorphHolesky, 2710111));
451    pub static OPBNB: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Opbnb, 204));
452    pub static OPTIMISM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Optimism, 10));
453    pub static OPTIMISM_SEPOLIA: LazyLock<Chain> =
454        LazyLock::new(|| Chain::new(Blockchain::OptimismSepolia, 11155420));
455    pub static PHAROS_DEVNET: LazyLock<Chain> =
456        LazyLock::new(|| Chain::new(Blockchain::PharosDevnet, 1337));
457    pub static POLYGON: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Polygon, 137));
458    pub static POLYGON_AMOY: LazyLock<Chain> =
459        LazyLock::new(|| Chain::new(Blockchain::PolygonAmoy, 80002));
460    pub static POLYGON_ZKEVM: LazyLock<Chain> =
461        LazyLock::new(|| Chain::new(Blockchain::PolygonZkEvm, 1101));
462    pub static ROOTSTOCK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Rootstock, 30));
463    pub static SAAKURU: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Saakuru, 1204));
464    pub static SCROLL: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Scroll, 534352));
465    pub static SEPOLIA: LazyLock<Chain> =
466        LazyLock::new(|| Chain::new(Blockchain::Sepolia, 11155111));
467    pub static SHIMMER_EVM: LazyLock<Chain> =
468        LazyLock::new(|| Chain::new(Blockchain::ShimmerEvm, 148));
469    pub static SONEIUM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Soneium, 109));
470    pub static SOPHON: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Sophon, 138));
471    pub static SOPHON_TESTNET: LazyLock<Chain> =
472        LazyLock::new(|| Chain::new(Blockchain::SophonTestnet, 139));
473    pub static SUPERSEED: LazyLock<Chain> =
474        LazyLock::new(|| Chain::new(Blockchain::Superseed, 10001));
475    pub static UNICHAIN: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Unichain, 9999));
476    pub static UNICHAIN_SEPOLIA: LazyLock<Chain> =
477        LazyLock::new(|| Chain::new(Blockchain::UnichainSepolia, 9997));
478    pub static XDC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Xdc, 50));
479    pub static XDC_TESTNET: LazyLock<Chain> =
480        LazyLock::new(|| Chain::new(Blockchain::XdcTestnet, 51));
481    pub static ZETA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zeta, 7000));
482    pub static ZIRCUIT: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zircuit, 78600));
483    pub static ZKSYNC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::ZKsync, 324));
484    pub static ZORA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zora, 7777777));
485}
486
487////////////////////////////////////////////////////////////////////////////////
488// Tests
489////////////////////////////////////////////////////////////////////////////////
490
491#[cfg(test)]
492mod tests {
493    use rstest::rstest;
494
495    use super::*;
496
497    #[rstest]
498    fn test_ethereum_chain() {
499        let eth_chain = chains::ETHEREUM.clone();
500        assert_eq!(eth_chain.to_string(), "Chain(name=Ethereum, id=1)");
501        assert_eq!(eth_chain.name, Blockchain::Ethereum);
502        assert_eq!(eth_chain.chain_id, 1);
503        assert_eq!(eth_chain.hypersync_url.as_str(), "https://1.hypersync.xyz")
504    }
505
506    #[rstest]
507    fn test_arbitrum_chain() {
508        let arbitrum_chain = chains::ARBITRUM.clone();
509        assert_eq!(arbitrum_chain.to_string(), "Chain(name=Arbitrum, id=42161)");
510        assert_eq!(arbitrum_chain.name, Blockchain::Arbitrum);
511        assert_eq!(arbitrum_chain.chain_id, 42161);
512        assert_eq!(
513            arbitrum_chain.hypersync_url.as_str(),
514            "https://42161.hypersync.xyz"
515        );
516    }
517
518    #[rstest]
519    fn test_chain_constructor() {
520        let chain = Chain::new(Blockchain::Polygon, 137);
521
522        assert_eq!(chain.name, Blockchain::Polygon);
523        assert_eq!(chain.chain_id, 137);
524        assert_eq!(chain.hypersync_url, "https://137.hypersync.xyz");
525        assert!(chain.rpc_url.is_none());
526        assert_eq!(chain.native_currency_decimals, 18);
527    }
528
529    #[rstest]
530    fn test_chain_set_rpc_url() {
531        let mut chain = Chain::new(Blockchain::Ethereum, 1);
532        assert!(chain.rpc_url.is_none());
533
534        let rpc_url = "https://mainnet.infura.io/v3/YOUR-PROJECT-ID".to_string();
535        chain.set_rpc_url(rpc_url.clone());
536
537        assert_eq!(chain.rpc_url, Some(rpc_url));
538    }
539
540    #[rstest]
541    fn test_chain_from_chain_id_valid() {
542        // Test some known chain IDs
543        assert!(Chain::from_chain_id(1).is_some()); // Ethereum
544        assert!(Chain::from_chain_id(137).is_some()); // Polygon
545        assert!(Chain::from_chain_id(42161).is_some()); // Arbitrum
546        assert!(Chain::from_chain_id(8453).is_some()); // Base
547
548        // Verify specific chain
549        let eth_chain = Chain::from_chain_id(1).unwrap();
550        assert_eq!(eth_chain.name, Blockchain::Ethereum);
551        assert_eq!(eth_chain.chain_id, 1);
552    }
553
554    #[rstest]
555    fn test_chain_from_chain_id_invalid() {
556        // Test unknown chain ID
557        assert!(Chain::from_chain_id(999999).is_none());
558        assert!(Chain::from_chain_id(0).is_none());
559    }
560
561    #[rstest]
562    fn test_chain_from_chain_name_valid() {
563        // Test some known chain names
564        assert!(Chain::from_chain_name("Ethereum").is_some());
565        assert!(Chain::from_chain_name("Polygon").is_some());
566        assert!(Chain::from_chain_name("Arbitrum").is_some());
567        assert!(Chain::from_chain_name("Base").is_some());
568
569        // Verify specific chain
570        let eth_chain = Chain::from_chain_name("Ethereum").unwrap();
571        assert_eq!(eth_chain.name, Blockchain::Ethereum);
572        assert_eq!(eth_chain.chain_id, 1);
573
574        // Verify ArbitrumNova (compound name)
575        let arbitrum_nova_chain = Chain::from_chain_name("ArbitrumNova").unwrap();
576        assert_eq!(arbitrum_nova_chain.name, Blockchain::ArbitrumNova);
577        assert_eq!(arbitrum_nova_chain.chain_id, 42170);
578
579        // Verify BSC (abbreviated name)
580        let bsc_chain = Chain::from_chain_name("Bsc").unwrap();
581        assert_eq!(bsc_chain.name, Blockchain::Bsc);
582        assert_eq!(bsc_chain.chain_id, 56);
583    }
584
585    #[rstest]
586    fn test_chain_from_chain_name_invalid() {
587        // Test unknown chain names
588        assert!(Chain::from_chain_name("InvalidChain").is_none());
589        assert!(Chain::from_chain_name("").is_none());
590        assert!(Chain::from_chain_name("NonExistentNetwork").is_none());
591    }
592
593    #[rstest]
594    fn test_chain_from_chain_name_case_sensitive() {
595        // Test case sensitivity - should be case insensitive
596        assert!(Chain::from_chain_name("Ethereum").is_some());
597        assert!(Chain::from_chain_name("ethereum").is_some()); // lowercase
598        assert!(Chain::from_chain_name("ETHEREUM").is_some()); // uppercase
599        assert!(Chain::from_chain_name("EtHeReUm").is_some()); // mixed case
600
601        assert!(Chain::from_chain_name("Arbitrum").is_some());
602        assert!(Chain::from_chain_name("arbitrum").is_some()); // lowercase
603    }
604
605    #[rstest]
606    fn test_chain_from_chain_name_consistency_with_id() {
607        // Test that from_chain_name and from_chain_id return the same chain instances
608        let chains_to_test = [
609            ("Ethereum", 1),
610            ("Polygon", 137),
611            ("Arbitrum", 42161),
612            ("Base", 8453),
613            ("Optimism", 10),
614            ("Avalanche", 43114),
615            ("Fantom", 250),
616            ("Bsc", 56),
617        ];
618
619        for (name, id) in chains_to_test {
620            let chain_by_name =
621                Chain::from_chain_name(name).unwrap_or_else(|| panic!("Chain {name} should exist"));
622            let chain_by_id =
623                Chain::from_chain_id(id).unwrap_or_else(|| panic!("Chain {name} should exist"));
624
625            // Should return the same chain instance
626            assert_eq!(chain_by_name.name, chain_by_id.name);
627            assert_eq!(chain_by_name.chain_id, chain_by_id.chain_id);
628            assert_eq!(chain_by_name.hypersync_url, chain_by_id.hypersync_url);
629        }
630    }
631}