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, Serialize, Clone)]
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, Deserialize, Serialize, Clone)]
124pub struct RegisterAssetResponse {
125 pub success: bool,
127 pub message: Option<String>,
129 #[serde(flatten)]
131 pub asset_data: Option<Asset>,
132}
133
134#[derive(Debug, Serialize)]
135pub struct EditAssetRequest {
136 pub issuer_authorization_endpoint: String,
137}
138
139#[derive(Debug, Deserialize)]
140pub struct RegisteredUserResponse {
141 pub id: i64,
142 #[serde(rename = "GAID")]
143 pub gaid: Option<String>,
144 pub is_company: bool,
145 pub name: String,
146 pub categories: Vec<i64>,
147 pub creator: i64,
148}
149
150#[derive(Debug, Serialize)]
151pub struct RegisteredUserAdd {
152 pub name: String,
153 #[serde(rename = "GAID")]
154 pub gaid: Option<String>,
155 pub is_company: bool,
156}
157
158#[derive(Debug, Serialize)]
159pub struct RegisteredUserEdit {
160 pub name: Option<String>,
161}
162
163#[derive(Debug, Serialize)]
164pub struct GaidRequest {
165 #[serde(rename = "GAID")]
166 pub gaid: String,
167}
168
169#[derive(Debug, Serialize)]
170pub struct CategoriesRequest {
171 pub categories: Vec<i64>,
172}
173
174#[derive(Debug, Deserialize)]
175pub struct CategoryResponse {
176 pub id: i64,
177 pub name: String,
178 pub description: Option<String>,
179 pub registered_users: Vec<i64>,
180 pub assets: Vec<String>,
181}
182
183#[derive(Debug, Serialize)]
184pub struct CategoryAdd {
185 pub name: String,
186 pub description: Option<String>,
187}
188
189#[derive(Debug, Serialize)]
190pub struct CategoryEdit {
191 pub name: Option<String>,
192 pub description: Option<String>,
193}
194
195#[derive(Debug, Deserialize)]
196pub struct ValidateGaidResponse {
197 pub is_valid: bool,
198 pub error: Option<String>,
199}
200
201#[derive(Debug, Deserialize)]
202pub struct AddressGaidResponse {
203 pub address: String,
204 pub error: Option<String>,
205}
206
207#[derive(Debug, Deserialize)]
208pub struct Manager {
209 pub username: String,
210 pub id: i64,
211 pub is_locked: bool,
212 pub assets: Vec<String>,
213}
214
215#[derive(Debug, Serialize)]
216pub struct ManagerCreate {
217 pub username: String,
218 pub password: String,
219}
220
221#[derive(Debug, Deserialize, Serialize)]
222#[serde(rename_all = "UPPERCASE")]
223pub enum Status {
224 Unconfirmed,
225 Confirmed,
226}
227
228#[derive(Debug, Deserialize, Serialize)]
229pub struct DistributionAssignment {
230 pub registered_user: i64,
231 pub amount: i64,
232 pub vout: i64,
233}
234
235#[derive(Debug, Deserialize, Serialize)]
236pub struct Transaction {
237 pub txid: String,
238 pub transaction_status: Status,
239 pub included_blockheight: i64,
240 pub confirmed_datetime: String,
241 pub assignments: Vec<DistributionAssignment>,
242}
243
244#[derive(Debug, Deserialize, Serialize)]
245pub struct Distribution {
246 pub distribution_uuid: String,
247 pub distribution_status: Status,
248 pub transactions: Vec<Transaction>,
249}
250
251#[derive(Debug, Serialize, Deserialize, Clone)]
252pub struct CreateAssetAssignmentRequest {
253 pub registered_user: i64,
254 pub amount: i64,
255 #[serde(skip_serializing_if = "Option::is_none")]
256 pub vesting_timestamp: Option<i64>, #[serde(default = "default_ready_for_distribution")]
258 pub ready_for_distribution: bool, }
260
261const fn default_ready_for_distribution() -> bool {
262 false
263}
264
265#[derive(Debug, Serialize)]
266pub struct CreateAssetAssignmentRequestWrapper {
267 pub assignments: Vec<CreateAssetAssignmentRequest>,
268}
269
270#[derive(Debug, Deserialize, Serialize)]
271pub struct Assignment {
272 pub id: i64,
273 pub registered_user: i64,
274 pub amount: i64,
275 pub receiving_address: Option<String>,
276 pub distribution_uuid: Option<String>,
277 pub ready_for_distribution: bool,
278 pub vesting_datetime: Option<String>,
279 pub vesting_timestamp: Option<i64>,
280 pub has_vested: bool,
281 pub is_distributed: bool,
282 pub creator: i64,
283 #[serde(rename = "GAID")]
284 pub gaid: Option<String>,
285 #[serde(skip_serializing_if = "Option::is_none")]
287 pub investor: Option<i64>,
288}
289
290#[derive(Debug, Deserialize, Serialize)]
291pub struct RegisteredUserSummary {
292 pub asset_uuid: String,
293 pub asset_id: String,
294 pub assignments: Vec<Assignment>,
295 pub assignments_sum: i64,
296 pub distributions: Vec<Distribution>,
297 pub distributions_sum: i64,
298 pub balance: i64,
299}
300
301#[derive(Debug, Deserialize, Serialize)]
302pub struct Activity {
303 #[serde(rename = "type")]
304 pub activity_type: String,
305 pub datetime: String,
306 pub description: String,
307 pub txid: String,
308 pub vout: i64,
309 pub blockheight: i64,
310 pub asset_blinder: String,
311 pub amount_blinder: String,
312 #[serde(rename = "registered user")]
313 pub registered_user: Option<i64>,
314 pub amount: i64,
315}
316
317#[derive(Debug, Serialize, Default)]
318pub struct AssetActivityParams {
319 #[serde(skip_serializing_if = "Option::is_none")]
320 pub start: Option<i64>,
321 #[serde(skip_serializing_if = "Option::is_none")]
322 pub count: Option<i64>,
323 #[serde(skip_serializing_if = "Option::is_none")]
324 pub sortcolumn: Option<String>,
325 #[serde(skip_serializing_if = "Option::is_none")]
326 pub sortorder: Option<String>,
327 #[serde(skip_serializing_if = "Option::is_none")]
328 pub height_start: Option<i64>,
329 #[serde(skip_serializing_if = "Option::is_none")]
330 pub height_stop: Option<i64>,
331}
332
333#[derive(Debug, Deserialize, Serialize)]
334pub struct Ownership {
335 pub owner: String,
336 pub amount: i64,
337 #[serde(rename = "GAID")]
338 pub gaid: Option<String>,
339}
340
341#[derive(Debug, Deserialize, Serialize)]
342pub struct Outpoint {
343 pub txid: String,
344 pub vout: i64,
345}
346
347pub type LostOutputs = Vec<Outpoint>;
348
349#[derive(Debug, Deserialize, Serialize)]
350pub struct GaidBalanceEntry {
351 pub asset_uuid: String,
352 pub asset_id: String,
353 pub balance: i64,
354}
355
356pub type Balance = Vec<GaidBalanceEntry>;
357
358#[derive(Debug, Deserialize, Serialize)]
359pub struct AssetLostOutputs {
360 pub lost_outputs: LostOutputs,
361 pub reissuance_lost_outputs: LostOutputs,
362}
363
364#[derive(Debug, Deserialize, Serialize)]
365pub struct AssetSummary {
366 pub asset_id: String,
367 pub reissuance_token_id: Option<String>,
368 pub issued: i64,
369 pub reissued: i64,
370 pub assigned: i64,
371 pub distributed: i64,
372 pub burned: i64,
373 pub blacklisted: i64,
374 pub registered_users: i64,
375 pub active_registered_users: i64,
376 pub active_green_subaccounts: i64,
377 #[serde(rename = "reissuance_tokens")]
378 pub reissuance_tokens: i64,
379}
380
381#[derive(Debug, Deserialize, Serialize)]
382pub struct Utxo {
383 pub txid: String,
384 pub vout: i64,
385 pub asset: String,
386 pub amount: i64,
387 pub registered_user: Option<i64>,
388 pub gaid: Option<String>,
389 pub blacklisted: bool,
390}
391
392#[derive(Debug, Deserialize, Serialize)]
393pub struct Reissuance {
394 pub txid: String,
395 pub vout: i64,
396 pub destination_address: String,
397 pub reissuance_amount: i64,
398 pub confirmed_in_block: String,
399 pub created: String,
400}
401
402#[derive(Debug, Serialize)]
403pub struct ReissueRequest {
404 pub amount_to_reissue: i64,
405}
406
407#[derive(Debug, Serialize)]
408pub struct ReissueConfirmRequest {
409 pub details: serde_json::Value,
410 pub listissuances: Vec<serde_json::Value>,
411 pub reissuance_output: serde_json::Value,
412}
413
414#[derive(Debug, Serialize)]
415pub struct BurnRequest {
416 pub amount: i64,
417}
418
419#[derive(Debug, Deserialize, Serialize)]
420pub struct BurnCreate {
421 pub command: String,
422 pub min_supported_client_script_version: i64,
423 pub base_url: String,
424 pub asset_uuid: String,
425 pub asset_id: String,
426 pub amount: f64,
427 pub utxos: Vec<Outpoint>,
428}
429
430#[derive(Debug, Serialize)]
431pub struct BurnConfirmRequest {
432 pub tx_data: serde_json::Value,
433 pub change_data: Vec<serde_json::Value>,
434}
435
436#[derive(Debug, Serialize)]
437pub struct SetAssetMemoRequest {
438 pub memo: String,
439}
440
441#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
442#[serde(rename_all = "lowercase")]
443pub enum Permission {
444 View,
445 Receive,
446 Transfer,
447 Assign,
448 Distribute,
449 Reissue,
450 Burn,
451 Acquire,
452 Manage,
453 Permissions,
454}
455
456#[derive(Debug, Deserialize, Serialize)]
457pub struct BroadcastResponse {
458 pub txid: String,
459 pub hex: String,
460}
461
462#[derive(Debug, Clone, Serialize, Deserialize)]
464pub struct TokenData {
465 #[serde(with = "secret_serde")]
466 pub token: Secret<String>,
467 pub expires_at: DateTime<Utc>,
468 pub obtained_at: DateTime<Utc>,
469}
470
471impl TokenData {
472 #[must_use]
483 pub fn new(token: String, expires_at: DateTime<Utc>) -> Self {
484 Self {
485 token: Secret::new(token),
486 expires_at,
487 obtained_at: Utc::now(),
488 }
489 }
490
491 #[must_use]
508 pub fn is_expired(&self) -> bool {
509 Utc::now() > self.expires_at
510 }
511
512 #[must_use]
529 pub fn expires_soon(&self, threshold: Duration) -> bool {
530 Utc::now() + threshold > self.expires_at
531 }
532
533 #[must_use]
547 pub fn age(&self) -> Duration {
548 Utc::now() - self.obtained_at
549 }
550}
551
552#[derive(Debug, Clone)]
554pub struct TokenInfo {
555 pub expires_at: DateTime<Utc>,
556 pub obtained_at: DateTime<Utc>,
557 pub expires_in: Duration,
558 pub age: Duration,
559 pub is_expired: bool,
560 pub expires_soon: bool,
561}
562
563impl From<&TokenData> for TokenInfo {
564 fn from(token_data: &TokenData) -> Self {
565 let now = Utc::now();
566 let expires_in = token_data.expires_at - now;
567 let expires_soon_threshold = Duration::minutes(5);
568
569 Self {
570 expires_at: token_data.expires_at,
571 obtained_at: token_data.obtained_at,
572 expires_in,
573 age: token_data.age(),
574 is_expired: token_data.is_expired(),
575 expires_soon: token_data.expires_soon(expires_soon_threshold),
576 }
577 }
578}
579
580#[derive(Debug, Clone, Serialize, Deserialize)]
582pub struct AssetDistributionAssignment {
583 pub user_id: String,
584 pub address: String,
585 pub amount: f64,
586}
587
588#[derive(Debug, Clone, Serialize, Deserialize)]
590pub struct DistributionAssignmentRequest {
591 pub user_uuid: String,
592 pub amount: f64,
593 pub address: String,
594}
595
596#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct CreateDistributionRequest {
599 pub assignments: Vec<DistributionAssignmentRequest>,
600}
601
602#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct Unspent {
605 pub txid: String,
606 pub vout: u32,
607 pub amount: f64,
608 pub asset: String,
609 pub address: String,
610 pub spendable: bool,
611 #[serde(skip_serializing_if = "Option::is_none")]
612 pub confirmations: Option<u32>,
613 #[serde(skip_serializing_if = "Option::is_none")]
614 pub scriptpubkey: Option<String>,
615 #[serde(skip_serializing_if = "Option::is_none")]
616 pub redeemscript: Option<String>,
617 #[serde(skip_serializing_if = "Option::is_none")]
618 pub witnessscript: Option<String>,
619 #[serde(skip_serializing_if = "Option::is_none")]
621 pub amountblinder: Option<String>,
622 #[serde(skip_serializing_if = "Option::is_none")]
624 pub assetblinder: Option<String>,
625}
626
627#[derive(Debug, Clone, Serialize, Deserialize)]
629pub struct TransactionDetail {
630 pub txid: String,
631 pub confirmations: u32,
632 #[serde(skip_serializing_if = "Option::is_none")]
633 pub blockheight: Option<u64>,
634 pub hex: String,
635 #[serde(skip_serializing_if = "Option::is_none")]
636 pub blockhash: Option<String>,
637 #[serde(skip_serializing_if = "Option::is_none")]
638 pub blocktime: Option<i64>,
639 #[serde(skip_serializing_if = "Option::is_none")]
640 pub time: Option<i64>,
641 #[serde(skip_serializing_if = "Option::is_none")]
642 pub timereceived: Option<i64>,
643 #[serde(skip_serializing_if = "Option::is_none")]
645 pub details: Option<Vec<serde_json::Value>>,
646}
647
648#[derive(Debug, Clone, Serialize, Deserialize)]
650pub struct TransactionOutputDetail {
651 pub account: String,
652 pub address: String,
653 pub category: String,
654 pub amount: f64,
655 pub vout: u32,
656 #[serde(skip_serializing_if = "Option::is_none")]
657 pub fee: Option<f64>,
658 #[serde(skip_serializing_if = "Option::is_none")]
659 pub confirmations: Option<u32>,
660 #[serde(skip_serializing_if = "Option::is_none")]
661 pub blockhash: Option<String>,
662 #[serde(skip_serializing_if = "Option::is_none")]
663 pub blockindex: Option<u32>,
664 #[serde(skip_serializing_if = "Option::is_none")]
665 pub blocktime: Option<i64>,
666 #[serde(skip_serializing_if = "Option::is_none")]
667 pub txid: Option<String>,
668 #[serde(skip_serializing_if = "Option::is_none")]
669 pub time: Option<i64>,
670 #[serde(skip_serializing_if = "Option::is_none")]
671 pub timereceived: Option<i64>,
672 #[serde(skip_serializing_if = "Option::is_none")]
673 pub asset: Option<String>,
674 #[serde(skip_serializing_if = "Option::is_none")]
675 pub assetblinder: Option<String>,
676 #[serde(skip_serializing_if = "Option::is_none")]
677 pub amountblinder: Option<String>,
678}
679
680#[derive(Debug, Clone, Serialize, Deserialize)]
682pub struct TxInput {
683 pub txid: String,
684 pub vout: u32,
685 #[serde(skip_serializing_if = "Option::is_none")]
686 pub sequence: Option<u32>,
687}
688
689#[derive(Debug, Clone, Serialize, Deserialize)]
691pub struct DistributionResponse {
692 pub distribution_uuid: String,
693 pub map_address_amount: std::collections::HashMap<String, f64>,
694 pub map_address_asset: std::collections::HashMap<String, String>,
695 pub asset_id: String,
696}
697
698#[derive(Debug, Clone, Serialize, Deserialize)]
700pub struct ReceivedByAddress {
701 #[serde(skip_serializing_if = "Option::is_none")]
702 pub address: Option<String>,
703 #[serde(skip_serializing_if = "Option::is_none")]
704 pub amount: Option<std::collections::HashMap<String, f64>>,
705 #[serde(skip_serializing_if = "Option::is_none")]
706 pub confirmations: Option<u32>,
707 #[serde(skip_serializing_if = "Option::is_none")]
708 pub label: Option<String>,
709 #[serde(skip_serializing_if = "Option::is_none")]
710 pub txids: Option<Vec<String>>,
711}
712
713#[derive(Debug, Clone, Serialize, Deserialize)]
715pub struct DistributionTxData {
716 pub details: TransactionDetail,
717 pub txid: String,
718}
719
720#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct AmpTxData {
723 pub details: serde_json::Value,
725 pub txid: String,
726}
727
728#[derive(Debug, Clone, Serialize, Deserialize)]
730pub struct ConfirmDistributionRequest {
731 pub tx_data: AmpTxData,
732 pub change_data: Vec<Unspent>,
733}
734
735#[cfg(test)]
736mod tests {
737 use super::*;
738
739 #[test]
740 fn test_asset_distribution_assignment_creation() {
741 let assignment = AssetDistributionAssignment {
742 user_id: "user123".to_string(),
743 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
744 amount: 100.5,
745 };
746
747 assert_eq!(assignment.user_id, "user123");
748 assert_eq!(
749 assignment.address,
750 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
751 );
752 assert_eq!(assignment.amount, 100.5);
753 }
754
755 #[test]
756 fn test_asset_distribution_assignment_serialization() {
757 let assignment = AssetDistributionAssignment {
758 user_id: "user123".to_string(),
759 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
760 amount: 100.5,
761 };
762
763 let json = serde_json::to_string(&assignment).unwrap();
765 assert!(json.contains("user123"));
766 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
767 assert!(json.contains("100.5"));
768
769 let deserialized: AssetDistributionAssignment = serde_json::from_str(&json).unwrap();
771 assert_eq!(deserialized.user_id, assignment.user_id);
772 assert_eq!(deserialized.address, assignment.address);
773 assert_eq!(deserialized.amount, assignment.amount);
774 }
775
776 #[test]
777 fn test_asset_distribution_assignment_clone() {
778 let assignment = AssetDistributionAssignment {
779 user_id: "user123".to_string(),
780 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
781 amount: 100.5,
782 };
783
784 let cloned = assignment.clone();
785 assert_eq!(assignment.user_id, cloned.user_id);
786 assert_eq!(assignment.address, cloned.address);
787 assert_eq!(assignment.amount, cloned.amount);
788 }
789
790 #[test]
791 fn test_unspent_creation_and_serialization() {
792 let unspent = Unspent {
793 txid: "abc123def456".to_string(),
794 vout: 1,
795 amount: 50.0,
796 asset: "asset_id_hex".to_string(),
797 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
798 spendable: true,
799 confirmations: Some(6),
800 scriptpubkey: Some("76a914...88ac".to_string()),
801 redeemscript: None,
802 witnessscript: None,
803 amountblinder: Some(
804 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
805 ),
806 assetblinder: Some(
807 "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210".to_string(),
808 ),
809 };
810
811 let json = serde_json::to_string(&unspent).unwrap();
813 assert!(json.contains("abc123def456"));
814 assert!(json.contains("50.0"));
815 assert!(json.contains("asset_id_hex"));
816
817 let deserialized: Unspent = serde_json::from_str(&json).unwrap();
819 assert_eq!(deserialized.txid, unspent.txid);
820 assert_eq!(deserialized.vout, unspent.vout);
821 assert_eq!(deserialized.amount, unspent.amount);
822 assert_eq!(deserialized.asset, unspent.asset);
823 assert_eq!(deserialized.confirmations, unspent.confirmations);
824 }
825
826 #[test]
827 fn test_transaction_detail_creation() {
828 let tx_detail = TransactionDetail {
829 txid: "def456abc123".to_string(),
830 confirmations: 3,
831 blockheight: Some(12345),
832 hex: "020000000001...".to_string(),
833 blockhash: Some("block_hash_hex".to_string()),
834 details: Some(vec![]),
835 blocktime: Some(1640995200),
836 time: Some(1640995200),
837 timereceived: Some(1640995180),
838 };
839
840 assert_eq!(tx_detail.txid, "def456abc123");
841 assert_eq!(tx_detail.confirmations, 3);
842 assert_eq!(tx_detail.blockheight, Some(12345));
843
844 let json = serde_json::to_string(&tx_detail).unwrap();
846 assert!(json.contains("def456abc123"));
847 assert!(json.contains("\"confirmations\":3"));
848 }
849
850 #[test]
851 fn test_tx_input_creation() {
852 let tx_input = TxInput {
853 txid: "input_txid_123".to_string(),
854 vout: 2,
855 sequence: Some(0xffffffff),
856 };
857
858 assert_eq!(tx_input.txid, "input_txid_123");
859 assert_eq!(tx_input.vout, 2);
860 assert_eq!(tx_input.sequence, Some(0xffffffff));
861
862 let json = serde_json::to_string(&tx_input).unwrap();
864 assert!(json.contains("input_txid_123"));
865 assert!(json.contains("\"vout\":2"));
866 assert!(json.contains("4294967295")); }
868
869 #[test]
870 fn test_distribution_response_creation() {
871 let mut map_address_amount = HashMap::new();
872 map_address_amount.insert("address1".to_string(), 100.0);
873 map_address_amount.insert("address2".to_string(), 50.0);
874
875 let mut map_address_asset = HashMap::new();
876 map_address_asset.insert("address1".to_string(), "asset_id_1".to_string());
877 map_address_asset.insert("address2".to_string(), "asset_id_1".to_string());
878
879 let distribution_response = DistributionResponse {
880 distribution_uuid: "dist_uuid_123".to_string(),
881 map_address_amount,
882 map_address_asset,
883 asset_id: "main_asset_id".to_string(),
884 };
885
886 assert_eq!(distribution_response.distribution_uuid, "dist_uuid_123");
887 assert_eq!(distribution_response.asset_id, "main_asset_id");
888 assert_eq!(distribution_response.map_address_amount.len(), 2);
889 assert_eq!(distribution_response.map_address_asset.len(), 2);
890
891 let json = serde_json::to_string(&distribution_response).unwrap();
893 assert!(json.contains("dist_uuid_123"));
894 assert!(json.contains("main_asset_id"));
895 assert!(json.contains("address1"));
896 assert!(json.contains("100.0"));
897 }
898
899 #[test]
900 fn test_distribution_assignment_request_creation() {
901 let assignment_request = DistributionAssignmentRequest {
902 user_uuid: "user_uuid_123".to_string(),
903 amount: 150.0,
904 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
905 };
906
907 assert_eq!(assignment_request.user_uuid, "user_uuid_123");
908 assert_eq!(assignment_request.amount, 150.0);
909 assert_eq!(
910 assignment_request.address,
911 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
912 );
913
914 let json = serde_json::to_string(&assignment_request).unwrap();
916 assert!(json.contains("user_uuid_123"));
917 assert!(json.contains("150.0"));
918 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
919
920 let deserialized: DistributionAssignmentRequest = serde_json::from_str(&json).unwrap();
922 assert_eq!(deserialized.user_uuid, assignment_request.user_uuid);
923 assert_eq!(deserialized.amount, assignment_request.amount);
924 assert_eq!(deserialized.address, assignment_request.address);
925 }
926
927 #[test]
928 fn test_create_distribution_request_creation() {
929 let assignments = vec![
930 DistributionAssignmentRequest {
931 user_uuid: "user1".to_string(),
932 amount: 100.0,
933 address: "address1".to_string(),
934 },
935 DistributionAssignmentRequest {
936 user_uuid: "user2".to_string(),
937 amount: 50.0,
938 address: "address2".to_string(),
939 },
940 ];
941
942 let create_request = CreateDistributionRequest {
943 assignments: assignments.clone(),
944 };
945
946 assert_eq!(create_request.assignments.len(), 2);
947 assert_eq!(create_request.assignments[0].user_uuid, "user1");
948 assert_eq!(create_request.assignments[1].amount, 50.0);
949
950 let json = serde_json::to_string(&create_request).unwrap();
952 assert!(json.contains("user1"));
953 assert!(json.contains("user2"));
954 assert!(json.contains("100.0"));
955 assert!(json.contains("50.0"));
956 assert!(json.contains("assignments"));
957
958 let deserialized: CreateDistributionRequest = serde_json::from_str(&json).unwrap();
960 assert_eq!(deserialized.assignments.len(), 2);
961 assert_eq!(deserialized.assignments[0].user_uuid, "user1");
962 assert_eq!(deserialized.assignments[1].user_uuid, "user2");
963 }
964
965 #[test]
966 fn test_distribution_request_serialization_format() {
967 let assignment = DistributionAssignmentRequest {
968 user_uuid: "test_user".to_string(),
969 amount: 123.45,
970 address: "test_address".to_string(),
971 };
972
973 let request = CreateDistributionRequest {
974 assignments: vec![assignment],
975 };
976
977 let json = serde_json::to_string(&request).unwrap();
978
979 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
981
982 assert!(parsed.get("assignments").is_some());
983 let assignments_array = parsed["assignments"].as_array().unwrap();
984 assert_eq!(assignments_array.len(), 1);
985
986 let first_assignment = &assignments_array[0];
987 assert_eq!(first_assignment["user_uuid"], "test_user");
988 assert_eq!(first_assignment["amount"], 123.45);
989 assert_eq!(first_assignment["address"], "test_address");
990 }
991
992 #[test]
993 fn test_distribution_tx_data_creation() {
994 let tx_detail = TransactionDetail {
995 txid: "test_txid_123".to_string(),
996 confirmations: 2,
997 blockheight: Some(12345),
998 hex: "020000000001...".to_string(),
999 blockhash: Some("block_hash_hex".to_string()),
1000 blocktime: Some(1640995200),
1001 time: Some(1640995200),
1002 timereceived: Some(1640995180),
1003 details: Some(vec![]),
1004 };
1005
1006 let tx_data = DistributionTxData {
1007 details: tx_detail.clone(),
1008 txid: "test_txid_123".to_string(),
1009 };
1010
1011 assert_eq!(tx_data.txid, "test_txid_123");
1012 assert_eq!(tx_data.details.txid, "test_txid_123");
1013 assert_eq!(tx_data.details.confirmations, 2);
1014
1015 let json = serde_json::to_string(&tx_data).unwrap();
1017 assert!(json.contains("test_txid_123"));
1018 assert!(json.contains("\"confirmations\":2"));
1019
1020 let deserialized: DistributionTxData = serde_json::from_str(&json).unwrap();
1022 assert_eq!(deserialized.txid, tx_data.txid);
1023 assert_eq!(
1024 deserialized.details.confirmations,
1025 tx_data.details.confirmations
1026 );
1027 }
1028
1029 #[test]
1030 fn test_confirm_distribution_request_creation() {
1031 let _tx_detail = TransactionDetail {
1032 txid: "confirm_test_txid".to_string(),
1033 confirmations: 3,
1034 blockheight: Some(54321),
1035 hex: "020000000002...".to_string(),
1036 blockhash: Some("confirm_block_hash".to_string()),
1037 blocktime: Some(1640995300),
1038 time: Some(1640995300),
1039 timereceived: Some(1640995280),
1040 details: Some(vec![]),
1041 };
1042
1043 let tx_data = AmpTxData {
1044 details: serde_json::json!([]),
1045 txid: "confirm_test_txid".to_string(),
1046 };
1047
1048 let change_utxo = Unspent {
1049 txid: "change_txid_123".to_string(),
1050 vout: 1,
1051 amount: 25.0,
1052 asset: "change_asset_id".to_string(),
1053 address: "change_address".to_string(),
1054 spendable: true,
1055 confirmations: Some(3),
1056 scriptpubkey: Some("76a914...88ac".to_string()),
1057 redeemscript: None,
1058 witnessscript: None,
1059 amountblinder: Some(
1060 "1111111111111111111111111111111111111111111111111111111111111111".to_string(),
1061 ),
1062 assetblinder: Some(
1063 "2222222222222222222222222222222222222222222222222222222222222222".to_string(),
1064 ),
1065 };
1066
1067 let confirm_request = ConfirmDistributionRequest {
1068 tx_data,
1069 change_data: vec![change_utxo],
1070 };
1071
1072 assert_eq!(confirm_request.tx_data.txid, "confirm_test_txid");
1073 assert_eq!(confirm_request.change_data.len(), 1);
1074 assert_eq!(confirm_request.change_data[0].txid, "change_txid_123");
1075
1076 let json = serde_json::to_string(&confirm_request).unwrap();
1078 assert!(json.contains("confirm_test_txid"));
1079 assert!(json.contains("change_txid_123"));
1080 assert!(json.contains("tx_data"));
1081 assert!(json.contains("change_data"));
1082
1083 let deserialized: ConfirmDistributionRequest = serde_json::from_str(&json).unwrap();
1085 assert_eq!(deserialized.tx_data.txid, confirm_request.tx_data.txid);
1086 assert_eq!(deserialized.change_data.len(), 1);
1087 assert_eq!(deserialized.change_data[0].txid, "change_txid_123");
1088 }
1089
1090 #[test]
1091 fn test_confirm_distribution_request_serialization_format() {
1092 let _tx_detail = TransactionDetail {
1093 txid: "format_test_txid".to_string(),
1094 confirmations: 2,
1095 blockheight: Some(98765),
1096 hex: "format_test_hex".to_string(),
1097 blockhash: None,
1098 blocktime: None,
1099 time: None,
1100 timereceived: None,
1101 details: Some(vec![]),
1102 };
1103
1104 let tx_data = AmpTxData {
1105 details: serde_json::json!([]),
1106 txid: "format_test_txid".to_string(),
1107 };
1108
1109 let confirm_request = ConfirmDistributionRequest {
1110 tx_data,
1111 change_data: vec![],
1112 };
1113
1114 let json = serde_json::to_string(&confirm_request).unwrap();
1115
1116 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1118
1119 assert!(parsed.get("tx_data").is_some());
1120 assert!(parsed.get("change_data").is_some());
1121
1122 let tx_data_obj = &parsed["tx_data"];
1123 assert!(tx_data_obj.get("details").is_some());
1124 assert!(tx_data_obj.get("txid").is_some());
1125 assert_eq!(tx_data_obj["txid"], "format_test_txid");
1126
1127 let change_data_array = parsed["change_data"].as_array().unwrap();
1128 assert_eq!(change_data_array.len(), 0);
1129 }
1130}
1131
1132pub mod secret_serde {
1134 use super::{Deserialize, Deserializer, Secret, Serialize, Serializer};
1135
1136 pub fn serialize<S>(secret: &Secret<String>, serializer: S) -> Result<S::Ok, S::Error>
1139 where
1140 S: Serializer,
1141 {
1142 use secrecy::ExposeSecret;
1143 secret.expose_secret().serialize(serializer)
1144 }
1145
1146 pub fn deserialize<'de, D>(deserializer: D) -> Result<Secret<String>, D::Error>
1149 where
1150 D: Deserializer<'de>,
1151 {
1152 let s = String::deserialize(deserializer)?;
1153 Ok(Secret::new(s))
1154 }
1155}