1use std::{fmt, str::FromStr};
4
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use thiserror::Error;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
9pub enum Chain {
10 Any,
13
14 Solana,
16 Ethereum,
17 Terra,
18 Bsc,
19 Polygon,
20 Avalanche,
21 Oasis,
22 Algorand,
23 Aurora,
24 Fantom,
25 Karura,
26 Acala,
27 Klaytn,
28 Celo,
29 Near,
30 Moonbeam,
31 Neon,
32 Terra2,
33 Injective,
34 Osmosis,
35 Sui,
36 Aptos,
37 Arbitrum,
38 Optimism,
39 Gnosis,
40 Pythnet,
41 Xpla,
42 Btc,
43 Base,
44 Sei,
45 Rootstock,
46 Scroll,
47 Mantle,
48 Wormchain,
49 CosmosHub,
50 Evmos,
51 Kujira,
52 Neutron,
53 Celestia,
54 Stargaze,
55 Seda,
56 Dymension,
57 Sepolia,
58
59 Unknown(u16),
61}
62
63impl From<u16> for Chain {
64 fn from(other: u16) -> Chain {
65 match other {
66 0 => Chain::Any,
67 1 => Chain::Solana,
68 2 => Chain::Ethereum,
69 3 => Chain::Terra,
70 4 => Chain::Bsc,
71 5 => Chain::Polygon,
72 6 => Chain::Avalanche,
73 7 => Chain::Oasis,
74 8 => Chain::Algorand,
75 9 => Chain::Aurora,
76 10 => Chain::Fantom,
77 11 => Chain::Karura,
78 12 => Chain::Acala,
79 13 => Chain::Klaytn,
80 14 => Chain::Celo,
81 15 => Chain::Near,
82 16 => Chain::Moonbeam,
83 17 => Chain::Neon,
84 18 => Chain::Terra2,
85 19 => Chain::Injective,
86 20 => Chain::Osmosis,
87 21 => Chain::Sui,
88 22 => Chain::Aptos,
89 23 => Chain::Arbitrum,
90 24 => Chain::Optimism,
91 25 => Chain::Gnosis,
92 26 => Chain::Pythnet,
93 28 => Chain::Xpla,
94 29 => Chain::Btc,
95 30 => Chain::Base,
96 32 => Chain::Sei,
97 33 => Chain::Rootstock,
98 34 => Chain::Scroll,
99 35 => Chain::Mantle,
100 3104 => Chain::Wormchain,
101 4000 => Chain::CosmosHub,
102 4001 => Chain::Evmos,
103 4002 => Chain::Kujira,
104 4003 => Chain::Neutron,
105 4004 => Chain::Celestia,
106 4005 => Chain::Stargaze,
107 4006 => Chain::Seda,
108 4007 => Chain::Dymension,
109 10002 => Chain::Sepolia,
110 c => Chain::Unknown(c),
111 }
112 }
113}
114
115impl From<Chain> for u16 {
116 fn from(other: Chain) -> u16 {
117 match other {
118 Chain::Any => 0,
119 Chain::Solana => 1,
120 Chain::Ethereum => 2,
121 Chain::Terra => 3,
122 Chain::Bsc => 4,
123 Chain::Polygon => 5,
124 Chain::Avalanche => 6,
125 Chain::Oasis => 7,
126 Chain::Algorand => 8,
127 Chain::Aurora => 9,
128 Chain::Fantom => 10,
129 Chain::Karura => 11,
130 Chain::Acala => 12,
131 Chain::Klaytn => 13,
132 Chain::Celo => 14,
133 Chain::Near => 15,
134 Chain::Moonbeam => 16,
135 Chain::Neon => 17,
136 Chain::Terra2 => 18,
137 Chain::Injective => 19,
138 Chain::Osmosis => 20,
139 Chain::Sui => 21,
140 Chain::Aptos => 22,
141 Chain::Arbitrum => 23,
142 Chain::Optimism => 24,
143 Chain::Gnosis => 25,
144 Chain::Pythnet => 26,
145 Chain::Xpla => 28,
146 Chain::Btc => 29,
147 Chain::Base => 30,
148 Chain::Sei => 32,
149 Chain::Rootstock => 33,
150 Chain::Scroll => 34,
151 Chain::Mantle => 35,
152 Chain::Wormchain => 3104,
153 Chain::CosmosHub => 4000,
154 Chain::Evmos => 4001,
155 Chain::Kujira => 4002,
156 Chain::Neutron => 4003,
157 Chain::Celestia => 4004,
158 Chain::Stargaze => 4005,
159 Chain::Seda => 4006,
160 Chain::Dymension => 4007,
161 Chain::Sepolia => 10002,
162 Chain::Unknown(c) => c,
163 }
164 }
165}
166
167impl fmt::Display for Chain {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 Self::Any => f.write_str("Any"),
171 Self::Solana => f.write_str("Solana"),
172 Self::Ethereum => f.write_str("Ethereum"),
173 Self::Terra => f.write_str("Terra"),
174 Self::Bsc => f.write_str("Bsc"),
175 Self::Polygon => f.write_str("Polygon"),
176 Self::Avalanche => f.write_str("Avalanche"),
177 Self::Oasis => f.write_str("Oasis"),
178 Self::Algorand => f.write_str("Algorand"),
179 Self::Aurora => f.write_str("Aurora"),
180 Self::Fantom => f.write_str("Fantom"),
181 Self::Karura => f.write_str("Karura"),
182 Self::Acala => f.write_str("Acala"),
183 Self::Klaytn => f.write_str("Klaytn"),
184 Self::Celo => f.write_str("Celo"),
185 Self::Near => f.write_str("Near"),
186 Self::Moonbeam => f.write_str("Moonbeam"),
187 Self::Neon => f.write_str("Neon"),
188 Self::Terra2 => f.write_str("Terra2"),
189 Self::Injective => f.write_str("Injective"),
190 Self::Osmosis => f.write_str("Osmosis"),
191 Self::Sui => f.write_str("Sui"),
192 Self::Aptos => f.write_str("Aptos"),
193 Self::Arbitrum => f.write_str("Arbitrum"),
194 Self::Optimism => f.write_str("Optimism"),
195 Self::Gnosis => f.write_str("Gnosis"),
196 Self::Pythnet => f.write_str("Pythnet"),
197 Self::Xpla => f.write_str("Xpla"),
198 Self::Btc => f.write_str("Btc"),
199 Self::Base => f.write_str("Base"),
200 Self::Sei => f.write_str("Sei"),
201 Self::Rootstock => f.write_str("Rootstock"),
202 Self::Scroll => f.write_str("Scroll"),
203 Self::Mantle => f.write_str("Mantle"),
204 Self::Sepolia => f.write_str("Sepolia"),
205 Self::Wormchain => f.write_str("Wormchain"),
206 Self::CosmosHub => f.write_str("CosmosHub"),
207 Self::Evmos => f.write_str("Evmos"),
208 Self::Kujira => f.write_str("Kujira"),
209 Self::Neutron => f.write_str("Neutron"),
210 Self::Celestia => f.write_str("Celestia"),
211 Self::Stargaze => f.write_str("Stargaze"),
212 Self::Seda => f.write_str("Seda"),
213 Self::Dymension => f.write_str("Dymension"),
214 Self::Unknown(v) => write!(f, "Unknown({v})"),
215 }
216 }
217}
218
219#[derive(Debug, Error)]
220#[error("invalid chain: {0}")]
221pub struct InvalidChainError(String);
222
223impl FromStr for Chain {
224 type Err = InvalidChainError;
225
226 fn from_str(s: &str) -> Result<Self, Self::Err> {
227 match s {
228 "Any" | "any" | "ANY" => Ok(Chain::Any),
229 "Solana" | "solana" | "SOLANA" => Ok(Chain::Solana),
230 "Ethereum" | "ethereum" | "ETHEREUM" => Ok(Chain::Ethereum),
231 "Terra" | "terra" | "TERRA" => Ok(Chain::Terra),
232 "Bsc" | "bsc" | "BSC" => Ok(Chain::Bsc),
233 "Polygon" | "polygon" | "POLYGON" => Ok(Chain::Polygon),
234 "Avalanche" | "avalanche" | "AVALANCHE" => Ok(Chain::Avalanche),
235 "Oasis" | "oasis" | "OASIS" => Ok(Chain::Oasis),
236 "Algorand" | "algorand" | "ALGORAND" => Ok(Chain::Algorand),
237 "Aurora" | "aurora" | "AURORA" => Ok(Chain::Aurora),
238 "Fantom" | "fantom" | "FANTOM" => Ok(Chain::Fantom),
239 "Karura" | "karura" | "KARURA" => Ok(Chain::Karura),
240 "Acala" | "acala" | "ACALA" => Ok(Chain::Acala),
241 "Klaytn" | "klaytn" | "KLAYTN" => Ok(Chain::Klaytn),
242 "Celo" | "celo" | "CELO" => Ok(Chain::Celo),
243 "Near" | "near" | "NEAR" => Ok(Chain::Near),
244 "Moonbeam" | "moonbeam" | "MOONBEAM" => Ok(Chain::Moonbeam),
245 "Neon" | "neon" | "NEON" => Ok(Chain::Neon),
246 "Terra2" | "terra2" | "TERRA2" => Ok(Chain::Terra2),
247 "Injective" | "injective" | "INJECTIVE" => Ok(Chain::Injective),
248 "Osmosis" | "osmosis" | "OSMOSIS" => Ok(Chain::Osmosis),
249 "Sui" | "sui" | "SUI" => Ok(Chain::Sui),
250 "Aptos" | "aptos" | "APTOS" => Ok(Chain::Aptos),
251 "Arbitrum" | "arbitrum" | "ARBITRUM" => Ok(Chain::Arbitrum),
252 "Optimism" | "optimism" | "OPTIMISM" => Ok(Chain::Optimism),
253 "Gnosis" | "gnosis" | "GNOSIS" => Ok(Chain::Gnosis),
254 "Pythnet" | "pythnet" | "PYTHNET" => Ok(Chain::Pythnet),
255 "Xpla" | "xpla" | "XPLA" => Ok(Chain::Xpla),
256 "Btc" | "btc" | "BTC" => Ok(Chain::Btc),
257 "Base" | "base" | "BASE" => Ok(Chain::Base),
258 "Sei" | "sei" | "SEI" => Ok(Chain::Sei),
259 "Rootstock" | "rootstock" | "ROOTSTOCK" => Ok(Chain::Rootstock),
260 "Scroll" | "scroll" | "SCROLL" => Ok(Chain::Scroll),
261 "Mantle" | "mantle" | "MANTLE" => Ok(Chain::Mantle),
262 "Sepolia" | "sepolia" | "SEPOLIA" => Ok(Chain::Sepolia),
263 "Wormchain" | "wormchain" | "WORMCHAIN" => Ok(Chain::Wormchain),
264 "CosmosHub" | "cosmoshub" | "COSMOSHUB" => Ok(Chain::CosmosHub),
265 "Evmos" | "evmos" | "EVMOS" => Ok(Chain::Evmos),
266 "Kujira" | "kujira" | "KUJIRA" => Ok(Chain::Kujira),
267 "Neutron" | "neutron" | "NEUTRON" => Ok(Chain::Neutron),
268 "Celestia" | "celestia" | "CELESTIA" => Ok(Chain::Celestia),
269 "Stargaze" | "stargaze" | "STARGAZE" => Ok(Chain::Stargaze),
270 "Seda" | "seda" | "SEDA" => Ok(Chain::Seda),
271 "Dymension" | "dymension" | "DYMENSION" => Ok(Chain::Dymension),
272 _ => {
273 let mut parts = s.split(&['(', ')']);
274 let _ = parts
275 .next()
276 .filter(|name| name.eq_ignore_ascii_case("unknown"))
277 .ok_or_else(|| InvalidChainError(s.into()))?;
278
279 parts
280 .next()
281 .and_then(|v| v.parse::<u16>().ok())
282 .map(Chain::from)
283 .ok_or_else(|| InvalidChainError(s.into()))
284 }
285 }
286 }
287}
288
289impl Default for Chain {
290 fn default() -> Self {
291 Self::Any
292 }
293}
294
295impl Serialize for Chain {
296 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
297 where
298 S: Serializer,
299 {
300 serializer.serialize_u16((*self).into())
301 }
302}
303
304impl<'de> Deserialize<'de> for Chain {
305 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
306 where
307 D: Deserializer<'de>,
308 {
309 <u16 as Deserialize>::deserialize(deserializer).map(Self::from)
310 }
311}
312
313#[cfg(test)]
314mod test {
315 use super::*;
316
317 #[test]
318 fn isomorphic_from() {
319 for i in 0u16..=u16::MAX {
320 assert_eq!(i, u16::from(Chain::from(i)));
321 }
322 }
323
324 #[test]
325 fn isomorphic_display() {
326 for i in 0u16..=u16::MAX {
327 let c = Chain::from(i);
328 assert_eq!(c, c.to_string().parse().unwrap());
329 }
330 }
331}