1use serde::de::Deserializer;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
5pub enum Chain {
6 #[serde(rename = "ARB")]
7 Arbitrum,
8 #[serde(rename = "AURORA")]
9 Aurora,
10 #[serde(rename = "AVAX")]
11 Avax,
12 #[serde(rename = "BASE")]
13 Base,
14 #[serde(rename = "BLAST")]
15 Blast,
16 #[serde(rename = "BOB")]
17 Bob,
18 #[serde(rename = "BSC")]
19 Bsc,
20 #[serde(rename = "CSDK")]
21 Csdk,
22 #[serde(rename = "CYBER")]
23 Cyber,
24 #[serde(rename = "DOT")]
25 Polkadot,
26 #[serde(rename = "ES")]
27 Eclipse,
28 #[serde(rename = "ETH")]
29 Ethereum,
30 #[serde(rename = "ETHERLINK")]
31 Etherlink,
32 #[serde(rename = "FRAX")]
33 Fraxtal,
34 #[serde(rename = "HYPE")]
35 Hype,
36 #[serde(rename = "INK")]
37 Ink,
38 #[serde(rename = "LENS")]
39 Lens,
40 #[serde(rename = "LINEA")]
41 Linea,
42 #[serde(rename = "LISK")]
43 Lisk,
44 #[serde(rename = "METIS")]
45 Metis,
46 #[serde(rename = "MODE")]
47 Mode,
48 #[serde(rename = "NEO")]
49 Neo,
50 #[serde(rename = "NULS")]
51 Nuls,
52 #[serde(rename = "NULS2")]
53 Nuls2,
54 #[serde(rename = "OP")]
55 Optimism,
56 #[serde(rename = "POL")]
57 Pol,
58 #[serde(rename = "SOL")]
59 Sol,
60 #[serde(rename = "STT")]
61 Somnia,
62 #[serde(rename = "SONIC")]
63 Sonic,
64 #[serde(rename = "TEZOS")]
65 Tezos,
66 #[serde(rename = "UNICHAIN")]
67 Unichain,
68 #[serde(rename = "WLD")]
69 Worldchain,
70 #[serde(rename = "ZORA")]
71 Zora,
72}
73
74impl std::fmt::Display for Chain {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 let s = match self {
77 Chain::Arbitrum => "ARB",
78 Chain::Aurora => "AURORA",
79 Chain::Avax => "AVAX",
80 Chain::Base => "BASE",
81 Chain::Blast => "BLAST",
82 Chain::Bob => "BOB",
83 Chain::Bsc => "BSC",
84 Chain::Csdk => "CSDK",
85 Chain::Cyber => "CYBER",
86 Chain::Polkadot => "DOT",
87 Chain::Eclipse => "ES",
88 Chain::Ethereum => "ETH",
89 Chain::Etherlink => "ETHERLINK",
90 Chain::Fraxtal => "FRAX",
91 Chain::Hype => "HYPE",
92 Chain::Ink => "INK",
93 Chain::Lens => "LENS",
94 Chain::Linea => "LINEA",
95 Chain::Lisk => "LISK",
96 Chain::Metis => "METIS",
97 Chain::Mode => "MODE",
98 Chain::Neo => "NEO",
99 Chain::Nuls => "NULS",
100 Chain::Nuls2 => "NULS2",
101 Chain::Optimism => "OP",
102 Chain::Pol => "POL",
103 Chain::Sol => "SOL",
104 Chain::Somnia => "STT",
105 Chain::Sonic => "SONIC",
106 Chain::Tezos => "TEZOS",
107 Chain::Unichain => "UNICHAIN",
108 Chain::Worldchain => "WLD",
109 Chain::Zora => "ZORA",
110 };
111 f.write_str(s)
112 }
113}
114
115impl Chain {
116 pub fn is_evm(&self) -> bool {
122 matches!(
123 self,
124 Chain::Arbitrum
125 | Chain::Aurora
126 | Chain::Avax
127 | Chain::Base
128 | Chain::Blast
129 | Chain::Bob
130 | Chain::Bsc
131 | Chain::Cyber
132 | Chain::Ethereum
133 | Chain::Etherlink
134 | Chain::Fraxtal
135 | Chain::Hype
136 | Chain::Ink
137 | Chain::Lens
138 | Chain::Linea
139 | Chain::Lisk
140 | Chain::Metis
141 | Chain::Mode
142 | Chain::Optimism
143 | Chain::Pol
144 | Chain::Somnia
145 | Chain::Sonic
146 | Chain::Unichain
147 | Chain::Worldchain
148 | Chain::Zora
149 )
150 }
151
152 pub fn is_svm(&self) -> bool {
158 matches!(self, Chain::Eclipse | Chain::Sol)
159 }
160}
161
162#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
163pub struct Address(String);
164
165impl std::fmt::Display for Address {
166 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167 write!(f, "{}", self.0)
168 }
169}
170
171impl Address {
172 pub fn as_str(&self) -> &str {
173 &self.0
174 }
175}
176
177impl From<String> for Address {
178 fn from(value: String) -> Self {
179 Self(value)
180 }
181}
182
183#[macro_export]
192macro_rules! address {
193 ($address:expr) => {{ $crate::chain::Address::from($address.to_string()) }};
194}
195
196#[derive(Clone, Debug, PartialEq, Eq, Hash)]
203pub struct Signature {
204 value: String,
206 public_key: Option<String>,
209}
210
211impl Signature {
212 pub fn as_str(&self) -> &str {
214 &self.value
215 }
216
217 pub fn public_key(&self) -> Option<&str> {
219 self.public_key.as_deref()
220 }
221}
222
223impl From<String> for Signature {
224 fn from(value: String) -> Self {
225 Self {
226 value,
227 public_key: None,
228 }
229 }
230}
231
232impl Serialize for Signature {
233 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
234 where
235 S: serde::Serializer,
236 {
237 match &self.public_key {
238 None => serializer.serialize_str(&self.value),
239 Some(pk) => {
240 use serde::ser::SerializeStruct;
241 let mut state = serializer.serialize_struct("Signature", 2)?;
242 state.serialize_field("signature", &self.value)?;
243 state.serialize_field("publicKey", pk)?;
244 state.end()
245 }
246 }
247 }
248}
249
250impl<'de> Deserialize<'de> for Signature {
251 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
252 where
253 D: Deserializer<'de>,
254 {
255 #[derive(Deserialize)]
256 struct StructuredSig {
257 signature: String,
258 #[serde(rename = "publicKey")]
259 public_key: String,
260 }
261
262 #[derive(Deserialize)]
263 #[serde(untagged)]
264 enum SigFormat {
265 Plain(String),
266 Structured(StructuredSig),
267 }
268
269 match SigFormat::deserialize(deserializer)? {
270 SigFormat::Plain(s) => Ok(Signature {
271 value: s,
272 public_key: None,
273 }),
274 SigFormat::Structured(s) => Ok(Signature {
275 value: s.signature,
276 public_key: Some(s.public_key),
277 }),
278 }
279 }
280}
281
282#[macro_export]
291macro_rules! signature {
292 ($signature:expr) => {{ $crate::chain::Signature::from($signature.to_string()) }};
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298
299 #[test]
303 fn test_chain_display_matches_serde() {
304 let chains = [
305 Chain::Arbitrum,
306 Chain::Aurora,
307 Chain::Avax,
308 Chain::Base,
309 Chain::Blast,
310 Chain::Bob,
311 Chain::Bsc,
312 Chain::Csdk,
313 Chain::Cyber,
314 Chain::Polkadot,
315 Chain::Eclipse,
316 Chain::Ethereum,
317 Chain::Etherlink,
318 Chain::Fraxtal,
319 Chain::Hype,
320 Chain::Ink,
321 Chain::Lens,
322 Chain::Linea,
323 Chain::Lisk,
324 Chain::Metis,
325 Chain::Mode,
326 Chain::Neo,
327 Chain::Nuls,
328 Chain::Nuls2,
329 Chain::Optimism,
330 Chain::Pol,
331 Chain::Sol,
332 Chain::Somnia,
333 Chain::Sonic,
334 Chain::Tezos,
335 Chain::Unichain,
336 Chain::Worldchain,
337 Chain::Zora,
338 ];
339
340 for chain in &chains {
341 let display = chain.to_string();
342 let serde = serde_json::to_string(chain).unwrap();
343 let serde_unquoted = serde.trim_matches('"');
344 assert_eq!(
345 display, serde_unquoted,
346 "Display and serde disagree for {chain:?}: Display={display}, serde={serde_unquoted}"
347 );
348 }
349 }
350}