Skip to main content

bybit/
asset.rs

1#![allow(unused_imports, unreachable_code, unused_variables)]
2use crate::prelude::*;
3use serde_json::{json, Value};
4
5use crate::util::{build_json_request, build_request};
6
7#[derive(Clone)]
8pub struct AssetManager {
9    pub client: Client,
10    pub recv_window: u16,
11}
12
13impl AssetManager {
14    /// Get USDC session settlement records
15    ///
16    /// Query session settlement records of USDC perpetual and futures.
17    /// During periods of extreme market volatility, this interface may experience
18    /// increased latency or temporary delays in data delivery.
19    ///
20    /// # Arguments
21    ///
22    /// * `req` - A `SettlementRecordRequest` containing the query parameters
23    ///
24    /// # Returns
25    ///
26    /// Returns a `Result` containing `SettlementRecordResponse` if successful,
27    /// or `BybitError` if an error occurs.
28    pub async fn get_settlement_record<'a>(
29        &self,
30        req: SettlementRecordRequest<'a>,
31    ) -> Result<SettlementRecordResponse, BybitError> {
32        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
33
34        parameters.insert("category".to_owned(), req.category.to_string());
35
36        if let Some(symbol) = req.symbol {
37            parameters.insert("symbol".to_owned(), symbol.to_string());
38        }
39
40        if let Some(start_time) = req.start_time {
41            parameters.insert("startTime".to_owned(), start_time.to_string());
42        }
43
44        if let Some(end_time) = req.end_time {
45            parameters.insert("endTime".to_owned(), end_time.to_string());
46        }
47
48        if let Some(limit) = req.limit {
49            parameters.insert("limit".to_owned(), limit.to_string());
50        }
51
52        if let Some(cursor) = req.cursor {
53            parameters.insert("cursor".to_owned(), cursor.to_string());
54        }
55
56        let request = build_request(&parameters);
57        let response: BybitApiResponse<SettlementRecordResponse> = self
58            .client
59            .get_signed(
60                API::Asset(Asset::SettlementRecord),
61                self.recv_window,
62                Some(request),
63            )
64            .await?;
65        Ok(response.result)
66    }
67
68    /// Get coin exchange records
69    ///
70    /// Query the coin exchange records.
71    /// It sometimes has 5 secs delay.
72    ///
73    /// # Arguments
74    ///
75    /// * `req` - A `CoinExchangeRecordRequest` containing the query parameters
76    ///
77    /// # Returns
78    ///
79    /// Returns a `Result` containing `CoinExchangeRecordResponse` if successful,
80    /// or `BybitError` if an error occurs.
81    pub async fn get_coin_exchange_records<'a>(
82        &self,
83        req: CoinExchangeRecordRequest<'a>,
84    ) -> Result<CoinExchangeRecordResponse, BybitError> {
85        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
86
87        if let Some(from_coin) = req.from_coin {
88            parameters.insert("fromCoin".to_owned(), from_coin.to_string());
89        }
90
91        if let Some(to_coin) = req.to_coin {
92            parameters.insert("toCoin".to_owned(), to_coin.to_string());
93        }
94
95        if let Some(limit) = req.limit {
96            parameters.insert("limit".to_owned(), limit.to_string());
97        }
98
99        if let Some(cursor) = req.cursor {
100            parameters.insert("cursor".to_owned(), cursor.to_string());
101        }
102
103        let request = build_request(&parameters);
104        let response: BybitApiResponse<CoinExchangeRecordResponse> = self
105            .client
106            .get_signed(
107                API::Asset(Asset::CoinExchangeRecord),
108                self.recv_window,
109                Some(request),
110            )
111            .await?;
112        Ok(response.result)
113    }
114
115    /// Get coin information
116    ///
117    /// Query coin information, including chain information, withdraw and deposit status.
118    ///
119    /// # Arguments
120    ///
121    /// * `req` - A `CoinInfoRequest` containing the query parameters
122    ///
123    /// # Returns
124    ///
125    /// Returns a `Result` containing `CoinInfoResponse` if successful,
126    /// or `BybitError` if an error occurs.
127    pub async fn get_coin_info<'a>(
128        &self,
129        req: CoinInfoRequest<'a>,
130    ) -> Result<CoinInfoResponse, BybitError> {
131        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
132
133        if let Some(coin) = req.coin {
134            parameters.insert("coin".to_owned(), coin.to_string());
135        }
136
137        let request = build_request(&parameters);
138        let response: BybitApiResponse<CoinInfoResponse> = self
139            .client
140            .get_signed(
141                API::Asset(Asset::QueryInfo),
142                self.recv_window,
143                Some(request),
144            )
145            .await?;
146        Ok(response.result)
147    }
148
149    /// Get sub UID list
150    ///
151    /// Query the sub UIDs under a main UID. It returns up to 2000 sub accounts.
152    /// Query by the master UID's api key **only**.
153    ///
154    /// # Returns
155    ///
156    /// Returns a `Result` containing `SubUidResponse` if successful,
157    /// or `BybitError` if an error occurs.
158    pub async fn get_sub_uid(&self) -> Result<SubUidResponse, BybitError> {
159        let response: BybitApiResponse<SubUidResponse> = self
160            .client
161            .get_signed(
162                API::Asset(Asset::QueryTransferSubmemberList),
163                self.recv_window,
164                None,
165            )
166            .await?;
167        Ok(response.result)
168    }
169
170    /// Get delivery record
171    ///
172    /// Query delivery records of Inverse Futures, USDC Futures, USDT Futures and Options,
173    /// sorted by `deliveryTime` in descending order.
174    /// During periods of extreme market volatility, this interface may experience
175    /// increased latency or temporary delays in data delivery.
176    ///
177    /// # Arguments
178    ///
179    /// * `req` - A `DeliveryRecordRequest` containing the query parameters
180    ///
181    /// # Returns
182    ///
183    /// Returns a `Result` containing `DeliveryRecordResponse` if successful,
184    /// or `BybitError` if an error occurs.
185    pub async fn get_delivery_record<'a>(
186        &self,
187        req: DeliveryRecordRequest<'a>,
188    ) -> Result<DeliveryRecordResponse, BybitError> {
189        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
190
191        parameters.insert("category".to_owned(), req.category.to_string());
192
193        if let Some(symbol) = req.symbol {
194            parameters.insert("symbol".to_owned(), symbol.to_string());
195        }
196
197        if let Some(start_time) = req.start_time {
198            parameters.insert("startTime".to_owned(), start_time.to_string());
199        }
200
201        if let Some(end_time) = req.end_time {
202            parameters.insert("endTime".to_owned(), end_time.to_string());
203        }
204
205        if let Some(exp_date) = req.exp_date {
206            parameters.insert("expDate".to_owned(), exp_date.to_string());
207        }
208
209        if let Some(limit) = req.limit {
210            parameters.insert("limit".to_owned(), limit.to_string());
211        }
212
213        if let Some(cursor) = req.cursor {
214            parameters.insert("cursor".to_owned(), cursor.to_string());
215        }
216
217        let request = build_request(&parameters);
218        let response: BybitApiResponse<DeliveryRecordResponse> = self
219            .client
220            .get_signed(
221                API::Asset(Asset::DeliveryRecord),
222                self.recv_window,
223                Some(request),
224            )
225            .await?;
226        Ok(response.result)
227    }
228
229    /// Get single coin balance
230    ///
231    /// Query the balance of a specific coin in a specific account type.
232    /// Supports querying sub UID's balance. Also, you can check the transferable
233    /// amount from master to sub account, sub to master account or sub to sub account,
234    /// especially for user who has an institutional loan.
235    ///
236    /// During periods of extreme market volatility, this interface may experience
237    /// increased latency or temporary delays in data delivery.
238    ///
239    /// # Arguments
240    ///
241    /// * `req` - A `SingleCoinBalanceRequest` containing the query parameters
242    ///
243    /// # Returns
244    ///
245    /// Returns a `Result` containing `SingleCoinBalanceResponse` if successful,
246    /// or `BybitError` if an error occurs.
247    pub async fn get_single_coin_balance<'a>(
248        &self,
249        req: SingleCoinBalanceRequest<'a>,
250    ) -> Result<SingleCoinBalanceResponse, BybitError> {
251        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
252
253        parameters.insert("accountType".to_owned(), req.account_type.to_string());
254        parameters.insert("coin".to_owned(), req.coin.to_string());
255
256        if let Some(member_id) = req.member_id {
257            parameters.insert("memberId".to_owned(), member_id.to_string());
258        }
259
260        if let Some(to_member_id) = req.to_member_id {
261            parameters.insert("toMemberId".to_owned(), to_member_id.to_string());
262        }
263
264        if let Some(to_account_type) = req.to_account_type {
265            parameters.insert("toAccountType".to_owned(), to_account_type.to_string());
266        }
267
268        if let Some(with_bonus) = req.with_bonus {
269            parameters.insert("withBonus".to_owned(), with_bonus.to_string());
270        }
271
272        if let Some(with_transfer_safe_amount) = req.with_transfer_safe_amount {
273            parameters.insert(
274                "withTransferSafeAmount".to_owned(),
275                with_transfer_safe_amount.to_string(),
276            );
277        }
278
279        if let Some(with_ltv_transfer_safe_amount) = req.with_ltv_transfer_safe_amount {
280            parameters.insert(
281                "withLtvTransferSafeAmount".to_owned(),
282                with_ltv_transfer_safe_amount.to_string(),
283            );
284        }
285
286        let request = build_request(&parameters);
287        let response: BybitApiResponse<SingleCoinBalanceResponse> = self
288            .client
289            .get_signed(
290                API::Asset(Asset::QuerySingleAccountCoinBalance),
291                self.recv_window,
292                Some(request),
293            )
294            .await?;
295        Ok(response.result)
296    }
297
298    /// Get all coins balance
299    ///
300    /// You could get all coin balance of all account types under the master account,
301    /// and sub account.
302    ///
303    /// During periods of extreme market volatility, this interface may experience
304    /// increased latency or temporary delays in data delivery.
305    ///
306    /// # Arguments
307    ///
308    /// * `req` - A `AllCoinsBalanceRequest` containing the query parameters
309    ///
310    /// # Returns
311    ///
312    /// Returns a `Result` containing `AllCoinsBalanceResponse` if successful,
313    /// or `BybitError` if an error occurs.
314    pub async fn get_all_coins_balance<'a>(
315        &self,
316        req: AllCoinsBalanceRequest<'a>,
317    ) -> Result<AllCoinsBalanceResponse, BybitError> {
318        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
319
320        parameters.insert("accountType".to_owned(), req.account_type.to_string());
321
322        if let Some(member_id) = req.member_id {
323            parameters.insert("memberId".to_owned(), member_id.to_string());
324        }
325
326        if let Some(coin) = req.coin {
327            parameters.insert("coin".to_owned(), coin.to_string());
328        }
329
330        if let Some(with_bonus) = req.with_bonus {
331            parameters.insert("withBonus".to_owned(), with_bonus.to_string());
332        }
333
334        let request = build_request(&parameters);
335        let response: BybitApiResponse<AllCoinsBalanceResponse> = self
336            .client
337            .get_signed(
338                API::Asset(Asset::QueryAccountCoinBalance),
339                self.recv_window,
340                Some(request),
341            )
342            .await?;
343        Ok(response.result)
344    }
345
346    /// Get withdrawable amount
347    ///
348    /// Query the withdrawable amount for a specific coin.
349    ///
350    /// During periods of extreme market volatility, this interface may experience
351    /// increased latency or temporary delays in data delivery.
352    ///
353    /// # Arguments
354    ///
355    /// * `req` - A `WithdrawableAmountRequest` containing the query parameters
356    ///
357    /// # Returns
358    ///
359    /// Returns a `Result` containing `WithdrawableAmountResponse` if successful,
360    /// or `BybitError` if an error occurs.
361    pub async fn get_withdrawable_amount<'a>(
362        &self,
363        req: WithdrawableAmountRequest<'a>,
364    ) -> Result<WithdrawableAmountResponse, BybitError> {
365        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
366
367        parameters.insert("coin".to_owned(), req.coin.to_string());
368
369        let request = build_request(&parameters);
370        let response: BybitApiResponse<WithdrawableAmountResponse> = self
371            .client
372            .get_signed(
373                API::Asset(Asset::WithdrawableAmount),
374                self.recv_window,
375                Some(request),
376            )
377            .await?;
378        Ok(response.result)
379    }
380
381    /// Create internal transfer
382    ///
383    /// Create the internal transfer between different account types under the same UID.
384    ///
385    /// # Arguments
386    ///
387    /// * `req` - A `InternalTransferRequest` containing the transfer parameters
388    ///
389    /// # Returns
390    ///
391    /// Returns a `Result` containing `InternalTransferResponse` if successful,
392    /// or `BybitError` if an error occurs.
393    pub async fn create_internal_transfer<'a>(
394        &self,
395        req: InternalTransferRequest<'a>,
396    ) -> Result<InternalTransferResponse, BybitError> {
397        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
398        parameters.insert("transferId".to_owned(), req.transfer_id.to_string());
399        parameters.insert("coin".to_owned(), req.coin.to_string());
400        parameters.insert("amount".to_owned(), req.amount.to_string());
401        parameters.insert(
402            "fromAccountType".to_owned(),
403            req.from_account_type.to_string(),
404        );
405        parameters.insert("toAccountType".to_owned(), req.to_account_type.to_string());
406
407        let request = build_json_request(&parameters);
408        let response: BybitApiResponse<InternalTransferResponse> = self
409            .client
410            .post_signed(
411                API::Asset(Asset::Intertransfer),
412                self.recv_window,
413                Some(request),
414            )
415            .await?;
416        Ok(response.result)
417    }
418
419    /// Get internal transfer records
420    ///
421    /// Query the internal transfer records between different account types under the same UID.
422    ///
423    /// # Arguments
424    ///
425    /// * `req` - A `InternalTransferRecordsRequest` containing the query parameters
426    ///
427    /// # Returns
428    ///
429    /// Returns a `Result` containing `InternalTransferRecordsResponse` if successful,
430    /// or `BybitError` if an error occurs.
431    pub async fn get_internal_transfer_records<'a>(
432        &self,
433        req: InternalTransferRecordsRequest<'a>,
434    ) -> Result<InternalTransferRecordsResponse, BybitError> {
435        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
436
437        if let Some(transfer_id) = req.transfer_id {
438            parameters.insert("transferId".to_owned(), transfer_id.to_string());
439        }
440
441        if let Some(coin) = req.coin {
442            parameters.insert("coin".to_owned(), coin.to_string());
443        }
444
445        if let Some(status) = req.status {
446            parameters.insert("status".to_owned(), status.to_string());
447        }
448
449        if let Some(start_time) = req.start_time {
450            parameters.insert("startTime".to_owned(), start_time.to_string());
451        }
452
453        if let Some(end_time) = req.end_time {
454            parameters.insert("endTime".to_owned(), end_time.to_string());
455        }
456
457        if let Some(limit) = req.limit {
458            parameters.insert("limit".to_owned(), limit.to_string());
459        }
460
461        if let Some(cursor) = req.cursor {
462            parameters.insert("cursor".to_owned(), cursor.to_string());
463        }
464
465        let request = build_request(&parameters);
466        let response: BybitApiResponse<InternalTransferRecordsResponse> = self
467            .client
468            .get_signed(
469                API::Asset(Asset::QueryTransferList),
470                self.recv_window,
471                Some(request),
472            )
473            .await?;
474        Ok(response.result)
475    }
476
477    /// Create universal transfer
478    ///
479    /// Transfer between sub-sub or main-sub.
480    ///
481    /// # Arguments
482    ///
483    /// * `req` - A `UniversalTransferRequest` containing the transfer parameters
484    ///
485    /// # Returns
486    ///
487    /// Returns a `Result` containing `UniversalTransferResponse` if successful,
488    /// or `BybitError` if an error occurs.
489    pub async fn create_universal_transfer<'a>(
490        &self,
491        req: UniversalTransferRequest<'a>,
492    ) -> Result<UniversalTransferResponse, BybitError> {
493        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
494        parameters.insert("transferId".to_owned(), req.transfer_id.to_string());
495        parameters.insert("coin".to_owned(), req.coin.to_string());
496        parameters.insert("amount".to_owned(), req.amount.to_string());
497        parameters.insert("fromMemberId".to_owned(), req.from_member_id.to_string());
498        parameters.insert("toMemberId".to_owned(), req.to_member_id.to_string());
499        parameters.insert(
500            "fromAccountType".to_owned(),
501            req.from_account_type.to_string(),
502        );
503        parameters.insert("toAccountType".to_owned(), req.to_account_type.to_string());
504
505        let request = build_json_request(&parameters);
506        let response: BybitApiResponse<UniversalTransferResponse> = self
507            .client
508            .post_signed(
509                API::Asset(Asset::UniversalTransfer),
510                self.recv_window,
511                Some(request),
512            )
513            .await?;
514        Ok(response.result)
515    }
516
517    /// Get universal transfer records
518    ///
519    /// Query universal transfer records.
520    ///
521    /// # Arguments
522    ///
523    /// * `req` - A `UniversalTransferRecordsRequest` containing the query parameters
524    ///
525    /// # Returns
526    ///
527    /// Returns a `Result` containing `UniversalTransferRecordsResponse` if successful,
528    /// or `BybitError` if an error occurs.
529    pub async fn get_universal_transfer_records<'a>(
530        &self,
531        req: UniversalTransferRecordsRequest<'a>,
532    ) -> Result<UniversalTransferRecordsResponse, BybitError> {
533        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
534
535        if let Some(transfer_id) = req.transfer_id {
536            parameters.insert("transferId".to_owned(), transfer_id.to_string());
537        }
538
539        if let Some(coin) = req.coin {
540            parameters.insert("coin".to_owned(), coin.to_string());
541        }
542
543        if let Some(status) = req.status {
544            parameters.insert("status".to_owned(), status.to_string());
545        }
546
547        if let Some(start_time) = req.start_time {
548            parameters.insert("startTime".to_owned(), start_time.to_string());
549        }
550
551        if let Some(end_time) = req.end_time {
552            parameters.insert("endTime".to_owned(), end_time.to_string());
553        }
554
555        if let Some(limit) = req.limit {
556            parameters.insert("limit".to_owned(), limit.to_string());
557        }
558
559        if let Some(cursor) = req.cursor {
560            parameters.insert("cursor".to_owned(), cursor.to_string());
561        }
562
563        let request = build_request(&parameters);
564        let response: BybitApiResponse<UniversalTransferRecordsResponse> = self
565            .client
566            .get_signed(
567                API::Asset(Asset::QueryUniversalTransferList),
568                self.recv_window,
569                Some(request),
570            )
571            .await?;
572        Ok(response.result)
573    }
574
575    /// Get transferable coin list
576    ///
577    /// Query the transferable coin list between each account type.
578    ///
579    /// # Arguments
580    ///
581    /// * `req` - A `TransferableCoinRequest` containing the query parameters
582    ///
583    /// # Returns
584    ///
585    /// Returns a `Result` containing `TransferableCoinResponse` if successful,
586    /// or `BybitError` if an error occurs.
587    pub async fn get_transferable_coin_list<'a>(
588        &self,
589        req: TransferableCoinRequest<'a>,
590    ) -> Result<TransferableCoinResponse, BybitError> {
591        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
592
593        parameters.insert(
594            "fromAccountType".to_owned(),
595            req.from_account_type.to_string(),
596        );
597        parameters.insert("toAccountType".to_owned(), req.to_account_type.to_string());
598
599        let request = build_request(&parameters);
600        let response: BybitApiResponse<TransferableCoinResponse> = self
601            .client
602            .get_signed(
603                API::Asset(Asset::QueryTransferCoinList),
604                self.recv_window,
605                Some(request),
606            )
607            .await?;
608        Ok(response.result)
609    }
610
611    /// Set deposit account
612    ///
613    /// Set auto transfer account after deposit. The same function as the setting
614    /// for Deposit on web GUI.
615    ///
616    /// Only **main** UID can access.
617    ///
618    /// # Arguments
619    ///
620    /// * `req` - A `SetDepositAccountRequest` containing the account type
621    ///
622    /// # Returns
623    ///
624    /// Returns a `Result` containing `SetDepositAccountResponse` if successful,
625    /// or `BybitError` if an error occurs.
626    pub async fn set_deposit_account<'a>(
627        &self,
628        req: SetDepositAccountRequest<'a>,
629    ) -> Result<SetDepositAccountResponse, BybitError> {
630        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
631        parameters.insert("accountType".to_owned(), req.account_type.to_string());
632
633        let request = build_json_request(&parameters);
634        let response: BybitApiResponse<SetDepositAccountResponse> = self
635            .client
636            .post_signed(
637                API::Asset(Asset::SetDepositAccount),
638                self.recv_window,
639                Some(request),
640            )
641            .await?;
642        Ok(response.result)
643    }
644
645    /// Get deposit records (on-chain)
646    ///
647    /// Query deposit records.
648    /// - `endTime` - `startTime` should be less than 30 days. Query last 30 days records by default.
649    /// - Support using **main or sub** UID api key to query deposit records respectively.
650    ///
651    /// # Arguments
652    ///
653    /// * `req` - A `DepositRecordRequest` containing the query parameters
654    ///
655    /// # Returns
656    ///
657    /// Returns a `Result` containing `DepositRecordResponse` if successful,
658    /// or `BybitError` if an error occurs.
659    pub async fn get_deposit_records<'a>(
660        &self,
661        req: DepositRecordRequest<'a>,
662    ) -> Result<DepositRecordResponse, BybitError> {
663        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
664
665        if let Some(id) = req.id {
666            parameters.insert("id".to_owned(), id.to_string());
667        }
668
669        if let Some(tx_id) = req.tx_id {
670            parameters.insert("txID".to_owned(), tx_id.to_string());
671        }
672
673        if let Some(coin) = req.coin {
674            parameters.insert("coin".to_owned(), coin.to_string());
675        }
676
677        if let Some(start_time) = req.start_time {
678            parameters.insert("startTime".to_owned(), start_time.to_string());
679        }
680
681        if let Some(end_time) = req.end_time {
682            parameters.insert("endTime".to_owned(), end_time.to_string());
683        }
684
685        if let Some(limit) = req.limit {
686            parameters.insert("limit".to_owned(), limit.to_string());
687        }
688
689        if let Some(cursor) = req.cursor {
690            parameters.insert("cursor".to_owned(), cursor.to_string());
691        }
692
693        let request = build_request(&parameters);
694        let response: BybitApiResponse<DepositRecordResponse> = self
695            .client
696            .get_signed(
697                API::Asset(Asset::QueryRecord),
698                self.recv_window,
699                Some(request),
700            )
701            .await?;
702        Ok(response.result)
703    }
704
705    /// Get sub deposit records (on-chain)
706    ///
707    /// Query subaccount's deposit records by **main** UID's API key.
708    /// - `endTime` - `startTime` should be less than 30 days. Queries for the last 30 days worth of records by default.
709    ///
710    /// # Arguments
711    ///
712    /// * `req` - A `SubDepositRecordRequest` containing the query parameters
713    ///
714    /// # Returns
715    ///
716    /// Returns a `Result` containing `SubDepositRecordResponse` if successful,
717    /// or `BybitError` if an error occurs.
718    pub async fn get_sub_deposit_records<'a>(
719        &self,
720        req: SubDepositRecordRequest<'a>,
721    ) -> Result<SubDepositRecordResponse, BybitError> {
722        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
723
724        parameters.insert("subMemberId".to_owned(), req.sub_member_id.to_string());
725
726        if let Some(id) = req.id {
727            parameters.insert("id".to_owned(), id.to_string());
728        }
729
730        if let Some(tx_id) = req.tx_id {
731            parameters.insert("txID".to_owned(), tx_id.to_string());
732        }
733
734        if let Some(coin) = req.coin {
735            parameters.insert("coin".to_owned(), coin.to_string());
736        }
737
738        if let Some(start_time) = req.start_time {
739            parameters.insert("startTime".to_owned(), start_time.to_string());
740        }
741
742        if let Some(end_time) = req.end_time {
743            parameters.insert("endTime".to_owned(), end_time.to_string());
744        }
745
746        if let Some(limit) = req.limit {
747            parameters.insert("limit".to_owned(), limit.to_string());
748        }
749
750        if let Some(cursor) = req.cursor {
751            parameters.insert("cursor".to_owned(), cursor.to_string());
752        }
753
754        let request = build_request(&parameters);
755        let response: BybitApiResponse<SubDepositRecordResponse> = self
756            .client
757            .get_signed(
758                API::Asset(Asset::QuerySubMemberRecord),
759                self.recv_window,
760                Some(request),
761            )
762            .await?;
763        Ok(response.result)
764    }
765
766    /// Get internal deposit records (off-chain)
767    ///
768    /// Query deposit records within the Bybit platform. These transactions are not on the blockchain.
769    /// - The maximum difference between the start time and the end time is 30 days
770    /// - Support to get deposit records by Master or Sub Member Api Key
771    ///
772    /// # Arguments
773    ///
774    /// * `req` - A `InternalDepositRecordRequest` containing the query parameters
775    ///
776    /// # Returns
777    ///
778    /// Returns a `Result` containing `InternalDepositRecordResponse` if successful,
779    /// or `BybitError` if an error occurs.
780    pub async fn get_internal_deposit_records<'a>(
781        &self,
782        req: InternalDepositRecordRequest<'a>,
783    ) -> Result<InternalDepositRecordResponse, BybitError> {
784        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
785
786        if let Some(tx_id) = req.tx_id {
787            parameters.insert("txID".to_owned(), tx_id.to_string());
788        }
789
790        if let Some(start_time) = req.start_time {
791            parameters.insert("startTime".to_owned(), start_time.to_string());
792        }
793
794        if let Some(end_time) = req.end_time {
795            parameters.insert("endTime".to_owned(), end_time.to_string());
796        }
797
798        if let Some(coin) = req.coin {
799            parameters.insert("coin".to_owned(), coin.to_string());
800        }
801
802        if let Some(cursor) = req.cursor {
803            parameters.insert("cursor".to_owned(), cursor.to_string());
804        }
805
806        if let Some(limit) = req.limit {
807            parameters.insert("limit".to_owned(), limit.to_string());
808        }
809
810        let request = build_request(&parameters);
811        let response: BybitApiResponse<InternalDepositRecordResponse> = self
812            .client
813            .get_signed(
814                API::Asset(Asset::QueryInternalRecord),
815                self.recv_window,
816                Some(request),
817            )
818            .await?;
819        Ok(response.result)
820    }
821
822    /// Get master deposit address
823    ///
824    /// Query the deposit address information of MASTER account.
825    ///
826    /// # Arguments
827    ///
828    /// * `req` - A `MasterDepositAddressRequest` containing the query parameters
829    ///
830    /// # Returns
831    ///
832    /// Returns a `Result` containing `MasterDepositAddressResponse` if successful,
833    /// or `BybitError` if an error occurs.
834    pub async fn get_master_deposit_address<'a>(
835        &self,
836        req: MasterDepositAddressRequest<'a>,
837    ) -> Result<MasterDepositAddressResponse, BybitError> {
838        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
839
840        parameters.insert("coin".to_owned(), req.coin.to_string());
841
842        if let Some(chain_type) = req.chain_type {
843            parameters.insert("chainType".to_owned(), chain_type.to_string());
844        }
845
846        let request = build_request(&parameters);
847        let response: BybitApiResponse<MasterDepositAddressResponse> = self
848            .client
849            .get_signed(
850                API::Asset(Asset::QueryAddress),
851                self.recv_window,
852                Some(request),
853            )
854            .await?;
855        Ok(response.result)
856    }
857
858    /// Get sub deposit address
859    ///
860    /// Query the deposit address information of SUB account.
861    /// - Use master UID's api key **only**
862    /// - Custodial sub account deposit address cannot be obtained
863    ///
864    /// # Arguments
865    ///
866    /// * `req` - A `SubDepositAddressRequest` containing the query parameters
867    ///
868    /// # Returns
869    ///
870    /// Returns a `Result` containing `SubDepositAddressResponse` if successful,
871    /// or `BybitError` if an error occurs.
872    pub async fn get_sub_deposit_address<'a>(
873        &self,
874        req: SubDepositAddressRequest<'a>,
875    ) -> Result<SubDepositAddressResponse, BybitError> {
876        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
877
878        parameters.insert("coin".to_owned(), req.coin.to_string());
879        parameters.insert("chainType".to_owned(), req.chain_type.to_string());
880        parameters.insert("subMemberId".to_owned(), req.sub_member_id.to_string());
881
882        let request = build_request(&parameters);
883        let response: BybitApiResponse<SubDepositAddressResponse> = self
884            .client
885            .get_signed(
886                API::Asset(Asset::QuerySubmemberAddress),
887                self.recv_window,
888                Some(request),
889            )
890            .await?;
891        Ok(response.result)
892    }
893
894    /// Get withdrawal address list
895    ///
896    /// Query the withdrawal addresses in the address book.
897    ///
898    /// # Note
899    /// The API key for querying this endpoint must have withdrawal permissions.
900    ///
901    /// # Arguments
902    ///
903    /// * `req` - A `WithdrawalAddressRequest` containing the query parameters
904    ///
905    /// # Returns
906    ///
907    /// Returns a `Result` containing `WithdrawalAddressResponse` if successful,
908    /// or `BybitError` if an error occurs.
909    pub async fn get_withdrawal_address<'a>(
910        &self,
911        req: WithdrawalAddressRequest<'a>,
912    ) -> Result<WithdrawalAddressResponse, BybitError> {
913        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
914
915        if let Some(coin) = req.coin {
916            parameters.insert("coin".to_owned(), coin.to_string());
917        }
918
919        if let Some(chain) = req.chain {
920            parameters.insert("chain".to_owned(), chain.to_string());
921        }
922
923        if let Some(address_type) = req.address_type {
924            parameters.insert("addressType".to_owned(), address_type.to_string());
925        }
926
927        if let Some(limit) = req.limit {
928            parameters.insert("limit".to_owned(), limit.to_string());
929        }
930
931        if let Some(cursor) = req.cursor {
932            parameters.insert("cursor".to_owned(), cursor.to_string());
933        }
934
935        let request = build_request(&parameters);
936        let response: BybitApiResponse<WithdrawalAddressResponse> = self
937            .client
938            .get_signed(
939                API::Asset(Asset::QueryWithdrawalAddress),
940                self.recv_window,
941                Some(request),
942            )
943            .await?;
944        Ok(response.result)
945    }
946
947    /// Get withdrawal records
948    ///
949    /// Query withdrawal records.
950    ///
951    /// # Notes
952    /// - `endTime` - `startTime` should be less than 30 days. Query last 30 days records by default.
953    /// - Can query by the master UID's api key **only**
954    ///
955    /// # Arguments
956    ///
957    /// * `req` - A `WithdrawalRecordRequest` containing the query parameters
958    ///
959    /// # Returns
960    ///
961    /// Returns a `Result` containing `WithdrawalRecordResponse` if successful,
962    /// or `BybitError` if an error occurs.
963    pub async fn get_withdrawal_records<'a>(
964        &self,
965        req: WithdrawalRecordRequest<'a>,
966    ) -> Result<WithdrawalRecordResponse, BybitError> {
967        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
968
969        if let Some(withdraw_id) = req.withdraw_id {
970            parameters.insert("withdrawID".to_owned(), withdraw_id.to_string());
971        }
972
973        if let Some(tx_id) = req.tx_id {
974            parameters.insert("txID".to_owned(), tx_id.to_string());
975        }
976
977        if let Some(coin) = req.coin {
978            parameters.insert("coin".to_owned(), coin.to_string());
979        }
980
981        if let Some(withdraw_type) = req.withdraw_type {
982            parameters.insert("withdrawType".to_owned(), withdraw_type.to_string());
983        }
984
985        if let Some(start_time) = req.start_time {
986            parameters.insert("startTime".to_owned(), start_time.to_string());
987        }
988
989        if let Some(end_time) = req.end_time {
990            parameters.insert("endTime".to_owned(), end_time.to_string());
991        }
992
993        if let Some(limit) = req.limit {
994            parameters.insert("limit".to_owned(), limit.to_string());
995        }
996
997        if let Some(cursor) = req.cursor {
998            parameters.insert("cursor".to_owned(), cursor.to_string());
999        }
1000
1001        let request = build_request(&parameters);
1002        let response: BybitApiResponse<WithdrawalRecordResponse> = self
1003            .client
1004            .get_signed(
1005                API::Asset(Asset::QueryWithdrawalRecord),
1006                self.recv_window,
1007                Some(request),
1008            )
1009            .await?;
1010        Ok(response.result)
1011    }
1012
1013    /// Get available WASPs (Virtual Asset Service Providers)
1014    ///
1015    /// This endpoint is used for querying the available WASPs.
1016    /// This API distinguishes which compliance zone the user belongs to
1017    /// and the corresponding list of exchanges based on the user's UID.
1018    ///
1019    /// # Arguments
1020    ///
1021    /// None - This endpoint takes no parameters
1022    ///
1023    /// # Returns
1024    ///
1025    /// Returns a `Result` containing `VaspListResponse` if successful,
1026    /// or `BybitError` if an error occurs.
1027    pub async fn get_vasp_list(&self) -> Result<VaspListResponse, BybitError> {
1028        let response: BybitApiResponse<VaspListResponse> = self
1029            .client
1030            .get_signed(API::Asset(Asset::QueryVaspList), self.recv_window, None)
1031            .await?;
1032        Ok(response.result)
1033    }
1034
1035    /// Create a withdrawal
1036    ///
1037    /// Withdraw assets from your Bybit account. You can make an off-chain transfer
1038    /// if the target wallet address is from Bybit. This means that no blockchain
1039    /// fee will be charged.
1040    ///
1041    /// # Notes
1042    /// - Although the API rate limit for this endpoint is 5 req/s, there is also a
1043    ///   secondary limit: you can only withdraw once every 10 seconds per chain/coin combination.
1044    /// - Make sure you have whitelisted your wallet address
1045    /// - Request by the master UID's api key **only**
1046    ///
1047    /// # Arguments
1048    ///
1049    /// * `req` - A `WithdrawRequest` containing the withdrawal parameters
1050    ///
1051    /// # Returns
1052    ///
1053    /// Returns a `Result` containing `WithdrawResponse` if successful,
1054    /// or `BybitError` if an error occurs.
1055    pub async fn withdraw<'a>(
1056        &self,
1057        req: WithdrawRequest<'a>,
1058    ) -> Result<WithdrawResponse, BybitError> {
1059        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
1060
1061        parameters.insert("coin".to_owned(), req.coin.to_string());
1062        parameters.insert("address".to_owned(), req.address.to_string());
1063        parameters.insert("amount".to_owned(), req.amount.to_string());
1064        parameters.insert("timestamp".to_owned(), req.timestamp.to_string());
1065        parameters.insert("accountType".to_owned(), req.account_type.to_string());
1066
1067        if let Some(chain) = req.chain {
1068            parameters.insert("chain".to_owned(), chain.to_string());
1069        }
1070
1071        if let Some(tag) = req.tag {
1072            parameters.insert("tag".to_owned(), tag.to_string());
1073        }
1074
1075        if let Some(force_chain) = req.force_chain {
1076            parameters.insert("forceChain".to_owned(), force_chain.to_string());
1077        }
1078
1079        if let Some(fee_type) = req.fee_type {
1080            parameters.insert("feeType".to_owned(), fee_type.to_string());
1081        }
1082
1083        if let Some(request_id) = req.request_id {
1084            parameters.insert("requestId".to_owned(), request_id.to_string());
1085        }
1086
1087        // Handle beneficiary info if present
1088        if let Some(beneficiary) = req.beneficiary {
1089            if let Some(beneficiary_transaction_purpose) =
1090                beneficiary.beneficiary_transaction_purpose
1091            {
1092                parameters.insert(
1093                    "beneficiaryTransactionPurpose".to_owned(),
1094                    beneficiary_transaction_purpose.to_string(),
1095                );
1096            }
1097
1098            if let Some(beneficiary_representative_first_name) =
1099                beneficiary.beneficiary_representative_first_name
1100            {
1101                parameters.insert(
1102                    "beneficiaryRepresentativeFirstName".to_owned(),
1103                    beneficiary_representative_first_name.to_string(),
1104                );
1105            }
1106
1107            if let Some(beneficiary_representative_last_name) =
1108                beneficiary.beneficiary_representative_last_name
1109            {
1110                parameters.insert(
1111                    "beneficiaryRepresentativeLastName".to_owned(),
1112                    beneficiary_representative_last_name.to_string(),
1113                );
1114            }
1115
1116            if let Some(vasp_entity_id) = beneficiary.vasp_entity_id {
1117                parameters.insert("vaspEntityId".to_owned(), vasp_entity_id.to_string());
1118            }
1119
1120            if let Some(beneficiary_name) = beneficiary.beneficiary_name {
1121                parameters.insert("beneficiaryName".to_owned(), beneficiary_name.to_string());
1122            }
1123
1124            if let Some(beneficiary_legal_type) = beneficiary.beneficiary_legal_type {
1125                parameters.insert(
1126                    "beneficiaryLegalType".to_owned(),
1127                    beneficiary_legal_type.to_string(),
1128                );
1129            }
1130
1131            if let Some(beneficiary_wallet_type) = beneficiary.beneficiary_wallet_type {
1132                parameters.insert(
1133                    "beneficiaryWalletType".to_owned(),
1134                    beneficiary_wallet_type.to_string(),
1135                );
1136            }
1137
1138            if let Some(beneficiary_unhosted_wallet_type) =
1139                beneficiary.beneficiary_unhosted_wallet_type
1140            {
1141                parameters.insert(
1142                    "beneficiaryUnhostedWalletType".to_owned(),
1143                    beneficiary_unhosted_wallet_type.to_string(),
1144                );
1145            }
1146
1147            if let Some(beneficiary_poi_number) = beneficiary.beneficiary_poi_number {
1148                parameters.insert(
1149                    "beneficiaryPoiNumber".to_owned(),
1150                    beneficiary_poi_number.to_string(),
1151                );
1152            }
1153
1154            if let Some(beneficiary_poi_type) = beneficiary.beneficiary_poi_type {
1155                parameters.insert(
1156                    "beneficiaryPoiType".to_owned(),
1157                    beneficiary_poi_type.to_string(),
1158                );
1159            }
1160
1161            if let Some(beneficiary_poi_issuing_country) =
1162                beneficiary.beneficiary_poi_issuing_country
1163            {
1164                parameters.insert(
1165                    "beneficiaryPoiIssuingCountry".to_owned(),
1166                    beneficiary_poi_issuing_country.to_string(),
1167                );
1168            }
1169
1170            if let Some(beneficiary_poi_expired_date) = beneficiary.beneficiary_poi_expired_date {
1171                parameters.insert(
1172                    "beneficiaryPoiExpiredDate".to_owned(),
1173                    beneficiary_poi_expired_date.to_string(),
1174                );
1175            }
1176
1177            if let Some(beneficiary_address_country) = beneficiary.beneficiary_address_country {
1178                parameters.insert(
1179                    "beneficiaryAddressCountry".to_owned(),
1180                    beneficiary_address_country.to_string(),
1181                );
1182            }
1183
1184            if let Some(beneficiary_address_state) = beneficiary.beneficiary_address_state {
1185                parameters.insert(
1186                    "beneficiaryAddressState".to_owned(),
1187                    beneficiary_address_state.to_string(),
1188                );
1189            }
1190
1191            if let Some(beneficiary_address_city) = beneficiary.beneficiary_address_city {
1192                parameters.insert(
1193                    "beneficiaryAddressCity".to_owned(),
1194                    beneficiary_address_city.to_string(),
1195                );
1196            }
1197
1198            if let Some(beneficiary_address_building) = beneficiary.beneficiary_address_building {
1199                parameters.insert(
1200                    "beneficiaryAddressBuilding".to_owned(),
1201                    beneficiary_address_building.to_string(),
1202                );
1203            }
1204
1205            if let Some(beneficiary_address_street) = beneficiary.beneficiary_address_street {
1206                parameters.insert(
1207                    "beneficiaryAddressStreet".to_owned(),
1208                    beneficiary_address_street.to_string(),
1209                );
1210            }
1211
1212            if let Some(beneficiary_address_postal_code) =
1213                beneficiary.beneficiary_address_postal_code
1214            {
1215                parameters.insert(
1216                    "beneficiaryAddressPostalCode".to_owned(),
1217                    beneficiary_address_postal_code.to_string(),
1218                );
1219            }
1220
1221            if let Some(beneficiary_date_of_birth) = beneficiary.beneficiary_date_of_birth {
1222                parameters.insert(
1223                    "beneficiaryDateOfBirth".to_owned(),
1224                    beneficiary_date_of_birth.to_string(),
1225                );
1226            }
1227
1228            if let Some(beneficiary_place_of_birth) = beneficiary.beneficiary_place_of_birth {
1229                parameters.insert(
1230                    "beneficiaryPlaceOfBirth".to_owned(),
1231                    beneficiary_place_of_birth.to_string(),
1232                );
1233            }
1234        }
1235
1236        let request = build_json_request(&parameters);
1237        let response: BybitApiResponse<WithdrawResponse> = self
1238            .client
1239            .post_signed(API::Asset(Asset::Withdraw), self.recv_window, Some(request))
1240            .await?;
1241        Ok(response.result)
1242    }
1243
1244    /// Cancel a withdrawal
1245    ///
1246    /// Cancel the withdrawal
1247    ///
1248    /// # Arguments
1249    ///
1250    /// * `req` - A `CancelWithdrawRequest` containing the withdrawal ID to cancel
1251    ///
1252    /// # Returns
1253    ///
1254    /// Returns a `Result` containing `CancelWithdrawResponse` if successful,
1255    /// or `BybitError` if an error occurs.
1256    pub async fn cancel_withdraw<'a>(
1257        &self,
1258        req: CancelWithdrawRequest<'a>,
1259    ) -> Result<CancelWithdrawResponse, BybitError> {
1260        let mut parameters: BTreeMap<String, String> = BTreeMap::new();
1261
1262        parameters.insert("id".to_owned(), req.id.to_string());
1263
1264        let request = build_json_request(&parameters);
1265        let response: BybitApiResponse<CancelWithdrawResponse> = self
1266            .client
1267            .post_signed(
1268                API::Asset(Asset::CancelWithdraw),
1269                self.recv_window,
1270                Some(request),
1271            )
1272            .await?;
1273        Ok(response.result)
1274    }
1275}