1use crate::model::transfer::AddressType;
13use pretty_simple_display::{DebugPretty, DisplaySimple};
14use serde::{Deserialize, Serialize};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
20#[serde(rename_all = "snake_case")]
21pub enum WithdrawalState {
22 #[default]
24 Unconfirmed,
25 Confirmed,
27 Cancelled,
29 Completed,
31 Interrupted,
33 Rejected,
35}
36
37impl WithdrawalState {
38 #[must_use]
40 pub fn as_str(&self) -> &'static str {
41 match self {
42 Self::Unconfirmed => "unconfirmed",
43 Self::Confirmed => "confirmed",
44 Self::Cancelled => "cancelled",
45 Self::Completed => "completed",
46 Self::Interrupted => "interrupted",
47 Self::Rejected => "rejected",
48 }
49 }
50
51 #[must_use]
53 pub fn is_terminal(&self) -> bool {
54 matches!(
55 self,
56 Self::Cancelled | Self::Completed | Self::Interrupted | Self::Rejected
57 )
58 }
59
60 #[must_use]
62 pub fn is_successful(&self) -> bool {
63 matches!(self, Self::Completed)
64 }
65}
66
67impl std::fmt::Display for WithdrawalState {
68 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "{}", self.as_str())
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
77#[serde(rename_all = "snake_case")]
78pub enum WithdrawalPriority {
79 Insane,
81 ExtremeHigh,
83 VeryHigh,
85 #[default]
87 High,
88 Mid,
90 Low,
92 VeryLow,
94}
95
96impl WithdrawalPriority {
97 #[must_use]
99 pub fn as_str(&self) -> &'static str {
100 match self {
101 Self::Insane => "insane",
102 Self::ExtremeHigh => "extreme_high",
103 Self::VeryHigh => "very_high",
104 Self::High => "high",
105 Self::Mid => "mid",
106 Self::Low => "low",
107 Self::VeryLow => "very_low",
108 }
109 }
110}
111
112impl std::fmt::Display for WithdrawalPriority {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "{}", self.as_str())
115 }
116}
117
118#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
123pub struct DepositAddress {
124 pub address: String,
126 pub currency: String,
128 #[serde(rename = "type")]
130 pub address_type: AddressType,
131 pub creation_timestamp: i64,
133}
134
135impl DepositAddress {
136 #[must_use]
138 pub fn new(address: String, currency: String, creation_timestamp: i64) -> Self {
139 Self {
140 address,
141 currency,
142 address_type: AddressType::Deposit,
143 creation_timestamp,
144 }
145 }
146}
147
148#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Serialize, Deserialize)]
152pub struct WithdrawalRequest {
153 pub currency: String,
155 pub address: String,
157 pub amount: f64,
159 #[serde(skip_serializing_if = "Option::is_none")]
161 pub priority: Option<WithdrawalPriority>,
162}
163
164impl WithdrawalRequest {
165 #[must_use]
167 pub fn new(currency: String, address: String, amount: f64) -> Self {
168 Self {
169 currency,
170 address,
171 amount,
172 priority: None,
173 }
174 }
175
176 #[must_use]
178 pub fn with_priority(mut self, priority: WithdrawalPriority) -> Self {
179 self.priority = Some(priority);
180 self
181 }
182}
183
184#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
189pub struct DepositId {
190 pub currency: String,
192 pub user_id: i64,
194 pub address: String,
196 pub tx_hash: String,
198}
199
200impl DepositId {
201 #[must_use]
203 pub fn new(currency: String, user_id: i64, address: String, tx_hash: String) -> Self {
204 Self {
205 currency,
206 user_id,
207 address,
208 tx_hash,
209 }
210 }
211}
212
213#[derive(DebugPretty, DisplaySimple, Clone, PartialEq, Eq, Serialize, Deserialize)]
218pub struct ClearanceOriginator {
219 pub is_personal: bool,
221 #[serde(skip_serializing_if = "Option::is_none")]
223 pub first_name: Option<String>,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub last_name: Option<String>,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub company_name: Option<String>,
230 pub address: String,
232}
233
234impl ClearanceOriginator {
235 #[must_use]
237 pub fn personal(address: String) -> Self {
238 Self {
239 is_personal: true,
240 first_name: None,
241 last_name: None,
242 company_name: None,
243 address,
244 }
245 }
246
247 #[must_use]
249 pub fn individual(first_name: String, last_name: String, address: String) -> Self {
250 Self {
251 is_personal: false,
252 first_name: Some(first_name),
253 last_name: Some(last_name),
254 company_name: None,
255 address,
256 }
257 }
258
259 #[must_use]
261 pub fn company(company_name: String, address: String) -> Self {
262 Self {
263 is_personal: false,
264 first_name: None,
265 last_name: None,
266 company_name: Some(company_name),
267 address,
268 }
269 }
270
271 #[must_use]
273 pub fn is_self_transfer(&self) -> bool {
274 self.is_personal
275 }
276
277 #[must_use]
279 pub fn is_individual(&self) -> bool {
280 !self.is_personal && self.first_name.is_some() && self.last_name.is_some()
281 }
282
283 #[must_use]
285 pub fn is_company(&self) -> bool {
286 !self.is_personal && self.company_name.is_some()
287 }
288}
289
290#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
294#[serde(rename_all = "snake_case")]
295pub enum ClearanceState {
296 #[default]
298 NotRequired,
299 InProgress,
301 Completed,
303 Failed,
305}
306
307impl ClearanceState {
308 #[must_use]
310 pub fn as_str(&self) -> &'static str {
311 match self {
312 Self::NotRequired => "not_required",
313 Self::InProgress => "in_progress",
314 Self::Completed => "completed",
315 Self::Failed => "failed",
316 }
317 }
318
319 #[must_use]
321 pub fn is_cleared(&self) -> bool {
322 matches!(self, Self::NotRequired | Self::Completed)
323 }
324}
325
326impl std::fmt::Display for ClearanceState {
327 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
328 write!(f, "{}", self.as_str())
329 }
330}
331
332#[cfg(test)]
333mod tests {
334 use super::*;
335
336 #[test]
337 fn test_withdrawal_state_default() {
338 let state = WithdrawalState::default();
339 assert_eq!(state, WithdrawalState::Unconfirmed);
340 }
341
342 #[test]
343 fn test_withdrawal_state_as_str() {
344 assert_eq!(WithdrawalState::Unconfirmed.as_str(), "unconfirmed");
345 assert_eq!(WithdrawalState::Confirmed.as_str(), "confirmed");
346 assert_eq!(WithdrawalState::Cancelled.as_str(), "cancelled");
347 assert_eq!(WithdrawalState::Completed.as_str(), "completed");
348 assert_eq!(WithdrawalState::Interrupted.as_str(), "interrupted");
349 assert_eq!(WithdrawalState::Rejected.as_str(), "rejected");
350 }
351
352 #[test]
353 fn test_withdrawal_state_is_terminal() {
354 assert!(!WithdrawalState::Unconfirmed.is_terminal());
355 assert!(!WithdrawalState::Confirmed.is_terminal());
356 assert!(WithdrawalState::Cancelled.is_terminal());
357 assert!(WithdrawalState::Completed.is_terminal());
358 assert!(WithdrawalState::Interrupted.is_terminal());
359 assert!(WithdrawalState::Rejected.is_terminal());
360 }
361
362 #[test]
363 fn test_withdrawal_state_is_successful() {
364 assert!(!WithdrawalState::Unconfirmed.is_successful());
365 assert!(!WithdrawalState::Cancelled.is_successful());
366 assert!(WithdrawalState::Completed.is_successful());
367 }
368
369 #[test]
370 fn test_withdrawal_state_serialization() {
371 let state = WithdrawalState::Completed;
372 let json = serde_json::to_string(&state).unwrap();
373 assert_eq!(json, "\"completed\"");
374
375 let deserialized: WithdrawalState = serde_json::from_str(&json).unwrap();
376 assert_eq!(deserialized, WithdrawalState::Completed);
377 }
378
379 #[test]
380 fn test_withdrawal_priority_default() {
381 let priority = WithdrawalPriority::default();
382 assert_eq!(priority, WithdrawalPriority::High);
383 }
384
385 #[test]
386 fn test_withdrawal_priority_as_str() {
387 assert_eq!(WithdrawalPriority::Insane.as_str(), "insane");
388 assert_eq!(WithdrawalPriority::ExtremeHigh.as_str(), "extreme_high");
389 assert_eq!(WithdrawalPriority::VeryHigh.as_str(), "very_high");
390 assert_eq!(WithdrawalPriority::High.as_str(), "high");
391 assert_eq!(WithdrawalPriority::Mid.as_str(), "mid");
392 assert_eq!(WithdrawalPriority::Low.as_str(), "low");
393 assert_eq!(WithdrawalPriority::VeryLow.as_str(), "very_low");
394 }
395
396 #[test]
397 fn test_withdrawal_priority_serialization() {
398 let priority = WithdrawalPriority::VeryHigh;
399 let json = serde_json::to_string(&priority).unwrap();
400 assert_eq!(json, "\"very_high\"");
401
402 let deserialized: WithdrawalPriority = serde_json::from_str(&json).unwrap();
403 assert_eq!(deserialized, WithdrawalPriority::VeryHigh);
404 }
405
406 #[test]
407 fn test_deposit_address_new() {
408 let addr = DepositAddress::new(
409 "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string(),
410 "BTC".to_string(),
411 1640995200000,
412 );
413 assert_eq!(addr.address, "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh");
414 assert_eq!(addr.currency, "BTC");
415 assert_eq!(addr.address_type, AddressType::Deposit);
416 assert_eq!(addr.creation_timestamp, 1640995200000);
417 }
418
419 #[test]
420 fn test_deposit_address_serialization() {
421 let addr = DepositAddress::new(
422 "0x742d35Cc6634C0532925a3b844Bc9e7595f".to_string(),
423 "ETH".to_string(),
424 1640995200000,
425 );
426 let json = serde_json::to_string(&addr).unwrap();
427 let deserialized: DepositAddress = serde_json::from_str(&json).unwrap();
428 assert_eq!(addr, deserialized);
429 }
430
431 #[test]
432 fn test_withdrawal_request_new() {
433 let request = WithdrawalRequest::new(
434 "BTC".to_string(),
435 "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh".to_string(),
436 1.5,
437 );
438 assert_eq!(request.currency, "BTC");
439 assert_eq!(request.amount, 1.5);
440 assert!(request.priority.is_none());
441 }
442
443 #[test]
444 fn test_withdrawal_request_with_priority() {
445 let request = WithdrawalRequest::new("BTC".to_string(), "address".to_string(), 1.0)
446 .with_priority(WithdrawalPriority::VeryHigh);
447 assert_eq!(request.priority, Some(WithdrawalPriority::VeryHigh));
448 }
449
450 #[test]
451 fn test_deposit_id_new() {
452 let deposit_id = DepositId::new(
453 "BTC".to_string(),
454 123,
455 "2NBqqD5GRJ8wHy1PYyCXTe9ke5226FhavBz".to_string(),
456 "230669110fdaf0a0dbcdc079b6b8b43d5af29cc73683835b9bc6b3406c065fda".to_string(),
457 );
458 assert_eq!(deposit_id.currency, "BTC");
459 assert_eq!(deposit_id.user_id, 123);
460 }
461
462 #[test]
463 fn test_deposit_id_serialization() {
464 let deposit_id = DepositId::new(
465 "BTC".to_string(),
466 123,
467 "address".to_string(),
468 "tx_hash".to_string(),
469 );
470 let json = serde_json::to_string(&deposit_id).unwrap();
471 let deserialized: DepositId = serde_json::from_str(&json).unwrap();
472 assert_eq!(deposit_id, deserialized);
473 }
474
475 #[test]
476 fn test_clearance_originator_personal() {
477 let originator = ClearanceOriginator::personal("123 Main St".to_string());
478 assert!(originator.is_personal);
479 assert!(originator.is_self_transfer());
480 assert!(!originator.is_individual());
481 assert!(!originator.is_company());
482 }
483
484 #[test]
485 fn test_clearance_originator_individual() {
486 let originator = ClearanceOriginator::individual(
487 "John".to_string(),
488 "Doe".to_string(),
489 "123 Main St".to_string(),
490 );
491 assert!(!originator.is_personal);
492 assert!(!originator.is_self_transfer());
493 assert!(originator.is_individual());
494 assert!(!originator.is_company());
495 assert_eq!(originator.first_name, Some("John".to_string()));
496 assert_eq!(originator.last_name, Some("Doe".to_string()));
497 }
498
499 #[test]
500 fn test_clearance_originator_company() {
501 let originator =
502 ClearanceOriginator::company("Acme Corp".to_string(), "456 Business Ave".to_string());
503 assert!(!originator.is_personal);
504 assert!(!originator.is_self_transfer());
505 assert!(!originator.is_individual());
506 assert!(originator.is_company());
507 assert_eq!(originator.company_name, Some("Acme Corp".to_string()));
508 }
509
510 #[test]
511 fn test_clearance_originator_serialization() {
512 let originator = ClearanceOriginator::individual(
513 "John".to_string(),
514 "Doe".to_string(),
515 "123 Main St".to_string(),
516 );
517 let json = serde_json::to_string(&originator).unwrap();
518 let deserialized: ClearanceOriginator = serde_json::from_str(&json).unwrap();
519 assert_eq!(originator, deserialized);
520 }
521
522 #[test]
523 fn test_clearance_state_default() {
524 let state = ClearanceState::default();
525 assert_eq!(state, ClearanceState::NotRequired);
526 }
527
528 #[test]
529 fn test_clearance_state_as_str() {
530 assert_eq!(ClearanceState::NotRequired.as_str(), "not_required");
531 assert_eq!(ClearanceState::InProgress.as_str(), "in_progress");
532 assert_eq!(ClearanceState::Completed.as_str(), "completed");
533 assert_eq!(ClearanceState::Failed.as_str(), "failed");
534 }
535
536 #[test]
537 fn test_clearance_state_is_cleared() {
538 assert!(ClearanceState::NotRequired.is_cleared());
539 assert!(!ClearanceState::InProgress.is_cleared());
540 assert!(ClearanceState::Completed.is_cleared());
541 assert!(!ClearanceState::Failed.is_cleared());
542 }
543
544 #[test]
545 fn test_clearance_state_serialization() {
546 let state = ClearanceState::InProgress;
547 let json = serde_json::to_string(&state).unwrap();
548 assert_eq!(json, "\"in_progress\"");
549
550 let deserialized: ClearanceState = serde_json::from_str(&json).unwrap();
551 assert_eq!(deserialized, ClearanceState::InProgress);
552 }
553}