1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub enum Chain {
9 Ethereum,
11 Goerli,
13 Sepolia,
15 Holesky,
17 Optimism,
19 OptimismSepolia,
21 Bsc,
23 BscTestnet,
25 Gnosis,
27 Polygon,
29 Mumbai,
31 Amoy,
33 Fantom,
35 FantomTestnet,
37 Moonbeam,
39 Moonriver,
41 Arbitrum,
43 ArbitrumNova,
45 ArbitrumSepolia,
47 Avalanche,
49 AvalancheFuji,
51 Celo,
53 Base,
55 BaseSepolia,
57 Linea,
59 LineaTestnet,
61 ZkSync,
63 ZkSyncSepolia,
65 Scroll,
67 ScrollSepolia,
69 Blast,
71 BlastSepolia,
73 Mantle,
75 Mode,
77 Fraxtal,
79 Klaytn,
81 Aurora,
83 PolygonZkEvm,
85 Other(u64),
87}
88
89impl Chain {
90 #[must_use]
92 pub const fn id(&self) -> u64 {
93 match self {
94 Self::Ethereum => 1,
95 Self::Goerli => 5,
96 Self::Sepolia => 11_155_111,
97 Self::Holesky => 17000,
98 Self::Optimism => 10,
99 Self::OptimismSepolia => 11_155_420,
100 Self::Bsc => 56,
101 Self::BscTestnet => 97,
102 Self::Gnosis => 100,
103 Self::Polygon => 137,
104 Self::Mumbai => 80001,
105 Self::Amoy => 80002,
106 Self::Fantom => 250,
107 Self::FantomTestnet => 4002,
108 Self::Moonbeam => 1284,
109 Self::Moonriver => 1285,
110 Self::Arbitrum => 42161,
111 Self::ArbitrumNova => 42170,
112 Self::ArbitrumSepolia => 421_614,
113 Self::Avalanche => 43114,
114 Self::AvalancheFuji => 43113,
115 Self::Celo => 42220,
116 Self::Base => 8453,
117 Self::BaseSepolia => 84532,
118 Self::Linea => 59144,
119 Self::LineaTestnet => 59140,
120 Self::ZkSync => 324,
121 Self::ZkSyncSepolia => 300,
122 Self::Scroll => 534_352,
123 Self::ScrollSepolia => 534_351,
124 Self::Blast => 81457,
125 Self::BlastSepolia => 168_587_773,
126 Self::Mantle => 5000,
127 Self::Mode => 34443,
128 Self::Fraxtal => 252,
129 Self::Klaytn => 8217,
130 Self::Aurora => 1_313_161_554,
131 Self::PolygonZkEvm => 1101,
132 Self::Other(id) => *id,
133 }
134 }
135
136 #[must_use]
138 pub const fn name(&self) -> &'static str {
139 match self {
140 Self::Ethereum => "ethereum",
141 Self::Goerli => "goerli",
142 Self::Sepolia => "sepolia",
143 Self::Holesky => "holesky",
144 Self::Optimism => "optimism",
145 Self::OptimismSepolia => "optimism-sepolia",
146 Self::Bsc => "bsc",
147 Self::BscTestnet => "bsc-testnet",
148 Self::Gnosis => "gnosis",
149 Self::Polygon => "polygon",
150 Self::Mumbai => "mumbai",
151 Self::Amoy => "amoy",
152 Self::Fantom => "fantom",
153 Self::FantomTestnet => "fantom-testnet",
154 Self::Moonbeam => "moonbeam",
155 Self::Moonriver => "moonriver",
156 Self::Arbitrum => "arbitrum",
157 Self::ArbitrumNova => "arbitrum-nova",
158 Self::ArbitrumSepolia => "arbitrum-sepolia",
159 Self::Avalanche => "avalanche",
160 Self::AvalancheFuji => "avalanche-fuji",
161 Self::Celo => "celo",
162 Self::Base => "base",
163 Self::BaseSepolia => "base-sepolia",
164 Self::Linea => "linea",
165 Self::LineaTestnet => "linea-testnet",
166 Self::ZkSync => "zksync",
167 Self::ZkSyncSepolia => "zksync-sepolia",
168 Self::Scroll => "scroll",
169 Self::ScrollSepolia => "scroll-sepolia",
170 Self::Blast => "blast",
171 Self::BlastSepolia => "blast-sepolia",
172 Self::Mantle => "mantle",
173 Self::Mode => "mode",
174 Self::Fraxtal => "fraxtal",
175 Self::Klaytn => "klaytn",
176 Self::Aurora => "aurora",
177 Self::PolygonZkEvm => "polygon-zkevm",
178 Self::Other(_) => "unknown",
179 }
180 }
181
182 #[must_use]
184 pub const fn display_name(&self) -> &'static str {
185 match self {
186 Self::Ethereum => "Ethereum",
187 Self::Goerli => "Goerli",
188 Self::Sepolia => "Sepolia",
189 Self::Holesky => "Holesky",
190 Self::Optimism => "Optimism",
191 Self::OptimismSepolia => "Optimism Sepolia",
192 Self::Bsc => "BNB Smart Chain",
193 Self::BscTestnet => "BNB Smart Chain Testnet",
194 Self::Gnosis => "Gnosis",
195 Self::Polygon => "Polygon",
196 Self::Mumbai => "Mumbai",
197 Self::Amoy => "Amoy",
198 Self::Fantom => "Fantom",
199 Self::FantomTestnet => "Fantom Testnet",
200 Self::Moonbeam => "Moonbeam",
201 Self::Moonriver => "Moonriver",
202 Self::Arbitrum => "Arbitrum One",
203 Self::ArbitrumNova => "Arbitrum Nova",
204 Self::ArbitrumSepolia => "Arbitrum Sepolia",
205 Self::Avalanche => "Avalanche",
206 Self::AvalancheFuji => "Avalanche Fuji",
207 Self::Celo => "Celo",
208 Self::Base => "Base",
209 Self::BaseSepolia => "Base Sepolia",
210 Self::Linea => "Linea",
211 Self::LineaTestnet => "Linea Testnet",
212 Self::ZkSync => "zkSync Era",
213 Self::ZkSyncSepolia => "zkSync Sepolia",
214 Self::Scroll => "Scroll",
215 Self::ScrollSepolia => "Scroll Sepolia",
216 Self::Blast => "Blast",
217 Self::BlastSepolia => "Blast Sepolia",
218 Self::Mantle => "Mantle",
219 Self::Mode => "Mode",
220 Self::Fraxtal => "Fraxtal",
221 Self::Klaytn => "Klaytn",
222 Self::Aurora => "Aurora",
223 Self::PolygonZkEvm => "Polygon zkEVM",
224 Self::Other(_) => "Unknown",
225 }
226 }
227
228 #[must_use]
230 pub const fn native_currency(&self) -> &'static str {
231 match self {
232 Self::Ethereum | Self::Goerli | Self::Sepolia | Self::Holesky => "ETH",
233 Self::Optimism | Self::OptimismSepolia => "ETH",
234 Self::Bsc | Self::BscTestnet => "BNB",
235 Self::Gnosis => "xDAI",
236 Self::Polygon | Self::Mumbai | Self::Amoy => "MATIC",
237 Self::Fantom | Self::FantomTestnet => "FTM",
238 Self::Moonbeam => "GLMR",
239 Self::Moonriver => "MOVR",
240 Self::Arbitrum | Self::ArbitrumNova | Self::ArbitrumSepolia => "ETH",
241 Self::Avalanche | Self::AvalancheFuji => "AVAX",
242 Self::Celo => "CELO",
243 Self::Base | Self::BaseSepolia => "ETH",
244 Self::Linea | Self::LineaTestnet => "ETH",
245 Self::ZkSync | Self::ZkSyncSepolia => "ETH",
246 Self::Scroll | Self::ScrollSepolia => "ETH",
247 Self::Blast | Self::BlastSepolia => "ETH",
248 Self::Mantle => "MNT",
249 Self::Mode => "ETH",
250 Self::Fraxtal => "frxETH",
251 Self::Klaytn => "KLAY",
252 Self::Aurora => "ETH",
253 Self::PolygonZkEvm => "ETH",
254 Self::Other(_) => "ETH",
255 }
256 }
257
258 #[must_use]
260 pub const fn is_testnet(&self) -> bool {
261 matches!(
262 self,
263 Self::Goerli
264 | Self::Sepolia
265 | Self::Holesky
266 | Self::OptimismSepolia
267 | Self::BscTestnet
268 | Self::Mumbai
269 | Self::Amoy
270 | Self::FantomTestnet
271 | Self::ArbitrumSepolia
272 | Self::AvalancheFuji
273 | Self::BaseSepolia
274 | Self::LineaTestnet
275 | Self::ZkSyncSepolia
276 | Self::ScrollSepolia
277 | Self::BlastSepolia
278 )
279 }
280
281 #[must_use]
283 pub const fn is_mainnet(&self) -> bool {
284 !self.is_testnet()
285 }
286
287 #[must_use]
289 pub const fn from_id(id: u64) -> Self {
290 match id {
291 1 => Self::Ethereum,
292 5 => Self::Goerli,
293 11_155_111 => Self::Sepolia,
294 17000 => Self::Holesky,
295 10 => Self::Optimism,
296 11_155_420 => Self::OptimismSepolia,
297 56 => Self::Bsc,
298 97 => Self::BscTestnet,
299 100 => Self::Gnosis,
300 137 => Self::Polygon,
301 80001 => Self::Mumbai,
302 80002 => Self::Amoy,
303 250 => Self::Fantom,
304 4002 => Self::FantomTestnet,
305 1284 => Self::Moonbeam,
306 1285 => Self::Moonriver,
307 42161 => Self::Arbitrum,
308 42170 => Self::ArbitrumNova,
309 421_614 => Self::ArbitrumSepolia,
310 43114 => Self::Avalanche,
311 43113 => Self::AvalancheFuji,
312 42220 => Self::Celo,
313 8453 => Self::Base,
314 84532 => Self::BaseSepolia,
315 59144 => Self::Linea,
316 59140 => Self::LineaTestnet,
317 324 => Self::ZkSync,
318 300 => Self::ZkSyncSepolia,
319 534_352 => Self::Scroll,
320 534_351 => Self::ScrollSepolia,
321 81457 => Self::Blast,
322 168_587_773 => Self::BlastSepolia,
323 5000 => Self::Mantle,
324 34443 => Self::Mode,
325 252 => Self::Fraxtal,
326 8217 => Self::Klaytn,
327 1_313_161_554 => Self::Aurora,
328 1101 => Self::PolygonZkEvm,
329 _ => Self::Other(id),
330 }
331 }
332
333 #[must_use]
335 pub fn from_name(name: &str) -> Option<Self> {
336 let name_lower = name.to_lowercase();
337 match name_lower.as_str() {
338 "ethereum" | "eth" | "mainnet" => Some(Self::Ethereum),
339 "goerli" => Some(Self::Goerli),
340 "sepolia" => Some(Self::Sepolia),
341 "holesky" => Some(Self::Holesky),
342 "optimism" | "op" => Some(Self::Optimism),
343 "optimism-sepolia" | "op-sepolia" => Some(Self::OptimismSepolia),
344 "bsc" | "bnb" | "binance" => Some(Self::Bsc),
345 "bsc-testnet" | "bnb-testnet" => Some(Self::BscTestnet),
346 "gnosis" | "xdai" => Some(Self::Gnosis),
347 "polygon" | "matic" => Some(Self::Polygon),
348 "mumbai" => Some(Self::Mumbai),
349 "amoy" => Some(Self::Amoy),
350 "fantom" | "ftm" => Some(Self::Fantom),
351 "fantom-testnet" => Some(Self::FantomTestnet),
352 "moonbeam" => Some(Self::Moonbeam),
353 "moonriver" => Some(Self::Moonriver),
354 "arbitrum" | "arb" => Some(Self::Arbitrum),
355 "arbitrum-nova" | "arb-nova" => Some(Self::ArbitrumNova),
356 "arbitrum-sepolia" | "arb-sepolia" => Some(Self::ArbitrumSepolia),
357 "avalanche" | "avax" => Some(Self::Avalanche),
358 "avalanche-fuji" | "fuji" => Some(Self::AvalancheFuji),
359 "celo" => Some(Self::Celo),
360 "base" => Some(Self::Base),
361 "base-sepolia" => Some(Self::BaseSepolia),
362 "linea" => Some(Self::Linea),
363 "linea-testnet" => Some(Self::LineaTestnet),
364 "zksync" | "zksync-era" | "era" => Some(Self::ZkSync),
365 "zksync-sepolia" => Some(Self::ZkSyncSepolia),
366 "scroll" => Some(Self::Scroll),
367 "scroll-sepolia" => Some(Self::ScrollSepolia),
368 "blast" => Some(Self::Blast),
369 "blast-sepolia" => Some(Self::BlastSepolia),
370 "mantle" => Some(Self::Mantle),
371 "mode" => Some(Self::Mode),
372 "fraxtal" => Some(Self::Fraxtal),
373 "klaytn" | "klay" => Some(Self::Klaytn),
374 "aurora" => Some(Self::Aurora),
375 "polygon-zkevm" | "polygonzkevm" | "zkevm" => Some(Self::PolygonZkEvm),
376 _ => None,
377 }
378 }
379
380 #[must_use]
382 pub const fn mainnets() -> &'static [Chain] {
383 &[
384 Self::Ethereum,
385 Self::Optimism,
386 Self::Bsc,
387 Self::Gnosis,
388 Self::Polygon,
389 Self::Fantom,
390 Self::Moonbeam,
391 Self::Moonriver,
392 Self::Arbitrum,
393 Self::ArbitrumNova,
394 Self::Avalanche,
395 Self::Celo,
396 Self::Base,
397 Self::Linea,
398 Self::ZkSync,
399 Self::Scroll,
400 Self::Blast,
401 Self::Mantle,
402 Self::Mode,
403 Self::Fraxtal,
404 Self::Klaytn,
405 Self::Aurora,
406 Self::PolygonZkEvm,
407 ]
408 }
409}
410
411impl fmt::Display for Chain {
412 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
413 write!(f, "{}", self.display_name())
414 }
415}
416
417impl From<u64> for Chain {
418 fn from(id: u64) -> Self {
419 Self::from_id(id)
420 }
421}
422
423impl From<Chain> for u64 {
424 fn from(chain: Chain) -> Self {
425 chain.id()
426 }
427}
428
429#[cfg(test)]
430mod tests {
431 use super::*;
432
433 #[test]
434 fn test_chain_id_roundtrip() {
435 assert_eq!(Chain::from_id(1), Chain::Ethereum);
436 assert_eq!(Chain::Ethereum.id(), 1);
437
438 assert_eq!(Chain::from_id(137), Chain::Polygon);
439 assert_eq!(Chain::Polygon.id(), 137);
440
441 assert_eq!(Chain::from_id(8453), Chain::Base);
442 assert_eq!(Chain::Base.id(), 8453);
443 }
444
445 #[test]
446 fn test_chain_from_name() {
447 assert_eq!(Chain::from_name("ethereum"), Some(Chain::Ethereum));
448 assert_eq!(Chain::from_name("ETH"), Some(Chain::Ethereum));
449 assert_eq!(Chain::from_name("mainnet"), Some(Chain::Ethereum));
450
451 assert_eq!(Chain::from_name("polygon"), Some(Chain::Polygon));
452 assert_eq!(Chain::from_name("MATIC"), Some(Chain::Polygon));
453
454 assert_eq!(Chain::from_name("base"), Some(Chain::Base));
455 assert_eq!(Chain::from_name("unknown-chain"), None);
456 }
457
458 #[test]
459 fn test_is_testnet() {
460 assert!(!Chain::Ethereum.is_testnet());
461 assert!(Chain::Sepolia.is_testnet());
462 assert!(!Chain::Base.is_testnet());
463 assert!(Chain::BaseSepolia.is_testnet());
464 }
465
466 #[test]
467 fn test_native_currency() {
468 assert_eq!(Chain::Ethereum.native_currency(), "ETH");
469 assert_eq!(Chain::Polygon.native_currency(), "MATIC");
470 assert_eq!(Chain::Bsc.native_currency(), "BNB");
471 assert_eq!(Chain::Avalanche.native_currency(), "AVAX");
472 }
473
474 #[test]
475 fn test_unknown_chain() {
476 let chain = Chain::from_id(999999);
477 assert_eq!(chain, Chain::Other(999999));
478 assert_eq!(chain.id(), 999999);
479 assert_eq!(chain.name(), "unknown");
480 }
481}