fireblocks_sdk/
assets.rs

1use {
2    serde::{Deserialize, Deserializer, Serialize, Serializer},
3    std::{
4        borrow::Borrow,
5        fmt::{Debug, Display, Formatter},
6        str::FromStr,
7    },
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum EthNetwork {
12    Main,
13    Test,
14}
15
16#[derive(Debug, Default, Clone, PartialEq, Eq)]
17pub enum Network {
18    #[default]
19    Main,
20    Test,
21}
22
23/// A collection of common asset symbols for convenience
24///
25/// ```rust
26/// use fireblocks_sdk::Client;
27/// use fireblocks_sdk::{ASSET_SOL, Asset};
28///
29/// async fn asset(client: Client) -> anyhow::Result<()> {
30///  let response  = client.create_asset("0", ASSET_SOL ).await?;
31///  // same call but with string arg: let (response, request_id)  = client.create_address(0, "SOL" ).await?;
32///  println!("{response:#?}");
33///
34/// // create a new sh*tcoin
35/// assert_eq!("sh*tcoin", Asset::new("sh*tcoin").to_string());
36/// Ok(())
37/// }
38/// ```
39///
40/// See
41/// [getSupportedAssets](https://docs.fireblocks.com/api/swagger-ui/#/Blockchains%20%26%20assets/getSupportedAssets)
42#[derive(Debug, Clone, PartialEq, Eq)]
43#[allow(clippy::upper_case_acronyms)]
44pub enum Asset {
45    BTC(Network),
46    SOL(Network),
47    Dodge(Network),
48    ETH(EthNetwork),
49    Unknown(String),
50}
51
52impl Asset {
53    /// Return a new asset type enum
54    /// This should never fail
55    #[allow(clippy::missing_panics_doc)]
56    pub fn new(a: &str) -> Self {
57        match Self::from_str(a) {
58            Ok(asset) => asset,
59            Err(e) => panic!("this should never happen! {e}"),
60        }
61    }
62}
63
64impl Default for Asset {
65    fn default() -> Self {
66        Self::BTC(Network::default())
67    }
68}
69
70impl<'de> Deserialize<'de> for Asset {
71    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
72    where
73        D: Deserializer<'de>,
74    {
75        let a: String = String::deserialize(deserializer)?;
76        Self::from_str(&a).map_err(|_| serde::de::Error::custom("this should never happen"))
77    }
78}
79
80impl Serialize for Asset {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: Serializer,
84    {
85        let a = self.to_string();
86        a.serialize(serializer)
87    }
88}
89
90impl AsRef<str> for Asset {
91    #[allow(clippy::match_same_arms)]
92    fn as_ref(&self) -> &str {
93        match self {
94            Self::BTC(Network::Main) => "BTC",
95            Self::BTC(Network::Test) => "BTC_TEST",
96            Self::Dodge(Network::Main) => "DOGE",
97            Self::Dodge(Network::Test) => "DOGE_TEST",
98            Self::SOL(Network::Main) => "SOL",
99            Self::SOL(Network::Test) => "SOL_TEST",
100            Self::ETH(EthNetwork::Main) => "ETH",
101            Self::ETH(EthNetwork::Test) => "ETH_TEST5",
102            Self::Unknown(s) => s,
103        }
104    }
105}
106
107impl Borrow<str> for Asset {
108    fn borrow(&self) -> &str {
109        self.as_ref()
110    }
111}
112
113impl Display for Asset {
114    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115        write!(f, "{}", self.as_ref())
116    }
117}
118
119impl From<Asset> for String {
120    fn from(value: Asset) -> Self {
121        format!("{value}")
122    }
123}
124
125/// Convert a String to an [`Asset`]
126/// Note: This method will never fail
127impl FromStr for Asset {
128    type Err = std::string::ParseError;
129
130    fn from_str(s: &str) -> Result<Self, Self::Err> {
131        match s.to_uppercase().as_str() {
132            "BTC" => Ok(ASSET_BTC),
133            "BTC_TEST" => Ok(ASSET_BTC_TEST),
134            "SOL" => Ok(ASSET_SOL),
135            "SOL_TEST" => Ok(ASSET_SOL_TEST),
136            "DOGE" => Ok(ASSET_DOGE),
137            "DOGE_TEST" => Ok(ASSET_DOGE_TEST),
138            "ETH" => Ok(ASSET_ETH),
139            "ETH_TEST5" => Ok(ASSET_ETH_TEST),
140            _ => Ok(Self::Unknown(String::from(s))),
141        }
142    }
143}
144
145pub const ASSET_BTC: Asset = Asset::BTC(Network::Main);
146pub const ASSET_BTC_TEST: Asset = Asset::BTC(Network::Test);
147pub const ASSET_SOL: Asset = Asset::SOL(Network::Main);
148pub const ASSET_SOL_TEST: Asset = Asset::SOL(Network::Test);
149pub const ASSET_ETH: Asset = Asset::ETH(EthNetwork::Main);
150pub const ASSET_ETH_TEST: Asset = Asset::ETH(EthNetwork::Test);
151pub const ASSET_DOGE: Asset = Asset::Dodge(Network::Main);
152pub const ASSET_DOGE_TEST: Asset = Asset::Dodge(Network::Test);
153
154#[cfg(test)]
155mod tests {
156    use {
157        crate::{
158            ASSET_BTC,
159            ASSET_BTC_TEST,
160            ASSET_SOL,
161            ASSET_SOL_TEST,
162            assets::{ASSET_DOGE, ASSET_DOGE_TEST, ASSET_ETH, ASSET_ETH_TEST, Asset},
163        },
164        serde_json::json,
165        std::str::FromStr,
166    };
167
168    #[test]
169    fn asset_from_string() -> anyhow::Result<()> {
170        let a = Asset::from_str("BTC")?;
171        assert_eq!(a, ASSET_BTC);
172
173        let a = Asset::from_str("BTC_TEST")?;
174        assert_eq!(a, ASSET_BTC_TEST);
175
176        let a = Asset::from_str("SOL")?;
177        assert_eq!(a, ASSET_SOL);
178
179        let a = Asset::from_str("SOL_TEST")?;
180        assert_eq!(a, ASSET_SOL_TEST);
181
182        let a = Asset::from_str("DOGE")?;
183        assert_eq!(a, ASSET_DOGE);
184
185        let a = Asset::from_str("DOGE_TEST")?;
186        assert_eq!(a, ASSET_DOGE_TEST);
187
188        let a = Asset::from_str("ETH")?;
189        assert_eq!(a, ASSET_ETH);
190
191        let a = Asset::from_str("ETH_TEST5")?;
192        assert_eq!(a, ASSET_ETH_TEST);
193
194        let a = Asset::from_str("UNKNOWN")?;
195        assert_eq!(a, Asset::Unknown("UNKNOWN".to_string()));
196
197        assert_eq!(Asset::default(), ASSET_BTC);
198
199        assert_eq!("\"SOL\"", serde_json::to_string(&ASSET_SOL)?);
200
201        assert_eq!("BTC", ASSET_BTC.as_ref());
202        assert_eq!("DOGE", ASSET_DOGE.as_ref());
203        assert_eq!("DOGE_TEST", ASSET_DOGE_TEST.as_ref());
204        assert_eq!("ETH", ASSET_ETH.as_ref());
205        assert_eq!("ETH_TEST5", ASSET_ETH_TEST.as_ref());
206
207        assert_eq!(Asset::Unknown("blah".to_owned()).to_string(), "blah");
208        assert_eq!(ASSET_BTC.to_string(), "BTC");
209
210        assert_eq!("BLAH", Asset::new("BLAH").to_string());
211        let v = json!("SOL_TEST");
212        let a: Asset = serde_json::from_value(v)?;
213        assert_eq!("SOL_TEST", a.to_string().as_str());
214        Ok(())
215    }
216}