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, Clone)]
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)]
360pub struct AssetBalanceResponse {
361 #[serde(flatten)]
362 pub balance: std::collections::HashMap<String, serde_json::Value>,
363 pub lost_outputs: LostOutputs,
364 #[serde(default)]
365 pub reissuance_lost_outputs: LostOutputs,
366}
367
368#[derive(Debug, Deserialize, Serialize)]
369pub struct AssetLostOutputs {
370 pub lost_outputs: LostOutputs,
371 pub reissuance_lost_outputs: LostOutputs,
372}
373
374#[derive(Debug, Deserialize, Serialize)]
375pub struct AssetSummary {
376 pub asset_id: String,
377 pub reissuance_token_id: Option<String>,
378 pub issued: i64,
379 pub reissued: i64,
380 pub assigned: i64,
381 pub distributed: i64,
382 pub burned: i64,
383 pub blacklisted: i64,
384 pub registered_users: i64,
385 pub active_registered_users: i64,
386 pub active_green_subaccounts: i64,
387 #[serde(rename = "reissuance_tokens")]
388 pub reissuance_tokens: i64,
389}
390
391#[derive(Debug, Deserialize, Serialize)]
392pub struct Utxo {
393 pub txid: String,
394 pub vout: i64,
395 pub asset: String,
396 pub amount: i64,
397 pub registered_user: Option<i64>,
398 pub gaid: Option<String>,
399 pub blacklisted: bool,
400}
401
402#[derive(Debug, Deserialize, Serialize)]
403pub struct Reissuance {
404 pub txid: String,
405 pub vout: i64,
406 pub destination_address: String,
407 pub reissuance_amount: i64,
408 pub confirmed_in_block: String,
409 pub created: String,
410}
411
412#[derive(Debug, Serialize)]
413pub struct ReissueRequest {
414 pub amount_to_reissue: i64,
415}
416
417#[derive(Debug, Deserialize, Serialize, Clone)]
419pub struct ReissueRequestResponse {
420 pub command: String,
421 pub min_supported_client_script_version: i64,
422 pub base_url: String,
423 pub asset_uuid: String,
424 pub asset_id: String,
425 pub amount: f64,
426 pub reissuance_utxos: Vec<Outpoint>,
427}
428
429#[derive(Debug, Serialize)]
431pub struct ReissueConfirmRequest {
432 pub details: serde_json::Value,
433 pub listissuances: Vec<serde_json::Value>,
434 pub reissuance_output: serde_json::Value,
435}
436
437#[derive(Debug, Deserialize, Serialize)]
439pub struct ReissueResponse {
440 pub txid: String,
441 pub vin: i64,
442 pub reissuance_amount: i64,
443}
444
445#[derive(Debug, Serialize)]
446pub struct BurnRequest {
447 pub amount: i64,
448}
449
450#[derive(Debug, Deserialize, Serialize)]
451pub struct BurnCreate {
452 pub command: String,
453 pub min_supported_client_script_version: i64,
454 pub base_url: String,
455 pub asset_uuid: String,
456 pub asset_id: String,
457 pub amount: f64,
458 pub utxos: Vec<Outpoint>,
459}
460
461#[derive(Debug, Deserialize, Serialize)]
462pub struct BurnResponse {
463 pub success: bool,
464 #[serde(default)]
465 pub message: Option<String>,
466}
467
468#[derive(Debug, Serialize)]
469pub struct BurnConfirmRequest {
470 pub tx_data: serde_json::Value,
471 pub change_data: Vec<serde_json::Value>,
472}
473
474#[derive(Debug, Serialize)]
475pub struct SetAssetMemoRequest {
476 pub memo: String,
477}
478
479#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, Eq)]
480#[serde(rename_all = "lowercase")]
481pub enum Permission {
482 View,
483 Receive,
484 Transfer,
485 Assign,
486 Distribute,
487 Reissue,
488 Burn,
489 Acquire,
490 Manage,
491 Permissions,
492}
493
494#[derive(Debug, Deserialize, Serialize)]
495pub struct BroadcastResponse {
496 pub txid: String,
497 pub hex: String,
498}
499
500#[derive(Debug, Clone, Serialize, Deserialize)]
502pub struct TokenData {
503 #[serde(with = "secret_serde")]
504 pub token: Secret<String>,
505 pub expires_at: DateTime<Utc>,
506 pub obtained_at: DateTime<Utc>,
507}
508
509impl TokenData {
510 #[must_use]
521 pub fn new(token: String, expires_at: DateTime<Utc>) -> Self {
522 Self {
523 token: Secret::new(token),
524 expires_at,
525 obtained_at: Utc::now(),
526 }
527 }
528
529 #[must_use]
546 pub fn is_expired(&self) -> bool {
547 Utc::now() > self.expires_at
548 }
549
550 #[must_use]
567 pub fn expires_soon(&self, threshold: Duration) -> bool {
568 Utc::now() + threshold > self.expires_at
569 }
570
571 #[must_use]
585 pub fn age(&self) -> Duration {
586 Utc::now() - self.obtained_at
587 }
588}
589
590#[derive(Debug, Clone)]
592pub struct TokenInfo {
593 pub expires_at: DateTime<Utc>,
594 pub obtained_at: DateTime<Utc>,
595 pub expires_in: Duration,
596 pub age: Duration,
597 pub is_expired: bool,
598 pub expires_soon: bool,
599}
600
601impl From<&TokenData> for TokenInfo {
602 fn from(token_data: &TokenData) -> Self {
603 let now = Utc::now();
604 let expires_in = token_data.expires_at - now;
605 let expires_soon_threshold = Duration::minutes(5);
606
607 Self {
608 expires_at: token_data.expires_at,
609 obtained_at: token_data.obtained_at,
610 expires_in,
611 age: token_data.age(),
612 is_expired: token_data.is_expired(),
613 expires_soon: token_data.expires_soon(expires_soon_threshold),
614 }
615 }
616}
617
618#[derive(Debug, Clone, Serialize, Deserialize)]
620pub struct AssetDistributionAssignment {
621 pub user_id: String,
622 pub address: String,
623 pub amount: f64,
624}
625
626#[derive(Debug, Clone, Serialize, Deserialize)]
628pub struct DistributionAssignmentRequest {
629 pub user_uuid: String,
630 pub amount: f64,
631 pub address: String,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
636pub struct CreateDistributionRequest {
637 pub assignments: Vec<DistributionAssignmentRequest>,
638}
639
640#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct Unspent {
643 pub txid: String,
644 pub vout: u32,
645 pub amount: f64,
646 pub asset: String,
647 pub address: String,
648 pub spendable: bool,
649 #[serde(skip_serializing_if = "Option::is_none")]
650 pub confirmations: Option<u32>,
651 #[serde(skip_serializing_if = "Option::is_none")]
652 pub scriptpubkey: Option<String>,
653 #[serde(skip_serializing_if = "Option::is_none")]
654 pub redeemscript: Option<String>,
655 #[serde(skip_serializing_if = "Option::is_none")]
656 pub witnessscript: Option<String>,
657 #[serde(skip_serializing_if = "Option::is_none")]
659 pub amountblinder: Option<String>,
660 #[serde(skip_serializing_if = "Option::is_none")]
662 pub assetblinder: Option<String>,
663}
664
665#[derive(Debug, Clone, Serialize, Deserialize)]
667pub struct TransactionDetail {
668 pub txid: String,
669 pub confirmations: u32,
670 #[serde(skip_serializing_if = "Option::is_none")]
671 pub blockheight: Option<u64>,
672 pub hex: String,
673 #[serde(skip_serializing_if = "Option::is_none")]
674 pub blockhash: Option<String>,
675 #[serde(skip_serializing_if = "Option::is_none")]
676 pub blocktime: Option<i64>,
677 #[serde(skip_serializing_if = "Option::is_none")]
678 pub time: Option<i64>,
679 #[serde(skip_serializing_if = "Option::is_none")]
680 pub timereceived: Option<i64>,
681 #[serde(skip_serializing_if = "Option::is_none")]
683 pub details: Option<Vec<serde_json::Value>>,
684}
685
686#[derive(Debug, Clone, Serialize, Deserialize)]
688pub struct TransactionOutputDetail {
689 pub account: String,
690 pub address: String,
691 pub category: String,
692 pub amount: f64,
693 pub vout: u32,
694 #[serde(skip_serializing_if = "Option::is_none")]
695 pub fee: Option<f64>,
696 #[serde(skip_serializing_if = "Option::is_none")]
697 pub confirmations: Option<u32>,
698 #[serde(skip_serializing_if = "Option::is_none")]
699 pub blockhash: Option<String>,
700 #[serde(skip_serializing_if = "Option::is_none")]
701 pub blockindex: Option<u32>,
702 #[serde(skip_serializing_if = "Option::is_none")]
703 pub blocktime: Option<i64>,
704 #[serde(skip_serializing_if = "Option::is_none")]
705 pub txid: Option<String>,
706 #[serde(skip_serializing_if = "Option::is_none")]
707 pub time: Option<i64>,
708 #[serde(skip_serializing_if = "Option::is_none")]
709 pub timereceived: Option<i64>,
710 #[serde(skip_serializing_if = "Option::is_none")]
711 pub asset: Option<String>,
712 #[serde(skip_serializing_if = "Option::is_none")]
713 pub assetblinder: Option<String>,
714 #[serde(skip_serializing_if = "Option::is_none")]
715 pub amountblinder: Option<String>,
716}
717
718#[derive(Debug, Clone, Serialize, Deserialize)]
720pub struct TxInput {
721 pub txid: String,
722 pub vout: u32,
723 #[serde(skip_serializing_if = "Option::is_none")]
724 pub sequence: Option<u32>,
725}
726
727#[derive(Debug, Clone, Serialize, Deserialize)]
729pub struct DistributionResponse {
730 pub distribution_uuid: String,
731 pub map_address_amount: std::collections::HashMap<String, f64>,
732 pub map_address_asset: std::collections::HashMap<String, String>,
733 pub asset_id: String,
734}
735
736#[derive(Debug, Clone, Serialize, Deserialize)]
738pub struct ReceivedByAddress {
739 #[serde(skip_serializing_if = "Option::is_none")]
740 pub address: Option<String>,
741 #[serde(skip_serializing_if = "Option::is_none")]
742 pub amount: Option<std::collections::HashMap<String, f64>>,
743 #[serde(skip_serializing_if = "Option::is_none")]
744 pub confirmations: Option<u32>,
745 #[serde(skip_serializing_if = "Option::is_none")]
746 pub label: Option<String>,
747 #[serde(skip_serializing_if = "Option::is_none")]
748 pub txids: Option<Vec<String>>,
749}
750
751#[derive(Debug, Clone, Serialize, Deserialize)]
753pub struct DistributionTxData {
754 pub details: TransactionDetail,
755 pub txid: String,
756}
757
758#[derive(Debug, Clone, Serialize, Deserialize)]
760pub struct AmpTxData {
761 pub details: serde_json::Value,
763 pub txid: String,
764}
765
766#[derive(Debug, Clone, Serialize, Deserialize)]
768pub struct ConfirmDistributionRequest {
769 pub tx_data: AmpTxData,
770 pub change_data: Vec<Unspent>,
771}
772
773#[cfg(test)]
774mod tests {
775 use super::*;
776
777 #[test]
778 fn test_asset_distribution_assignment_creation() {
779 let assignment = AssetDistributionAssignment {
780 user_id: "user123".to_string(),
781 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
782 amount: 100.5,
783 };
784
785 assert_eq!(assignment.user_id, "user123");
786 assert_eq!(
787 assignment.address,
788 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
789 );
790 assert_eq!(assignment.amount, 100.5);
791 }
792
793 #[test]
794 fn test_asset_distribution_assignment_serialization() {
795 let assignment = AssetDistributionAssignment {
796 user_id: "user123".to_string(),
797 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
798 amount: 100.5,
799 };
800
801 let json = serde_json::to_string(&assignment).unwrap();
803 assert!(json.contains("user123"));
804 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
805 assert!(json.contains("100.5"));
806
807 let deserialized: AssetDistributionAssignment = serde_json::from_str(&json).unwrap();
809 assert_eq!(deserialized.user_id, assignment.user_id);
810 assert_eq!(deserialized.address, assignment.address);
811 assert_eq!(deserialized.amount, assignment.amount);
812 }
813
814 #[test]
815 fn test_asset_distribution_assignment_clone() {
816 let assignment = AssetDistributionAssignment {
817 user_id: "user123".to_string(),
818 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
819 amount: 100.5,
820 };
821
822 let cloned = assignment.clone();
823 assert_eq!(assignment.user_id, cloned.user_id);
824 assert_eq!(assignment.address, cloned.address);
825 assert_eq!(assignment.amount, cloned.amount);
826 }
827
828 #[test]
829 fn test_unspent_creation_and_serialization() {
830 let unspent = Unspent {
831 txid: "abc123def456".to_string(),
832 vout: 1,
833 amount: 50.0,
834 asset: "asset_id_hex".to_string(),
835 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
836 spendable: true,
837 confirmations: Some(6),
838 scriptpubkey: Some("76a914...88ac".to_string()),
839 redeemscript: None,
840 witnessscript: None,
841 amountblinder: Some(
842 "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef".to_string(),
843 ),
844 assetblinder: Some(
845 "fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210".to_string(),
846 ),
847 };
848
849 let json = serde_json::to_string(&unspent).unwrap();
851 assert!(json.contains("abc123def456"));
852 assert!(json.contains("50.0"));
853 assert!(json.contains("asset_id_hex"));
854
855 let deserialized: Unspent = serde_json::from_str(&json).unwrap();
857 assert_eq!(deserialized.txid, unspent.txid);
858 assert_eq!(deserialized.vout, unspent.vout);
859 assert_eq!(deserialized.amount, unspent.amount);
860 assert_eq!(deserialized.asset, unspent.asset);
861 assert_eq!(deserialized.confirmations, unspent.confirmations);
862 }
863
864 #[test]
865 fn test_transaction_detail_creation() {
866 let tx_detail = TransactionDetail {
867 txid: "def456abc123".to_string(),
868 confirmations: 3,
869 blockheight: Some(12345),
870 hex: "020000000001...".to_string(),
871 blockhash: Some("block_hash_hex".to_string()),
872 details: Some(vec![]),
873 blocktime: Some(1640995200),
874 time: Some(1640995200),
875 timereceived: Some(1640995180),
876 };
877
878 assert_eq!(tx_detail.txid, "def456abc123");
879 assert_eq!(tx_detail.confirmations, 3);
880 assert_eq!(tx_detail.blockheight, Some(12345));
881
882 let json = serde_json::to_string(&tx_detail).unwrap();
884 assert!(json.contains("def456abc123"));
885 assert!(json.contains("\"confirmations\":3"));
886 }
887
888 #[test]
889 fn test_tx_input_creation() {
890 let tx_input = TxInput {
891 txid: "input_txid_123".to_string(),
892 vout: 2,
893 sequence: Some(0xffffffff),
894 };
895
896 assert_eq!(tx_input.txid, "input_txid_123");
897 assert_eq!(tx_input.vout, 2);
898 assert_eq!(tx_input.sequence, Some(0xffffffff));
899
900 let json = serde_json::to_string(&tx_input).unwrap();
902 assert!(json.contains("input_txid_123"));
903 assert!(json.contains("\"vout\":2"));
904 assert!(json.contains("4294967295")); }
906
907 #[test]
908 fn test_distribution_response_creation() {
909 let mut map_address_amount = HashMap::new();
910 map_address_amount.insert("address1".to_string(), 100.0);
911 map_address_amount.insert("address2".to_string(), 50.0);
912
913 let mut map_address_asset = HashMap::new();
914 map_address_asset.insert("address1".to_string(), "asset_id_1".to_string());
915 map_address_asset.insert("address2".to_string(), "asset_id_1".to_string());
916
917 let distribution_response = DistributionResponse {
918 distribution_uuid: "dist_uuid_123".to_string(),
919 map_address_amount,
920 map_address_asset,
921 asset_id: "main_asset_id".to_string(),
922 };
923
924 assert_eq!(distribution_response.distribution_uuid, "dist_uuid_123");
925 assert_eq!(distribution_response.asset_id, "main_asset_id");
926 assert_eq!(distribution_response.map_address_amount.len(), 2);
927 assert_eq!(distribution_response.map_address_asset.len(), 2);
928
929 let json = serde_json::to_string(&distribution_response).unwrap();
931 assert!(json.contains("dist_uuid_123"));
932 assert!(json.contains("main_asset_id"));
933 assert!(json.contains("address1"));
934 assert!(json.contains("100.0"));
935 }
936
937 #[test]
938 fn test_distribution_assignment_request_creation() {
939 let assignment_request = DistributionAssignmentRequest {
940 user_uuid: "user_uuid_123".to_string(),
941 amount: 150.0,
942 address: "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq".to_string(),
943 };
944
945 assert_eq!(assignment_request.user_uuid, "user_uuid_123");
946 assert_eq!(assignment_request.amount, 150.0);
947 assert_eq!(
948 assignment_request.address,
949 "lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"
950 );
951
952 let json = serde_json::to_string(&assignment_request).unwrap();
954 assert!(json.contains("user_uuid_123"));
955 assert!(json.contains("150.0"));
956 assert!(json.contains("lq1qq2xvpcvfup5j8zscjq05u2wxxjcyewk7979f9lq"));
957
958 let deserialized: DistributionAssignmentRequest = serde_json::from_str(&json).unwrap();
960 assert_eq!(deserialized.user_uuid, assignment_request.user_uuid);
961 assert_eq!(deserialized.amount, assignment_request.amount);
962 assert_eq!(deserialized.address, assignment_request.address);
963 }
964
965 #[test]
966 fn test_create_distribution_request_creation() {
967 let assignments = vec![
968 DistributionAssignmentRequest {
969 user_uuid: "user1".to_string(),
970 amount: 100.0,
971 address: "address1".to_string(),
972 },
973 DistributionAssignmentRequest {
974 user_uuid: "user2".to_string(),
975 amount: 50.0,
976 address: "address2".to_string(),
977 },
978 ];
979
980 let create_request = CreateDistributionRequest {
981 assignments: assignments.clone(),
982 };
983
984 assert_eq!(create_request.assignments.len(), 2);
985 assert_eq!(create_request.assignments[0].user_uuid, "user1");
986 assert_eq!(create_request.assignments[1].amount, 50.0);
987
988 let json = serde_json::to_string(&create_request).unwrap();
990 assert!(json.contains("user1"));
991 assert!(json.contains("user2"));
992 assert!(json.contains("100.0"));
993 assert!(json.contains("50.0"));
994 assert!(json.contains("assignments"));
995
996 let deserialized: CreateDistributionRequest = serde_json::from_str(&json).unwrap();
998 assert_eq!(deserialized.assignments.len(), 2);
999 assert_eq!(deserialized.assignments[0].user_uuid, "user1");
1000 assert_eq!(deserialized.assignments[1].user_uuid, "user2");
1001 }
1002
1003 #[test]
1004 fn test_distribution_request_serialization_format() {
1005 let assignment = DistributionAssignmentRequest {
1006 user_uuid: "test_user".to_string(),
1007 amount: 123.45,
1008 address: "test_address".to_string(),
1009 };
1010
1011 let request = CreateDistributionRequest {
1012 assignments: vec![assignment],
1013 };
1014
1015 let json = serde_json::to_string(&request).unwrap();
1016
1017 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1019
1020 assert!(parsed.get("assignments").is_some());
1021 let assignments_array = parsed["assignments"].as_array().unwrap();
1022 assert_eq!(assignments_array.len(), 1);
1023
1024 let first_assignment = &assignments_array[0];
1025 assert_eq!(first_assignment["user_uuid"], "test_user");
1026 assert_eq!(first_assignment["amount"], 123.45);
1027 assert_eq!(first_assignment["address"], "test_address");
1028 }
1029
1030 #[test]
1031 fn test_distribution_tx_data_creation() {
1032 let tx_detail = TransactionDetail {
1033 txid: "test_txid_123".to_string(),
1034 confirmations: 2,
1035 blockheight: Some(12345),
1036 hex: "020000000001...".to_string(),
1037 blockhash: Some("block_hash_hex".to_string()),
1038 blocktime: Some(1640995200),
1039 time: Some(1640995200),
1040 timereceived: Some(1640995180),
1041 details: Some(vec![]),
1042 };
1043
1044 let tx_data = DistributionTxData {
1045 details: tx_detail.clone(),
1046 txid: "test_txid_123".to_string(),
1047 };
1048
1049 assert_eq!(tx_data.txid, "test_txid_123");
1050 assert_eq!(tx_data.details.txid, "test_txid_123");
1051 assert_eq!(tx_data.details.confirmations, 2);
1052
1053 let json = serde_json::to_string(&tx_data).unwrap();
1055 assert!(json.contains("test_txid_123"));
1056 assert!(json.contains("\"confirmations\":2"));
1057
1058 let deserialized: DistributionTxData = serde_json::from_str(&json).unwrap();
1060 assert_eq!(deserialized.txid, tx_data.txid);
1061 assert_eq!(
1062 deserialized.details.confirmations,
1063 tx_data.details.confirmations
1064 );
1065 }
1066
1067 #[test]
1068 fn test_confirm_distribution_request_creation() {
1069 let _tx_detail = TransactionDetail {
1070 txid: "confirm_test_txid".to_string(),
1071 confirmations: 3,
1072 blockheight: Some(54321),
1073 hex: "020000000002...".to_string(),
1074 blockhash: Some("confirm_block_hash".to_string()),
1075 blocktime: Some(1640995300),
1076 time: Some(1640995300),
1077 timereceived: Some(1640995280),
1078 details: Some(vec![]),
1079 };
1080
1081 let tx_data = AmpTxData {
1082 details: serde_json::json!([]),
1083 txid: "confirm_test_txid".to_string(),
1084 };
1085
1086 let change_utxo = Unspent {
1087 txid: "change_txid_123".to_string(),
1088 vout: 1,
1089 amount: 25.0,
1090 asset: "change_asset_id".to_string(),
1091 address: "change_address".to_string(),
1092 spendable: true,
1093 confirmations: Some(3),
1094 scriptpubkey: Some("76a914...88ac".to_string()),
1095 redeemscript: None,
1096 witnessscript: None,
1097 amountblinder: Some(
1098 "1111111111111111111111111111111111111111111111111111111111111111".to_string(),
1099 ),
1100 assetblinder: Some(
1101 "2222222222222222222222222222222222222222222222222222222222222222".to_string(),
1102 ),
1103 };
1104
1105 let confirm_request = ConfirmDistributionRequest {
1106 tx_data,
1107 change_data: vec![change_utxo],
1108 };
1109
1110 assert_eq!(confirm_request.tx_data.txid, "confirm_test_txid");
1111 assert_eq!(confirm_request.change_data.len(), 1);
1112 assert_eq!(confirm_request.change_data[0].txid, "change_txid_123");
1113
1114 let json = serde_json::to_string(&confirm_request).unwrap();
1116 assert!(json.contains("confirm_test_txid"));
1117 assert!(json.contains("change_txid_123"));
1118 assert!(json.contains("tx_data"));
1119 assert!(json.contains("change_data"));
1120
1121 let deserialized: ConfirmDistributionRequest = serde_json::from_str(&json).unwrap();
1123 assert_eq!(deserialized.tx_data.txid, confirm_request.tx_data.txid);
1124 assert_eq!(deserialized.change_data.len(), 1);
1125 assert_eq!(deserialized.change_data[0].txid, "change_txid_123");
1126 }
1127
1128 #[test]
1129 fn test_confirm_distribution_request_serialization_format() {
1130 let _tx_detail = TransactionDetail {
1131 txid: "format_test_txid".to_string(),
1132 confirmations: 2,
1133 blockheight: Some(98765),
1134 hex: "format_test_hex".to_string(),
1135 blockhash: None,
1136 blocktime: None,
1137 time: None,
1138 timereceived: None,
1139 details: Some(vec![]),
1140 };
1141
1142 let tx_data = AmpTxData {
1143 details: serde_json::json!([]),
1144 txid: "format_test_txid".to_string(),
1145 };
1146
1147 let confirm_request = ConfirmDistributionRequest {
1148 tx_data,
1149 change_data: vec![],
1150 };
1151
1152 let json = serde_json::to_string(&confirm_request).unwrap();
1153
1154 let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
1156
1157 assert!(parsed.get("tx_data").is_some());
1158 assert!(parsed.get("change_data").is_some());
1159
1160 let tx_data_obj = &parsed["tx_data"];
1161 assert!(tx_data_obj.get("details").is_some());
1162 assert!(tx_data_obj.get("txid").is_some());
1163 assert_eq!(tx_data_obj["txid"], "format_test_txid");
1164
1165 let change_data_array = parsed["change_data"].as_array().unwrap();
1166 assert_eq!(change_data_array.len(), 0);
1167 }
1168}
1169
1170pub mod secret_serde {
1172 use super::{Deserialize, Deserializer, Secret, Serialize, Serializer};
1173
1174 pub fn serialize<S>(secret: &Secret<String>, serializer: S) -> Result<S::Ok, S::Error>
1177 where
1178 S: Serializer,
1179 {
1180 use secrecy::ExposeSecret;
1181 secret.expose_secret().serialize(serializer)
1182 }
1183
1184 pub fn deserialize<'de, D>(deserializer: D) -> Result<Secret<String>, D::Error>
1187 where
1188 D: Deserializer<'de>,
1189 {
1190 let s = String::deserialize(deserializer)?;
1191 Ok(Secret::new(s))
1192 }
1193}