1use alloc::borrow::Cow;
2use alloc::vec::Vec;
3use serde::{Deserialize, Serialize};
4use serde_with::skip_serializing_none;
5
6use crate::models::amount::XRPAmount;
7use crate::models::{Amount, FlagCollection, Model, NoFlags, ValidateCurrencies, XRPLModelResult};
8
9use super::vault_common::{validate_positive_amount, validate_vault_id};
10use super::{CommonFields, CommonTransactionBuilder, Memo, Signer, Transaction, TransactionType};
11
12#[skip_serializing_none]
20#[derive(
21 Debug,
22 Default,
23 Serialize,
24 Deserialize,
25 PartialEq,
26 Eq,
27 Clone,
28 xrpl_rust_macros::ValidateCurrencies,
29)]
30#[serde(rename_all = "PascalCase")]
31pub struct VaultDeposit<'a> {
32 #[serde(flatten)]
37 pub common_fields: CommonFields<'a, NoFlags>,
38 #[serde(rename = "VaultID")]
40 pub vault_id: Cow<'a, str>,
41 pub amount: Amount<'a>,
43}
44
45impl Model for VaultDeposit<'_> {
46 fn get_errors(&self) -> XRPLModelResult<()> {
47 self.validate_currencies()?;
48 validate_vault_id(&self.vault_id)?;
49 validate_positive_amount("amount", &self.amount)
50 }
51}
52
53impl<'a> Transaction<'a, NoFlags> for VaultDeposit<'a> {
54 fn get_common_fields(&self) -> &CommonFields<'_, NoFlags> {
55 &self.common_fields
56 }
57
58 fn get_mut_common_fields(&mut self) -> &mut CommonFields<'a, NoFlags> {
59 &mut self.common_fields
60 }
61
62 fn get_transaction_type(&self) -> &TransactionType {
63 self.common_fields.get_transaction_type()
64 }
65}
66
67impl<'a> CommonTransactionBuilder<'a, NoFlags> for VaultDeposit<'a> {
68 fn get_mut_common_fields(&mut self) -> &mut CommonFields<'a, NoFlags> {
69 &mut self.common_fields
70 }
71
72 fn into_self(self) -> Self {
73 self
74 }
75}
76
77impl<'a> VaultDeposit<'a> {
78 pub fn new(
79 account: Cow<'a, str>,
80 account_txn_id: Option<Cow<'a, str>>,
81 fee: Option<XRPAmount<'a>>,
82 last_ledger_sequence: Option<u32>,
83 memos: Option<Vec<Memo>>,
84 sequence: Option<u32>,
85 signers: Option<Vec<Signer>>,
86 source_tag: Option<u32>,
87 ticket_sequence: Option<u32>,
88 vault_id: Cow<'a, str>,
89 amount: Amount<'a>,
90 ) -> VaultDeposit<'a> {
91 VaultDeposit {
92 common_fields: CommonFields::new(
93 account,
94 TransactionType::VaultDeposit,
95 account_txn_id,
96 fee,
97 Some(FlagCollection::default()),
98 last_ledger_sequence,
99 memos,
100 None,
101 sequence,
102 signers,
103 None,
104 source_tag,
105 ticket_sequence,
106 None,
107 ),
108 vault_id,
109 amount,
110 }
111 }
112}
113
114#[cfg(test)]
115mod tests {
116 use super::*;
117 use crate::models::{IssuedCurrencyAmount, XRPAmount};
118
119 const VAULT_ID: &str = "A0000000000000000000000000000000000000000000000000000000DEADBEEF";
120
121 #[test]
122 fn test_serde() {
123 let vault_deposit = VaultDeposit {
124 common_fields: CommonFields {
125 account: "rDepositor123".into(),
126 transaction_type: TransactionType::VaultDeposit,
127 signing_pub_key: Some("".into()),
128 ..Default::default()
129 },
130 vault_id: VAULT_ID.into(),
131 amount: Amount::XRPAmount(XRPAmount::from("1000000")),
132 };
133
134 let json_str = r#"{"Account":"rDepositor123","TransactionType":"VaultDeposit","Flags":0,"SigningPubKey":"","VaultID":"A0000000000000000000000000000000000000000000000000000000DEADBEEF","Amount":"1000000"}"#;
135
136 let serialized = serde_json::to_string(&vault_deposit).unwrap();
138 assert_eq!(
139 serde_json::to_value(&serialized).unwrap(),
140 serde_json::to_value(json_str).unwrap()
141 );
142
143 let deserialized: VaultDeposit = serde_json::from_str(json_str).unwrap();
145 assert_eq!(vault_deposit, deserialized);
146 }
147
148 #[test]
149 fn test_serde_issued_currency() {
150 let vault_deposit = VaultDeposit {
151 common_fields: CommonFields {
152 account: "rDepositorICA456".into(),
153 transaction_type: TransactionType::VaultDeposit,
154 signing_pub_key: Some("".into()),
155 ..Default::default()
156 },
157 vault_id: VAULT_ID.into(),
158 amount: Amount::IssuedCurrencyAmount(IssuedCurrencyAmount::new(
159 "USD".into(),
160 "rIssuer789".into(),
161 "1000".into(),
162 )),
163 };
164
165 let serialized = serde_json::to_string(&vault_deposit).unwrap();
166 let deserialized: VaultDeposit = serde_json::from_str(&serialized).unwrap();
167 assert_eq!(vault_deposit, deserialized);
168 }
169
170 #[test]
171 fn test_builder_pattern() {
172 let vault_deposit = VaultDeposit {
173 common_fields: CommonFields {
174 account: "rDepositor123".into(),
175 transaction_type: TransactionType::VaultDeposit,
176 ..Default::default()
177 },
178 vault_id: VAULT_ID.into(),
179 amount: Amount::XRPAmount(XRPAmount::from("1000000")),
180 }
181 .with_fee("12".into())
182 .with_sequence(100)
183 .with_last_ledger_sequence(7108682)
184 .with_source_tag(12345)
185 .with_memo(Memo {
186 memo_data: Some("depositing into vault".into()),
187 memo_format: None,
188 memo_type: Some("text".into()),
189 });
190
191 assert_eq!(vault_deposit.vault_id, VAULT_ID);
192 assert_eq!(vault_deposit.common_fields.fee.as_ref().unwrap().0, "12");
193 assert_eq!(vault_deposit.common_fields.sequence, Some(100));
194 assert_eq!(
195 vault_deposit.common_fields.last_ledger_sequence,
196 Some(7108682)
197 );
198 assert_eq!(vault_deposit.common_fields.source_tag, Some(12345));
199 assert_eq!(vault_deposit.common_fields.memos.as_ref().unwrap().len(), 1);
200 }
201
202 #[test]
203 fn test_default() {
204 let vault_deposit = VaultDeposit {
205 common_fields: CommonFields {
206 account: "rDepositor789".into(),
207 transaction_type: TransactionType::VaultDeposit,
208 ..Default::default()
209 },
210 vault_id: VAULT_ID.into(),
211 amount: Amount::XRPAmount(XRPAmount::from("5000000")),
212 };
213
214 assert_eq!(vault_deposit.common_fields.account, "rDepositor789");
215 assert_eq!(
216 vault_deposit.common_fields.transaction_type,
217 TransactionType::VaultDeposit
218 );
219 assert_eq!(vault_deposit.vault_id, VAULT_ID);
220 assert!(vault_deposit.common_fields.fee.is_none());
221 assert!(vault_deposit.common_fields.sequence.is_none());
222 }
223
224 #[test]
225 fn test_ticket_sequence() {
226 let ticket_deposit = VaultDeposit {
227 common_fields: CommonFields {
228 account: "rTicketDepositor111".into(),
229 transaction_type: TransactionType::VaultDeposit,
230 ..Default::default()
231 },
232 vault_id: VAULT_ID.into(),
233 amount: Amount::XRPAmount(XRPAmount::from("2000000")),
234 }
235 .with_ticket_sequence(54321)
236 .with_fee("12".into());
237
238 assert_eq!(ticket_deposit.common_fields.ticket_sequence, Some(54321));
239 assert!(ticket_deposit.common_fields.sequence.is_none());
240 }
241
242 #[test]
243 fn test_multiple_memos() {
244 let multi_memo_deposit = VaultDeposit {
245 common_fields: CommonFields {
246 account: "rMultiMemoDepositor222".into(),
247 transaction_type: TransactionType::VaultDeposit,
248 ..Default::default()
249 },
250 vault_id: VAULT_ID.into(),
251 amount: Amount::IssuedCurrencyAmount(IssuedCurrencyAmount::new(
252 "USD".into(),
253 "rUSDIssuer333".into(),
254 "500".into(),
255 )),
256 }
257 .with_memo(Memo {
258 memo_data: Some("first deposit".into()),
259 memo_format: None,
260 memo_type: Some("text".into()),
261 })
262 .with_memo(Memo {
263 memo_data: Some("additional note".into()),
264 memo_format: None,
265 memo_type: Some("text".into()),
266 })
267 .with_fee("18".into())
268 .with_sequence(400);
269
270 assert_eq!(
271 multi_memo_deposit
272 .common_fields
273 .memos
274 .as_ref()
275 .unwrap()
276 .len(),
277 2
278 );
279 assert_eq!(multi_memo_deposit.common_fields.sequence, Some(400));
280 }
281
282 #[test]
283 fn test_new_constructor() {
284 let vault_deposit = VaultDeposit::new(
285 "rNewDepositor444".into(),
286 None,
287 Some("12".into()),
288 Some(7108682),
289 None,
290 Some(100),
291 None,
292 None,
293 None,
294 VAULT_ID.into(),
295 Amount::XRPAmount(XRPAmount::from("10000000")),
296 );
297
298 assert_eq!(vault_deposit.common_fields.account, "rNewDepositor444");
299 assert_eq!(
300 vault_deposit.common_fields.transaction_type,
301 TransactionType::VaultDeposit
302 );
303 assert_eq!(vault_deposit.common_fields.fee.as_ref().unwrap().0, "12");
304 assert_eq!(vault_deposit.vault_id, VAULT_ID);
305 }
306
307 #[test]
308 fn test_validate() {
309 let vault_deposit = VaultDeposit {
310 common_fields: CommonFields {
311 account: "rValidateDepositor555".into(),
312 transaction_type: TransactionType::VaultDeposit,
313 ..Default::default()
314 },
315 vault_id: VAULT_ID.into(),
316 amount: Amount::XRPAmount(XRPAmount::from("1000000")),
317 }
318 .with_fee("12".into())
319 .with_sequence(300);
320
321 assert!(vault_deposit.validate().is_ok());
322 }
323
324 #[test]
325 fn test_amount_zero_rejected() {
326 let vault_deposit = VaultDeposit {
327 common_fields: CommonFields {
328 account: "rDepositor".into(),
329 transaction_type: TransactionType::VaultDeposit,
330 ..Default::default()
331 },
332 vault_id: VAULT_ID.into(),
333 amount: Amount::XRPAmount(XRPAmount::from("0")),
334 };
335 assert!(vault_deposit.validate().is_err());
336 }
337
338 #[test]
339 fn test_amount_negative_rejected() {
340 let vault_deposit = VaultDeposit {
341 common_fields: CommonFields {
342 account: "rDepositor".into(),
343 transaction_type: TransactionType::VaultDeposit,
344 ..Default::default()
345 },
346 vault_id: VAULT_ID.into(),
347 amount: Amount::IssuedCurrencyAmount(crate::models::amount::IssuedCurrencyAmount::new(
348 "USD".into(),
349 "rIssuer".into(),
350 "-10".into(),
351 )),
352 };
353 assert!(vault_deposit.validate().is_err());
354 }
355
356 #[test]
357 fn test_amount_non_numeric_rejected() {
358 let vault_deposit = VaultDeposit {
359 common_fields: CommonFields {
360 account: "rDepositor".into(),
361 transaction_type: TransactionType::VaultDeposit,
362 ..Default::default()
363 },
364 vault_id: VAULT_ID.into(),
365 amount: Amount::XRPAmount(XRPAmount::from("not-a-number")),
366 };
367 assert!(vault_deposit.validate().is_err());
368 }
369}