stellar_strkey/
strkey.rs

1use core::{
2    fmt::{Debug, Display},
3    str::FromStr,
4};
5
6use heapless::String as HeaplessString;
7
8use crate::{
9    convert::{binary_len, decode, encode, encode_len},
10    ed25519,
11    error::DecodeError,
12    version,
13};
14
15#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)]
16#[cfg_attr(
17    feature = "serde",
18    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
19)]
20pub enum Strkey {
21    PublicKeyEd25519(ed25519::PublicKey),
22    PrivateKeyEd25519(ed25519::PrivateKey),
23    PreAuthTx(PreAuthTx),
24    HashX(HashX),
25    MuxedAccountEd25519(ed25519::MuxedAccount),
26    SignedPayloadEd25519(ed25519::SignedPayload),
27    Contract(Contract),
28    LiquidityPool(LiquidityPool),
29    ClaimableBalance(ClaimableBalance),
30}
31
32impl Strkey {
33    // SignedPayload is the longest strkey type.
34    const MAX_PAYLOAD_LEN: usize = ed25519::SignedPayload::MAX_PAYLOAD_LEN;
35    const MAX_BINARY_LEN: usize = binary_len(Self::MAX_PAYLOAD_LEN);
36    const MAX_ENCODED_LEN: usize = encode_len(Self::MAX_BINARY_LEN);
37    const _ASSERTS: () = {
38        assert!(Self::MAX_PAYLOAD_LEN == 100);
39        assert!(Self::MAX_BINARY_LEN == 103);
40        assert!(Self::MAX_ENCODED_LEN == 165);
41        // Verify MAX_PAYLOAD_LEN >= all type payload lengths.
42        assert!(Self::MAX_PAYLOAD_LEN >= ed25519::PrivateKey::PAYLOAD_LEN);
43        assert!(Self::MAX_PAYLOAD_LEN >= ed25519::PublicKey::PAYLOAD_LEN);
44        assert!(Self::MAX_PAYLOAD_LEN >= ed25519::MuxedAccount::PAYLOAD_LEN);
45        assert!(Self::MAX_PAYLOAD_LEN >= ed25519::SignedPayload::MAX_PAYLOAD_LEN);
46        assert!(Self::MAX_PAYLOAD_LEN >= PreAuthTx::PAYLOAD_LEN);
47        assert!(Self::MAX_PAYLOAD_LEN >= HashX::PAYLOAD_LEN);
48        assert!(Self::MAX_PAYLOAD_LEN >= Contract::PAYLOAD_LEN);
49        assert!(Self::MAX_PAYLOAD_LEN >= LiquidityPool::PAYLOAD_LEN);
50        assert!(Self::MAX_PAYLOAD_LEN >= ClaimableBalance::PAYLOAD_LEN);
51        // Verify MAX_BINARY_LEN >= all type binary lengths.
52        assert!(Self::MAX_BINARY_LEN >= ed25519::PrivateKey::BINARY_LEN);
53        assert!(Self::MAX_BINARY_LEN >= ed25519::PublicKey::BINARY_LEN);
54        assert!(Self::MAX_BINARY_LEN >= ed25519::MuxedAccount::BINARY_LEN);
55        assert!(Self::MAX_BINARY_LEN >= ed25519::SignedPayload::MAX_BINARY_LEN);
56        assert!(Self::MAX_BINARY_LEN >= PreAuthTx::BINARY_LEN);
57        assert!(Self::MAX_BINARY_LEN >= HashX::BINARY_LEN);
58        assert!(Self::MAX_BINARY_LEN >= Contract::BINARY_LEN);
59        assert!(Self::MAX_BINARY_LEN >= LiquidityPool::BINARY_LEN);
60        assert!(Self::MAX_BINARY_LEN >= ClaimableBalance::BINARY_LEN);
61        // Verify MAX_ENCODED_LEN >= all type encoded lengths.
62        assert!(Self::MAX_ENCODED_LEN >= ed25519::PrivateKey::ENCODED_LEN);
63        assert!(Self::MAX_ENCODED_LEN >= ed25519::PublicKey::ENCODED_LEN);
64        assert!(Self::MAX_ENCODED_LEN >= ed25519::MuxedAccount::ENCODED_LEN);
65        assert!(Self::MAX_ENCODED_LEN >= ed25519::SignedPayload::MAX_ENCODED_LEN);
66        assert!(Self::MAX_ENCODED_LEN >= PreAuthTx::ENCODED_LEN);
67        assert!(Self::MAX_ENCODED_LEN >= HashX::ENCODED_LEN);
68        assert!(Self::MAX_ENCODED_LEN >= Contract::ENCODED_LEN);
69        assert!(Self::MAX_ENCODED_LEN >= LiquidityPool::ENCODED_LEN);
70        assert!(Self::MAX_ENCODED_LEN >= ClaimableBalance::ENCODED_LEN);
71    };
72
73    pub fn to_string(&self) -> HeaplessString<{ Self::MAX_ENCODED_LEN }> {
74        let mut s: HeaplessString<{ Self::MAX_ENCODED_LEN }> = HeaplessString::new();
75        match self {
76            Self::PublicKeyEd25519(x) => s.push_str(x.to_string().as_str()).unwrap(),
77            Self::PrivateKeyEd25519(x) => s.push_str(x.to_string().as_str()).unwrap(),
78            Self::PreAuthTx(x) => s.push_str(x.to_string().as_str()).unwrap(),
79            Self::HashX(x) => s.push_str(x.to_string().as_str()).unwrap(),
80            Self::MuxedAccountEd25519(x) => s.push_str(x.to_string().as_str()).unwrap(),
81            Self::SignedPayloadEd25519(x) => s.push_str(x.to_string().as_str()).unwrap(),
82            Self::Contract(x) => s.push_str(x.to_string().as_str()).unwrap(),
83            Self::LiquidityPool(x) => s.push_str(x.to_string().as_str()).unwrap(),
84            Self::ClaimableBalance(x) => s.push_str(x.to_string().as_str()).unwrap(),
85        }
86        s
87    }
88
89    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
90        Self::from_slice(s.as_bytes())
91    }
92
93    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
94        let (ver, payload) = decode::<{ Self::MAX_PAYLOAD_LEN }, { Self::MAX_BINARY_LEN }>(s)?;
95        match ver {
96            version::PUBLIC_KEY_ED25519 => Ok(Self::PublicKeyEd25519(
97                ed25519::PublicKey::from_payload(&payload)?,
98            )),
99            version::PRIVATE_KEY_ED25519 => Ok(Self::PrivateKeyEd25519(
100                ed25519::PrivateKey::from_payload(&payload)?,
101            )),
102            version::PRE_AUTH_TX => Ok(Self::PreAuthTx(PreAuthTx::from_payload(&payload)?)),
103            version::HASH_X => Ok(Self::HashX(HashX::from_payload(&payload)?)),
104            version::MUXED_ACCOUNT_ED25519 => Ok(Self::MuxedAccountEd25519(
105                ed25519::MuxedAccount::from_payload(&payload)?,
106            )),
107            version::SIGNED_PAYLOAD_ED25519 => Ok(Self::SignedPayloadEd25519(
108                ed25519::SignedPayload::from_payload(&payload)?,
109            )),
110            version::CONTRACT => Ok(Self::Contract(Contract::from_payload(&payload)?)),
111            version::LIQUIDITY_POOL => {
112                Ok(Self::LiquidityPool(LiquidityPool::from_payload(&payload)?))
113            }
114            version::CLAIMABLE_BALANCE => Ok(Self::ClaimableBalance(
115                ClaimableBalance::from_payload(&payload)?,
116            )),
117            _ => Err(DecodeError::Invalid),
118        }
119    }
120}
121
122impl Display for Strkey {
123    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124        write!(f, "{}", self.to_string())
125    }
126}
127
128impl FromStr for Strkey {
129    type Err = DecodeError;
130
131    fn from_str(s: &str) -> Result<Self, Self::Err> {
132        Strkey::from_string(s)
133    }
134}
135
136#[cfg(feature = "serde-decoded")]
137mod strkey_decoded_serde_impl {
138    use super::*;
139    use crate::decoded_json_format::Decoded;
140    use serde::{
141        de::{self, MapAccess, Visitor},
142        ser::SerializeMap,
143        Deserialize, Deserializer, Serialize, Serializer,
144    };
145
146    impl Serialize for Decoded<&Strkey> {
147        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
148            let mut map = serializer.serialize_map(Some(1))?;
149            match self.0 {
150                Strkey::PublicKeyEd25519(key) => {
151                    map.serialize_entry("public_key_ed25519", &Decoded(key))?;
152                }
153                Strkey::PrivateKeyEd25519(key) => {
154                    map.serialize_entry("private_key_ed25519", &Decoded(key))?;
155                }
156                Strkey::PreAuthTx(key) => {
157                    map.serialize_entry("pre_auth_tx", &Decoded(key))?;
158                }
159                Strkey::HashX(key) => {
160                    map.serialize_entry("hash_x", &Decoded(key))?;
161                }
162                Strkey::MuxedAccountEd25519(key) => {
163                    map.serialize_entry("muxed_account_ed25519", &Decoded(key))?;
164                }
165                Strkey::SignedPayloadEd25519(key) => {
166                    map.serialize_entry("signed_payload_ed25519", &Decoded(key))?;
167                }
168                Strkey::Contract(key) => {
169                    map.serialize_entry("contract", &Decoded(key))?;
170                }
171                Strkey::LiquidityPool(key) => {
172                    map.serialize_entry("liquidity_pool", &Decoded(key))?;
173                }
174                Strkey::ClaimableBalance(key) => {
175                    map.serialize_entry("claimable_balance", &Decoded(key))?;
176                }
177            }
178            map.end()
179        }
180    }
181
182    impl<'de> Deserialize<'de> for Decoded<Strkey> {
183        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
184            struct StrkeyVisitor;
185
186            impl<'de> Visitor<'de> for StrkeyVisitor {
187                type Value = Decoded<Strkey>;
188
189                fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
190                    formatter.write_str("a strkey object")
191                }
192
193                fn visit_map<M: MapAccess<'de>>(self, mut map: M) -> Result<Self::Value, M::Error> {
194                    let key: &str = map
195                        .next_key()?
196                        .ok_or_else(|| de::Error::custom("expected a variant key"))?;
197
198                    let strkey = match key {
199                        "public_key_ed25519" => {
200                            let Decoded(inner) = map.next_value()?;
201                            Strkey::PublicKeyEd25519(inner)
202                        }
203                        "private_key_ed25519" => {
204                            let Decoded(inner) = map.next_value()?;
205                            Strkey::PrivateKeyEd25519(inner)
206                        }
207                        "pre_auth_tx" => {
208                            let Decoded(inner) = map.next_value()?;
209                            Strkey::PreAuthTx(inner)
210                        }
211                        "hash_x" => {
212                            let Decoded(inner) = map.next_value()?;
213                            Strkey::HashX(inner)
214                        }
215                        "muxed_account_ed25519" => {
216                            let Decoded(inner) = map.next_value()?;
217                            Strkey::MuxedAccountEd25519(inner)
218                        }
219                        "signed_payload_ed25519" => {
220                            let Decoded(inner) = map.next_value()?;
221                            Strkey::SignedPayloadEd25519(inner)
222                        }
223                        "contract" => {
224                            let Decoded(inner) = map.next_value()?;
225                            Strkey::Contract(inner)
226                        }
227                        "liquidity_pool" => {
228                            let Decoded(inner) = map.next_value()?;
229                            Strkey::LiquidityPool(inner)
230                        }
231                        "claimable_balance" => {
232                            let Decoded(inner) = map.next_value()?;
233                            Strkey::ClaimableBalance(inner)
234                        }
235                        _ => {
236                            return Err(de::Error::unknown_variant(
237                                key,
238                                &[
239                                    "public_key_ed25519",
240                                    "private_key_ed25519",
241                                    "pre_auth_tx",
242                                    "hash_x",
243                                    "muxed_account_ed25519",
244                                    "signed_payload_ed25519",
245                                    "contract",
246                                    "liquidity_pool",
247                                    "claimable_balance",
248                                ],
249                            ))
250                        }
251                    };
252
253                    Ok(Decoded(strkey))
254                }
255            }
256
257            deserializer.deserialize_map(StrkeyVisitor)
258        }
259    }
260}
261
262#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
263#[cfg_attr(
264    feature = "serde",
265    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
266)]
267pub struct PreAuthTx(pub [u8; 32]);
268
269impl Debug for PreAuthTx {
270    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
271        write!(f, "PreAuthTx(")?;
272        for b in &self.0 {
273            write!(f, "{b:02x}")?;
274        }
275        write!(f, ")")
276    }
277}
278
279impl PreAuthTx {
280    pub(crate) const PAYLOAD_LEN: usize = 32;
281    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
282    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
283    const _ASSERTS: () = {
284        assert!(Self::BINARY_LEN == 35);
285        assert!(Self::ENCODED_LEN == 56);
286    };
287
288    pub fn to_string(&self) -> HeaplessString<{ Self::ENCODED_LEN }> {
289        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
290            version::PRE_AUTH_TX,
291            &self.0,
292        )
293    }
294
295    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
296        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
297    }
298
299    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
300        Self::from_slice(s.as_bytes())
301    }
302
303    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
304        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
305        match ver {
306            version::PRE_AUTH_TX => Self::from_payload(&payload),
307            _ => Err(DecodeError::Invalid),
308        }
309    }
310}
311
312impl Display for PreAuthTx {
313    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
314        write!(f, "{}", self.to_string())
315    }
316}
317
318impl FromStr for PreAuthTx {
319    type Err = DecodeError;
320
321    fn from_str(s: &str) -> Result<Self, Self::Err> {
322        PreAuthTx::from_string(s)
323    }
324}
325
326#[cfg(feature = "serde-decoded")]
327mod pre_auth_tx_decoded_serde_impl {
328    use super::*;
329    use crate::decoded_json_format::Decoded;
330    use serde::{Deserialize, Deserializer, Serialize, Serializer};
331    use serde_with::serde_as;
332
333    #[serde_as]
334    #[derive(Serialize)]
335    #[serde(transparent)]
336    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
337
338    #[serde_as]
339    #[derive(Deserialize)]
340    #[serde(transparent)]
341    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
342
343    impl Serialize for Decoded<&PreAuthTx> {
344        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
345            let Self(PreAuthTx(bytes)) = self;
346            DecodedBorrowed(bytes).serialize(serializer)
347        }
348    }
349
350    impl<'de> Deserialize<'de> for Decoded<PreAuthTx> {
351        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
352            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
353            Ok(Decoded(PreAuthTx(bytes)))
354        }
355    }
356}
357
358#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
359#[cfg_attr(
360    feature = "serde",
361    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
362)]
363pub struct HashX(pub [u8; 32]);
364
365impl Debug for HashX {
366    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
367        write!(f, "HashX(")?;
368        for b in &self.0 {
369            write!(f, "{b:02x}")?;
370        }
371        write!(f, ")")
372    }
373}
374
375impl HashX {
376    pub(crate) const PAYLOAD_LEN: usize = 32;
377    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
378    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
379    const _ASSERTS: () = {
380        assert!(Self::BINARY_LEN == 35);
381        assert!(Self::ENCODED_LEN == 56);
382    };
383
384    pub fn to_string(&self) -> HeaplessString<{ Self::ENCODED_LEN }> {
385        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
386            version::HASH_X,
387            &self.0,
388        )
389    }
390
391    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
392        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
393    }
394
395    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
396        Self::from_slice(s.as_bytes())
397    }
398
399    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
400        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
401        match ver {
402            version::HASH_X => Self::from_payload(&payload),
403            _ => Err(DecodeError::Invalid),
404        }
405    }
406}
407
408impl Display for HashX {
409    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
410        write!(f, "{}", self.to_string())
411    }
412}
413
414impl FromStr for HashX {
415    type Err = DecodeError;
416
417    fn from_str(s: &str) -> Result<Self, Self::Err> {
418        HashX::from_string(s)
419    }
420}
421
422#[cfg(feature = "serde-decoded")]
423mod hash_x_decoded_serde_impl {
424    use super::*;
425    use crate::decoded_json_format::Decoded;
426    use serde::{Deserialize, Deserializer, Serialize, Serializer};
427    use serde_with::serde_as;
428
429    #[serde_as]
430    #[derive(Serialize)]
431    #[serde(transparent)]
432    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
433
434    #[serde_as]
435    #[derive(Deserialize)]
436    #[serde(transparent)]
437    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
438
439    impl Serialize for Decoded<&HashX> {
440        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
441            let Self(HashX(bytes)) = self;
442            DecodedBorrowed(bytes).serialize(serializer)
443        }
444    }
445
446    impl<'de> Deserialize<'de> for Decoded<HashX> {
447        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
448            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
449            Ok(Decoded(HashX(bytes)))
450        }
451    }
452}
453
454#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
455#[cfg_attr(
456    feature = "serde",
457    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
458)]
459pub struct Contract(pub [u8; 32]);
460
461impl Debug for Contract {
462    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
463        write!(f, "Contract(")?;
464        for b in &self.0 {
465            write!(f, "{b:02x}")?;
466        }
467        write!(f, ")")
468    }
469}
470
471impl Contract {
472    pub(crate) const PAYLOAD_LEN: usize = 32;
473    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
474    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
475    const _ASSERTS: () = {
476        assert!(Self::BINARY_LEN == 35);
477        assert!(Self::ENCODED_LEN == 56);
478    };
479
480    pub fn to_string(&self) -> HeaplessString<{ Self::ENCODED_LEN }> {
481        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
482            version::CONTRACT,
483            &self.0,
484        )
485    }
486
487    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
488        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
489    }
490
491    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
492        Self::from_slice(s.as_bytes())
493    }
494
495    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
496        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
497        match ver {
498            version::CONTRACT => Self::from_payload(&payload),
499            _ => Err(DecodeError::Invalid),
500        }
501    }
502}
503
504impl Display for Contract {
505    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
506        write!(f, "{}", self.to_string())
507    }
508}
509
510impl FromStr for Contract {
511    type Err = DecodeError;
512
513    fn from_str(s: &str) -> Result<Self, Self::Err> {
514        Contract::from_string(s)
515    }
516}
517
518#[cfg(feature = "serde-decoded")]
519mod contract_decoded_serde_impl {
520    use super::*;
521    use crate::decoded_json_format::Decoded;
522    use serde::{Deserialize, Deserializer, Serialize, Serializer};
523    use serde_with::serde_as;
524
525    #[serde_as]
526    #[derive(Serialize)]
527    #[serde(transparent)]
528    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
529
530    #[serde_as]
531    #[derive(Deserialize)]
532    #[serde(transparent)]
533    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
534
535    impl Serialize for Decoded<&Contract> {
536        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
537            let Self(Contract(bytes)) = self;
538            DecodedBorrowed(bytes).serialize(serializer)
539        }
540    }
541
542    impl<'de> Deserialize<'de> for Decoded<Contract> {
543        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
544            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
545            Ok(Decoded(Contract(bytes)))
546        }
547    }
548}
549
550#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
551#[cfg_attr(
552    feature = "serde",
553    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
554)]
555pub struct LiquidityPool(pub [u8; 32]);
556
557impl Debug for LiquidityPool {
558    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
559        write!(f, "LiquidityPool(")?;
560        for b in &self.0 {
561            write!(f, "{b:02x}")?;
562        }
563        write!(f, ")")
564    }
565}
566
567impl LiquidityPool {
568    pub(crate) const PAYLOAD_LEN: usize = 32;
569    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
570    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
571    const _ASSERTS: () = {
572        assert!(Self::BINARY_LEN == 35);
573        assert!(Self::ENCODED_LEN == 56);
574    };
575
576    pub fn to_string(&self) -> HeaplessString<{ Self::ENCODED_LEN }> {
577        encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
578            version::LIQUIDITY_POOL,
579            &self.0,
580        )
581    }
582
583    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
584        Ok(Self(payload.try_into().map_err(|_| DecodeError::Invalid)?))
585    }
586
587    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
588        Self::from_slice(s.as_bytes())
589    }
590
591    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
592        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
593        match ver {
594            version::LIQUIDITY_POOL => Self::from_payload(&payload),
595            _ => Err(DecodeError::Invalid),
596        }
597    }
598}
599
600impl Display for LiquidityPool {
601    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
602        write!(f, "{}", self.to_string())
603    }
604}
605
606impl FromStr for LiquidityPool {
607    type Err = DecodeError;
608
609    fn from_str(s: &str) -> Result<Self, Self::Err> {
610        LiquidityPool::from_string(s)
611    }
612}
613
614#[cfg(feature = "serde-decoded")]
615mod liquidity_pool_decoded_serde_impl {
616    use super::*;
617    use crate::decoded_json_format::Decoded;
618    use serde::{Deserialize, Deserializer, Serialize, Serializer};
619    use serde_with::serde_as;
620
621    #[serde_as]
622    #[derive(Serialize)]
623    #[serde(transparent)]
624    struct DecodedBorrowed<'a>(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]);
625
626    #[serde_as]
627    #[derive(Deserialize)]
628    #[serde(transparent)]
629    struct DecodedOwned(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]);
630
631    impl Serialize for Decoded<&LiquidityPool> {
632        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
633            let Self(LiquidityPool(bytes)) = self;
634            DecodedBorrowed(bytes).serialize(serializer)
635        }
636    }
637
638    impl<'de> Deserialize<'de> for Decoded<LiquidityPool> {
639        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
640            let DecodedOwned(bytes) = DecodedOwned::deserialize(deserializer)?;
641            Ok(Decoded(LiquidityPool(bytes)))
642        }
643    }
644}
645
646#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
647#[cfg_attr(
648    feature = "serde",
649    derive(serde_with::SerializeDisplay, serde_with::DeserializeFromStr)
650)]
651pub enum ClaimableBalance {
652    V0([u8; 32]),
653}
654
655impl Debug for ClaimableBalance {
656    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
657        write!(f, "ClaimableBalance(")?;
658        match self {
659            Self::V0(v0) => {
660                write!(f, "V0(")?;
661                for b in v0 {
662                    write!(f, "{b:02x}")?;
663                }
664                write!(f, ")")?;
665            }
666        }
667        write!(f, ")")
668    }
669}
670
671impl ClaimableBalance {
672    // Payload: 1 version byte + 32 hash bytes = 33
673    pub(crate) const PAYLOAD_LEN: usize = 1 + 32;
674    pub(crate) const BINARY_LEN: usize = binary_len(Self::PAYLOAD_LEN);
675    pub(crate) const ENCODED_LEN: usize = encode_len(Self::BINARY_LEN);
676    const _ASSERTS: () = {
677        assert!(Self::PAYLOAD_LEN == 33);
678        assert!(Self::BINARY_LEN == 36);
679        assert!(Self::ENCODED_LEN == 58);
680    };
681
682    pub fn to_string(&self) -> HeaplessString<{ Self::ENCODED_LEN }> {
683        match self {
684            Self::V0(v0) => {
685                // First byte is zero for v0
686                let mut payload = [0; Self::PAYLOAD_LEN];
687                payload[1..].copy_from_slice(v0);
688                encode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }, { Self::ENCODED_LEN }>(
689                    version::CLAIMABLE_BALANCE,
690                    &payload,
691                )
692            }
693        }
694    }
695
696    fn from_payload(payload: &[u8]) -> Result<Self, DecodeError> {
697        match payload {
698            // First byte is zero for v0
699            [0, rest @ ..] => Ok(Self::V0(rest.try_into().map_err(|_| DecodeError::Invalid)?)),
700            _ => Err(DecodeError::Invalid),
701        }
702    }
703
704    pub fn from_string(s: &str) -> Result<Self, DecodeError> {
705        Self::from_slice(s.as_bytes())
706    }
707
708    pub fn from_slice(s: &[u8]) -> Result<Self, DecodeError> {
709        let (ver, payload) = decode::<{ Self::PAYLOAD_LEN }, { Self::BINARY_LEN }>(s)?;
710        match ver {
711            version::CLAIMABLE_BALANCE => Self::from_payload(&payload),
712            _ => Err(DecodeError::Invalid),
713        }
714    }
715}
716
717impl Display for ClaimableBalance {
718    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
719        write!(f, "{}", self.to_string())
720    }
721}
722
723impl FromStr for ClaimableBalance {
724    type Err = DecodeError;
725
726    fn from_str(s: &str) -> Result<Self, Self::Err> {
727        ClaimableBalance::from_string(s)
728    }
729}
730
731#[cfg(feature = "serde-decoded")]
732mod claimable_balance_decoded_serde_impl {
733    use super::*;
734    use crate::decoded_json_format::Decoded;
735    use serde::{Deserialize, Deserializer, Serialize, Serializer};
736    use serde_with::serde_as;
737
738    #[serde_as]
739    #[derive(Serialize)]
740    #[serde(rename_all = "snake_case")]
741    enum DecodedBorrowed<'a> {
742        V0(#[serde_as(as = "serde_with::hex::Hex")] &'a [u8; 32]),
743    }
744
745    #[serde_as]
746    #[derive(Deserialize)]
747    #[serde(rename_all = "snake_case")]
748    enum DecodedOwned {
749        V0(#[serde_as(as = "serde_with::hex::Hex")] [u8; 32]),
750    }
751
752    impl Serialize for Decoded<&ClaimableBalance> {
753        fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
754            match self.0 {
755                ClaimableBalance::V0(bytes) => DecodedBorrowed::V0(bytes).serialize(serializer),
756            }
757        }
758    }
759
760    impl<'de> Deserialize<'de> for Decoded<ClaimableBalance> {
761        fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
762            let decoded = DecodedOwned::deserialize(deserializer)?;
763            Ok(Decoded(match decoded {
764                DecodedOwned::V0(bytes) => ClaimableBalance::V0(bytes),
765            }))
766        }
767    }
768}