1#![allow(unused_imports)]
15use async_trait::async_trait;
16use derive_builder::Builder;
17use reqwest;
18use rust_decimal::prelude::*;
19use serde::{Deserialize, Serialize};
20use serde_json::{Value, json};
21use std::collections::BTreeMap;
22
23use crate::common::{
24 config::ConfigurationRestApi,
25 models::{ParamBuildError, RestApiResponse},
26 utils::send_request,
27};
28use crate::w3w_prediction::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait TransferApi: Send + Sync {
34 async fn apply_mm_deposit(
35 &self,
36 params: ApplyMmDepositParams,
37 ) -> anyhow::Result<RestApiResponse<models::ApplyMmDepositResponse>>;
38 async fn apply_mm_withdraw(
39 &self,
40 params: ApplyMmWithdrawParams,
41 ) -> anyhow::Result<RestApiResponse<models::ApplyMmWithdrawResponse>>;
42 async fn create_inbound_transfer(
43 &self,
44 params: CreateInboundTransferParams,
45 ) -> anyhow::Result<RestApiResponse<models::CreateInboundTransferResponse>>;
46 async fn create_outbound_transfer(
47 &self,
48 params: CreateOutboundTransferParams,
49 ) -> anyhow::Result<RestApiResponse<models::CreateOutboundTransferResponse>>;
50 async fn query_transfer_list(
51 &self,
52 params: QueryTransferListParams,
53 ) -> anyhow::Result<RestApiResponse<models::QueryTransferListResponse>>;
54 async fn query_transfer_status(
55 &self,
56 params: QueryTransferStatusParams,
57 ) -> anyhow::Result<RestApiResponse<models::QueryTransferStatusResponse>>;
58}
59
60#[derive(Debug, Clone)]
61pub struct TransferApiClient {
62 configuration: ConfigurationRestApi,
63}
64
65impl TransferApiClient {
66 pub fn new(configuration: ConfigurationRestApi) -> Self {
67 Self { configuration }
68 }
69}
70
71#[allow(non_camel_case_types)]
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub enum ApplyMmDepositAccountTypeEnum {
74 #[serde(rename = "SPOT")]
75 Spot,
76 #[serde(rename = "FUNDING")]
77 Funding,
78}
79
80impl ApplyMmDepositAccountTypeEnum {
81 #[must_use]
82 pub fn as_str(&self) -> &'static str {
83 match self {
84 Self::Spot => "SPOT",
85 Self::Funding => "FUNDING",
86 }
87 }
88}
89
90impl std::str::FromStr for ApplyMmDepositAccountTypeEnum {
91 type Err = Box<dyn std::error::Error + Send + Sync>;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 match s {
95 "SPOT" => Ok(Self::Spot),
96 "FUNDING" => Ok(Self::Funding),
97 other => Err(format!("invalid ApplyMmDepositAccountTypeEnum: {}", other).into()),
98 }
99 }
100}
101
102#[allow(non_camel_case_types)]
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub enum ApplyMmWithdrawWalletTypeEnum {
105 #[serde(rename = "0")]
106 WalletType0,
107 #[serde(rename = "1")]
108 WalletType1,
109}
110
111impl ApplyMmWithdrawWalletTypeEnum {
112 #[must_use]
113 pub fn as_str(&self) -> &'static str {
114 match self {
115 Self::WalletType0 => "0",
116 Self::WalletType1 => "1",
117 }
118 }
119}
120
121impl std::str::FromStr for ApplyMmWithdrawWalletTypeEnum {
122 type Err = Box<dyn std::error::Error + Send + Sync>;
123
124 fn from_str(s: &str) -> Result<Self, Self::Err> {
125 match s {
126 "0" => Ok(Self::WalletType0),
127 "1" => Ok(Self::WalletType1),
128 other => Err(format!("invalid ApplyMmWithdrawWalletTypeEnum: {}", other).into()),
129 }
130 }
131}
132
133#[allow(non_camel_case_types)]
134#[derive(Debug, Clone, Serialize, Deserialize)]
135pub enum CreateInboundTransferAccountTypeEnum {
136 #[serde(rename = "SPOT")]
137 Spot,
138 #[serde(rename = "FUNDING")]
139 Funding,
140}
141
142impl CreateInboundTransferAccountTypeEnum {
143 #[must_use]
144 pub fn as_str(&self) -> &'static str {
145 match self {
146 Self::Spot => "SPOT",
147 Self::Funding => "FUNDING",
148 }
149 }
150}
151
152impl std::str::FromStr for CreateInboundTransferAccountTypeEnum {
153 type Err = Box<dyn std::error::Error + Send + Sync>;
154
155 fn from_str(s: &str) -> Result<Self, Self::Err> {
156 match s {
157 "SPOT" => Ok(Self::Spot),
158 "FUNDING" => Ok(Self::Funding),
159 other => Err(format!("invalid CreateInboundTransferAccountTypeEnum: {}", other).into()),
160 }
161 }
162}
163
164#[allow(non_camel_case_types)]
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub enum CreateOutboundTransferAccountTypeEnum {
167 #[serde(rename = "SPOT")]
168 Spot,
169 #[serde(rename = "FUNDING")]
170 Funding,
171}
172
173impl CreateOutboundTransferAccountTypeEnum {
174 #[must_use]
175 pub fn as_str(&self) -> &'static str {
176 match self {
177 Self::Spot => "SPOT",
178 Self::Funding => "FUNDING",
179 }
180 }
181}
182
183impl std::str::FromStr for CreateOutboundTransferAccountTypeEnum {
184 type Err = Box<dyn std::error::Error + Send + Sync>;
185
186 fn from_str(s: &str) -> Result<Self, Self::Err> {
187 match s {
188 "SPOT" => Ok(Self::Spot),
189 "FUNDING" => Ok(Self::Funding),
190 other => {
191 Err(format!("invalid CreateOutboundTransferAccountTypeEnum: {}", other).into())
192 }
193 }
194 }
195}
196
197#[allow(non_camel_case_types)]
198#[derive(Debug, Clone, Serialize, Deserialize)]
199pub enum CreateOutboundTransferSourceBizEnum {
200 #[serde(rename = "USER_TRANSFER")]
201 UserTransfer,
202 #[serde(rename = "PREDICTION_BUY")]
203 PredictionBuy,
204}
205
206impl CreateOutboundTransferSourceBizEnum {
207 #[must_use]
208 pub fn as_str(&self) -> &'static str {
209 match self {
210 Self::UserTransfer => "USER_TRANSFER",
211 Self::PredictionBuy => "PREDICTION_BUY",
212 }
213 }
214}
215
216impl std::str::FromStr for CreateOutboundTransferSourceBizEnum {
217 type Err = Box<dyn std::error::Error + Send + Sync>;
218
219 fn from_str(s: &str) -> Result<Self, Self::Err> {
220 match s {
221 "USER_TRANSFER" => Ok(Self::UserTransfer),
222 "PREDICTION_BUY" => Ok(Self::PredictionBuy),
223 other => Err(format!("invalid CreateOutboundTransferSourceBizEnum: {}", other).into()),
224 }
225 }
226}
227
228#[allow(non_camel_case_types)]
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub enum QueryTransferListDirectionEnum {
231 #[serde(rename = "INBOUND")]
232 Inbound,
233 #[serde(rename = "OUTBOUND")]
234 Outbound,
235}
236
237impl QueryTransferListDirectionEnum {
238 #[must_use]
239 pub fn as_str(&self) -> &'static str {
240 match self {
241 Self::Inbound => "INBOUND",
242 Self::Outbound => "OUTBOUND",
243 }
244 }
245}
246
247impl std::str::FromStr for QueryTransferListDirectionEnum {
248 type Err = Box<dyn std::error::Error + Send + Sync>;
249
250 fn from_str(s: &str) -> Result<Self, Self::Err> {
251 match s {
252 "INBOUND" => Ok(Self::Inbound),
253 "OUTBOUND" => Ok(Self::Outbound),
254 other => Err(format!("invalid QueryTransferListDirectionEnum: {}", other).into()),
255 }
256 }
257}
258
259#[derive(Clone, Debug, Builder, Deserialize)]
264#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
265pub struct ApplyMmDepositParams {
266 #[builder(setter(into))]
270 #[serde(rename = "fromToken")]
271 pub from_token: String,
272 #[builder(setter(into))]
276 #[serde(rename = "fromTokenAmount")]
277 pub from_token_amount: String,
278 #[builder(setter(into))]
282 #[serde(rename = "toToken")]
283 pub to_token: String,
284 #[builder(setter(into))]
288 #[serde(rename = "accountType")]
289 pub account_type: ApplyMmDepositAccountTypeEnum,
290 #[builder(setter(into), default)]
294 #[serde(rename = "chainId", default)]
295 pub chain_id: Option<String>,
296}
297
298impl ApplyMmDepositParams {
299 #[must_use]
309 pub fn builder(
310 from_token: String,
311 from_token_amount: String,
312 to_token: String,
313 account_type: ApplyMmDepositAccountTypeEnum,
314 ) -> ApplyMmDepositParamsBuilder {
315 ApplyMmDepositParamsBuilder::default()
316 .from_token(from_token)
317 .from_token_amount(from_token_amount)
318 .to_token(to_token)
319 .account_type(account_type)
320 }
321}
322#[derive(Clone, Debug, Builder, Deserialize)]
327#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
328pub struct ApplyMmWithdrawParams {
329 #[builder(setter(into))]
333 #[serde(rename = "coin")]
334 pub coin: String,
335 #[builder(setter(into))]
339 #[serde(rename = "network")]
340 pub network: String,
341 #[builder(setter(into))]
345 #[serde(rename = "amount")]
346 pub amount: String,
347 #[builder(setter(into), default)]
351 #[serde(rename = "withdrawOrderId", default)]
352 pub withdraw_order_id: Option<String>,
353 #[builder(setter(into), default)]
357 #[serde(rename = "walletType", default)]
358 pub wallet_type: Option<ApplyMmWithdrawWalletTypeEnum>,
359 #[builder(setter(into), default)]
363 #[serde(rename = "name", default)]
364 pub name: Option<String>,
365}
366
367impl ApplyMmWithdrawParams {
368 #[must_use]
377 pub fn builder(coin: String, network: String, amount: String) -> ApplyMmWithdrawParamsBuilder {
378 ApplyMmWithdrawParamsBuilder::default()
379 .coin(coin)
380 .network(network)
381 .amount(amount)
382 }
383}
384#[derive(Clone, Debug, Builder, Deserialize)]
389#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
390pub struct CreateInboundTransferParams {
391 #[builder(setter(into))]
395 #[serde(rename = "walletId")]
396 pub wallet_id: String,
397 #[builder(setter(into))]
401 #[serde(rename = "walletAddress")]
402 pub wallet_address: String,
403 #[builder(setter(into))]
407 #[serde(rename = "fromTokenAmount")]
408 pub from_token_amount: String,
409 #[builder(setter(into))]
413 #[serde(rename = "accountType")]
414 pub account_type: CreateInboundTransferAccountTypeEnum,
415 #[builder(setter(into), default)]
419 #[serde(rename = "fromToken", default)]
420 pub from_token: Option<String>,
421 #[builder(setter(into), default)]
425 #[serde(rename = "toToken", default)]
426 pub to_token: Option<String>,
427 #[builder(setter(into), default)]
431 #[serde(rename = "chainId", default)]
432 pub chain_id: Option<String>,
433}
434
435impl CreateInboundTransferParams {
436 #[must_use]
446 pub fn builder(
447 wallet_id: String,
448 wallet_address: String,
449 from_token_amount: String,
450 account_type: CreateInboundTransferAccountTypeEnum,
451 ) -> CreateInboundTransferParamsBuilder {
452 CreateInboundTransferParamsBuilder::default()
453 .wallet_id(wallet_id)
454 .wallet_address(wallet_address)
455 .from_token_amount(from_token_amount)
456 .account_type(account_type)
457 }
458}
459#[derive(Clone, Debug, Builder, Deserialize)]
464#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
465pub struct CreateOutboundTransferParams {
466 #[builder(setter(into))]
470 #[serde(rename = "walletId")]
471 pub wallet_id: String,
472 #[builder(setter(into))]
476 #[serde(rename = "walletAddress")]
477 pub wallet_address: String,
478 #[builder(setter(into))]
482 #[serde(rename = "fromTokenAmount")]
483 pub from_token_amount: String,
484 #[builder(setter(into))]
488 #[serde(rename = "accountType")]
489 pub account_type: CreateOutboundTransferAccountTypeEnum,
490 #[builder(setter(into))]
494 #[serde(rename = "sourceBiz")]
495 pub source_biz: CreateOutboundTransferSourceBizEnum,
496 #[builder(setter(into), default)]
500 #[serde(rename = "fromToken", default)]
501 pub from_token: Option<String>,
502 #[builder(setter(into), default)]
506 #[serde(rename = "toToken", default)]
507 pub to_token: Option<String>,
508 #[builder(setter(into), default)]
512 #[serde(rename = "chainId", default)]
513 pub chain_id: Option<String>,
514}
515
516impl CreateOutboundTransferParams {
517 #[must_use]
528 pub fn builder(
529 wallet_id: String,
530 wallet_address: String,
531 from_token_amount: String,
532 account_type: CreateOutboundTransferAccountTypeEnum,
533 source_biz: CreateOutboundTransferSourceBizEnum,
534 ) -> CreateOutboundTransferParamsBuilder {
535 CreateOutboundTransferParamsBuilder::default()
536 .wallet_id(wallet_id)
537 .wallet_address(wallet_address)
538 .from_token_amount(from_token_amount)
539 .account_type(account_type)
540 .source_biz(source_biz)
541 }
542}
543#[derive(Clone, Debug, Builder, Deserialize)]
548#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
549pub struct QueryTransferListParams {
550 #[builder(setter(into))]
554 #[serde(rename = "walletAddress")]
555 pub wallet_address: String,
556 #[builder(setter(into))]
560 #[serde(rename = "startDate")]
561 pub start_date: String,
562 #[builder(setter(into))]
566 #[serde(rename = "endDate")]
567 pub end_date: String,
568 #[builder(setter(into), default)]
572 #[serde(rename = "tokenSymbol", default)]
573 pub token_symbol: Option<String>,
574 #[builder(setter(into), default)]
578 #[serde(rename = "direction", default)]
579 pub direction: Option<QueryTransferListDirectionEnum>,
580 #[builder(setter(into), default)]
584 #[serde(rename = "offset", default)]
585 pub offset: Option<i32>,
586 #[builder(setter(into), default)]
590 #[serde(rename = "limit", default)]
591 pub limit: Option<i32>,
592 #[builder(setter(into), default)]
596 #[serde(rename = "recvWindow", default)]
597 pub recv_window: Option<i64>,
598}
599
600impl QueryTransferListParams {
601 #[must_use]
610 pub fn builder(
611 wallet_address: String,
612 start_date: String,
613 end_date: String,
614 ) -> QueryTransferListParamsBuilder {
615 QueryTransferListParamsBuilder::default()
616 .wallet_address(wallet_address)
617 .start_date(start_date)
618 .end_date(end_date)
619 }
620}
621#[derive(Clone, Debug, Builder, Deserialize)]
626#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
627pub struct QueryTransferStatusParams {
628 #[builder(setter(into))]
632 #[serde(rename = "transferId")]
633 pub transfer_id: String,
634 #[builder(setter(into), default)]
638 #[serde(rename = "recvWindow", default)]
639 pub recv_window: Option<i64>,
640}
641
642impl QueryTransferStatusParams {
643 #[must_use]
650 pub fn builder(transfer_id: String) -> QueryTransferStatusParamsBuilder {
651 QueryTransferStatusParamsBuilder::default().transfer_id(transfer_id)
652 }
653}
654
655#[async_trait]
656impl TransferApi for TransferApiClient {
657 async fn apply_mm_deposit(
658 &self,
659 params: ApplyMmDepositParams,
660 ) -> anyhow::Result<RestApiResponse<models::ApplyMmDepositResponse>> {
661 let ApplyMmDepositParams {
662 from_token,
663 from_token_amount,
664 to_token,
665 account_type,
666 chain_id,
667 } = params;
668
669 let mut query_params = BTreeMap::new();
670 let body_params = BTreeMap::new();
671
672 query_params.insert("fromToken".to_string(), json!(from_token));
673
674 query_params.insert("fromTokenAmount".to_string(), json!(from_token_amount));
675
676 query_params.insert("toToken".to_string(), json!(to_token));
677
678 query_params.insert("accountType".to_string(), json!(account_type));
679
680 if let Some(rw) = chain_id {
681 query_params.insert("chainId".to_string(), json!(rw));
682 }
683
684 send_request::<models::ApplyMmDepositResponse>(
685 &self.configuration,
686 "/sapi/v1/w3w/wallet/prediction/deposit/apply",
687 reqwest::Method::POST,
688 query_params,
689 body_params,
690 if HAS_TIME_UNIT {
691 self.configuration.time_unit
692 } else {
693 None
694 },
695 true,
696 )
697 .await
698 }
699
700 async fn apply_mm_withdraw(
701 &self,
702 params: ApplyMmWithdrawParams,
703 ) -> anyhow::Result<RestApiResponse<models::ApplyMmWithdrawResponse>> {
704 let ApplyMmWithdrawParams {
705 coin,
706 network,
707 amount,
708 withdraw_order_id,
709 wallet_type,
710 name,
711 } = params;
712
713 let mut query_params = BTreeMap::new();
714 let body_params = BTreeMap::new();
715
716 query_params.insert("coin".to_string(), json!(coin));
717
718 query_params.insert("network".to_string(), json!(network));
719
720 query_params.insert("amount".to_string(), json!(amount));
721
722 if let Some(rw) = withdraw_order_id {
723 query_params.insert("withdrawOrderId".to_string(), json!(rw));
724 }
725
726 if let Some(rw) = wallet_type {
727 query_params.insert("walletType".to_string(), json!(rw));
728 }
729
730 if let Some(rw) = name {
731 query_params.insert("name".to_string(), json!(rw));
732 }
733
734 send_request::<models::ApplyMmWithdrawResponse>(
735 &self.configuration,
736 "/sapi/v1/w3w/wallet/prediction/withdraw/apply",
737 reqwest::Method::POST,
738 query_params,
739 body_params,
740 if HAS_TIME_UNIT {
741 self.configuration.time_unit
742 } else {
743 None
744 },
745 true,
746 )
747 .await
748 }
749
750 async fn create_inbound_transfer(
751 &self,
752 params: CreateInboundTransferParams,
753 ) -> anyhow::Result<RestApiResponse<models::CreateInboundTransferResponse>> {
754 let CreateInboundTransferParams {
755 wallet_id,
756 wallet_address,
757 from_token_amount,
758 account_type,
759 from_token,
760 to_token,
761 chain_id,
762 } = params;
763
764 let mut query_params = BTreeMap::new();
765 let body_params = BTreeMap::new();
766
767 query_params.insert("walletId".to_string(), json!(wallet_id));
768
769 query_params.insert("walletAddress".to_string(), json!(wallet_address));
770
771 query_params.insert("fromTokenAmount".to_string(), json!(from_token_amount));
772
773 query_params.insert("accountType".to_string(), json!(account_type));
774
775 if let Some(rw) = from_token {
776 query_params.insert("fromToken".to_string(), json!(rw));
777 }
778
779 if let Some(rw) = to_token {
780 query_params.insert("toToken".to_string(), json!(rw));
781 }
782
783 if let Some(rw) = chain_id {
784 query_params.insert("chainId".to_string(), json!(rw));
785 }
786
787 send_request::<models::CreateInboundTransferResponse>(
788 &self.configuration,
789 "/sapi/v1/w3w/wallet/prediction/transfer/inbound",
790 reqwest::Method::POST,
791 query_params,
792 body_params,
793 if HAS_TIME_UNIT {
794 self.configuration.time_unit
795 } else {
796 None
797 },
798 true,
799 )
800 .await
801 }
802
803 async fn create_outbound_transfer(
804 &self,
805 params: CreateOutboundTransferParams,
806 ) -> anyhow::Result<RestApiResponse<models::CreateOutboundTransferResponse>> {
807 let CreateOutboundTransferParams {
808 wallet_id,
809 wallet_address,
810 from_token_amount,
811 account_type,
812 source_biz,
813 from_token,
814 to_token,
815 chain_id,
816 } = params;
817
818 let mut query_params = BTreeMap::new();
819 let body_params = BTreeMap::new();
820
821 query_params.insert("walletId".to_string(), json!(wallet_id));
822
823 query_params.insert("walletAddress".to_string(), json!(wallet_address));
824
825 query_params.insert("fromTokenAmount".to_string(), json!(from_token_amount));
826
827 query_params.insert("accountType".to_string(), json!(account_type));
828
829 query_params.insert("sourceBiz".to_string(), json!(source_biz));
830
831 if let Some(rw) = from_token {
832 query_params.insert("fromToken".to_string(), json!(rw));
833 }
834
835 if let Some(rw) = to_token {
836 query_params.insert("toToken".to_string(), json!(rw));
837 }
838
839 if let Some(rw) = chain_id {
840 query_params.insert("chainId".to_string(), json!(rw));
841 }
842
843 send_request::<models::CreateOutboundTransferResponse>(
844 &self.configuration,
845 "/sapi/v1/w3w/wallet/prediction/transfer/outbound",
846 reqwest::Method::POST,
847 query_params,
848 body_params,
849 if HAS_TIME_UNIT {
850 self.configuration.time_unit
851 } else {
852 None
853 },
854 true,
855 )
856 .await
857 }
858
859 async fn query_transfer_list(
860 &self,
861 params: QueryTransferListParams,
862 ) -> anyhow::Result<RestApiResponse<models::QueryTransferListResponse>> {
863 let QueryTransferListParams {
864 wallet_address,
865 start_date,
866 end_date,
867 token_symbol,
868 direction,
869 offset,
870 limit,
871 recv_window,
872 } = params;
873
874 let mut query_params = BTreeMap::new();
875 let body_params = BTreeMap::new();
876
877 query_params.insert("walletAddress".to_string(), json!(wallet_address));
878
879 query_params.insert("startDate".to_string(), json!(start_date));
880
881 query_params.insert("endDate".to_string(), json!(end_date));
882
883 if let Some(rw) = token_symbol {
884 query_params.insert("tokenSymbol".to_string(), json!(rw));
885 }
886
887 if let Some(rw) = direction {
888 query_params.insert("direction".to_string(), json!(rw));
889 }
890
891 if let Some(rw) = offset {
892 query_params.insert("offset".to_string(), json!(rw));
893 }
894
895 if let Some(rw) = limit {
896 query_params.insert("limit".to_string(), json!(rw));
897 }
898
899 if let Some(rw) = recv_window {
900 query_params.insert("recvWindow".to_string(), json!(rw));
901 }
902
903 send_request::<models::QueryTransferListResponse>(
904 &self.configuration,
905 "/sapi/v1/w3w/wallet/prediction/transfer/list",
906 reqwest::Method::GET,
907 query_params,
908 body_params,
909 if HAS_TIME_UNIT {
910 self.configuration.time_unit
911 } else {
912 None
913 },
914 true,
915 )
916 .await
917 }
918
919 async fn query_transfer_status(
920 &self,
921 params: QueryTransferStatusParams,
922 ) -> anyhow::Result<RestApiResponse<models::QueryTransferStatusResponse>> {
923 let QueryTransferStatusParams {
924 transfer_id,
925 recv_window,
926 } = params;
927
928 let mut query_params = BTreeMap::new();
929 let body_params = BTreeMap::new();
930
931 query_params.insert("transferId".to_string(), json!(transfer_id));
932
933 if let Some(rw) = recv_window {
934 query_params.insert("recvWindow".to_string(), json!(rw));
935 }
936
937 send_request::<models::QueryTransferStatusResponse>(
938 &self.configuration,
939 "/sapi/v1/w3w/wallet/prediction/transfer/status",
940 reqwest::Method::GET,
941 query_params,
942 body_params,
943 if HAS_TIME_UNIT {
944 self.configuration.time_unit
945 } else {
946 None
947 },
948 true,
949 )
950 .await
951 }
952}
953
954#[cfg(all(test, feature = "w3w_prediction"))]
955mod tests {
956 use super::*;
957 use crate::TOKIO_SHARED_RT;
958 use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
959 use async_trait::async_trait;
960 use std::collections::HashMap;
961
962 struct DummyRestApiResponse<T> {
963 inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
964 status: u16,
965 headers: HashMap<String, String>,
966 rate_limits: Option<Vec<RestApiRateLimit>>,
967 }
968
969 impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
970 fn from(dummy: DummyRestApiResponse<T>) -> Self {
971 Self {
972 data_fn: dummy.inner,
973 status: dummy.status,
974 headers: dummy.headers,
975 rate_limits: dummy.rate_limits,
976 }
977 }
978 }
979
980 struct MockTransferApiClient {
981 force_error: bool,
982 }
983
984 #[async_trait]
985 impl TransferApi for MockTransferApiClient {
986 async fn apply_mm_deposit(
987 &self,
988 _params: ApplyMmDepositParams,
989 ) -> anyhow::Result<RestApiResponse<models::ApplyMmDepositResponse>> {
990 if self.force_error {
991 return Err(ConnectorError::ConnectorClientError {
992 msg: "ResponseError".to_string(),
993 code: None,
994 }
995 .into());
996 }
997
998 let resp_json: Value = serde_json::from_str(
999 r#"{"transferId":"26080400000000454343","status":"PROCESSING"}"#,
1000 )
1001 .unwrap_or_else(|_| serde_json::json!({}));
1002 let dummy_response: models::ApplyMmDepositResponse =
1003 serde_json::from_value(resp_json.clone())
1004 .expect("should parse into models::ApplyMmDepositResponse");
1005
1006 let dummy = DummyRestApiResponse {
1007 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1008 status: 200,
1009 headers: HashMap::new(),
1010 rate_limits: None,
1011 };
1012
1013 Ok(dummy.into())
1014 }
1015
1016 async fn apply_mm_withdraw(
1017 &self,
1018 _params: ApplyMmWithdrawParams,
1019 ) -> anyhow::Result<RestApiResponse<models::ApplyMmWithdrawResponse>> {
1020 if self.force_error {
1021 return Err(ConnectorError::ConnectorClientError {
1022 msg: "ResponseError".to_string(),
1023 code: None,
1024 }
1025 .into());
1026 }
1027
1028 let resp_json: Value = serde_json::from_str(r#"{"id":"123456789","walletId":"0ecd3be8e3674b54b1258e24f9c3706b","walletAddress":"0x2ac3a1fb164da5c4e76f67ae6dbe6be821c000c8","transferId":"26080400000000454344"}"#).unwrap_or_else(|_| serde_json::json!({}));
1029 let dummy_response: models::ApplyMmWithdrawResponse =
1030 serde_json::from_value(resp_json.clone())
1031 .expect("should parse into models::ApplyMmWithdrawResponse");
1032
1033 let dummy = DummyRestApiResponse {
1034 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1035 status: 200,
1036 headers: HashMap::new(),
1037 rate_limits: None,
1038 };
1039
1040 Ok(dummy.into())
1041 }
1042
1043 async fn create_inbound_transfer(
1044 &self,
1045 _params: CreateInboundTransferParams,
1046 ) -> anyhow::Result<RestApiResponse<models::CreateInboundTransferResponse>> {
1047 if self.force_error {
1048 return Err(ConnectorError::ConnectorClientError {
1049 msg: "ResponseError".to_string(),
1050 code: None,
1051 }
1052 .into());
1053 }
1054
1055 let resp_json: Value = serde_json::from_str(
1056 r#"{"transferId":"tf_20260525_in_001","status":"PROCESSING"}"#,
1057 )
1058 .unwrap_or_else(|_| serde_json::json!({}));
1059 let dummy_response: models::CreateInboundTransferResponse =
1060 serde_json::from_value(resp_json.clone())
1061 .expect("should parse into models::CreateInboundTransferResponse");
1062
1063 let dummy = DummyRestApiResponse {
1064 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1065 status: 200,
1066 headers: HashMap::new(),
1067 rate_limits: None,
1068 };
1069
1070 Ok(dummy.into())
1071 }
1072
1073 async fn create_outbound_transfer(
1074 &self,
1075 _params: CreateOutboundTransferParams,
1076 ) -> anyhow::Result<RestApiResponse<models::CreateOutboundTransferResponse>> {
1077 if self.force_error {
1078 return Err(ConnectorError::ConnectorClientError {
1079 msg: "ResponseError".to_string(),
1080 code: None,
1081 }
1082 .into());
1083 }
1084
1085 let resp_json: Value = serde_json::from_str(
1086 r#"{"transferId":"tf_20260525_out_001","status":"PROCESSING"}"#,
1087 )
1088 .unwrap_or_else(|_| serde_json::json!({}));
1089 let dummy_response: models::CreateOutboundTransferResponse =
1090 serde_json::from_value(resp_json.clone())
1091 .expect("should parse into models::CreateOutboundTransferResponse");
1092
1093 let dummy = DummyRestApiResponse {
1094 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1095 status: 200,
1096 headers: HashMap::new(),
1097 rate_limits: None,
1098 };
1099
1100 Ok(dummy.into())
1101 }
1102
1103 async fn query_transfer_list(
1104 &self,
1105 _params: QueryTransferListParams,
1106 ) -> anyhow::Result<RestApiResponse<models::QueryTransferListResponse>> {
1107 if self.force_error {
1108 return Err(ConnectorError::ConnectorClientError {
1109 msg: "ResponseError".to_string(),
1110 code: None,
1111 }
1112 .into());
1113 }
1114
1115 let resp_json: Value = serde_json::from_str(r#"{"transfers":[{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"SUCCESS","walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00","completeAt":"2026-05-25T04:00:05.000+00:00"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1116 let dummy_response: models::QueryTransferListResponse =
1117 serde_json::from_value(resp_json.clone())
1118 .expect("should parse into models::QueryTransferListResponse");
1119
1120 let dummy = DummyRestApiResponse {
1121 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1122 status: 200,
1123 headers: HashMap::new(),
1124 rate_limits: None,
1125 };
1126
1127 Ok(dummy.into())
1128 }
1129
1130 async fn query_transfer_status(
1131 &self,
1132 _params: QueryTransferStatusParams,
1133 ) -> anyhow::Result<RestApiResponse<models::QueryTransferStatusResponse>> {
1134 if self.force_error {
1135 return Err(ConnectorError::ConnectorClientError {
1136 msg: "ResponseError".to_string(),
1137 code: None,
1138 }
1139 .into());
1140 }
1141
1142 let resp_json: Value = serde_json::from_str(r#"{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"COMPLETED","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00"}"#).unwrap_or_else(|_| serde_json::json!({}));
1143 let dummy_response: models::QueryTransferStatusResponse =
1144 serde_json::from_value(resp_json.clone())
1145 .expect("should parse into models::QueryTransferStatusResponse");
1146
1147 let dummy = DummyRestApiResponse {
1148 inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
1149 status: 200,
1150 headers: HashMap::new(),
1151 rate_limits: None,
1152 };
1153
1154 Ok(dummy.into())
1155 }
1156 }
1157
1158 #[test]
1159 fn apply_mm_deposit_required_params_success() {
1160 TOKIO_SHARED_RT.block_on(async {
1161 let client = MockTransferApiClient { force_error: false };
1162
1163 let params = ApplyMmDepositParams::builder(
1164 "USDT".to_string(),
1165 "1000000000000000000".to_string(),
1166 "USDT".to_string(),
1167 ApplyMmDepositAccountTypeEnum::Spot,
1168 )
1169 .build()
1170 .unwrap();
1171
1172 let resp_json: Value = serde_json::from_str(
1173 r#"{"transferId":"26080400000000454343","status":"PROCESSING"}"#,
1174 )
1175 .unwrap_or_else(|_| serde_json::json!({}));
1176 let expected_response: models::ApplyMmDepositResponse =
1177 serde_json::from_value(resp_json.clone())
1178 .expect("should parse into models::ApplyMmDepositResponse");
1179
1180 let resp = client
1181 .apply_mm_deposit(params)
1182 .await
1183 .expect("Expected a response");
1184 let data_future = resp.data();
1185 let actual_response = data_future.await.unwrap();
1186 assert_eq!(actual_response, expected_response);
1187 });
1188 }
1189
1190 #[test]
1191 fn apply_mm_deposit_optional_params_success() {
1192 TOKIO_SHARED_RT.block_on(async {
1193 let client = MockTransferApiClient { force_error: false };
1194
1195 let params = ApplyMmDepositParams::builder(
1196 "USDT".to_string(),
1197 "1000000000000000000".to_string(),
1198 "USDT".to_string(),
1199 ApplyMmDepositAccountTypeEnum::Spot,
1200 )
1201 .chain_id("56".to_string())
1202 .build()
1203 .unwrap();
1204
1205 let resp_json: Value = serde_json::from_str(
1206 r#"{"transferId":"26080400000000454343","status":"PROCESSING"}"#,
1207 )
1208 .unwrap_or_else(|_| serde_json::json!({}));
1209 let expected_response: models::ApplyMmDepositResponse =
1210 serde_json::from_value(resp_json.clone())
1211 .expect("should parse into models::ApplyMmDepositResponse");
1212
1213 let resp = client
1214 .apply_mm_deposit(params)
1215 .await
1216 .expect("Expected a response");
1217 let data_future = resp.data();
1218 let actual_response = data_future.await.unwrap();
1219 assert_eq!(actual_response, expected_response);
1220 });
1221 }
1222
1223 #[test]
1224 fn apply_mm_deposit_response_error() {
1225 TOKIO_SHARED_RT.block_on(async {
1226 let client = MockTransferApiClient { force_error: true };
1227
1228 let params = ApplyMmDepositParams::builder(
1229 "USDT".to_string(),
1230 "1000000000000000000".to_string(),
1231 "USDT".to_string(),
1232 ApplyMmDepositAccountTypeEnum::Spot,
1233 )
1234 .build()
1235 .unwrap();
1236
1237 match client.apply_mm_deposit(params).await {
1238 Ok(_) => panic!("Expected an error"),
1239 Err(err) => {
1240 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1241 }
1242 }
1243 });
1244 }
1245
1246 #[test]
1247 fn apply_mm_withdraw_required_params_success() {
1248 TOKIO_SHARED_RT.block_on(async {
1249 let client = MockTransferApiClient { force_error: false };
1250
1251 let params = ApplyMmWithdrawParams::builder("USDT".to_string(),"BEP20".to_string(),"100.00".to_string(),).build().unwrap();
1252
1253 let resp_json: Value = serde_json::from_str(r#"{"id":"123456789","walletId":"0ecd3be8e3674b54b1258e24f9c3706b","walletAddress":"0x2ac3a1fb164da5c4e76f67ae6dbe6be821c000c8","transferId":"26080400000000454344"}"#).unwrap_or_else(|_| serde_json::json!({}));
1254 let expected_response : models::ApplyMmWithdrawResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ApplyMmWithdrawResponse");
1255
1256 let resp = client.apply_mm_withdraw(params).await.expect("Expected a response");
1257 let data_future = resp.data();
1258 let actual_response = data_future.await.unwrap();
1259 assert_eq!(actual_response, expected_response);
1260 });
1261 }
1262
1263 #[test]
1264 fn apply_mm_withdraw_optional_params_success() {
1265 TOKIO_SHARED_RT.block_on(async {
1266 let client = MockTransferApiClient { force_error: false };
1267
1268 let params = ApplyMmWithdrawParams::builder("USDT".to_string(),"BEP20".to_string(),"100.00".to_string(),).withdraw_order_id("wd_20260814_001".to_string()).wallet_type(ApplyMmWithdrawWalletTypeEnum::WalletType0).name("MM withdraw".to_string()).build().unwrap();
1269
1270 let resp_json: Value = serde_json::from_str(r#"{"id":"123456789","walletId":"0ecd3be8e3674b54b1258e24f9c3706b","walletAddress":"0x2ac3a1fb164da5c4e76f67ae6dbe6be821c000c8","transferId":"26080400000000454344"}"#).unwrap_or_else(|_| serde_json::json!({}));
1271 let expected_response : models::ApplyMmWithdrawResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::ApplyMmWithdrawResponse");
1272
1273 let resp = client.apply_mm_withdraw(params).await.expect("Expected a response");
1274 let data_future = resp.data();
1275 let actual_response = data_future.await.unwrap();
1276 assert_eq!(actual_response, expected_response);
1277 });
1278 }
1279
1280 #[test]
1281 fn apply_mm_withdraw_response_error() {
1282 TOKIO_SHARED_RT.block_on(async {
1283 let client = MockTransferApiClient { force_error: true };
1284
1285 let params = ApplyMmWithdrawParams::builder(
1286 "USDT".to_string(),
1287 "BEP20".to_string(),
1288 "100.00".to_string(),
1289 )
1290 .build()
1291 .unwrap();
1292
1293 match client.apply_mm_withdraw(params).await {
1294 Ok(_) => panic!("Expected an error"),
1295 Err(err) => {
1296 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1297 }
1298 }
1299 });
1300 }
1301
1302 #[test]
1303 fn create_inbound_transfer_required_params_success() {
1304 TOKIO_SHARED_RT.block_on(async {
1305 let client = MockTransferApiClient { force_error: false };
1306
1307 let params = CreateInboundTransferParams::builder(
1308 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1309 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1310 "1000000000000000000".to_string(),
1311 CreateInboundTransferAccountTypeEnum::Spot,
1312 )
1313 .build()
1314 .unwrap();
1315
1316 let resp_json: Value = serde_json::from_str(
1317 r#"{"transferId":"tf_20260525_in_001","status":"PROCESSING"}"#,
1318 )
1319 .unwrap_or_else(|_| serde_json::json!({}));
1320 let expected_response: models::CreateInboundTransferResponse =
1321 serde_json::from_value(resp_json.clone())
1322 .expect("should parse into models::CreateInboundTransferResponse");
1323
1324 let resp = client
1325 .create_inbound_transfer(params)
1326 .await
1327 .expect("Expected a response");
1328 let data_future = resp.data();
1329 let actual_response = data_future.await.unwrap();
1330 assert_eq!(actual_response, expected_response);
1331 });
1332 }
1333
1334 #[test]
1335 fn create_inbound_transfer_optional_params_success() {
1336 TOKIO_SHARED_RT.block_on(async {
1337 let client = MockTransferApiClient { force_error: false };
1338
1339 let params = CreateInboundTransferParams::builder(
1340 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1341 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1342 "1000000000000000000".to_string(),
1343 CreateInboundTransferAccountTypeEnum::Spot,
1344 )
1345 .from_token("USDT".to_string())
1346 .to_token("USDT".to_string())
1347 .chain_id("56".to_string())
1348 .build()
1349 .unwrap();
1350
1351 let resp_json: Value = serde_json::from_str(
1352 r#"{"transferId":"tf_20260525_in_001","status":"PROCESSING"}"#,
1353 )
1354 .unwrap_or_else(|_| serde_json::json!({}));
1355 let expected_response: models::CreateInboundTransferResponse =
1356 serde_json::from_value(resp_json.clone())
1357 .expect("should parse into models::CreateInboundTransferResponse");
1358
1359 let resp = client
1360 .create_inbound_transfer(params)
1361 .await
1362 .expect("Expected a response");
1363 let data_future = resp.data();
1364 let actual_response = data_future.await.unwrap();
1365 assert_eq!(actual_response, expected_response);
1366 });
1367 }
1368
1369 #[test]
1370 fn create_inbound_transfer_response_error() {
1371 TOKIO_SHARED_RT.block_on(async {
1372 let client = MockTransferApiClient { force_error: true };
1373
1374 let params = CreateInboundTransferParams::builder(
1375 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1376 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1377 "1000000000000000000".to_string(),
1378 CreateInboundTransferAccountTypeEnum::Spot,
1379 )
1380 .build()
1381 .unwrap();
1382
1383 match client.create_inbound_transfer(params).await {
1384 Ok(_) => panic!("Expected an error"),
1385 Err(err) => {
1386 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1387 }
1388 }
1389 });
1390 }
1391
1392 #[test]
1393 fn create_outbound_transfer_required_params_success() {
1394 TOKIO_SHARED_RT.block_on(async {
1395 let client = MockTransferApiClient { force_error: false };
1396
1397 let params = CreateOutboundTransferParams::builder(
1398 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1399 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1400 "1000000000000000000".to_string(),
1401 CreateOutboundTransferAccountTypeEnum::Spot,
1402 CreateOutboundTransferSourceBizEnum::UserTransfer,
1403 )
1404 .build()
1405 .unwrap();
1406
1407 let resp_json: Value = serde_json::from_str(
1408 r#"{"transferId":"tf_20260525_out_001","status":"PROCESSING"}"#,
1409 )
1410 .unwrap_or_else(|_| serde_json::json!({}));
1411 let expected_response: models::CreateOutboundTransferResponse =
1412 serde_json::from_value(resp_json.clone())
1413 .expect("should parse into models::CreateOutboundTransferResponse");
1414
1415 let resp = client
1416 .create_outbound_transfer(params)
1417 .await
1418 .expect("Expected a response");
1419 let data_future = resp.data();
1420 let actual_response = data_future.await.unwrap();
1421 assert_eq!(actual_response, expected_response);
1422 });
1423 }
1424
1425 #[test]
1426 fn create_outbound_transfer_optional_params_success() {
1427 TOKIO_SHARED_RT.block_on(async {
1428 let client = MockTransferApiClient { force_error: false };
1429
1430 let params = CreateOutboundTransferParams::builder(
1431 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1432 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1433 "1000000000000000000".to_string(),
1434 CreateOutboundTransferAccountTypeEnum::Spot,
1435 CreateOutboundTransferSourceBizEnum::UserTransfer,
1436 )
1437 .from_token("USDT".to_string())
1438 .to_token("USDT".to_string())
1439 .chain_id("56".to_string())
1440 .build()
1441 .unwrap();
1442
1443 let resp_json: Value = serde_json::from_str(
1444 r#"{"transferId":"tf_20260525_out_001","status":"PROCESSING"}"#,
1445 )
1446 .unwrap_or_else(|_| serde_json::json!({}));
1447 let expected_response: models::CreateOutboundTransferResponse =
1448 serde_json::from_value(resp_json.clone())
1449 .expect("should parse into models::CreateOutboundTransferResponse");
1450
1451 let resp = client
1452 .create_outbound_transfer(params)
1453 .await
1454 .expect("Expected a response");
1455 let data_future = resp.data();
1456 let actual_response = data_future.await.unwrap();
1457 assert_eq!(actual_response, expected_response);
1458 });
1459 }
1460
1461 #[test]
1462 fn create_outbound_transfer_response_error() {
1463 TOKIO_SHARED_RT.block_on(async {
1464 let client = MockTransferApiClient { force_error: true };
1465
1466 let params = CreateOutboundTransferParams::builder(
1467 "5b5c1ec3be4e4416a5872b21c1ca5d20".to_string(),
1468 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1469 "1000000000000000000".to_string(),
1470 CreateOutboundTransferAccountTypeEnum::Spot,
1471 CreateOutboundTransferSourceBizEnum::UserTransfer,
1472 )
1473 .build()
1474 .unwrap();
1475
1476 match client.create_outbound_transfer(params).await {
1477 Ok(_) => panic!("Expected an error"),
1478 Err(err) => {
1479 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1480 }
1481 }
1482 });
1483 }
1484
1485 #[test]
1486 fn query_transfer_list_required_params_success() {
1487 TOKIO_SHARED_RT.block_on(async {
1488 let client = MockTransferApiClient { force_error: false };
1489
1490 let params = QueryTransferListParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"2026-05-01".to_string(),"2026-05-25".to_string(),).build().unwrap();
1491
1492 let resp_json: Value = serde_json::from_str(r#"{"transfers":[{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"SUCCESS","walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00","completeAt":"2026-05-25T04:00:05.000+00:00"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1493 let expected_response : models::QueryTransferListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryTransferListResponse");
1494
1495 let resp = client.query_transfer_list(params).await.expect("Expected a response");
1496 let data_future = resp.data();
1497 let actual_response = data_future.await.unwrap();
1498 assert_eq!(actual_response, expected_response);
1499 });
1500 }
1501
1502 #[test]
1503 fn query_transfer_list_optional_params_success() {
1504 TOKIO_SHARED_RT.block_on(async {
1505 let client = MockTransferApiClient { force_error: false };
1506
1507 let params = QueryTransferListParams::builder("0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),"2026-05-01".to_string(),"2026-05-25".to_string(),).token_symbol("USDT".to_string()).direction(QueryTransferListDirectionEnum::Inbound).offset(0).limit(20).recv_window(5000).build().unwrap();
1508
1509 let resp_json: Value = serde_json::from_str(r#"{"transfers":[{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"SUCCESS","walletAddress":"0x12e32db8817e292508c34111cbc4b23340df542c","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00","completeAt":"2026-05-25T04:00:05.000+00:00"}]}"#).unwrap_or_else(|_| serde_json::json!({}));
1510 let expected_response : models::QueryTransferListResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryTransferListResponse");
1511
1512 let resp = client.query_transfer_list(params).await.expect("Expected a response");
1513 let data_future = resp.data();
1514 let actual_response = data_future.await.unwrap();
1515 assert_eq!(actual_response, expected_response);
1516 });
1517 }
1518
1519 #[test]
1520 fn query_transfer_list_response_error() {
1521 TOKIO_SHARED_RT.block_on(async {
1522 let client = MockTransferApiClient { force_error: true };
1523
1524 let params = QueryTransferListParams::builder(
1525 "0x12e32db8817e292508c34111cbc4b23340df542c".to_string(),
1526 "2026-05-01".to_string(),
1527 "2026-05-25".to_string(),
1528 )
1529 .build()
1530 .unwrap();
1531
1532 match client.query_transfer_list(params).await {
1533 Ok(_) => panic!("Expected an error"),
1534 Err(err) => {
1535 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1536 }
1537 }
1538 });
1539 }
1540
1541 #[test]
1542 fn query_transfer_status_required_params_success() {
1543 TOKIO_SHARED_RT.block_on(async {
1544 let client = MockTransferApiClient { force_error: false };
1545
1546 let params = QueryTransferStatusParams::builder("tf_20260525_out_001".to_string(),).build().unwrap();
1547
1548 let resp_json: Value = serde_json::from_str(r#"{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"COMPLETED","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00"}"#).unwrap_or_else(|_| serde_json::json!({}));
1549 let expected_response : models::QueryTransferStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryTransferStatusResponse");
1550
1551 let resp = client.query_transfer_status(params).await.expect("Expected a response");
1552 let data_future = resp.data();
1553 let actual_response = data_future.await.unwrap();
1554 assert_eq!(actual_response, expected_response);
1555 });
1556 }
1557
1558 #[test]
1559 fn query_transfer_status_optional_params_success() {
1560 TOKIO_SHARED_RT.block_on(async {
1561 let client = MockTransferApiClient { force_error: false };
1562
1563 let params = QueryTransferStatusParams::builder("tf_20260525_out_001".to_string(),).recv_window(5000).build().unwrap();
1564
1565 let resp_json: Value = serde_json::from_str(r#"{"transferId":"tf_20260525_out_001","direction":"OUTBOUND","status":"COMPLETED","fromToken":"USDT","fromTokenAmount":"100.00","toToken":"USDT","toTokenAmount":"100.00","errorCode":"errorCode","errorMessage":"errorMessage","createTime":"2026-05-25T04:00:00.000+00:00","updateTime":"2026-05-25T04:00:05.000+00:00"}"#).unwrap_or_else(|_| serde_json::json!({}));
1566 let expected_response : models::QueryTransferStatusResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::QueryTransferStatusResponse");
1567
1568 let resp = client.query_transfer_status(params).await.expect("Expected a response");
1569 let data_future = resp.data();
1570 let actual_response = data_future.await.unwrap();
1571 assert_eq!(actual_response, expected_response);
1572 });
1573 }
1574
1575 #[test]
1576 fn query_transfer_status_response_error() {
1577 TOKIO_SHARED_RT.block_on(async {
1578 let client = MockTransferApiClient { force_error: true };
1579
1580 let params = QueryTransferStatusParams::builder("tf_20260525_out_001".to_string())
1581 .build()
1582 .unwrap();
1583
1584 match client.query_transfer_status(params).await {
1585 Ok(_) => panic!("Expected an error"),
1586 Err(err) => {
1587 assert_eq!(err.to_string(), "Connector client error: ResponseError");
1588 }
1589 }
1590 });
1591 }
1592}