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