Skip to main content

binance_sdk/margin_trading/rest_api/apis/
transfer_api.rs

1/*
2 * Binance Margin Trading REST API
3 *
4 * OpenAPI Specification for the Binance Margin 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::margin_trading::rest_api::models;
29
30const HAS_TIME_UNIT: bool = false;
31
32#[async_trait]
33pub trait TransferApi: Send + Sync {
34    async fn get_cross_margin_transfer_history(
35        &self,
36        params: GetCrossMarginTransferHistoryParams,
37    ) -> anyhow::Result<RestApiResponse<models::GetCrossMarginTransferHistoryResponse>>;
38    async fn query_max_transfer_out_amount(
39        &self,
40        params: QueryMaxTransferOutAmountParams,
41    ) -> anyhow::Result<RestApiResponse<models::QueryMaxTransferOutAmountResponse>>;
42}
43
44#[derive(Debug, Clone)]
45pub struct TransferApiClient {
46    configuration: ConfigurationRestApi,
47}
48
49impl TransferApiClient {
50    pub fn new(configuration: ConfigurationRestApi) -> Self {
51        Self { configuration }
52    }
53}
54
55/// Request parameters for the [`get_cross_margin_transfer_history`] operation.
56///
57/// This struct holds all of the inputs you can pass when calling
58/// [`get_cross_margin_transfer_history`](#method.get_cross_margin_transfer_history).
59#[derive(Clone, Debug, Builder, Default)]
60#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
61pub struct GetCrossMarginTransferHistoryParams {
62    ///
63    /// The `asset` parameter.
64    ///
65    /// This field is **optional.
66    #[builder(setter(into), default)]
67    pub asset: Option<String>,
68    /// Transfer Type: `ROLL_IN`, `ROLL_OUT`
69    ///
70    /// This field is **optional.
71    #[builder(setter(into), default)]
72    pub r#type: Option<String>,
73    /// Only supports querying data from the past 90 days.
74    ///
75    /// This field is **optional.
76    #[builder(setter(into), default)]
77    pub start_time: Option<i64>,
78    ///
79    /// The `end_time` parameter.
80    ///
81    /// This field is **optional.
82    #[builder(setter(into), default)]
83    pub end_time: Option<i64>,
84    /// Currently querying page. Start from 1. Default:1
85    ///
86    /// This field is **optional.
87    #[builder(setter(into), default)]
88    pub current: Option<i64>,
89    /// Default:10 Max:100
90    ///
91    /// This field is **optional.
92    #[builder(setter(into), default)]
93    pub size: Option<i64>,
94    /// isolated symbol
95    ///
96    /// This field is **optional.
97    #[builder(setter(into), default)]
98    pub isolated_symbol: Option<String>,
99    /// No more than 60000
100    ///
101    /// This field is **optional.
102    #[builder(setter(into), default)]
103    pub recv_window: Option<i64>,
104}
105
106impl GetCrossMarginTransferHistoryParams {
107    /// Create a builder for [`get_cross_margin_transfer_history`].
108    ///
109    #[must_use]
110    pub fn builder() -> GetCrossMarginTransferHistoryParamsBuilder {
111        GetCrossMarginTransferHistoryParamsBuilder::default()
112    }
113}
114/// Request parameters for the [`query_max_transfer_out_amount`] operation.
115///
116/// This struct holds all of the inputs you can pass when calling
117/// [`query_max_transfer_out_amount`](#method.query_max_transfer_out_amount).
118#[derive(Clone, Debug, Builder)]
119#[builder(pattern = "owned", build_fn(error = "ParamBuildError"))]
120pub struct QueryMaxTransferOutAmountParams {
121    ///
122    /// The `asset` parameter.
123    ///
124    /// This field is **required.
125    #[builder(setter(into))]
126    pub asset: String,
127    /// isolated symbol
128    ///
129    /// This field is **optional.
130    #[builder(setter(into), default)]
131    pub isolated_symbol: Option<String>,
132    /// No more than 60000
133    ///
134    /// This field is **optional.
135    #[builder(setter(into), default)]
136    pub recv_window: Option<i64>,
137}
138
139impl QueryMaxTransferOutAmountParams {
140    /// Create a builder for [`query_max_transfer_out_amount`].
141    ///
142    /// Required parameters:
143    ///
144    /// * `asset` — String
145    ///
146    #[must_use]
147    pub fn builder(asset: String) -> QueryMaxTransferOutAmountParamsBuilder {
148        QueryMaxTransferOutAmountParamsBuilder::default().asset(asset)
149    }
150}
151
152#[async_trait]
153impl TransferApi for TransferApiClient {
154    async fn get_cross_margin_transfer_history(
155        &self,
156        params: GetCrossMarginTransferHistoryParams,
157    ) -> anyhow::Result<RestApiResponse<models::GetCrossMarginTransferHistoryResponse>> {
158        let GetCrossMarginTransferHistoryParams {
159            asset,
160            r#type,
161            start_time,
162            end_time,
163            current,
164            size,
165            isolated_symbol,
166            recv_window,
167        } = params;
168
169        let mut query_params = BTreeMap::new();
170        let body_params = BTreeMap::new();
171
172        if let Some(rw) = asset {
173            query_params.insert("asset".to_string(), json!(rw));
174        }
175
176        if let Some(rw) = r#type {
177            query_params.insert("type".to_string(), json!(rw));
178        }
179
180        if let Some(rw) = start_time {
181            query_params.insert("startTime".to_string(), json!(rw));
182        }
183
184        if let Some(rw) = end_time {
185            query_params.insert("endTime".to_string(), json!(rw));
186        }
187
188        if let Some(rw) = current {
189            query_params.insert("current".to_string(), json!(rw));
190        }
191
192        if let Some(rw) = size {
193            query_params.insert("size".to_string(), json!(rw));
194        }
195
196        if let Some(rw) = isolated_symbol {
197            query_params.insert("isolatedSymbol".to_string(), json!(rw));
198        }
199
200        if let Some(rw) = recv_window {
201            query_params.insert("recvWindow".to_string(), json!(rw));
202        }
203
204        send_request::<models::GetCrossMarginTransferHistoryResponse>(
205            &self.configuration,
206            "/sapi/v1/margin/transfer",
207            reqwest::Method::GET,
208            query_params,
209            body_params,
210            if HAS_TIME_UNIT {
211                self.configuration.time_unit
212            } else {
213                None
214            },
215            true,
216        )
217        .await
218    }
219
220    async fn query_max_transfer_out_amount(
221        &self,
222        params: QueryMaxTransferOutAmountParams,
223    ) -> anyhow::Result<RestApiResponse<models::QueryMaxTransferOutAmountResponse>> {
224        let QueryMaxTransferOutAmountParams {
225            asset,
226            isolated_symbol,
227            recv_window,
228        } = params;
229
230        let mut query_params = BTreeMap::new();
231        let body_params = BTreeMap::new();
232
233        query_params.insert("asset".to_string(), json!(asset));
234
235        if let Some(rw) = isolated_symbol {
236            query_params.insert("isolatedSymbol".to_string(), json!(rw));
237        }
238
239        if let Some(rw) = recv_window {
240            query_params.insert("recvWindow".to_string(), json!(rw));
241        }
242
243        send_request::<models::QueryMaxTransferOutAmountResponse>(
244            &self.configuration,
245            "/sapi/v1/margin/maxTransferable",
246            reqwest::Method::GET,
247            query_params,
248            body_params,
249            if HAS_TIME_UNIT {
250                self.configuration.time_unit
251            } else {
252                None
253            },
254            true,
255        )
256        .await
257    }
258}
259
260#[cfg(all(test, feature = "margin_trading"))]
261mod tests {
262    use super::*;
263    use crate::TOKIO_SHARED_RT;
264    use crate::{errors::ConnectorError, models::DataFuture, models::RestApiRateLimit};
265    use async_trait::async_trait;
266    use std::collections::HashMap;
267
268    struct DummyRestApiResponse<T> {
269        inner: Box<dyn FnOnce() -> DataFuture<Result<T, ConnectorError>> + Send + Sync>,
270        status: u16,
271        headers: HashMap<String, String>,
272        rate_limits: Option<Vec<RestApiRateLimit>>,
273    }
274
275    impl<T> From<DummyRestApiResponse<T>> for RestApiResponse<T> {
276        fn from(dummy: DummyRestApiResponse<T>) -> Self {
277            Self {
278                data_fn: dummy.inner,
279                status: dummy.status,
280                headers: dummy.headers,
281                rate_limits: dummy.rate_limits,
282            }
283        }
284    }
285
286    struct MockTransferApiClient {
287        force_error: bool,
288    }
289
290    #[async_trait]
291    impl TransferApi for MockTransferApiClient {
292        async fn get_cross_margin_transfer_history(
293            &self,
294            _params: GetCrossMarginTransferHistoryParams,
295        ) -> anyhow::Result<RestApiResponse<models::GetCrossMarginTransferHistoryResponse>>
296        {
297            if self.force_error {
298                return Err(ConnectorError::ConnectorClientError {
299                    msg: "ResponseError".to_string(),
300                    code: None,
301                }
302                .into());
303            }
304
305            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"amount":"0.10000000","asset":"BNB","status":"CONFIRMED","timestamp":1566898617,"txId":5240372201,"type":"ROLL_IN","transFrom":"SPOT","transTo":"ISOLATED_MARGIN"},{"amount":"5.00000000","asset":"USDT","status":"CONFIRMED","timestamp":1566888436,"txId":5239810406,"type":"ROLL_OUT","transFrom":"ISOLATED_MARGIN","transTo":"ISOLATED_MARGIN","fromSymbol":"BNBUSDT","toSymbol":"BTCUSDT"},{"amount":"1.00000000","asset":"EOS","status":"CONFIRMED","timestamp":1566888403,"txId":5239808703,"type":"ROLL_IN"}],"total":3}"#).unwrap();
306            let dummy_response: models::GetCrossMarginTransferHistoryResponse =
307                serde_json::from_value(resp_json.clone())
308                    .expect("should parse into models::GetCrossMarginTransferHistoryResponse");
309
310            let dummy = DummyRestApiResponse {
311                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
312                status: 200,
313                headers: HashMap::new(),
314                rate_limits: None,
315            };
316
317            Ok(dummy.into())
318        }
319
320        async fn query_max_transfer_out_amount(
321            &self,
322            _params: QueryMaxTransferOutAmountParams,
323        ) -> anyhow::Result<RestApiResponse<models::QueryMaxTransferOutAmountResponse>> {
324            if self.force_error {
325                return Err(ConnectorError::ConnectorClientError {
326                    msg: "ResponseError".to_string(),
327                    code: None,
328                }
329                .into());
330            }
331
332            let resp_json: Value = serde_json::from_str(r#"{"amount":"3.59498107"}"#).unwrap();
333            let dummy_response: models::QueryMaxTransferOutAmountResponse =
334                serde_json::from_value(resp_json.clone())
335                    .expect("should parse into models::QueryMaxTransferOutAmountResponse");
336
337            let dummy = DummyRestApiResponse {
338                inner: Box::new(move || Box::pin(async move { Ok(dummy_response) })),
339                status: 200,
340                headers: HashMap::new(),
341                rate_limits: None,
342            };
343
344            Ok(dummy.into())
345        }
346    }
347
348    #[test]
349    fn get_cross_margin_transfer_history_required_params_success() {
350        TOKIO_SHARED_RT.block_on(async {
351            let client = MockTransferApiClient { force_error: false };
352
353            let params = GetCrossMarginTransferHistoryParams::builder().build().unwrap();
354
355            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"amount":"0.10000000","asset":"BNB","status":"CONFIRMED","timestamp":1566898617,"txId":5240372201,"type":"ROLL_IN","transFrom":"SPOT","transTo":"ISOLATED_MARGIN"},{"amount":"5.00000000","asset":"USDT","status":"CONFIRMED","timestamp":1566888436,"txId":5239810406,"type":"ROLL_OUT","transFrom":"ISOLATED_MARGIN","transTo":"ISOLATED_MARGIN","fromSymbol":"BNBUSDT","toSymbol":"BTCUSDT"},{"amount":"1.00000000","asset":"EOS","status":"CONFIRMED","timestamp":1566888403,"txId":5239808703,"type":"ROLL_IN"}],"total":3}"#).unwrap();
356            let expected_response : models::GetCrossMarginTransferHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetCrossMarginTransferHistoryResponse");
357
358            let resp = client.get_cross_margin_transfer_history(params).await.expect("Expected a response");
359            let data_future = resp.data();
360            let actual_response = data_future.await.unwrap();
361            assert_eq!(actual_response, expected_response);
362        });
363    }
364
365    #[test]
366    fn get_cross_margin_transfer_history_optional_params_success() {
367        TOKIO_SHARED_RT.block_on(async {
368            let client = MockTransferApiClient { force_error: false };
369
370            let params = GetCrossMarginTransferHistoryParams::builder().asset("asset_example".to_string()).r#type("r#type_example".to_string()).start_time(1623319461670).end_time(1641782889000).current(1).size(10).isolated_symbol("isolated_symbol_example".to_string()).recv_window(5000).build().unwrap();
371
372            let resp_json: Value = serde_json::from_str(r#"{"rows":[{"amount":"0.10000000","asset":"BNB","status":"CONFIRMED","timestamp":1566898617,"txId":5240372201,"type":"ROLL_IN","transFrom":"SPOT","transTo":"ISOLATED_MARGIN"},{"amount":"5.00000000","asset":"USDT","status":"CONFIRMED","timestamp":1566888436,"txId":5239810406,"type":"ROLL_OUT","transFrom":"ISOLATED_MARGIN","transTo":"ISOLATED_MARGIN","fromSymbol":"BNBUSDT","toSymbol":"BTCUSDT"},{"amount":"1.00000000","asset":"EOS","status":"CONFIRMED","timestamp":1566888403,"txId":5239808703,"type":"ROLL_IN"}],"total":3}"#).unwrap();
373            let expected_response : models::GetCrossMarginTransferHistoryResponse = serde_json::from_value(resp_json.clone()).expect("should parse into models::GetCrossMarginTransferHistoryResponse");
374
375            let resp = client.get_cross_margin_transfer_history(params).await.expect("Expected a response");
376            let data_future = resp.data();
377            let actual_response = data_future.await.unwrap();
378            assert_eq!(actual_response, expected_response);
379        });
380    }
381
382    #[test]
383    fn get_cross_margin_transfer_history_response_error() {
384        TOKIO_SHARED_RT.block_on(async {
385            let client = MockTransferApiClient { force_error: true };
386
387            let params = GetCrossMarginTransferHistoryParams::builder()
388                .build()
389                .unwrap();
390
391            match client.get_cross_margin_transfer_history(params).await {
392                Ok(_) => panic!("Expected an error"),
393                Err(err) => {
394                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
395                }
396            }
397        });
398    }
399
400    #[test]
401    fn query_max_transfer_out_amount_required_params_success() {
402        TOKIO_SHARED_RT.block_on(async {
403            let client = MockTransferApiClient { force_error: false };
404
405            let params = QueryMaxTransferOutAmountParams::builder("asset_example".to_string())
406                .build()
407                .unwrap();
408
409            let resp_json: Value = serde_json::from_str(r#"{"amount":"3.59498107"}"#).unwrap();
410            let expected_response: models::QueryMaxTransferOutAmountResponse =
411                serde_json::from_value(resp_json.clone())
412                    .expect("should parse into models::QueryMaxTransferOutAmountResponse");
413
414            let resp = client
415                .query_max_transfer_out_amount(params)
416                .await
417                .expect("Expected a response");
418            let data_future = resp.data();
419            let actual_response = data_future.await.unwrap();
420            assert_eq!(actual_response, expected_response);
421        });
422    }
423
424    #[test]
425    fn query_max_transfer_out_amount_optional_params_success() {
426        TOKIO_SHARED_RT.block_on(async {
427            let client = MockTransferApiClient { force_error: false };
428
429            let params = QueryMaxTransferOutAmountParams::builder("asset_example".to_string())
430                .isolated_symbol("isolated_symbol_example".to_string())
431                .recv_window(5000)
432                .build()
433                .unwrap();
434
435            let resp_json: Value = serde_json::from_str(r#"{"amount":"3.59498107"}"#).unwrap();
436            let expected_response: models::QueryMaxTransferOutAmountResponse =
437                serde_json::from_value(resp_json.clone())
438                    .expect("should parse into models::QueryMaxTransferOutAmountResponse");
439
440            let resp = client
441                .query_max_transfer_out_amount(params)
442                .await
443                .expect("Expected a response");
444            let data_future = resp.data();
445            let actual_response = data_future.await.unwrap();
446            assert_eq!(actual_response, expected_response);
447        });
448    }
449
450    #[test]
451    fn query_max_transfer_out_amount_response_error() {
452        TOKIO_SHARED_RT.block_on(async {
453            let client = MockTransferApiClient { force_error: true };
454
455            let params = QueryMaxTransferOutAmountParams::builder("asset_example".to_string())
456                .build()
457                .unwrap();
458
459            match client.query_max_transfer_out_amount(params).await {
460                Ok(_) => panic!("Expected an error"),
461                Err(err) => {
462                    assert_eq!(err.to_string(), "Connector client error: ResponseError");
463                }
464            }
465        });
466    }
467}