1use chrono::{DateTime, Duration, Utc};
2use secrecy::{DebugSecret, Secret, SerializableSecret};
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4
5#[cfg(test)]
6use std::collections::HashMap;
7
8use zeroize::Zeroize;
9
10#[derive(Debug, Serialize)]
12pub struct TokenRequest {
13 pub username: String,
14 pub password: String,
15}
16
17#[derive(Debug, Deserialize)]
19pub struct TokenResponse {
20 pub token: String,
21}
22
23#[derive(Clone, Serialize, Deserialize)]
24pub struct Password(pub String);
25
26impl Zeroize for Password {
27 fn zeroize(&mut self) {
28 self.0.zeroize();
29 }
30}
31
32impl From<String> for Password {
33 fn from(s: String) -> Self {
34 Self(s)
35 }
36}
37
38impl SerializableSecret for Password {}
39
40impl DebugSecret for Password {}
41
42#[derive(Debug, Serialize)]
43pub struct ChangePasswordRequest {
44 pub password: Secret<Password>,
45}
46
47#[derive(Debug, Deserialize)]
48pub struct ChangePasswordResponse {
49 pub username: String,
50 pub password: Secret<Password>,
51 pub token: Secret<String>,
52}
53
54#[derive(Debug, Deserialize)]
55#[allow(clippy::struct_excessive_bools)]
56pub struct Asset {
57 pub name: String,
58 pub asset_uuid: String,
59 pub issuer: i64,
60 pub asset_id: String,
61 pub reissuance_token_id: Option<String>,
62 pub requirements: Vec<i64>,
63 pub ticker: Option<String>,
64 pub precision: i64,
65 pub domain: Option<String>,
66 pub pubkey: Option<String>,
67 pub is_registered: bool,
68 pub is_authorized: bool,
69 pub is_locked: bool,
70 pub issuer_authorization_endpoint: Option<String>,
71 pub transfer_restricted: bool,
72}
73
74#[derive(Debug, Serialize)]
75pub struct IssuanceRequest {
76 pub name: String,
77 pub amount: i64,
78 pub destination_address: String,
79 pub domain: String,
80 pub ticker: String,
81 pub pubkey: String,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub precision: Option<i64>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub is_confidential: Option<bool>,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub is_reissuable: Option<bool>,
88 #[serde(skip_serializing_if = "Option::is_none")]
89 pub reissuance_amount: Option<i64>,
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub reissuance_address: Option<String>,
92 #[serde(skip_serializing_if = "Option::is_none")]
93 pub transfer_restricted: Option<bool>,
94}
95
96#[derive(Debug, Deserialize)]
97pub struct IssuanceResponse {
98 pub name: String,
99 pub amount: i64,
100 pub destination_address: String,
101 pub domain: String,
102 pub ticker: String,
103 pub pubkey: String,
104 pub is_confidential: bool,
105 pub is_reissuable: bool,
106 pub reissuance_amount: i64,
107 pub reissuance_address: String,
108 pub asset_id: String,
109 pub reissuance_token_id: Option<String>,
110 pub asset_uuid: String,
111 pub txid: String,
112 pub vin: i64,
113 pub asset_vout: i64,
114 pub reissuance_vout: Option<i64>,
115 pub issuer_authorization_endpoint: Option<String>,
116 pub transfer_restricted: bool,
117 pub issuance_assetblinder: String,
118 pub issuance_tokenblinder: Option<String>,
119}
120
121#[derive(Debug, Serialize)]
122pub struct EditAssetRequest {
123 pub issuer_authorization_endpoint: String,
124}
125
126#[derive(Debug, Deserialize)]
127pub struct RegisteredUserResponse {
128 pub id: i64,
129 #[serde(rename = "GAID")]
130 pub gaid: Option<String>,
131 pub is_company: bool,
132 pub name: String,
133 pub categories: Vec<i64>,
134 pub creator: i64,
135}
136
137#[derive(Debug, Serialize)]
138pub struct RegisteredUserAdd {
139 pub name: String,
140 #[serde(rename = "GAID")]
141 pub gaid: Option<String>,
142 pub is_company: bool,
143}
144
145#[derive(Debug, Serialize)]
146pub struct RegisteredUserEdit {
147 pub name: Option<String>,
148}
149
150#[derive(Debug, Serialize)]
151pub struct GaidRequest {
152 pub gaid: String,
153}
154
155#[derive(Debug, Serialize)]
156pub struct CategoriesRequest {
157 pub categories: Vec<i64>,
158}
159
160#[derive(Debug, Deserialize)]
161pub struct CategoryResponse {
162 pub id: i64,
163 pub name: String,
164 pub description: Option<String>,
165 pub registered_users: Vec<i64>,
166 pub assets: Vec<String>,
167}
168
169#[derive(Debug, Serialize)]
170pub struct CategoryAdd {
171 pub name: String,
172 pub description: Option<String>,
173}
174
175#[derive(Debug, Serialize)]
176pub struct CategoryEdit {
177 pub name: Option<String>,
178 pub description: Option<String>,
179}
180
181#[derive(Debug, Deserialize)]
182pub struct ValidateGaidResponse {
183 pub is_valid: bool,
184 pub error: Option<String>,
185}
186
187#[derive(Debug, Deserialize)]
188pub struct AddressGaidResponse {
189 pub address: String,
190 pub error: Option<String>,
191}
192
193#[derive(Debug, Deserialize)]
194pub struct Manager {
195 pub username: String,
196 pub id: i64,
197 pub is_locked: bool,
198 pub assets: Vec<String>,
199}
200
201#[derive(Debug, Serialize)]
202pub struct ManagerCreate {
203 pub username: String,
204 pub password: String,
205}
206
207#[derive(Debug, Deserialize, Serialize)]
208#[serde(rename_all = "UPPERCASE")]
209pub enum Status {
210 Unconfirmed,
211 Confirmed,
212}
213
214#[derive(Debug, Deserialize, Serialize)]
215pub struct DistributionAssignment {
216 pub registered_user: i64,
217 pub amount: i64,
218 pub vout: i64,
219}
220
221#[derive(Debug, Deserialize, Serialize)]
222pub struct Transaction {
223 pub txid: String,
224 pub transaction_status: Status,
225 pub included_blockheight: i64,
226 pub confirmed_datetime: String,
227 pub assignments: Vec<DistributionAssignment>,
228}
229
230#[derive(Debug, Deserialize, Serialize)]
231pub struct Distribution {
232 pub distribution_uuid: String,
233 pub distribution_status: Status,
234 pub transactions: Vec<Transaction>,
235}
236
237#[derive(Debug, Serialize, Deserialize, Clone)]
238pub struct CreateAssetAssignmentRequest {
239 pub registered_user: i64,
240 pub amount: i64,
241 #[serde(skip_serializing_if = "Option::is_none")]
242 pub vesting_timestamp: Option<i64>, #[serde(default = "default_ready_for_distribution")]
244 pub ready_for_distribution: bool, }
246
247const fn default_ready_for_distribution() -> bool {
248 false
249}
250
251#[derive(Debug, Serialize)]
252pub struct CreateAssetAssignmentRequestWrapper {
253 pub assignments: Vec<CreateAssetAssignmentRequest>,
254}
255
256#[derive(Debug, Deserialize, Serialize)]
257pub struct Assignment {
258 pub id: i64,
259 pub registered_user: i64,
260 pub amount: i64,
261 pub receiving_address: Option<String>,
262 pub distribution_uuid: Option<String>,
263 pub ready_for_distribution: bool,
264 pub vesting_datetime: Option<String>,
265 pub vesting_timestamp: Option<i64>,
266 pub has_vested: bool,
267 pub is_distributed: bool,
268 pub creator: i64,
269 #[serde(rename = "GAID")]
270 pub gaid: Option<String>,
271 #[serde(skip_serializing_if = "Option::is_none")]
273 pub investor: Option<i64>,
274}
275
276#[derive(Debug, Deserialize, Serialize)]
277pub struct RegisteredUserSummary {
278 pub asset_uuid: String,
279 pub asset_id: String,
280 pub assignments: Vec<Assignment>,
281 pub assignments_sum: i64,
282 pub distributions: Vec<Distribution>,
283 pub distributions_sum: i64,
284 pub balance: i64,
285}
286
287#[derive(Debug, Deserialize, Serialize)]
288pub struct Activity {
289 #[serde(rename = "type")]
290 pub activity_type: String,
291 pub datetime: String,
292 pub description: String,
293 pub txid: String,
294 pub vout: i64,
295 pub blockheight: i64,
296 pub asset_blinder: String,
297 pub amount_blinder: String,
298 #[serde(rename = "registered user")]
299 pub registered_user: Option<i64>,
300 pub amount: i64,
301}
302
303#[derive(Debug, Serialize, Default)]
304pub struct AssetActivityParams {
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub start: Option<i64>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub count: Option<i64>,
309 #[serde(skip_serializing_if = "Option::is_none")]
310 pub sortcolumn: Option<String>,
311 #[serde(skip_serializing_if = "Option::is_none")]
312 pub sortorder: Option<String>,
313 #[serde(skip_serializing_if = "Option::is_none")]
314 pub height_start: Option<i64>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub height_stop: Option<i64>,
317}
318
319#[derive(Debug, Deserialize, Serialize)]
320pub struct Ownership {
321 pub owner: String,
322 pub amount: i64,
323 #[serde(rename = "GAID")]
324 pub gaid: Option<String>,
325}
326
327#[derive(Debug, Deserialize, Serialize)]
328pub struct Outpoint {
329 pub txid: String,
330 pub vout: i64,
331}
332
333pub type LostOutputs = Vec<Outpoint>;
334
335#[derive(Debug, Deserialize, Serialize)]
336pub struct GaidBalanceEntry {
337 pub asset_uuid: String,
338 pub asset_id: String,
339 pub balance: i64,
340}
341
342pub type Balance = Vec<GaidBalanceEntry>;
343
344#[derive(Debug, Deserialize, Serialize)]
345pub struct AssetLostOutputs {
346 pub lost_outputs: LostOutputs,
347 pub reissuance_lost_outputs: LostOutputs,
348}
349
350#[derive(Debug, Deserialize, Serialize)]
351pub struct AssetSummary {
352 pub asset_id: String,
353 pub reissuance_token_id: Option<String>,
354 pub issued: i64,
355 pub reissued: i64,
356 pub assigned: i64,
357 pub distributed: i64,
358 pub burned: i64,
359 pub blacklisted: i64,
360 pub registered_users: i64,
361 pub active_registered_users: i64,
362 pub active_green_subaccounts: i64,
363 #[serde(rename = "reissuance_tokens")]
364 pub reissuance_tokens: i64,
365}
366
367#[derive(Debug, Deserialize, Serialize)]
368pub struct Utxo {
369 pub txid: String,
370 pub vout: i64,
371 pub asset: String,
372 pub amount: i64,
373 pub registered_user: Option<i64>,
374 pub gaid: Option<String>,
375 pub blacklisted: bool,
376}
377
378#[derive(Debug, Deserialize, Serialize)]
379pub struct Reissuance {
380 pub txid: String,
381 pub vout: i64,
382 pub destination_address: String,
383 pub reissuance_amount: i64,
384 pub confirmed_in_block: String,
385 pub created: String,
386}
387
388#[derive(Debug, Serialize)]
389pub struct ReissueRequest {
390 pub amount_to_reissue: i64,
391}
392
393#[derive(Debug, Serialize)]
394pub struct ReissueConfirmRequest {
395 pub details: serde_json::Value,
396 pub listissuances: Vec<serde_json::Value>,
397 pub reissuance_output: serde_json::Value,
398}
399
400#[derive(Debug, Serialize)]
401pub struct BurnRequest {
402 pub amount: i64,
403}
404
405#[derive(Debug, Deserialize, Serialize)]
406pub struct BurnCreate {
407 pub command: String,
408 pub min_supported_client_script_version: i64,
409 pub base_url: String,
410 pub asset_uuid: String,
411 pub asset_id: String,
412 pub amount: f64,
413 pub utxos: Vec<Outpoint>,
414}
415
416#[derive(Debug, Serialize)]
417pub struct BurnConfirmRequest {
418 pub tx_data: serde_json::Value,
419 pub change_data: Vec<serde_json::Value>,
420}
421
422#[derive(Debug, Serialize)]
423pub struct SetAssetMemoRequest {
424 pub memo: String,
425}
426
427#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
428#[serde(rename_all = "lowercase")]
429pub enum Permission {
430 View,
431 Receive,
432 Transfer,
433 Assign,
434 Distribute,
435 Reissue,
436 Burn,
437 Acquire,
438 Manage,
439 Permissions,
440}
441
442#[derive(Debug, Deserialize, Serialize)]
443pub struct BroadcastResponse {
444 pub txid: String,
445 pub hex: String,
446}
447
448#[derive(Debug, Clone, Serialize, Deserialize)]
450pub struct TokenData {
451 #[serde(with = "secret_serde")]
452 pub token: Secret<String>,
453 pub expires_at: DateTime<Utc>,
454 pub obtained_at: DateTime<Utc>,
455}
456
457impl TokenData {
458 #[must_use]
469 pub fn new(token: String, expires_at: DateTime<Utc>) -> Self {
470 Self {
471 token: Secret::new(token),
472 expires_at,
473 obtained_at: Utc::now(),
474 }
475 }
476
477 #[must_use]
494 pub fn is_expired(&self) -> bool {
495 Utc::now() > self.expires_at
496 }
497
498 #[must_use]
515 pub fn expires_soon(&self, threshold: Duration) -> bool {
516 Utc::now() + threshold > self.expires_at
517 }
518
519 #[must_use]
533 pub fn age(&self) -> Duration {
534 Utc::now() - self.obtained_at
535 }
536}
537
538#[derive(Debug, Clone)]
540pub struct TokenInfo {
541 pub expires_at: DateTime<Utc>,
542 pub obtained_at: DateTime<Utc>,
543 pub expires_in: Duration,
544 pub age: Duration,
545 pub is_expired: bool,
546 pub expires_soon: bool,
547}
548
549impl From<&TokenData> for TokenInfo {
550 fn from(token_data: &TokenData) -> Self {
551 let now = Utc::now();
552 let expires_in = token_data.expires_at - now;
553 let expires_soon_threshold = Duration::minutes(5);
554
555 Self {
556 expires_at: token_data.expires_at,
557 obtained_at: token_data.obtained_at,
558 expires_in,
559 age: token_data.age(),
560 is_expired: token_data.is_expired(),
561 expires_soon: token_data.expires_soon(expires_soon_threshold),
562 }
563 }
564}
565
566#[derive(Debug, Clone, Serialize, Deserialize)]
568pub struct AssetDistributionAssignment {
569 pub user_id: String,
570 pub address: String,
571 pub amount: f64,
572}
573
574#[derive(Debug, Clone, Serialize, Deserialize)]
576pub struct DistributionAssignmentRequest {
577 pub user_uuid: String,
578 pub amount: f64,
579 pub address: String,
580}
581
582#[derive(Debug, Clone, Serialize, Deserialize)]
584pub struct CreateDistributionRequest {
585 pub assignments: Vec<DistributionAssignmentRequest>,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct Unspent {
591 pub txid: String,
592 pub vout: u32,
593 pub amount: f64,
594 pub asset: String,
595 pub address: String,
596 pub spendable: bool,
597 #[serde(skip_serializing_if = "Option::is_none")]
598 pub confirmations: Option<u32>,
599 #[serde(skip_serializing_if = "Option::is_none")]
600 pub scriptpubkey: Option<String>,
601 #[serde(skip_serializing_if = "Option::is_none")]
602 pub redeemscript: Option<String>,
603 #[serde(skip_serializing_if = "Option::is_none")]
604 pub witnessscript: Option<String>,
605 #[serde(skip_serializing_if = "Option::is_none")]
607 pub amountblinder: Option<String>,
608 #[serde(skip_serializing_if = "Option::is_none")]
610 pub assetblinder: Option<String>,
611}
612
613#[derive(Debug, Clone, Serialize, Deserialize)]
615pub struct TransactionDetail {
616 pub txid: String,
617 pub confirmations: u32,
618 #[serde(skip_serializing_if = "Option::is_none")]
619 pub blockheight: Option<u64>,
620 pub hex: String,
621 #[serde(skip_serializing_if = "Option::is_none")]
622 pub blockhash: Option<String>,
623 #[serde(skip_serializing_if = "Option::is_none")]
624 pub blocktime: Option<i64>,
625 #[serde(skip_serializing_if = "Option::is_none")]
626 pub time: Option<i64>,
627 #[serde(skip_serializing_if = "Option::is_none")]
628 pub timereceived: Option<i64>,
629 #[serde(skip_serializing_if = "Option::is_none")]
631 pub details: Option<Vec<serde_json::Value>>,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct TransactionOutputDetail {
637 pub account: String,
638 pub address: String,
639 pub category: String,
640 pub amount: f64,
641 pub vout: u32,
642 #[serde(skip_serializing_if = "Option::is_none")]
643 pub fee: Option<f64>,
644 #[serde(skip_serializing_if = "Option::is_none")]
645 pub confirmations: Option<u32>,
646 #[serde(skip_serializing_if = "Option::is_none")]
647 pub blockhash: Option<String>,
648 #[serde(skip_serializing_if = "Option::is_none")]
649 pub blockindex: Option<u32>,
650 #[serde(skip_serializing_if = "Option::is_none")]
651 pub blocktime: Option<i64>,
652 #[serde(skip_serializing_if = "Option::is_none")]
653 pub txid: Option<String>,
654 #[serde(skip_serializing_if = "Option::is_none")]
655 pub time: Option<i64>,
656 #[serde(skip_serializing_if = "Option::is_none")]
657 pub timereceived: Option<i64>,
658 #[serde(skip_serializing_if = "Option::is_none")]
659 pub asset: Option<String>,
660 #[serde(skip_serializing_if = "Option::is_none")]
661 pub assetblinder: Option<String>,
662 #[serde(skip_serializing_if = "Option::is_none")]
663 pub amountblinder: Option<String>,
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize)]
668pub struct TxInput {
669 pub txid: String,
670 pub vout: u32,
671 #[serde(skip_serializing_if = "Option::is_none")]
672 pub sequence: Option<u32>,
673}
674
675#[derive(Debug, Clone, Serialize, Deserialize)]
677pub struct DistributionResponse {
678 pub distribution_uuid: String,
679 pub map_address_amount: std::collections::HashMap<String, f64>,
680 pub map_address_asset: std::collections::HashMap<String, String>,
681 pub asset_id: String,
682}
683
684#[derive(Debug, Clone, Serialize, Deserialize)]
686pub struct DistributionTxData {
687 pub details: TransactionDetail,
688 pub txid: String,
689}
690
691#[derive(Debug, Clone, Serialize, Deserialize)]
693pub struct AmpTxData {
694 pub details: serde_json::Value,
696 pub txid: String,
697}
698
699#[derive(Debug, Clone, Serialize, Deserialize)]
701pub struct ConfirmDistributionRequest {
702 pub tx_data: AmpTxData,
703 pub change_data: Vec<Unspent>,
704}
705
706#[cfg(test)]
707mod tests {
708 use super::*;
709
710 #[test]
711 fn test_asset_distribution_assignment_creation() {
712 let assignment = AssetDistributionAssignment {
713 user_id: "user123".to_string(),
714 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
715 amount: 100.5,
716 };
717
718 assert_eq!(assignment.user_id, "user123");
719 assert_eq!(
720 assignment.address,
721 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
722 );
723 assert_eq!(assignment.amount, 100.5);
724 }
725
726 #[test]
727 fn test_asset_distribution_assignment_serialization() {
728 let assignment = AssetDistributionAssignment {
729 user_id: "user123".to_string(),
730 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
731 amount: 100.5,
732 };
733
734 let json = serde_json::to_string(&assignment).unwrap();
736 assert!(json.contains("user123"));
737 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
738 assert!(json.contains("100.5"));
739
740 let deserialized: AssetDistributionAssignment = serde_json::from_str(&json).unwrap();
742 assert_eq!(deserialized.user_id, assignment.user_id);
743 assert_eq!(deserialized.address, assignment.address);
744 assert_eq!(deserialized.amount, assignment.amount);
745 }
746
747 #[test]
748 fn test_asset_distribution_assignment_clone() {
749 let assignment = AssetDistributionAssignment {
750 user_id: "user123".to_string(),
751 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
752 amount: 100.5,
753 };
754
755 let cloned = assignment.clone();
756 assert_eq!(assignment.user_id, cloned.user_id);
757 assert_eq!(assignment.address, cloned.address);
758 assert_eq!(assignment.amount, cloned.amount);
759 }
760
761 #[test]
762 fn test_unspent_creation_and_serialization() {
763 let unspent = Unspent {
764 txid: "abc123def456".to_string(),
765 vout: 1,
766 amount: 50.0,
767 asset: "asset_id_hex".to_string(),
768 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
769 spendable: true,
770 confirmations: Some(6),
771 scriptpubkey: Some("76a914...88ac".to_string()),
772 redeemscript: None,
773 witnessscript: None,
774 amountblinder: Some(
775 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
776 ),
777 assetblinder: Some(
778 "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210".to_string(),
779 ),
780 };
781
782 let json = serde_json::to_string(&unspent).unwrap();
784 assert!(json.contains("abc123def456"));
785 assert!(json.contains("50.0"));
786 assert!(json.contains("asset_id_hex"));
787
788 let deserialized: Unspent = serde_json::from_str(&json).unwrap();
790 assert_eq!(deserialized.txid, unspent.txid);
791 assert_eq!(deserialized.vout, unspent.vout);
792 assert_eq!(deserialized.amount, unspent.amount);
793 assert_eq!(deserialized.asset, unspent.asset);
794 assert_eq!(deserialized.confirmations, unspent.confirmations);
795 }
796
797 #[test]
798 fn test_transaction_detail_creation() {
799 let tx_detail = TransactionDetail {
800 txid: "def456abc123".to_string(),
801 confirmations: 3,
802 blockheight: Some(12345),
803 hex: "020000000001...".to_string(),
804 blockhash: Some("block_hash_hex".to_string()),
805 details: Some(vec![]),
806 blocktime: Some(1640995200),
807 time: Some(1640995200),
808 timereceived: Some(1640995180),
809 };
810
811 assert_eq!(tx_detail.txid, "def456abc123");
812 assert_eq!(tx_detail.confirmations, 3);
813 assert_eq!(tx_detail.blockheight, Some(12345));
814
815 let json = serde_json::to_string(&tx_detail).unwrap();
817 assert!(json.contains("def456abc123"));
818 assert!(json.contains("\"confirmations\":3"));
819 }
820
821 #[test]
822 fn test_tx_input_creation() {
823 let tx_input = TxInput {
824 txid: "input_txid_123".to_string(),
825 vout: 2,
826 sequence: Some(0xffffffff),
827 };
828
829 assert_eq!(tx_input.txid, "input_txid_123");
830 assert_eq!(tx_input.vout, 2);
831 assert_eq!(tx_input.sequence, Some(0xffffffff));
832
833 let json = serde_json::to_string(&tx_input).unwrap();
835 assert!(json.contains("input_txid_123"));
836 assert!(json.contains("\"vout\":2"));
837 assert!(json.contains("4294967295")); }
839
840 #[test]
841 fn test_distribution_response_creation() {
842 let mut map_address_amount = HashMap::new();
843 map_address_amount.insert("address1".to_string(), 100.0);
844 map_address_amount.insert("address2".to_string(), 50.0);
845
846 let mut map_address_asset = HashMap::new();
847 map_address_asset.insert("address1".to_string(), "asset_id_1".to_string());
848 map_address_asset.insert("address2".to_string(), "asset_id_1".to_string());
849
850 let distribution_response = DistributionResponse {
851 distribution_uuid: "dist_uuid_123".to_string(),
852 map_address_amount,
853 map_address_asset,
854 asset_id: "main_asset_id".to_string(),
855 };
856
857 assert_eq!(distribution_response.distribution_uuid, "dist_uuid_123");
858 assert_eq!(distribution_response.asset_id, "main_asset_id");
859 assert_eq!(distribution_response.map_address_amount.len(), 2);
860 assert_eq!(distribution_response.map_address_asset.len(), 2);
861
862 let json = serde_json::to_string(&distribution_response).unwrap();
864 assert!(json.contains("dist_uuid_123"));
865 assert!(json.contains("main_asset_id"));
866 assert!(json.contains("address1"));
867 assert!(json.contains("100.0"));
868 }
869
870 #[test]
871 fn test_distribution_assignment_request_creation() {
872 let assignment_request = DistributionAssignmentRequest {
873 user_uuid: "user_uuid_123".to_string(),
874 amount: 150.0,
875 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
876 };
877
878 assert_eq!(assignment_request.user_uuid, "user_uuid_123");
879 assert_eq!(assignment_request.amount, 150.0);
880 assert_eq!(
881 assignment_request.address,
882 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
883 );
884
885 let json = serde_json::to_string(&assignment_request).unwrap();
887 assert!(json.contains("user_uuid_123"));
888 assert!(json.contains("150.0"));
889 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
890
891 let deserialized: DistributionAssignmentRequest = serde_json::from_str(&json).unwrap();
893 assert_eq!(deserialized.user_uuid, assignment_request.user_uuid);
894 assert_eq!(deserialized.amount, assignment_request.amount);
895 assert_eq!(deserialized.address, assignment_request.address);
896 }
897
898 #[test]
899 fn test_create_distribution_request_creation() {
900 let assignments = vec![
901 DistributionAssignmentRequest {
902 user_uuid: "user1".to_string(),
903 amount: 100.0,
904 address: "address1".to_string(),
905 },
906 DistributionAssignmentRequest {
907 user_uuid: "user2".to_string(),
908 amount: 50.0,
909 address: "address2".to_string(),
910 },
911 ];
912
913 let create_request = CreateDistributionRequest {
914 assignments: assignments.clone(),
915 };
916
917 assert_eq!(create_request.assignments.len(), 2);
918 assert_eq!(create_request.assignments[0].user_uuid, "user1");
919 assert_eq!(create_request.assignments[1].amount, 50.0);
920
921 let json = serde_json::to_string(&create_request).unwrap();
923 assert!(json.contains("user1"));
924 assert!(json.contains("user2"));
925 assert!(json.contains("100.0"));
926 assert!(json.contains("50.0"));
927 assert!(json.contains("assignments"));
928
929 let deserialized: CreateDistributionRequest = serde_json::from_str(&json).unwrap();
931 assert_eq!(deserialized.assignments.len(), 2);
932 assert_eq!(deserialized.assignments[0].user_uuid, "user1");
933 assert_eq!(deserialized.assignments[1].user_uuid, "user2");
934 }
935
936 #[test]
937 fn test_distribution_request_serialization_format() {
938 let assignment = DistributionAssignmentRequest {
939 user_uuid: "test_user".to_string(),
940 amount: 123.45,
941 address: "test_address".to_string(),
942 };
943
944 let request = CreateDistributionRequest {
945 assignments: vec![assignment],
946 };
947
948 let json = serde_json::to_string(&request).unwrap();
949
950 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
952
953 assert!(parsed.get("assignments").is_some());
954 let assignments_array = parsed["assignments"].as_array().unwrap();
955 assert_eq!(assignments_array.len(), 1);
956
957 let first_assignment = &assignments_array[0];
958 assert_eq!(first_assignment["user_uuid"], "test_user");
959 assert_eq!(first_assignment["amount"], 123.45);
960 assert_eq!(first_assignment["address"], "test_address");
961 }
962
963 #[test]
964 fn test_distribution_tx_data_creation() {
965 let tx_detail = TransactionDetail {
966 txid: "test_txid_123".to_string(),
967 confirmations: 2,
968 blockheight: Some(12345),
969 hex: "020000000001...".to_string(),
970 blockhash: Some("block_hash_hex".to_string()),
971 blocktime: Some(1640995200),
972 time: Some(1640995200),
973 timereceived: Some(1640995180),
974 details: Some(vec![]),
975 };
976
977 let tx_data = DistributionTxData {
978 details: tx_detail.clone(),
979 txid: "test_txid_123".to_string(),
980 };
981
982 assert_eq!(tx_data.txid, "test_txid_123");
983 assert_eq!(tx_data.details.txid, "test_txid_123");
984 assert_eq!(tx_data.details.confirmations, 2);
985
986 let json = serde_json::to_string(&tx_data).unwrap();
988 assert!(json.contains("test_txid_123"));
989 assert!(json.contains("\"confirmations\":2"));
990
991 let deserialized: DistributionTxData = serde_json::from_str(&json).unwrap();
993 assert_eq!(deserialized.txid, tx_data.txid);
994 assert_eq!(
995 deserialized.details.confirmations,
996 tx_data.details.confirmations
997 );
998 }
999
1000 #[test]
1001 fn test_confirm_distribution_request_creation() {
1002 let _tx_detail = TransactionDetail {
1003 txid: "confirm_test_txid".to_string(),
1004 confirmations: 3,
1005 blockheight: Some(54321),
1006 hex: "020000000002...".to_string(),
1007 blockhash: Some("confirm_block_hash".to_string()),
1008 blocktime: Some(1640995300),
1009 time: Some(1640995300),
1010 timereceived: Some(1640995280),
1011 details: Some(vec![]),
1012 };
1013
1014 let tx_data = AmpTxData {
1015 details: serde_json::json!([]),
1016 txid: "confirm_test_txid".to_string(),
1017 };
1018
1019 let change_utxo = Unspent {
1020 txid: "change_txid_123".to_string(),
1021 vout: 1,
1022 amount: 25.0,
1023 asset: "change_asset_id".to_string(),
1024 address: "change_address".to_string(),
1025 spendable: true,
1026 confirmations: Some(3),
1027 scriptpubkey: Some("76a914...88ac".to_string()),
1028 redeemscript: None,
1029 witnessscript: None,
1030 amountblinder: Some(
1031 "1111111111111111111111111111111111111111111111111111111111111111".to_string(),
1032 ),
1033 assetblinder: Some(
1034 "2222222222222222222222222222222222222222222222222222222222222222".to_string(),
1035 ),
1036 };
1037
1038 let confirm_request = ConfirmDistributionRequest {
1039 tx_data,
1040 change_data: vec![change_utxo],
1041 };
1042
1043 assert_eq!(confirm_request.tx_data.txid, "confirm_test_txid");
1044 assert_eq!(confirm_request.change_data.len(), 1);
1045 assert_eq!(confirm_request.change_data[0].txid, "change_txid_123");
1046
1047 let json = serde_json::to_string(&confirm_request).unwrap();
1049 assert!(json.contains("confirm_test_txid"));
1050 assert!(json.contains("change_txid_123"));
1051 assert!(json.contains("tx_data"));
1052 assert!(json.contains("change_data"));
1053
1054 let deserialized: ConfirmDistributionRequest = serde_json::from_str(&json).unwrap();
1056 assert_eq!(deserialized.tx_data.txid, confirm_request.tx_data.txid);
1057 assert_eq!(deserialized.change_data.len(), 1);
1058 assert_eq!(deserialized.change_data[0].txid, "change_txid_123");
1059 }
1060
1061 #[test]
1062 fn test_confirm_distribution_request_serialization_format() {
1063 let _tx_detail = TransactionDetail {
1064 txid: "format_test_txid".to_string(),
1065 confirmations: 2,
1066 blockheight: Some(98765),
1067 hex: "format_test_hex".to_string(),
1068 blockhash: None,
1069 blocktime: None,
1070 time: None,
1071 timereceived: None,
1072 details: Some(vec![]),
1073 };
1074
1075 let tx_data = AmpTxData {
1076 details: serde_json::json!([]),
1077 txid: "format_test_txid".to_string(),
1078 };
1079
1080 let confirm_request = ConfirmDistributionRequest {
1081 tx_data,
1082 change_data: vec![],
1083 };
1084
1085 let json = serde_json::to_string(&confirm_request).unwrap();
1086
1087 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1089
1090 assert!(parsed.get("tx_data").is_some());
1091 assert!(parsed.get("change_data").is_some());
1092
1093 let tx_data_obj = &parsed["tx_data"];
1094 assert!(tx_data_obj.get("details").is_some());
1095 assert!(tx_data_obj.get("txid").is_some());
1096 assert_eq!(tx_data_obj["txid"], "format_test_txid");
1097
1098 let change_data_array = parsed["change_data"].as_array().unwrap();
1099 assert_eq!(change_data_array.len(), 0);
1100 }
1101}
1102
1103pub mod secret_serde {
1105 use super::{Deserialize, Deserializer, Secret, Serialize, Serializer};
1106
1107 pub fn serialize<S>(secret: &Secret<String>, serializer: S) -> Result<S::Ok, S::Error>
1110 where
1111 S: Serializer,
1112 {
1113 use secrecy::ExposeSecret;
1114 secret.expose_secret().serialize(serializer)
1115 }
1116
1117 pub fn deserialize<'de, D>(deserializer: D) -> Result<Secret<String>, D::Error>
1120 where
1121 D: Deserializer<'de>,
1122 {
1123 let s = String::deserialize(deserializer)?;
1124 Ok(Secret::new(s))
1125 }
1126}