mpp_br/protocol/intents/
session.rs1use serde::{Deserialize, Serialize};
8
9use crate::error::{MppError, Result};
10
11#[derive(Debug, Clone, Serialize, Deserialize, Default)]
35pub struct SessionRequest {
36 pub amount: String,
38
39 #[serde(rename = "unitType", skip_serializing_if = "Option::is_none")]
41 pub unit_type: Option<String>,
42
43 pub currency: String,
45
46 #[serde(skip)]
52 pub decimals: Option<u8>,
53
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub recipient: Option<String>,
57
58 #[serde(rename = "suggestedDeposit", skip_serializing_if = "Option::is_none")]
60 pub suggested_deposit: Option<String>,
61
62 #[serde(rename = "methodDetails", skip_serializing_if = "Option::is_none")]
64 pub method_details: Option<serde_json::Value>,
65}
66
67impl SessionRequest {
68 pub fn with_base_units(mut self) -> Result<Self> {
73 if let Some(decimals) = self.decimals {
74 self.amount = super::parse_units(&self.amount, decimals)?;
75 if let Some(ref deposit) = self.suggested_deposit {
76 self.suggested_deposit = Some(super::parse_units(deposit, decimals)?);
77 }
78 self.decimals = None;
79 }
80 Ok(self)
81 }
82
83 pub fn parse_amount(&self) -> Result<u128> {
87 self.amount
88 .parse()
89 .map_err(|_| MppError::InvalidAmount(format!("Invalid amount: {}", self.amount)))
90 }
91
92 pub fn validate_max_amount(&self, max_amount: &str) -> Result<()> {
101 let amount = self.parse_amount()?;
102 let max: u128 = max_amount
103 .parse()
104 .map_err(|_| MppError::InvalidAmount(format!("Invalid max amount: {}", max_amount)))?;
105
106 if amount > max {
107 return Err(MppError::AmountExceedsMax {
108 required: amount,
109 max,
110 });
111 }
112 Ok(())
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn test_session_request_serialization() {
122 let req = SessionRequest {
123 amount: "1000".to_string(),
124 unit_type: Some("second".to_string()),
125 currency: "0x123".to_string(),
126 recipient: Some("0x456".to_string()),
127 suggested_deposit: Some("60000".to_string()),
128 method_details: Some(serde_json::json!({
129 "chainId": 42431,
130 "feePayer": true
131 })),
132 ..Default::default()
133 };
134
135 let json = serde_json::to_string(&req).unwrap();
136 assert!(json.contains("\"amount\":\"1000\""));
137 assert!(json.contains("\"unitType\":\"second\""));
138 assert!(json.contains("\"suggestedDeposit\":\"60000\""));
139 assert!(json.contains("\"methodDetails\""));
140 assert!(json.contains("\"chainId\":42431"));
141
142 let parsed: SessionRequest = serde_json::from_str(&json).unwrap();
143 assert_eq!(parsed.amount, "1000");
144 assert_eq!(parsed.unit_type, Some("second".to_string()));
145 assert_eq!(parsed.suggested_deposit.as_deref(), Some("60000"));
146 }
147
148 #[test]
149 fn test_session_request_optional_fields_omitted() {
150 let req = SessionRequest {
151 amount: "500".to_string(),
152 unit_type: Some("request".to_string()),
153 currency: "USD".to_string(),
154 ..Default::default()
155 };
156
157 let json = serde_json::to_string(&req).unwrap();
158 assert!(!json.contains("recipient"));
159 assert!(!json.contains("suggestedDeposit"));
160 assert!(!json.contains("methodDetails"));
161 }
162
163 #[test]
164 fn test_session_request_deserialization() {
165 let json = r#"{"amount":"2000","unitType":"minute","currency":"0xabc"}"#;
166 let parsed: SessionRequest = serde_json::from_str(json).unwrap();
167 assert_eq!(parsed.amount, "2000");
168 assert_eq!(parsed.unit_type, Some("minute".to_string()));
169 assert_eq!(parsed.currency, "0xabc");
170 assert!(parsed.recipient.is_none());
171 assert!(parsed.suggested_deposit.is_none());
172 assert!(parsed.method_details.is_none());
173 }
174
175 #[test]
176 fn test_parse_amount() {
177 let req = SessionRequest {
178 amount: "1000000".to_string(),
179 ..Default::default()
180 };
181 assert_eq!(req.parse_amount().unwrap(), 1_000_000u128);
182
183 let invalid = SessionRequest {
184 amount: "not-a-number".to_string(),
185 ..Default::default()
186 };
187 assert!(invalid.parse_amount().is_err());
188 }
189
190 #[test]
191 fn test_validate_max_amount() {
192 let req = SessionRequest {
193 amount: "1000".to_string(),
194 ..Default::default()
195 };
196
197 assert!(req.validate_max_amount("2000").is_ok());
198 assert!(req.validate_max_amount("1000").is_ok());
199 assert!(req.validate_max_amount("500").is_err());
200 }
201
202 #[test]
203 fn test_with_base_units() {
204 let req = SessionRequest {
205 amount: "1.5".to_string(),
206 unit_type: Some("second".to_string()),
207 currency: "0x123".to_string(),
208 decimals: Some(6),
209 suggested_deposit: Some("60".to_string()),
210 ..Default::default()
211 };
212 let converted = req.with_base_units().unwrap();
213 assert_eq!(converted.amount, "1500000");
214 assert_eq!(converted.suggested_deposit.as_deref(), Some("60000000"));
215 assert!(converted.decimals.is_none());
216 }
217
218 #[test]
219 fn test_with_base_units_no_decimals() {
220 let req = SessionRequest {
221 amount: "1000000".to_string(),
222 unit_type: Some("second".to_string()),
223 currency: "0x123".to_string(),
224 ..Default::default()
225 };
226 let converted = req.with_base_units().unwrap();
227 assert_eq!(converted.amount, "1000000");
228 }
229
230 #[test]
231 fn test_session_request_without_unit_type() {
232 let json = r#"{"amount":"2000","currency":"0xabc"}"#;
233 let parsed: SessionRequest = serde_json::from_str(json).unwrap();
234 assert_eq!(parsed.amount, "2000");
235 assert!(parsed.unit_type.is_none());
236 }
237}