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 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 [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}