stellar_strkey/
strkey.rs

1use alloc::{format, string::String};
2use core::{
3    fmt::{Debug, Display},
4    str::FromStr,
5};
6
7use crate::{
8    convert::{decode, encode},
9    ed25519,
10    error::DecodeError,
11    version,
12};
13
14#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
15#[cfg_attr(
16    feature = "serde",
17    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
18)]
19#[cfg_attr(
20    feature = "cli",
21    derive(serde::Serialize, serde::Deserialize),
22    serde(rename_all = "snake_case")
23)]
24pub enum Strkey {
25    PublicKeyEd25519(ed25519::PublicKey),
26    PrivateKeyEd25519(ed25519::PrivateKey),
27    PreAuthTx(PreAuthTx),
28    HashX(HashX),
29    MuxedAccountEd25519(ed25519::MuxedAccount),
30    SignedPayloadEd25519(ed25519::SignedPayload),
31    Contract(Contract),
32    LiquidityPool(LiquidityPool),
33    ClaimableBalance(ClaimableBalance),
34}
35
36impl Strkey {
37    pub fn to_string(&self) -> String {
38        match self {
39            Self::PublicKeyEd25519(x) => x.to_string(),
40            Self::PrivateKeyEd25519(x) => x.to_string(),
41            Self::PreAuthTx(x) => x.to_string(),
42            Self::HashX(x) => x.to_string(),
43            Self::MuxedAccountEd25519(x) => x.to_string(),
44            Self::SignedPayloadEd25519(x) => x.to_string(),
45            Self::Contract(x) => x.to_string(),
46            Self::LiquidityPool(x) => x.to_string(),
47            Self::ClaimableBalance(x) => x.to_string(),
48        }
49    }
50
51    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
52        let (ver, payload) = decode(s)?;
53        match ver {
54            version::PUBLIC_KEY_ED25519 => Ok(Self::PublicKeyEd25519(
55                ed25519::PublicKey::from_payload(&payload)?,
56            )),
57            version::PRIVATE_KEY_ED25519 => Ok(Self::PrivateKeyEd25519(
58                ed25519::PrivateKey::from_payload(&payload)?,
59            )),
60            version::PRE_AUTH_TX => Ok(Self::PreAuthTx(PreAuthTx::from_payload(&payload)?)),
61            version::HASH_X => Ok(Self::HashX(HashX::from_payload(&payload)?)),
62            version::MUXED_ACCOUNT_ED25519 => Ok(Self::MuxedAccountEd25519(
63                ed25519::MuxedAccount::from_payload(&payload)?,
64            )),
65            version::SIGNED_PAYLOAD_ED25519 => Ok(Self::SignedPayloadEd25519(
66                ed25519::SignedPayload::from_payload(&payload)?,
67            )),
68            version::CONTRACT => Ok(Self::Contract(Contract::from_payload(&payload)?)),
69            version::LIQUIDITY_POOL => {
70                Ok(Self::LiquidityPool(LiquidityPool::from_payload(&payload)?))
71            }
72            version::CLAIMABLE_BALANCE => Ok(Self::ClaimableBalance(
73                ClaimableBalance::from_payload(&payload)?,
74            )),
75            _ => Err(DecodeError::Invalid),
76        }
77    }
78}
79
80impl Display for Strkey {
81    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
82        write!(f, "{}", self.to_string())
83    }
84}
85
86impl FromStr for Strkey {
87    type Err = DecodeError;
88
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        Strkey::from_string(s)
91    }
92}
93
94#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
95#[cfg_attr(
96    feature = "serde",
97    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
98)]
99#[cfg_attr(
100    feature = "cli",
101    cfg_eval::cfg_eval,
102    serde_with::serde_as,
103    derive(serde::Serialize, serde::Deserialize),
104    serde(rename_all = "snake_case")
105)]
106pub struct PreAuthTx(
107    #[cfg_attr(feature = "cli", serde_as(as = "serde_with::hex::Hex"))] pub [u8; 32],
108);
109
110impl Debug for PreAuthTx {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        write!(f, "PreAuthTx(")?;
113        write!(
114            f,
115            "{}",
116            &self
117                .0
118                .iter()
119                .map(|b| format!("{b:02x}"))
120                .collect::<String>()
121        )?;
122        write!(f, ")")?;
123        Ok(())
124    }
125}
126
127impl PreAuthTx {
128    pub fn to_string(&self) -> String {
129        encode(version::PRE_AUTH_TX, &self.0)
130    }
131
132    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
133        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
134    }
135
136    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
137        let (ver, payload) = decode(s)?;
138        match ver {
139            version::PRE_AUTH_TX => Self::from_payload(&payload),
140            _ => Err(DecodeError::Invalid),
141        }
142    }
143}
144
145impl Display for PreAuthTx {
146    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
147        write!(f, "{}", self.to_string())
148    }
149}
150
151impl FromStr for PreAuthTx {
152    type Err = DecodeError;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        PreAuthTx::from_string(s)
156    }
157}
158
159#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
160#[cfg_attr(
161    feature = "serde",
162    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
163)]
164#[cfg_attr(
165    feature = "cli",
166    cfg_eval::cfg_eval,
167    serde_with::serde_as,
168    derive(serde::Serialize, serde::Deserialize),
169    serde(rename_all = "snake_case")
170)]
171pub struct HashX(#[cfg_attr(feature = "cli", serde_as(as = "serde_with::hex::Hex"))] pub [u8; 32]);
172
173impl Debug for HashX {
174    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
175        write!(f, "HashX(")?;
176        write!(
177            f,
178            "{}",
179            &self
180                .0
181                .iter()
182                .map(|b| format!("{b:02x}"))
183                .collect::<String>()
184        )?;
185        write!(f, ")")?;
186        Ok(())
187    }
188}
189
190impl HashX {
191    pub fn to_string(&self) -> String {
192        encode(version::HASH_X, &self.0)
193    }
194
195    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
196        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
197    }
198
199    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
200        let (ver, payload) = decode(s)?;
201        match ver {
202            version::HASH_X => Self::from_payload(&payload),
203            _ => Err(DecodeError::Invalid),
204        }
205    }
206}
207
208impl Display for HashX {
209    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
210        write!(f, "{}", self.to_string())
211    }
212}
213
214impl FromStr for HashX {
215    type Err = DecodeError;
216
217    fn from_str(s: &str) -> Result<Self, Self::Err> {
218        HashX::from_string(s)
219    }
220}
221
222#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
223#[cfg_attr(
224    feature = "serde",
225    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
226)]
227#[cfg_attr(
228    feature = "cli",
229    cfg_eval::cfg_eval,
230    serde_with::serde_as,
231    derive(serde::Serialize, serde::Deserialize),
232    serde(rename_all = "snake_case")
233)]
234pub struct Contract(
235    #[cfg_attr(feature = "cli", serde_as(as = "serde_with::hex::Hex"))] pub [u8; 32],
236);
237
238impl Debug for Contract {
239    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
240        write!(f, "Contract(")?;
241        write!(
242            f,
243            "{}",
244            &self
245                .0
246                .iter()
247                .map(|b| format!("{b:02x}"))
248                .collect::<String>()
249        )?;
250        write!(f, ")")?;
251        Ok(())
252    }
253}
254
255impl Contract {
256    pub fn to_string(&self) -> String {
257        encode(version::CONTRACT, &self.0)
258    }
259
260    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
261        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
262    }
263
264    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
265        let (ver, payload) = decode(s)?;
266        match ver {
267            version::CONTRACT => Self::from_payload(&payload),
268            _ => Err(DecodeError::Invalid),
269        }
270    }
271}
272
273impl Display for Contract {
274    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
275        write!(f, "{}", self.to_string())
276    }
277}
278
279impl FromStr for Contract {
280    type Err = DecodeError;
281
282    fn from_str(s: &str) -> Result<Self, Self::Err> {
283        Contract::from_string(s)
284    }
285}
286
287#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
288#[cfg_attr(
289    feature = "serde",
290    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
291)]
292#[cfg_attr(
293    feature = "cli",
294    cfg_eval::cfg_eval,
295    serde_with::serde_as,
296    derive(serde::Serialize, serde::Deserialize),
297    serde(rename_all = "snake_case")
298)]
299pub struct LiquidityPool(
300    #[cfg_attr(feature = "cli", serde_as(as = "serde_with::hex::Hex"))] pub [u8; 32],
301);
302
303impl Debug for LiquidityPool {
304    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
305        write!(f, "LiquidityPool(")?;
306        write!(
307            f,
308            "{}",
309            &self
310                .0
311                .iter()
312                .map(|b| format!("{b:02x}"))
313                .collect::<String>()
314        )?;
315        write!(f, ")")?;
316        Ok(())
317    }
318}
319
320impl LiquidityPool {
321    pub fn to_string(&self) -> String {
322        encode(version::LIQUIDITY_POOL, &self.0)
323    }
324
325    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
326        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
327    }
328
329    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
330        let (ver, payload) = decode(s)?;
331        match ver {
332            version::LIQUIDITY_POOL => Self::from_payload(&payload),
333            _ => Err(DecodeError::Invalid),
334        }
335    }
336}
337
338impl Display for LiquidityPool {
339    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
340        write!(f, "{}", self.to_string())
341    }
342}
343
344impl FromStr for LiquidityPool {
345    type Err = DecodeError;
346
347    fn from_str(s: &str) -> Result<Self, Self::Err> {
348        LiquidityPool::from_string(s)
349    }
350}
351
352#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
353#[cfg_attr(
354    feature = "serde",
355    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
356)]
357#[cfg_attr(
358    feature = "cli",
359    cfg_eval::cfg_eval,
360    serde_with::serde_as,
361    derive(serde::Serialize, serde::Deserialize),
362    serde(rename_all = "snake_case")
363)]
364pub enum ClaimableBalance {
365    V0(#[cfg_attr(feature = "cli", serde_as(as = "serde_with::hex::Hex"))] [u8; 32]),
366}
367
368impl Debug for ClaimableBalance {
369    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
370        write!(f, "ClaimableBalance(")?;
371        match self {
372            Self::V0(v0) => {
373                write!(
374                    f,
375                    "V0({})",
376                    &v0.iter().map(|b| format!("{b:02x}")).collect::<String>()
377                )?;
378            }
379        }
380        write!(f, ")")?;
381        Ok(())
382    }
383}
384
385impl ClaimableBalance {
386    pub fn to_string(&self) -> String {
387        match self {
388            Self::V0(v0) => {
389                // First byte is zero for v0
390                let mut payload = [0; 33];
391                payload[1..].copy_from_slice(v0);
392                encode(version::CLAIMABLE_BALANCE, &payload)
393            }
394        }
395    }
396
397    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
398        match payload {
399            // First byte is zero for v0
400            [0, rest @ ..] => Ok(Self::V0(rest.try_into().map_err(|_| DecodeError::Invalid)?)),
401            _ => Err(DecodeError::Invalid),
402        }
403    }
404
405    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
406        let (ver, payload) = decode(s)?;
407        match ver {
408            version::CLAIMABLE_BALANCE => Self::from_payload(&payload),
409            _ => Err(DecodeError::Invalid),
410        }
411    }
412}
413
414impl Display for ClaimableBalance {
415    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
416        write!(f, "{}", self.to_string())
417    }
418}
419
420impl FromStr for ClaimableBalance {
421    type Err = DecodeError;
422
423    fn from_str(s: &str) -> Result<Self, Self::Err> {
424        ClaimableBalance::from_string(s)
425    }
426}