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