Skip to main content

df_elysium_core/client/
network.rs

1//! Elysium's networks. Testnet only: Kinetiq has not published mainnet's chain id, RPC or
2//! contracts, so nothing here guesses them. Everything that differs between networks is a method
3//! on [`Network`], so mainnet is one more arm when it is published.
4
5use std::{fmt, str::FromStr};
6
7use crate::B256;
8use crate::bridge::Contracts;
9use crate::utils::Address;
10
11/// Elysium testnet's chain id.
12pub const TESTNET_CHAIN_ID: u64 = 99_801;
13
14#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
15pub enum Network {
16    #[default]
17    Testnet,
18}
19
20#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
21#[error("`{0}` is not an Elysium network; expected testnet")]
22pub struct UnknownNetwork(pub String);
23
24impl Network {
25    pub const ALL: [Network; 1] = [Network::Testnet];
26
27    /// The chain every transaction is signed under.
28    #[must_use]
29    pub const fn chain_id(self) -> u64 {
30        match self {
31            Network::Testnet => TESTNET_CHAIN_ID,
32        }
33    }
34
35    /// Kinetiq's public JSON-RPC: HTTPS only, no websocket, no mempool, and `eth_getLogs` over at
36    /// most [`Network::log_span`] blocks unless the filter names an address or a topic. Now and then
37    /// a backend behind it lags a block and refuses a read ([`crate::client::rpc::lagging`]).
38    #[must_use]
39    pub const fn rpc_url(self) -> &'static str {
40        match self {
41            Network::Testnet => "https://testnet-rpc.elysium.kinetiq.xyz",
42        }
43    }
44
45    /// Conduit's RPC for the same chain, the one its explorer hands wallets.
46    #[must_use]
47    pub const fn conduit_rpc_url(self) -> &'static str {
48        match self {
49            Network::Testnet => "https://rpc-elysium-testnet.t.conduit.xyz",
50        }
51    }
52
53    /// The most blocks one unfiltered `eth_getLogs` may span on [`Network::rpc_url`]:
54    /// `toBlock - fromBlock`, refused past it with `-32602` and HTTP 400.
55    #[must_use]
56    pub const fn log_span(self) -> u64 {
57        match self {
58            Network::Testnet => 2_000,
59        }
60    }
61
62    /// Conduit's explorer for the chain.
63    #[must_use]
64    pub const fn explorer_url(self) -> &'static str {
65        match self {
66            Network::Testnet => "https://test-explorer.elysium.kinetiq.xyz",
67        }
68    }
69
70    /// A transaction on the explorer, for a link a person follows.
71    #[must_use]
72    pub fn tx_url(self, hash: B256) -> String {
73        format!("{}/tx/{hash}", self.explorer_url())
74    }
75
76    /// An account or a contract on the explorer.
77    #[must_use]
78    pub fn address_url(self, address: Address) -> String {
79        format!("{}/address/{address}", self.explorer_url())
80    }
81
82    /// A block on the explorer.
83    #[must_use]
84    pub fn block_url(self, number: u64) -> String {
85        format!("{}/block/{number}", self.explorer_url())
86    }
87
88    /// Conduit's Sourcify, where Elysium's contracts are verified ([`crate::deploy::verify`]).
89    #[must_use]
90    pub const fn verifier_url(self) -> &'static str {
91        match self {
92            Network::Testnet => "https://contracts.conduit.xyz",
93        }
94    }
95
96    /// Kinetiq's faucet page, for testnet HYPE.
97    #[must_use]
98    pub const fn faucet_url(self) -> &'static str {
99        match self {
100            Network::Testnet => "https://elysium.kinetiq.xyz/testnet-faucet",
101        }
102    }
103
104    /// Kinetiq's bridge page, between HyperEVM and Elysium.
105    #[must_use]
106    pub const fn bridge_url(self) -> &'static str {
107        match self {
108            Network::Testnet => "https://elysium.kinetiq.xyz/testnet-bridge",
109        }
110    }
111
112    /// The Hyperliquid network whose HyperEVM Elysium settles to: its batches, assertions and
113    /// bridge live there ([`df_hyperliquid_core::Network::evm_rpc_url`]).
114    #[must_use]
115    pub const fn hyperliquid(self) -> df_hyperliquid_core::Network {
116        match self {
117            Network::Testnet => df_hyperliquid_core::Network::Testnet,
118        }
119    }
120
121    /// The parent chain's id: HyperEVM's.
122    #[must_use]
123    pub const fn parent_chain_id(self) -> u64 {
124        self.hyperliquid().evm_chain_id()
125    }
126
127    /// The rollup's contracts on the parent chain.
128    #[must_use]
129    pub const fn contracts(self) -> Contracts {
130        match self {
131            Network::Testnet => crate::bridge::contracts::TESTNET,
132        }
133    }
134
135    #[must_use]
136    pub const fn name(self) -> &'static str {
137        match self {
138            Network::Testnet => "testnet",
139        }
140    }
141
142    #[must_use]
143    pub const fn is_mainnet(self) -> bool {
144        match self {
145            Network::Testnet => false,
146        }
147    }
148
149    /// The network whose chain id this is.
150    #[must_use]
151    pub const fn of_chain(chain_id: u64) -> Option<Network> {
152        match chain_id {
153            TESTNET_CHAIN_ID => Some(Network::Testnet),
154            _ => None,
155        }
156    }
157}
158
159impl fmt::Display for Network {
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        f.write_str(self.name())
162    }
163}
164
165impl FromStr for Network {
166    type Err = UnknownNetwork;
167
168    fn from_str(text: &str) -> Result<Self, Self::Err> {
169        match text.trim().to_ascii_lowercase().as_str() {
170            "testnet" | "test" => Ok(Network::Testnet),
171            _ => Err(UnknownNetwork(text.trim().to_owned())),
172        }
173    }
174}
175
176#[cfg(test)]
177#[path = "tests/network.rs"]
178mod tests;