ethers_web/
explorer.rs

1use std::collections::HashMap;
2
3use serde::Deserialize;
4
5#[derive(Clone, Debug, Deserialize)]
6pub struct ExplorerResponse {
7    pub listings: HashMap<String, WalletData>,
8    pub count: u32,
9    pub total: u32,
10}
11
12impl ExplorerResponse {
13    pub fn parse_wallets(&self, project_id: &str) -> Vec<WalletDescription> {
14        let mut wallets: Vec<WalletDescription> = Vec::new();
15        for wallet in self.listings.values() {
16            if let Ok(mut w) = TryInto::<WalletDescription>::try_into(wallet) {
17                w.project_id = project_id.to_string();
18                wallets.push(w)
19            }
20        }
21        wallets
22    }
23}
24
25#[derive(Clone, Debug, Deserialize)]
26pub struct WalletData {
27    pub id: String,
28    pub name: String,
29    pub chains: Vec<String>,
30    pub image_id: String,
31    pub mobile: Option<LinkSet>,
32    pub desktop: Option<LinkSet>,
33    pub metadata: WalletMetadata,
34}
35
36pub enum ExplorerError {
37    BadWallet,
38}
39
40impl TryInto<WalletDescription> for &WalletData {
41    type Error = ExplorerError;
42
43    fn try_into(self) -> Result<WalletDescription, Self::Error> {
44        let chains =
45            self.chains
46                .iter()
47                .filter_map(|c| {
48                    if c.starts_with("eip155:") {
49                        c[6..].parse::<u64>().ok()
50                    } else {
51                        None
52                    }
53                })
54                .collect::<Vec<_>>();
55        let mobile_schema = match &self.mobile {
56            None => None,
57            Some(l) => match &l.native {
58                Some(url) => {
59                    if !url.is_empty() {
60                        Some(url.clone())
61                    } else {
62                        None
63                    }
64                }
65                None => None,
66            },
67        };
68        let desktop_schema = match &self.desktop {
69            None => None,
70            Some(l) => match &l.native {
71                Some(url) => {
72                    if !url.is_empty() {
73                        Some(url.clone())
74                    } else {
75                        None
76                    }
77                }
78                None => None,
79            },
80        };
81
82        if mobile_schema.is_none() && desktop_schema.is_none() {
83            return Err(ExplorerError::BadWallet);
84        }
85
86        Ok(WalletDescription {
87            id: self.id.clone(),
88            short_name: self.metadata.short_name.clone().unwrap_or(self.name.clone()),
89            name: self.name.clone(),
90            chains,
91            image_id: self.image_id.clone(),
92            project_id: "".to_owned(),
93            desktop_schema,
94            mobile_schema,
95        })
96    }
97}
98
99#[derive(Clone, Debug, Deserialize)]
100pub struct LinkSet {
101    pub native: Option<String>,
102    pub universal: Option<String>,
103}
104
105#[derive(Clone, Debug, Deserialize)]
106#[serde(rename_all = "camelCase")]
107pub struct WalletMetadata {
108    pub short_name: Option<String>,
109}
110
111pub enum ImageSize {
112    Small,
113    Medium,
114    Large,
115}
116
117#[derive(Clone, Debug, PartialEq)]
118pub struct WalletDescription {
119    pub id: String,
120    pub short_name: String,
121    pub name: String,
122    pub chains: Vec<u64>,
123    pub image_id: String,
124    pub project_id: String,
125    pub desktop_schema: Option<String>,
126    pub mobile_schema: Option<String>,
127}
128
129impl WalletDescription {
130    pub fn get_image(&self, size: ImageSize) -> String {
131        let size_mark = match size {
132            ImageSize::Small => "sm",
133            ImageSize::Medium => "md",
134            ImageSize::Large => "lg",
135        };
136        format!(
137            "https://explorer-api.walletconnect.com/v3/logo/{size_mark}/{}?projectId={}",
138            self.image_id, self.project_id
139        )
140    }
141}