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    sync::Arc,
21};
22
23use serde::{Deserialize, Serialize};
24use strum::{Display, EnumString};
25
26/// Represents different blockchain networks.
27#[derive(
28    Debug,
29    Clone,
30    Copy,
31    Hash,
32    PartialOrd,
33    PartialEq,
34    Ord,
35    Eq,
36    Display,
37    EnumString,
38    Serialize,
39    Deserialize,
40)]
41#[non_exhaustive]
42pub enum Blockchain {
43    Abstract,
44    Arbitrum,
45    ArbitrumNova,
46    ArbitrumSepolia,
47    Aurora,
48    Avalanche,
49    Base,
50    BaseSepolia,
51    Berachain,
52    BerachainBartio,
53    Blast,
54    BlastSepolia,
55    Boba,
56    Bsc,
57    BscTestnet,
58    Celo,
59    Chiliz,
60    CitreaTestnet,
61    Curtis,
62    Cyber,
63    Darwinia,
64    Ethereum,
65    Fantom,
66    Flare,
67    Fraxtal,
68    Fuji,
69    GaladrielDevnet,
70    Gnosis,
71    GnosisChiado,
72    GnosisTraces,
73    HarmonyShard0,
74    Holesky,
75    HoleskyTokenTest,
76    Hyperliquid,
77    HyperliquidTemp,
78    Ink,
79    InternalTestChain,
80    Kroma,
81    Linea,
82    Lisk,
83    Lukso,
84    LuksoTestnet,
85    Manta,
86    Mantle,
87    MegaethTestnet,
88    Merlin,
89    Metall2,
90    Metis,
91    MevCommit,
92    Mode,
93    MonadTestnet,
94    MonadTestnetBackup,
95    MoonbaseAlpha,
96    Moonbeam,
97    Morph,
98    MorphHolesky,
99    Opbnb,
100    Optimism,
101    OptimismSepolia,
102    PharosDevnet,
103    Polygon,
104    PolygonAmoy,
105    PolygonZkEvm,
106    Rootstock,
107    Saakuru,
108    Scroll,
109    Sepolia,
110    ShimmerEvm,
111    Soneium,
112    Sophon,
113    SophonTestnet,
114    Superseed,
115    Unichain,
116    UnichainSepolia,
117    Xdc,
118    XdcTestnet,
119    Zeta,
120    Zircuit,
121    ZKsync,
122    Zora,
123}
124
125/// Defines a blockchain with its unique identifiers and connection details for network interaction.
126#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127pub struct Chain {
128    /// The blockchain network type.
129    pub name: Blockchain,
130    /// The unique identifier for this blockchain.
131    pub chain_id: u32,
132    /// URL endpoint for HyperSync connection.
133    pub hypersync_url: String,
134    /// URL endpoint for the default RPC connection.
135    pub rpc_url: Option<String>,
136    /// The number of decimals for the native currency.
137    pub native_currency_decimals: u8,
138}
139
140/// A thread-safe shared pointer to a `Chain`, enabling efficient reuse across multiple components.
141pub type SharedChain = Arc<Chain>;
142
143impl Chain {
144    /// Creates a new [`Chain`] instance with the specified blockchain and chain ID.
145    pub fn new(name: Blockchain, chain_id: u32) -> Self {
146        Self {
147            chain_id,
148            name,
149            hypersync_url: format!("https://{chain_id}.hypersync.xyz"),
150            rpc_url: None,
151            native_currency_decimals: 18, // Default to 18 for EVM chains
152        }
153    }
154
155    /// Sets the RPC URL endpoint.
156    pub fn set_rpc_url(&mut self, rpc: String) {
157        self.rpc_url = Some(rpc);
158    }
159
160    /// Returns a reference to the `Chain` corresponding to the given `chain_id`, or `None` if it is not found.
161    pub fn from_chain_id(chain_id: u32) -> Option<&'static Chain> {
162        match chain_id {
163            2741 => Some(&chains::ABSTRACT),
164            42161 => Some(&chains::ARBITRUM),
165            42170 => Some(&chains::ARBITRUM_NOVA),
166            421614 => Some(&chains::ARBITRUM_SEPOLIA),
167            1313161554 => Some(&chains::AURORA),
168            43114 => Some(&chains::AVALANCHE),
169            8453 => Some(&chains::BASE),
170            84532 => Some(&chains::BASE_SEPOLIA),
171            80094 => Some(&chains::BERACHAIN),
172            80085 => Some(&chains::BERACHAIN_BARTIO),
173            81457 => Some(&chains::BLAST),
174            168587773 => Some(&chains::BLAST_SEPOLIA),
175            288 => Some(&chains::BOBA),
176            56 => Some(&chains::BSC),
177            97 => Some(&chains::BSC_TESTNET),
178            42220 => Some(&chains::CELO),
179            8888 => Some(&chains::CHILIZ),
180            3333 => Some(&chains::CITREA_TESTNET),
181            33111 => Some(&chains::CURTIS),
182            7560 => Some(&chains::CYBER),
183            46 => Some(&chains::DARWINIA),
184            1 => Some(&chains::ETHEREUM),
185            250 => Some(&chains::FANTOM),
186            14 => Some(&chains::FLARE),
187            252 => Some(&chains::FRAXTAL),
188            43113 => Some(&chains::FUJI),
189            696969 => Some(&chains::GALADRIEL_DEVNET),
190            100 => Some(&chains::GNOSIS),
191            10200 => Some(&chains::GNOSIS_CHIADO),
192            10300 => Some(&chains::GNOSIS_TRACES),
193            1666600000 => Some(&chains::HARMONY_SHARD_0),
194            17000 => Some(&chains::HOLESKY),
195            17001 => Some(&chains::HOLESKY_TOKEN_TEST),
196            7979 => Some(&chains::HYPERLIQUID),
197            7978 => Some(&chains::HYPERLIQUID_TEMP),
198            222 => Some(&chains::INK),
199            13337 => Some(&chains::INTERNAL_TEST_CHAIN),
200            255 => Some(&chains::KROMA),
201            59144 => Some(&chains::LINEA),
202            501 => Some(&chains::LISK),
203            42 => Some(&chains::LUKSO),
204            4201 => Some(&chains::LUKSO_TESTNET),
205            169 => Some(&chains::MANTA),
206            5000 => Some(&chains::MANTLE),
207            777 => Some(&chains::MEGAETH_TESTNET),
208            4200 => Some(&chains::MERLIN),
209            90 => Some(&chains::METALL2),
210            1088 => Some(&chains::METIS),
211            11 => Some(&chains::MEV_COMMIT),
212            34443 => Some(&chains::MODE),
213            2323 => Some(&chains::MONAD_TESTNET),
214            2358 => Some(&chains::MONAD_TESTNET_BACKUP),
215            1287 => Some(&chains::MOONBASE_ALPHA),
216            1284 => Some(&chains::MOONBEAM),
217            2710 => Some(&chains::MORPH),
218            2710111 => Some(&chains::MORPH_HOLESKY),
219            204 => Some(&chains::OPBNB),
220            10 => Some(&chains::OPTIMISM),
221            11155420 => Some(&chains::OPTIMISM_SEPOLIA),
222            1337 => Some(&chains::PHAROS_DEVNET),
223            137 => Some(&chains::POLYGON),
224            80002 => Some(&chains::POLYGON_AMOY),
225            1101 => Some(&chains::POLYGON_ZKEVM),
226            30 => Some(&chains::ROOTSTOCK),
227            1204 => Some(&chains::SAAKURU),
228            534352 => Some(&chains::SCROLL),
229            11155111 => Some(&chains::SEPOLIA),
230            148 => Some(&chains::SHIMMER_EVM),
231            109 => Some(&chains::SONEIUM),
232            138 => Some(&chains::SOPHON),
233            139 => Some(&chains::SOPHON_TESTNET),
234            10001 => Some(&chains::SUPERSEED),
235            9999 => Some(&chains::UNICHAIN),
236            9997 => Some(&chains::UNICHAIN_SEPOLIA),
237            50 => Some(&chains::XDC),
238            51 => Some(&chains::XDC_TESTNET),
239            7000 => Some(&chains::ZETA),
240            78600 => Some(&chains::ZIRCUIT),
241            324 => Some(&chains::ZKSYNC),
242            7777777 => Some(&chains::ZORA),
243            _ => None,
244        }
245    }
246}
247
248impl Display for Chain {
249    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
250        write!(f, "Chain(name={}, id={})", self.name, self.chain_id)
251    }
252}
253
254// Define a module to contain all the chain definitions.
255pub mod chains {
256    use std::sync::LazyLock;
257
258    use crate::defi::chain::{Blockchain, Chain};
259
260    pub static ABSTRACT: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Abstract, 2741));
261    pub static ARBITRUM: LazyLock<Chain> =
262        LazyLock::new(|| Chain::new(Blockchain::Arbitrum, 42161));
263    pub static ARBITRUM_NOVA: LazyLock<Chain> =
264        LazyLock::new(|| Chain::new(Blockchain::ArbitrumNova, 42170));
265    pub static ARBITRUM_SEPOLIA: LazyLock<Chain> =
266        LazyLock::new(|| Chain::new(Blockchain::ArbitrumSepolia, 421614));
267    pub static AURORA: LazyLock<Chain> =
268        LazyLock::new(|| Chain::new(Blockchain::Aurora, 1313161554));
269    pub static AVALANCHE: LazyLock<Chain> =
270        LazyLock::new(|| Chain::new(Blockchain::Avalanche, 43114));
271    pub static BASE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Base, 8453));
272    pub static BASE_SEPOLIA: LazyLock<Chain> =
273        LazyLock::new(|| Chain::new(Blockchain::BaseSepolia, 84532));
274    pub static BERACHAIN: LazyLock<Chain> =
275        LazyLock::new(|| Chain::new(Blockchain::Berachain, 80094));
276    pub static BERACHAIN_BARTIO: LazyLock<Chain> =
277        LazyLock::new(|| Chain::new(Blockchain::BerachainBartio, 80085));
278    pub static BLAST: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Blast, 81457));
279    pub static BLAST_SEPOLIA: LazyLock<Chain> =
280        LazyLock::new(|| Chain::new(Blockchain::BlastSepolia, 168587773));
281    pub static BOBA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Boba, 288));
282    pub static BSC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Bsc, 56));
283    pub static BSC_TESTNET: LazyLock<Chain> =
284        LazyLock::new(|| Chain::new(Blockchain::BscTestnet, 97));
285    pub static CELO: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Celo, 42220));
286    pub static CHILIZ: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Chiliz, 8888));
287    pub static CITREA_TESTNET: LazyLock<Chain> =
288        LazyLock::new(|| Chain::new(Blockchain::CitreaTestnet, 3333));
289    pub static CURTIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Curtis, 33111));
290    pub static CYBER: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Cyber, 7560));
291    pub static DARWINIA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Darwinia, 46));
292    pub static ETHEREUM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Ethereum, 1));
293    pub static FANTOM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fantom, 250));
294    pub static FLARE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Flare, 14));
295    pub static FRAXTAL: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fraxtal, 252));
296    pub static FUJI: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Fuji, 43113));
297    pub static GALADRIEL_DEVNET: LazyLock<Chain> =
298        LazyLock::new(|| Chain::new(Blockchain::GaladrielDevnet, 696969));
299    pub static GNOSIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Gnosis, 100));
300    pub static GNOSIS_CHIADO: LazyLock<Chain> =
301        LazyLock::new(|| Chain::new(Blockchain::GnosisChiado, 10200));
302    // Chain ID 10300 is reserved for the public *Gnosis Traces* test-network. The value was
303    // previously set to 100 (Mainnet) which caused `Chain::from_chain_id(10300)` to return a
304    // `Chain` whose `chain_id` field did not match the requested ID. This led to confusing log
305    // output and could break caching keyed by the numeric identifier. We therefore align the
306    // static definition with the mapping used in `from_chain_id` (10300).
307    pub static GNOSIS_TRACES: LazyLock<Chain> =
308        LazyLock::new(|| Chain::new(Blockchain::GnosisTraces, 10300));
309    pub static HARMONY_SHARD_0: LazyLock<Chain> =
310        LazyLock::new(|| Chain::new(Blockchain::HarmonyShard0, 1666600000));
311    pub static HOLESKY: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Holesky, 17000));
312    // The Holesky *token test* network uses a dedicated chain-ID (17001) distinct from the main
313    // Holesky devnet (17000). Align this constant with the value returned from `from_chain_id`.
314    pub static HOLESKY_TOKEN_TEST: LazyLock<Chain> =
315        LazyLock::new(|| Chain::new(Blockchain::HoleskyTokenTest, 17001));
316    // Hyperliquid main & temp test networks live on low numeric identifiers (7979 / 7978).
317    // Using the correct small IDs avoids overflow issues in certain front-ends that assume
318    // EVM-style 32-bit chain IDs.
319    pub static HYPERLIQUID: LazyLock<Chain> =
320        LazyLock::new(|| Chain::new(Blockchain::Hyperliquid, 7979));
321    pub static HYPERLIQUID_TEMP: LazyLock<Chain> =
322        LazyLock::new(|| Chain::new(Blockchain::HyperliquidTemp, 7978));
323    // Align with mapping – 222 is the well–known chain-ID for the `Ink` network.
324    pub static INK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Ink, 222));
325    // Use the `foundry`-style development chain-ID 13337 to match the lookup table above.
326    pub static INTERNAL_TEST_CHAIN: LazyLock<Chain> =
327        LazyLock::new(|| Chain::new(Blockchain::InternalTestChain, 13337));
328    pub static KROMA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Kroma, 255));
329    pub static LINEA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Linea, 59144));
330    pub static LISK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Lisk, 501));
331    pub static LUKSO: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Lukso, 42));
332    pub static LUKSO_TESTNET: LazyLock<Chain> =
333        LazyLock::new(|| Chain::new(Blockchain::LuksoTestnet, 4201));
334    pub static MANTA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Manta, 169));
335    pub static MANTLE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Mantle, 5000));
336    pub static MEGAETH_TESTNET: LazyLock<Chain> =
337        LazyLock::new(|| Chain::new(Blockchain::MegaethTestnet, 777));
338    pub static MERLIN: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Merlin, 4200));
339    pub static METALL2: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Metall2, 90));
340    pub static METIS: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Metis, 1088));
341    pub static MEV_COMMIT: LazyLock<Chain> =
342        LazyLock::new(|| Chain::new(Blockchain::MevCommit, 11));
343    pub static MODE: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Mode, 34443));
344    pub static MONAD_TESTNET: LazyLock<Chain> =
345        LazyLock::new(|| Chain::new(Blockchain::MonadTestnet, 2323));
346    pub static MONAD_TESTNET_BACKUP: LazyLock<Chain> =
347        LazyLock::new(|| Chain::new(Blockchain::MonadTestnetBackup, 2358));
348    pub static MOONBASE_ALPHA: LazyLock<Chain> =
349        LazyLock::new(|| Chain::new(Blockchain::MoonbaseAlpha, 1287));
350    pub static MOONBEAM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Moonbeam, 1284));
351    pub static MORPH: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Morph, 2710));
352    pub static MORPH_HOLESKY: LazyLock<Chain> =
353        LazyLock::new(|| Chain::new(Blockchain::MorphHolesky, 2710111));
354    pub static OPBNB: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Opbnb, 204));
355    pub static OPTIMISM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Optimism, 10));
356    pub static OPTIMISM_SEPOLIA: LazyLock<Chain> =
357        LazyLock::new(|| Chain::new(Blockchain::OptimismSepolia, 11155420));
358    pub static PHAROS_DEVNET: LazyLock<Chain> =
359        LazyLock::new(|| Chain::new(Blockchain::PharosDevnet, 1337));
360    pub static POLYGON: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Polygon, 137));
361    pub static POLYGON_AMOY: LazyLock<Chain> =
362        LazyLock::new(|| Chain::new(Blockchain::PolygonAmoy, 80002));
363    pub static POLYGON_ZKEVM: LazyLock<Chain> =
364        LazyLock::new(|| Chain::new(Blockchain::PolygonZkEvm, 1101));
365    pub static ROOTSTOCK: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Rootstock, 30));
366    pub static SAAKURU: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Saakuru, 1204));
367    pub static SCROLL: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Scroll, 534352));
368    pub static SEPOLIA: LazyLock<Chain> =
369        LazyLock::new(|| Chain::new(Blockchain::Sepolia, 11155111));
370    pub static SHIMMER_EVM: LazyLock<Chain> =
371        LazyLock::new(|| Chain::new(Blockchain::ShimmerEvm, 148));
372    pub static SONEIUM: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Soneium, 109));
373    pub static SOPHON: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Sophon, 138));
374    pub static SOPHON_TESTNET: LazyLock<Chain> =
375        LazyLock::new(|| Chain::new(Blockchain::SophonTestnet, 139));
376    pub static SUPERSEED: LazyLock<Chain> =
377        LazyLock::new(|| Chain::new(Blockchain::Superseed, 10001));
378    pub static UNICHAIN: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Unichain, 9999));
379    pub static UNICHAIN_SEPOLIA: LazyLock<Chain> =
380        LazyLock::new(|| Chain::new(Blockchain::UnichainSepolia, 9997));
381    pub static XDC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Xdc, 50));
382    pub static XDC_TESTNET: LazyLock<Chain> =
383        LazyLock::new(|| Chain::new(Blockchain::XdcTestnet, 51));
384    pub static ZETA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zeta, 7000));
385    pub static ZIRCUIT: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zircuit, 78600));
386    pub static ZKSYNC: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::ZKsync, 324));
387    pub static ZORA: LazyLock<Chain> = LazyLock::new(|| Chain::new(Blockchain::Zora, 7777777));
388}
389
390#[cfg(test)]
391mod tests {
392    use rstest::rstest;
393
394    use super::*;
395
396    #[rstest]
397    fn test_ethereum_chain() {
398        let eth_chain = chains::ETHEREUM.clone();
399        assert_eq!(eth_chain.to_string(), "Chain(name=Ethereum, id=1)");
400        assert_eq!(eth_chain.name, Blockchain::Ethereum);
401        assert_eq!(eth_chain.chain_id, 1);
402        assert_eq!(eth_chain.hypersync_url.as_str(), "https://1.hypersync.xyz")
403    }
404
405    #[rstest]
406    fn test_arbitrum_chain() {
407        let arbitrum_chain = chains::ARBITRUM.clone();
408        assert_eq!(arbitrum_chain.to_string(), "Chain(name=Arbitrum, id=42161)");
409        assert_eq!(arbitrum_chain.name, Blockchain::Arbitrum);
410        assert_eq!(arbitrum_chain.chain_id, 42161);
411        assert_eq!(
412            arbitrum_chain.hypersync_url.as_str(),
413            "https://42161.hypersync.xyz"
414        );
415    }
416
417    #[rstest]
418    fn test_chain_constructor() {
419        let chain = Chain::new(Blockchain::Polygon, 137);
420
421        assert_eq!(chain.name, Blockchain::Polygon);
422        assert_eq!(chain.chain_id, 137);
423        assert_eq!(chain.hypersync_url, "https://137.hypersync.xyz");
424        assert!(chain.rpc_url.is_none());
425        assert_eq!(chain.native_currency_decimals, 18);
426    }
427
428    #[rstest]
429    fn test_chain_set_rpc_url() {
430        let mut chain = Chain::new(Blockchain::Ethereum, 1);
431        assert!(chain.rpc_url.is_none());
432
433        let rpc_url = "https://mainnet.infura.io/v3/YOUR-PROJECT-ID".to_string();
434        chain.set_rpc_url(rpc_url.clone());
435
436        assert_eq!(chain.rpc_url, Some(rpc_url));
437    }
438
439    #[rstest]
440    fn test_chain_from_chain_id_valid() {
441        // Test some known chain IDs
442        assert!(Chain::from_chain_id(1).is_some()); // Ethereum
443        assert!(Chain::from_chain_id(137).is_some()); // Polygon
444        assert!(Chain::from_chain_id(42161).is_some()); // Arbitrum
445        assert!(Chain::from_chain_id(8453).is_some()); // Base
446
447        // Verify specific chain
448        let eth_chain = Chain::from_chain_id(1).unwrap();
449        assert_eq!(eth_chain.name, Blockchain::Ethereum);
450        assert_eq!(eth_chain.chain_id, 1);
451    }
452
453    #[rstest]
454    fn test_chain_from_chain_id_invalid() {
455        // Test unknown chain ID
456        assert!(Chain::from_chain_id(999999).is_none());
457        assert!(Chain::from_chain_id(0).is_none());
458    }
459}