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#[cfg(feature = "serde-decoded")]
90mod strkey_decoded_serde_impl {
91    use super::*;
92    use crate::decoded_json_format::Decoded;
93    use serde::{
94        de::{self, MapAccess, Visitor},
95        ser::SerializeMap,
96        Deserialize, Deserializer, Serialize, Serializer,
97    };
98
99    impl Serialize for Decoded<&Strkey> {
100        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
101            let mut map = serializer.serialize_map(Some(1))?;
102            match self.0 {
103                Strkey::PublicKeyEd25519(key) => {
104                    map.serialize_entry("public_key_ed25519", &Decoded(key))?;
105                }
106                Strkey::PrivateKeyEd25519(key) => {
107                    map.serialize_entry("private_key_ed25519", &Decoded(key))?;
108                }
109                Strkey::PreAuthTx(key) => {
110                    map.serialize_entry("pre_auth_tx", &Decoded(key))?;
111                }
112                Strkey::HashX(key) => {
113                    map.serialize_entry("hash_x", &Decoded(key))?;
114                }
115                Strkey::MuxedAccountEd25519(key) => {
116                    map.serialize_entry("muxed_account_ed25519", &Decoded(key))?;
117                }
118                Strkey::SignedPayloadEd25519(key) => {
119                    map.serialize_entry("signed_payload_ed25519", &Decoded(key))?;
120                }
121                Strkey::Contract(key) => {
122                    map.serialize_entry("contract", &Decoded(key))?;
123                }
124                Strkey::LiquidityPool(key) => {
125                    map.serialize_entry("liquidity_pool", &Decoded(key))?;
126                }
127                Strkey::ClaimableBalance(key) => {
128                    map.serialize_entry("claimable_balance", &Decoded(key))?;
129                }
130            }
131            map.end()
132        }
133    }
134
135    impl<'de> Deserialize<'de> for Decoded<Strkey> {
136        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
137            struct StrkeyVisitor;
138
139            impl<'de> Visitor<'de> for StrkeyVisitor {
140                type Value = Decoded<Strkey>;
141
142                fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
143                    formatter.write_str("a strkey object")
144                }
145
146                fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Self::Value, M::Error> {
147                    let key: &str = map
148                        .next_key()?
149                        .ok_or_else(|| de::Error::custom("expected a variant key"))?;
150
151                    let strkey = match key {
152                        "public_key_ed25519" => {
153                            let Decoded(inner) = map.next_value()?;
154                            Strkey::PublicKeyEd25519(inner)
155                        }
156                        "private_key_ed25519" => {
157                            let Decoded(inner) = map.next_value()?;
158                            Strkey::PrivateKeyEd25519(inner)
159                        }
160                        "pre_auth_tx" => {
161                            let Decoded(inner) = map.next_value()?;
162                            Strkey::PreAuthTx(inner)
163                        }
164                        "hash_x" => {
165                            let Decoded(inner) = map.next_value()?;
166                            Strkey::HashX(inner)
167                        }
168                        "muxed_account_ed25519" => {
169                            let Decoded(inner) = map.next_value()?;
170                            Strkey::MuxedAccountEd25519(inner)
171                        }
172                        "signed_payload_ed25519" => {
173                            let Decoded(inner) = map.next_value()?;
174                            Strkey::SignedPayloadEd25519(inner)
175                        }
176                        "contract" => {
177                            let Decoded(inner) = map.next_value()?;
178                            Strkey::Contract(inner)
179                        }
180                        "liquidity_pool" => {
181                            let Decoded(inner) = map.next_value()?;
182                            Strkey::LiquidityPool(inner)
183                        }
184                        "claimable_balance" => {
185                            let Decoded(inner) = map.next_value()?;
186                            Strkey::ClaimableBalance(inner)
187                        }
188                        _ => {
189                            return Err(de::Error::unknown_variant(
190                                key,
191                                &[
192                                    "public_key_ed25519",
193                                    "private_key_ed25519",
194                                    "pre_auth_tx",
195                                    "hash_x",
196                                    "muxed_account_ed25519",
197                                    "signed_payload_ed25519",
198                                    "contract",
199                                    "liquidity_pool",
200                                    "claimable_balance",
201                                ],
202                            ))
203                        }
204                    };
205
206                    Ok(Decoded(strkey))
207                }
208            }
209
210            deserializer.deserialize_map(StrkeyVisitor)
211        }
212    }
213}
214
215#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
216#[cfg_attr(
217    feature = "serde",
218    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
219)]
220pub struct PreAuthTx(pub [u8; 32]);
221
222impl Debug for PreAuthTx {
223    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
224        write!(f, "PreAuthTx(")?;
225        write!(
226            f,
227            "{}",
228            &self
229                .0
230                .iter()
231                .map(|b| format!("{b:02x}"))
232                .collect::<String>()
233        )?;
234        write!(f, ")")?;
235        Ok(())
236    }
237}
238
239impl PreAuthTx {
240    pub fn to_string(&self) -> String {
241        encode(version::PRE_AUTH_TX, &self.0)
242    }
243
244    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
245        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
246    }
247
248    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
249        let (ver, payload) = decode(s)?;
250        match ver {
251            version::PRE_AUTH_TX => Self::from_payload(&payload),
252            _ => Err(DecodeError::Invalid),
253        }
254    }
255}
256
257impl Display for PreAuthTx {
258    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
259        write!(f, "{}", self.to_string())
260    }
261}
262
263impl FromStr for PreAuthTx {
264    type Err = DecodeError;
265
266    fn from_str(s: &str) -> Result<Self, Self::Err> {
267        PreAuthTx::from_string(s)
268    }
269}
270
271#[cfg(feature = "serde-decoded")]
272mod pre_auth_tx_decoded_serde_impl {
273    use super::*;
274    use crate::decoded_json_format::Decoded;
275    use serde::{Deserialize, Deserializer, Serialize, Serializer};
276    use serde_with::serde_as;
277
278    #[serde_as]
279    #[derive(Serialize)]
280    #[serde(transparent)]
281    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
282
283    #[serde_as]
284    #[derive(Deserialize)]
285    #[serde(transparent)]
286    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
287
288    impl Serialize for Decoded<&PreAuthTx> {
289        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
290            let Self(PreAuthTx(bytes)) = self;
291            DecodedBorrowed(bytes).serialize(serializer)
292        }
293    }
294
295    impl<'de> Deserialize<'de> for Decoded<PreAuthTx> {
296        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
297            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
298            Ok(Decoded(PreAuthTx(bytes)))
299        }
300    }
301}
302
303#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
304#[cfg_attr(
305    feature = "serde",
306    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
307)]
308pub struct HashX(pub [u8; 32]);
309
310impl Debug for HashX {
311    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
312        write!(f, "HashX(")?;
313        write!(
314            f,
315            "{}",
316            &self
317                .0
318                .iter()
319                .map(|b| format!("{b:02x}"))
320                .collect::<String>()
321        )?;
322        write!(f, ")")?;
323        Ok(())
324    }
325}
326
327impl HashX {
328    pub fn to_string(&self) -> String {
329        encode(version::HASH_X, &self.0)
330    }
331
332    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
333        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
334    }
335
336    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
337        let (ver, payload) = decode(s)?;
338        match ver {
339            version::HASH_X => Self::from_payload(&payload),
340            _ => Err(DecodeError::Invalid),
341        }
342    }
343}
344
345impl Display for HashX {
346    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
347        write!(f, "{}", self.to_string())
348    }
349}
350
351impl FromStr for HashX {
352    type Err = DecodeError;
353
354    fn from_str(s: &str) -> Result<Self, Self::Err> {
355        HashX::from_string(s)
356    }
357}
358
359#[cfg(feature = "serde-decoded")]
360mod hash_x_decoded_serde_impl {
361    use super::*;
362    use crate::decoded_json_format::Decoded;
363    use serde::{Deserialize, Deserializer, Serialize, Serializer};
364    use serde_with::serde_as;
365
366    #[serde_as]
367    #[derive(Serialize)]
368    #[serde(transparent)]
369    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
370
371    #[serde_as]
372    #[derive(Deserialize)]
373    #[serde(transparent)]
374    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
375
376    impl Serialize for Decoded<&HashX> {
377        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
378            let Self(HashX(bytes)) = self;
379            DecodedBorrowed(bytes).serialize(serializer)
380        }
381    }
382
383    impl<'de> Deserialize<'de> for Decoded<HashX> {
384        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
385            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
386            Ok(Decoded(HashX(bytes)))
387        }
388    }
389}
390
391#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
392#[cfg_attr(
393    feature = "serde",
394    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
395)]
396pub struct Contract(pub [u8; 32]);
397
398impl Debug for Contract {
399    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
400        write!(f, "Contract(")?;
401        write!(
402            f,
403            "{}",
404            &self
405                .0
406                .iter()
407                .map(|b| format!("{b:02x}"))
408                .collect::<String>()
409        )?;
410        write!(f, ")")?;
411        Ok(())
412    }
413}
414
415impl Contract {
416    pub fn to_string(&self) -> String {
417        encode(version::CONTRACT, &self.0)
418    }
419
420    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
421        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
422    }
423
424    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
425        let (ver, payload) = decode(s)?;
426        match ver {
427            version::CONTRACT => Self::from_payload(&payload),
428            _ => Err(DecodeError::Invalid),
429        }
430    }
431}
432
433impl Display for Contract {
434    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
435        write!(f, "{}", self.to_string())
436    }
437}
438
439impl FromStr for Contract {
440    type Err = DecodeError;
441
442    fn from_str(s: &str) -> Result<Self, Self::Err> {
443        Contract::from_string(s)
444    }
445}
446
447#[cfg(feature = "serde-decoded")]
448mod contract_decoded_serde_impl {
449    use super::*;
450    use crate::decoded_json_format::Decoded;
451    use serde::{Deserialize, Deserializer, Serialize, Serializer};
452    use serde_with::serde_as;
453
454    #[serde_as]
455    #[derive(Serialize)]
456    #[serde(transparent)]
457    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
458
459    #[serde_as]
460    #[derive(Deserialize)]
461    #[serde(transparent)]
462    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
463
464    impl Serialize for Decoded<&Contract> {
465        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
466            let Self(Contract(bytes)) = self;
467            DecodedBorrowed(bytes).serialize(serializer)
468        }
469    }
470
471    impl<'de> Deserialize<'de> for Decoded<Contract> {
472        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
473            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
474            Ok(Decoded(Contract(bytes)))
475        }
476    }
477}
478
479#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
480#[cfg_attr(
481    feature = "serde",
482    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
483)]
484pub struct LiquidityPool(pub [u8; 32]);
485
486impl Debug for LiquidityPool {
487    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
488        write!(f, "LiquidityPool(")?;
489        write!(
490            f,
491            "{}",
492            &self
493                .0
494                .iter()
495                .map(|b| format!("{b:02x}"))
496                .collect::<String>()
497        )?;
498        write!(f, ")")?;
499        Ok(())
500    }
501}
502
503impl LiquidityPool {
504    pub fn to_string(&self) -> String {
505        encode(version::LIQUIDITY_POOL, &self.0)
506    }
507
508    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
509        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
510    }
511
512    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
513        let (ver, payload) = decode(s)?;
514        match ver {
515            version::LIQUIDITY_POOL => Self::from_payload(&payload),
516            _ => Err(DecodeError::Invalid),
517        }
518    }
519}
520
521impl Display for LiquidityPool {
522    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
523        write!(f, "{}", self.to_string())
524    }
525}
526
527impl FromStr for LiquidityPool {
528    type Err = DecodeError;
529
530    fn from_str(s: &str) -> Result<Self, Self::Err> {
531        LiquidityPool::from_string(s)
532    }
533}
534
535#[cfg(feature = "serde-decoded")]
536mod liquidity_pool_decoded_serde_impl {
537    use super::*;
538    use crate::decoded_json_format::Decoded;
539    use serde::{Deserialize, Deserializer, Serialize, Serializer};
540    use serde_with::serde_as;
541
542    #[serde_as]
543    #[derive(Serialize)]
544    #[serde(transparent)]
545    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
546
547    #[serde_as]
548    #[derive(Deserialize)]
549    #[serde(transparent)]
550    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
551
552    impl Serialize for Decoded<&LiquidityPool> {
553        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
554            let Self(LiquidityPool(bytes)) = self;
555            DecodedBorrowed(bytes).serialize(serializer)
556        }
557    }
558
559    impl<'de> Deserialize<'de> for Decoded<LiquidityPool> {
560        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
561            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
562            Ok(Decoded(LiquidityPool(bytes)))
563        }
564    }
565}
566
567#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
568#[cfg_attr(
569    feature = "serde",
570    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
571)]
572pub enum ClaimableBalance {
573    V0([u8; 32]),
574}
575
576impl Debug for ClaimableBalance {
577    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
578        write!(f, "ClaimableBalance(")?;
579        match self {
580            Self::V0(v0) => {
581                write!(
582                    f,
583                    "V0({})",
584                    &v0.iter().map(|b| format!("{b:02x}")).collect::<String>()
585                )?;
586            }
587        }
588        write!(f, ")")?;
589        Ok(())
590    }
591}
592
593impl ClaimableBalance {
594    pub fn to_string(&self) -> String {
595        match self {
596            Self::V0(v0) => {
597                // First byte is zero for v0
598                let mut payload = [0; 33];
599                payload[1..].copy_from_slice(v0);
600                encode(version::CLAIMABLE_BALANCE, &payload)
601            }
602        }
603    }
604
605    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
606        match payload {
607            // First byte is zero for v0
608            [0, rest @ ..] => Ok(Self::V0(rest.try_into().map_err(|_| DecodeError::Invalid)?)),
609            _ => Err(DecodeError::Invalid),
610        }
611    }
612
613    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
614        let (ver, payload) = decode(s)?;
615        match ver {
616            version::CLAIMABLE_BALANCE => Self::from_payload(&payload),
617            _ => Err(DecodeError::Invalid),
618        }
619    }
620}
621
622impl Display for ClaimableBalance {
623    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
624        write!(f, "{}", self.to_string())
625    }
626}
627
628impl FromStr for ClaimableBalance {
629    type Err = DecodeError;
630
631    fn from_str(s: &str) -> Result<Self, Self::Err> {
632        ClaimableBalance::from_string(s)
633    }
634}
635
636#[cfg(feature = "serde-decoded")]
637mod claimable_balance_decoded_serde_impl {
638    use super::*;
639    use crate::decoded_json_format::Decoded;
640    use serde::{Deserialize, Deserializer, Serialize, Serializer};
641    use serde_with::serde_as;
642
643    #[serde_as]
644    #[derive(Serialize)]
645    #[serde(rename_all = "snake_case")]
646    enum DecodedBorrowed<'a> {
647        V0(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]),
648    }
649
650    #[serde_as]
651    #[derive(Deserialize)]
652    #[serde(rename_all = "snake_case")]
653    enum DecodedOwned {
654        V0(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]),
655    }
656
657    impl Serialize for Decoded<&ClaimableBalance> {
658        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
659            match self.0 {
660                ClaimableBalance::V0(bytes) => DecodedBorrowed::V0(bytes).serialize(serializer),
661            }
662        }
663    }
664
665    impl<'de> Deserialize<'de> for Decoded<ClaimableBalance> {
666        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
667            let decoded = DecodedOwned::deserialize(deserializer)?;
668            Ok(Decoded(match decoded {
669                DecodedOwned::V0(bytes) => ClaimableBalance::V0(bytes),
670            }))
671        }
672    }
673}