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 pub fn with_public_key(value: String, public_key: String) -> Self {
224 Self {
225 value,
226 public_key: Some(public_key),
227 }
228 }
229}
230
231impl From<String> for Signature {
232 fn from(value: String) -> Self {
233 Self {
234 value,
235 public_key: None,
236 }
237 }
238}
239
240impl Serialize for Signature {
241 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
242 where
243 S: serde::Serializer,
244 {
245 match &self.public_key {
246 None => serializer.serialize_str(&self.value),
247 Some(pk) => {
248 use serde::ser::SerializeStruct;
249 let mut state = serializer.serialize_struct("Signature", 2)?;
250 state.serialize_field("signature", &self.value)?;
251 state.serialize_field("publicKey", pk)?;
252 state.end()
253 }
254 }
255 }
256}
257
258impl<'de> Deserialize<'de> for Signature {
259 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
260 where
261 D: Deserializer<'de>,
262 {
263 #[derive(Deserialize)]
264 struct StructuredSig {
265 signature: String,
266 #[serde(rename = "publicKey")]
267 public_key: String,
268 }
269
270 #[derive(Deserialize)]
271 #[serde(untagged)]
272 enum SigFormat {
273 Plain(String),
274 Structured(StructuredSig),
275 }
276
277 match SigFormat::deserialize(deserializer)? {
278 SigFormat::Plain(s) => Ok(Signature {
279 value: s,
280 public_key: None,
281 }),
282 SigFormat::Structured(s) => Ok(Signature {
283 value: s.signature,
284 public_key: Some(s.public_key),
285 }),
286 }
287 }
288}
289
290#[macro_export]
299macro_rules! signature {
300 ($signature:expr) => {{ $crate::chain::Signature::from($signature.to_string()) }};
301}
302
303#[cfg(test)]
304mod tests {
305 use super::*;
306
307 #[test]
311 fn test_chain_display_matches_serde() {
312 let chains = [
313 Chain::Arbitrum,
314 Chain::Aurora,
315 Chain::Avax,
316 Chain::Base,
317 Chain::Blast,
318 Chain::Bob,
319 Chain::Bsc,
320 Chain::Csdk,
321 Chain::Cyber,
322 Chain::Polkadot,
323 Chain::Eclipse,
324 Chain::Ethereum,
325 Chain::Etherlink,
326 Chain::Fraxtal,
327 Chain::Hype,
328 Chain::Ink,
329 Chain::Lens,
330 Chain::Linea,
331 Chain::Lisk,
332 Chain::Metis,
333 Chain::Mode,
334 Chain::Neo,
335 Chain::Nuls,
336 Chain::Nuls2,
337 Chain::Optimism,
338 Chain::Pol,
339 Chain::Sol,
340 Chain::Somnia,
341 Chain::Sonic,
342 Chain::Tezos,
343 Chain::Unichain,
344 Chain::Worldchain,
345 Chain::Zora,
346 ];
347
348 for chain in &chains {
349 let display = chain.to_string();
350 let serde = serde_json::to_string(chain).unwrap();
351 let serde_unquoted = serde.trim_matches('"');
352 assert_eq!(
353 display, serde_unquoted,
354 "Display and serde disagree for {chain:?}: Display={display}, serde={serde_unquoted}"
355 );
356 }
357 }
358
359 #[test]
360 fn test_signature_with_public_key() {
361 let sig = Signature::with_public_key("5HH5Z".to_string(), "5SwCe".to_string());
362 assert_eq!(sig.as_str(), "5HH5Z");
363 assert_eq!(sig.public_key(), Some("5SwCe"));
364
365 let json = serde_json::to_value(&sig).unwrap();
366 assert_eq!(json["signature"], "5HH5Z");
367 assert_eq!(json["publicKey"], "5SwCe");
368 }
369}