Skip to main content

binance_sdk/w3w_prediction/rest_api/apis/
transfer_api.rs

1/*
2 * Prediction Trading REST API
3 *
4 * Place and manage prediction market orders, query positions, and transfer funds via the Prediction Trading REST API.
5 *
6 * The version of the OpenAPI document: 1.0.0
7 *
8 *
9 * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
10 * https://openapi-generator.tech
11 * Do not edit the class manually.
12 */
13
14#![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/// Request parameters for the [`apply_mm_deposit`] operation.
260///
261/// This struct holds all of the inputs you can pass when calling
262/// [`apply_mm_deposit`](#method.apply_mm_deposit).
263#[derive(Clone, Debug, Builder, Deserialize)]
264#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
265pub struct ApplyMmDepositParams {
266    /// Source token symbol (e.g. `USDT`)
267    ///
268    /// This field is **required.
269    #[builder(setter(into))]
270    #[serde(rename = "fromToken")]
271    pub from_token: String,
272    /// Source token amount in WEI (18 decimals). Example: `1000000000000000000` = 1 USDT
273    ///
274    /// This field is **required.
275    #[builder(setter(into))]
276    #[serde(rename = "fromTokenAmount")]
277    pub from_token_amount: String,
278    /// Target token symbol (e.g. `USDT`)
279    ///
280    /// This field is **required.
281    #[builder(setter(into))]
282    #[serde(rename = "toToken")]
283    pub to_token: String,
284    /// Target CEX account type. Enum: `SPOT`, `FUNDING`
285    ///
286    /// This field is **required.
287    #[builder(setter(into))]
288    #[serde(rename = "accountType")]
289    pub account_type: ApplyMmDepositAccountTypeEnum,
290    /// Chain ID. Default `56` (BSC)
291    ///
292    /// This field is **optional.
293    #[builder(setter(into), default)]
294    #[serde(rename = "chainId", default)]
295    pub chain_id: Option<String>,
296}
297
298impl ApplyMmDepositParams {
299    /// Create a builder for [`apply_mm_deposit`].
300    ///
301    /// Required parameters:
302    ///
303    /// * `from_token` — Source token symbol (e.g. `USDT`)
304    /// * `from_token_amount` — Source token amount in WEI (18 decimals). Example: `1000000000000000000` = 1 USDT
305    /// * `to_token` — Target token symbol (e.g. `USDT`)
306    /// * `account_type` — Target CEX account type. Enum: `SPOT`, `FUNDING`
307    ///
308    #[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/// Request parameters for the [`apply_mm_withdraw`] operation.
323///
324/// This struct holds all of the inputs you can pass when calling
325/// [`apply_mm_withdraw`](#method.apply_mm_withdraw).
326#[derive(Clone, Debug, Builder, Deserialize)]
327#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
328pub struct ApplyMmWithdrawParams {
329    /// Coin to withdraw (e.g. `USDT`)
330    ///
331    /// This field is **required.
332    #[builder(setter(into))]
333    #[serde(rename = "coin")]
334    pub coin: String,
335    /// Network (e.g. `BEP20`)
336    ///
337    /// This field is **required.
338    #[builder(setter(into))]
339    #[serde(rename = "network")]
340    pub network: String,
341    /// Amount to withdraw (must be > 0)
342    ///
343    /// This field is **required.
344    #[builder(setter(into))]
345    #[serde(rename = "amount")]
346    pub amount: String,
347    /// Client withdraw order id (idempotency key)
348    ///
349    /// This field is **optional.
350    #[builder(setter(into), default)]
351    #[serde(rename = "withdrawOrderId", default)]
352    pub withdraw_order_id: Option<String>,
353    /// Source CEX account type. Enum: `0` (SPOT), `1` (FUNDING). Default `0`. Must be `0` or `1`; any other value is rejected
354    ///
355    /// This field is **optional.
356    #[builder(setter(into), default)]
357    #[serde(rename = "walletType", default)]
358    pub wallet_type: Option<ApplyMmWithdrawWalletTypeEnum>,
359    /// Remark for the withdraw
360    ///
361    /// This field is **optional.
362    #[builder(setter(into), default)]
363    #[serde(rename = "name", default)]
364    pub name: Option<String>,
365}
366
367impl ApplyMmWithdrawParams {
368    /// Create a builder for [`apply_mm_withdraw`].
369    ///
370    /// Required parameters:
371    ///
372    /// * `coin` — Coin to withdraw (e.g. `USDT`)
373    /// * `network` — Network (e.g. `BEP20`)
374    /// * `amount` — Amount to withdraw (must be > 0)
375    ///
376    #[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/// Request parameters for the [`create_inbound_transfer`] operation.
385///
386/// This struct holds all of the inputs you can pass when calling
387/// [`create_inbound_transfer`](#method.create_inbound_transfer).
388#[derive(Clone, Debug, Builder, Deserialize)]
389#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
390pub struct CreateInboundTransferParams {
391    /// Wallet ID
392    ///
393    /// This field is **required.
394    #[builder(setter(into))]
395    #[serde(rename = "walletId")]
396    pub wallet_id: String,
397    /// User's prediction wallet address
398    ///
399    /// This field is **required.
400    #[builder(setter(into))]
401    #[serde(rename = "walletAddress")]
402    pub wallet_address: String,
403    /// Transfer amount in wei (18 decimals). Must be > 0. Example: `1000000000000000000` = 1 USDT
404    ///
405    /// This field is **required.
406    #[builder(setter(into))]
407    #[serde(rename = "fromTokenAmount")]
408    pub from_token_amount: String,
409    /// Destination CEX account. Enum: `SPOT`, `FUNDING`
410    ///
411    /// This field is **required.
412    #[builder(setter(into))]
413    #[serde(rename = "accountType")]
414    pub account_type: CreateInboundTransferAccountTypeEnum,
415    /// Source token symbol. Default `USDT`
416    ///
417    /// This field is **optional.
418    #[builder(setter(into), default)]
419    #[serde(rename = "fromToken", default)]
420    pub from_token: Option<String>,
421    /// Destination token symbol. Default `USDT`
422    ///
423    /// This field is **optional.
424    #[builder(setter(into), default)]
425    #[serde(rename = "toToken", default)]
426    pub to_token: Option<String>,
427    /// Chain ID. Default `56` (BSC)
428    ///
429    /// This field is **optional.
430    #[builder(setter(into), default)]
431    #[serde(rename = "chainId", default)]
432    pub chain_id: Option<String>,
433}
434
435impl CreateInboundTransferParams {
436    /// Create a builder for [`create_inbound_transfer`].
437    ///
438    /// Required parameters:
439    ///
440    /// * `wallet_id` — Wallet ID
441    /// * `wallet_address` — User's prediction wallet address
442    /// * `from_token_amount` — Transfer amount in wei (18 decimals). Must be > 0. Example: `1000000000000000000` = 1 USDT
443    /// * `account_type` — Destination CEX account. Enum: `SPOT`, `FUNDING`
444    ///
445    #[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/// Request parameters for the [`create_outbound_transfer`] operation.
460///
461/// This struct holds all of the inputs you can pass when calling
462/// [`create_outbound_transfer`](#method.create_outbound_transfer).
463#[derive(Clone, Debug, Builder, Deserialize)]
464#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
465pub struct CreateOutboundTransferParams {
466    /// Wallet ID
467    ///
468    /// This field is **required.
469    #[builder(setter(into))]
470    #[serde(rename = "walletId")]
471    pub wallet_id: String,
472    /// User's prediction wallet address
473    ///
474    /// This field is **required.
475    #[builder(setter(into))]
476    #[serde(rename = "walletAddress")]
477    pub wallet_address: String,
478    /// Transfer amount in wei (18 decimals). Must be > 0. Example: `1000000000000000000` = 1 USDT
479    ///
480    /// This field is **required.
481    #[builder(setter(into))]
482    #[serde(rename = "fromTokenAmount")]
483    pub from_token_amount: String,
484    /// Source CEX account. Enum: `SPOT`, `FUNDING`
485    ///
486    /// This field is **required.
487    #[builder(setter(into))]
488    #[serde(rename = "accountType")]
489    pub account_type: CreateOutboundTransferAccountTypeEnum,
490    /// Business source. Enum: `USER_TRANSFER`, `PREDICTION_BUY`
491    ///
492    /// This field is **required.
493    #[builder(setter(into))]
494    #[serde(rename = "sourceBiz")]
495    pub source_biz: CreateOutboundTransferSourceBizEnum,
496    /// Source token symbol. Default `USDT`
497    ///
498    /// This field is **optional.
499    #[builder(setter(into), default)]
500    #[serde(rename = "fromToken", default)]
501    pub from_token: Option<String>,
502    /// Destination token symbol. Default `USDT`
503    ///
504    /// This field is **optional.
505    #[builder(setter(into), default)]
506    #[serde(rename = "toToken", default)]
507    pub to_token: Option<String>,
508    /// Chain ID. Default `56` (BSC)
509    ///
510    /// This field is **optional.
511    #[builder(setter(into), default)]
512    #[serde(rename = "chainId", default)]
513    pub chain_id: Option<String>,
514}
515
516impl CreateOutboundTransferParams {
517    /// Create a builder for [`create_outbound_transfer`].
518    ///
519    /// Required parameters:
520    ///
521    /// * `wallet_id` — Wallet ID
522    /// * `wallet_address` — User's prediction wallet address
523    /// * `from_token_amount` — Transfer amount in wei (18 decimals). Must be > 0. Example: `1000000000000000000` = 1 USDT
524    /// * `account_type` — Source CEX account. Enum: `SPOT`, `FUNDING`
525    /// * `source_biz` — Business source. Enum: `USER_TRANSFER`, `PREDICTION_BUY`
526    ///
527    #[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/// Request parameters for the [`query_transfer_list`] operation.
544///
545/// This struct holds all of the inputs you can pass when calling
546/// [`query_transfer_list`](#method.query_transfer_list).
547#[derive(Clone, Debug, Builder, Deserialize)]
548#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
549pub struct QueryTransferListParams {
550    /// User's prediction wallet address
551    ///
552    /// This field is **required.
553    #[builder(setter(into))]
554    #[serde(rename = "walletAddress")]
555    pub wallet_address: String,
556    /// Start date. Format: `yyyy-MM-dd`. Must be ≤ `endDate`
557    ///
558    /// This field is **required.
559    #[builder(setter(into))]
560    #[serde(rename = "startDate")]
561    pub start_date: String,
562    /// End date. Format: `yyyy-MM-dd`. Must be ≥ `startDate`
563    ///
564    /// This field is **required.
565    #[builder(setter(into))]
566    #[serde(rename = "endDate")]
567    pub end_date: String,
568    /// Filter by token symbol (e.g. `USDT`)
569    ///
570    /// This field is **optional.
571    #[builder(setter(into), default)]
572    #[serde(rename = "tokenSymbol", default)]
573    pub token_symbol: Option<String>,
574    /// Filter by direction. Enum: `INBOUND`, `OUTBOUND`
575    ///
576    /// This field is **optional.
577    #[builder(setter(into), default)]
578    #[serde(rename = "direction", default)]
579    pub direction: Option<QueryTransferListDirectionEnum>,
580    /// Pagination offset. Default `0`
581    ///
582    /// This field is **optional.
583    #[builder(setter(into), default)]
584    #[serde(rename = "offset", default)]
585    pub offset: Option<i32>,
586    /// Page size. Default `20`, range 1–100
587    ///
588    /// This field is **optional.
589    #[builder(setter(into), default)]
590    #[serde(rename = "limit", default)]
591    pub limit: Option<i32>,
592    /// Request validity window in milliseconds
593    ///
594    /// This field is **optional.
595    #[builder(setter(into), default)]
596    #[serde(rename = "recvWindow", default)]
597    pub recv_window: Option<i64>,
598}
599
600impl QueryTransferListParams {
601    /// Create a builder for [`query_transfer_list`].
602    ///
603    /// Required parameters:
604    ///
605    /// * `wallet_address` — User's prediction wallet address
606    /// * `start_date` — Start date. Format: `yyyy-MM-dd`. Must be ≤ `endDate`
607    /// * `end_date` — End date. Format: `yyyy-MM-dd`. Must be ≥ `startDate`
608    ///
609    #[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/// Request parameters for the [`query_transfer_status`] operation.
622///
623/// This struct holds all of the inputs you can pass when calling
624/// [`query_transfer_status`](#method.query_transfer_status).
625#[derive(Clone, Debug, Builder, Deserialize)]
626#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
627pub struct QueryTransferStatusParams {
628    /// Transfer ID returned from outbound/inbound transfer
629    ///
630    /// This field is **required.
631    #[builder(setter(into))]
632    #[serde(rename = "transferId")]
633    pub transfer_id: String,
634    /// Request validity window in milliseconds
635    ///
636    /// This field is **optional.
637    #[builder(setter(into), default)]
638    #[serde(rename = "recvWindow", default)]
639    pub recv_window: Option<i64>,
640}
641
642impl QueryTransferStatusParams {
643    /// Create a builder for [`query_transfer_status`].
644    ///
645    /// Required parameters:
646    ///
647    /// * `transfer_id` — Transfer ID returned from outbound/inbound transfer
648    ///
649    #[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}