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()).map_err(|e| {
36 SignerError::ConversionError(format!("Invalid memo text: {}", e))
37 })?;
38 Memo::Text(text)
39 }
40 MemoSpec::Id { value } => Memo::Id(value),
41 MemoSpec::Hash { value } => Memo::Hash(Hash(value)),
42 MemoSpec::Return { value } => Memo::Return(Hash(value)),
43 })
44 }
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn test_memo_none() {
53 let spec = MemoSpec::None;
54 let memo = Memo::try_from(spec).unwrap();
55 assert!(matches!(memo, Memo::None));
56 }
57
58 #[test]
59 fn test_memo_text() {
60 let spec = MemoSpec::Text {
61 value: "Hello World".to_string(),
62 };
63 let memo = Memo::try_from(spec).unwrap();
64 assert!(matches!(memo, Memo::Text(_)));
65 }
66
67 #[test]
68 fn test_memo_id() {
69 let spec = MemoSpec::Id { value: 12345 };
70 let memo = Memo::try_from(spec).unwrap();
71 assert!(matches!(memo, Memo::Id(12345)));
72 }
73
74 #[test]
75 fn test_memo_spec_serde() {
76 let spec = MemoSpec::Text {
77 value: "hello".to_string(),
78 };
79 let json = serde_json::to_string(&spec).unwrap();
80 assert!(json.contains("text"));
81 assert!(json.contains("hello"));
82
83 let deserialized: MemoSpec = serde_json::from_str(&json).unwrap();
84 assert_eq!(spec, deserialized);
85 }
86
87 #[test]
88 fn test_memo_spec_json_format() {
89 let none = MemoSpec::None;
91 let none_json = serde_json::to_value(&none).unwrap();
92 assert_eq!(none_json, serde_json::json!({"type": "none"}));
93
94 let text = MemoSpec::Text {
96 value: "hello".to_string(),
97 };
98 let text_json = serde_json::to_value(&text).unwrap();
99 assert_eq!(
100 text_json,
101 serde_json::json!({"type": "text", "value": "hello"})
102 );
103
104 let id = MemoSpec::Id { value: 12345 };
106 let id_json = serde_json::to_value(&id).unwrap();
107 assert_eq!(id_json, serde_json::json!({"type": "id", "value": 12345}));
108
109 let hash = MemoSpec::Hash { value: [0x42; 32] };
111 let hash_json = serde_json::to_value(&hash).unwrap();
112 assert_eq!(hash_json["type"], "hash");
113 assert!(hash_json["value"].is_string()); let ret = MemoSpec::Return { value: [0x42; 32] };
117 let ret_json = serde_json::to_value(&ret).unwrap();
118 assert_eq!(ret_json["type"], "return");
119 assert!(ret_json["value"].is_string()); }
121}