openzeppelin_relayer/models/transaction/stellar/
memo.rs1use crate::models::SignerError;
4use serde::{Deserialize, Serialize};
5use soroban_rs::xdr::{Hash, Memo, StringM};
6use std::convert::TryFrom;
7use utoipa::ToSchema;
8
9#[derive(Debug, Clone, Serialize, PartialEq, Deserialize, ToSchema)]
10#[serde(tag = "type", rename_all = "snake_case")]
11pub enum MemoSpec {
12 None,
13 Text {
14 value: String,
15 }, Id {
17 value: u64,
18 },
19 Hash {
20 #[serde(with = "hex::serde")]
21 value: [u8; 32],
22 },
23 Return {
24 #[serde(with = "hex::serde")]
25 value: [u8; 32],
26 },
27}
28
29impl TryFrom<MemoSpec> for Memo {
30 type Error = SignerError;
31 fn try_from(m: MemoSpec) -> Result<Self, Self::Error> {
32 Ok(match m {
33 MemoSpec::None => Memo::None,
34 MemoSpec::Text { value } => {
35 let text = StringM::<28>::try_from(value.as_str())
36 .map_err(|e| SignerError::ConversionError(format!("Invalid memo text: {e}")))?;
37 Memo::Text(text)
38 }
39 MemoSpec::Id { value } => Memo::Id(value),
40 MemoSpec::Hash { value } => Memo::Hash(Hash(value)),
41 MemoSpec::Return { value } => Memo::Return(Hash(value)),
42 })
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn test_memo_none() {
52 let spec = MemoSpec::None;
53 let memo = Memo::try_from(spec).unwrap();
54 assert!(matches!(memo, Memo::None));
55 }
56
57 #[test]
58 fn test_memo_text() {
59 let spec = MemoSpec::Text {
60 value: "Hello World".to_string(),
61 };
62 let memo = Memo::try_from(spec).unwrap();
63 assert!(matches!(memo, Memo::Text(_)));
64 }
65
66 #[test]
67 fn test_memo_id() {
68 let spec = MemoSpec::Id { value: 12345 };
69 let memo = Memo::try_from(spec).unwrap();
70 assert!(matches!(memo, Memo::Id(12345)));
71 }
72
73 #[test]
74 fn test_memo_spec_serde() {
75 let spec = MemoSpec::Text {
76 value: "hello".to_string(),
77 };
78 let json = serde_json::to_string(&spec).unwrap();
79 assert!(json.contains("text"));
80 assert!(json.contains("hello"));
81
82 let deserialized: MemoSpec = serde_json::from_str(&json).unwrap();
83 assert_eq!(spec, deserialized);
84 }
85
86 #[test]
87 fn test_memo_spec_json_format() {
88 let none = MemoSpec::None;
90 let none_json = serde_json::to_value(&none).unwrap();
91 assert_eq!(none_json, serde_json::json!({"type": "none"}));
92
93 let text = MemoSpec::Text {
95 value: "hello".to_string(),
96 };
97 let text_json = serde_json::to_value(&text).unwrap();
98 assert_eq!(
99 text_json,
100 serde_json::json!({"type": "text", "value": "hello"})
101 );
102
103 let id = MemoSpec::Id { value: 12345 };
105 let id_json = serde_json::to_value(&id).unwrap();
106 assert_eq!(id_json, serde_json::json!({"type": "id", "value": 12345}));
107
108 let hash = MemoSpec::Hash { value: [0x42; 32] };
110 let hash_json = serde_json::to_value(&hash).unwrap();
111 assert_eq!(hash_json["type"], "hash");
112 assert!(hash_json["value"].is_string()); let ret = MemoSpec::Return { value: [0x42; 32] };
116 let ret_json = serde_json::to_value(&ret).unwrap();
117 assert_eq!(ret_json["type"], "return");
118 assert!(ret_json["value"].is_string()); }
120}