Skip to main content

binance_api_client/api/
wallet.rs

1//! Wallet API endpoints (SAPI).
2//!
3//! This module provides access to Binance Wallet SAPI endpoints for:
4//! - System status
5//! - Coin information
6//! - Deposit/withdrawal operations
7//! - Asset management
8//! - Universal transfers
9
10use crate::client::Client;
11use crate::error::Result;
12use crate::models::wallet::{
13    AccountSnapshot, AccountSnapshotType, AccountStatus, ApiKeyPermissions, ApiTradingStatus,
14    AssetDetail, CoinInfo, DepositAddress, DepositRecord, FundingAsset, SystemStatus, TradeFee,
15    TransferHistory, TransferResponse, UniversalTransferType, WalletBalance, WithdrawRecord,
16    WithdrawResponse,
17};
18
19// SAPI endpoints.
20const SAPI_V1_SYSTEM_STATUS: &str = "/sapi/v1/system/status";
21const SAPI_V1_CAPITAL_CONFIG_GETALL: &str = "/sapi/v1/capital/config/getall";
22const SAPI_V1_ACCOUNT_SNAPSHOT: &str = "/sapi/v1/accountSnapshot";
23const SAPI_V1_CAPITAL_DEPOSIT_HISREC: &str = "/sapi/v1/capital/deposit/hisrec";
24const SAPI_V1_CAPITAL_DEPOSIT_ADDRESS: &str = "/sapi/v1/capital/deposit/address";
25const SAPI_V1_CAPITAL_WITHDRAW_APPLY: &str = "/sapi/v1/capital/withdraw/apply";
26const SAPI_V1_CAPITAL_WITHDRAW_HISTORY: &str = "/sapi/v1/capital/withdraw/history";
27const SAPI_V1_ASSET_ASSET_DETAIL: &str = "/sapi/v1/asset/assetDetail";
28const SAPI_V1_ASSET_TRADE_FEE: &str = "/sapi/v1/asset/tradeFee";
29const SAPI_V1_ASSET_TRANSFER: &str = "/sapi/v1/asset/transfer";
30const SAPI_V1_ASSET_GET_FUNDING_ASSET: &str = "/sapi/v1/asset/get-funding-asset";
31const SAPI_V1_ASSET_WALLET_BALANCE: &str = "/sapi/v1/asset/wallet/balance";
32const SAPI_V1_ACCOUNT_STATUS: &str = "/sapi/v1/account/status";
33const SAPI_V1_ACCOUNT_API_TRADING_STATUS: &str = "/sapi/v1/account/apiTradingStatus";
34const SAPI_V1_ACCOUNT_API_RESTRICTIONS: &str = "/sapi/v1/account/apiRestrictions";
35
36/// Wallet API client.
37///
38/// Provides access to Binance Wallet SAPI endpoints for asset management,
39/// deposits, withdrawals, and account status.
40///
41/// # Example
42///
43/// ```rust,ignore
44/// let client = Binance::new("api_key", "secret_key")?;
45///
46/// // Check system status
47/// let status = client.wallet().system_status().await?;
48/// if status.is_normal() {
49///     println!("System is operational");
50/// }
51///
52/// // Get all coin information
53/// let coins = client.wallet().all_coins().await?;
54/// for coin in coins {
55///     println!("{}: free={}", coin.coin, coin.free);
56/// }
57/// ```
58#[derive(Clone)]
59pub struct Wallet {
60    pub(crate) client: Client,
61}
62
63impl Wallet {
64    /// Create a new Wallet API client.
65    pub(crate) fn new(client: Client) -> Self {
66        Self { client }
67    }
68
69    // System Status.
70
71    /// Fetch system status.
72    ///
73    /// Returns whether the Binance system is operational or under maintenance.
74    ///
75    /// # Example
76    ///
77    /// ```rust,ignore
78    /// let status = client.wallet().system_status().await?;
79    /// if status.is_normal() {
80    ///     println!("System is operational");
81    /// } else {
82    ///     println!("System maintenance: {}", status.msg);
83    /// }
84    /// ```
85    pub async fn system_status(&self) -> Result<SystemStatus> {
86        self.client.get(SAPI_V1_SYSTEM_STATUS, None).await
87    }
88
89    // Coin Information.
90
91    /// Get information of all coins (available for deposit and withdraw).
92    ///
93    /// Returns detailed information about all supported coins including
94    /// deposit/withdraw status, fees, and network information.
95    ///
96    /// # Example
97    ///
98    /// ```rust,ignore
99    /// let coins = client.wallet().all_coins().await?;
100    /// for coin in coins {
101    ///     println!("{} ({}): deposit={}, withdraw={}",
102    ///         coin.coin, coin.name,
103    ///         coin.deposit_all_enable, coin.withdraw_all_enable);
104    /// }
105    /// ```
106    pub async fn all_coins(&self) -> Result<Vec<CoinInfo>> {
107        self.client
108            .get_signed(SAPI_V1_CAPITAL_CONFIG_GETALL, &[])
109            .await
110    }
111
112    // Account Snapshots.
113
114    /// Get daily account snapshot.
115    ///
116    /// Returns account balance snapshots for the specified time period.
117    /// The query time period must be less than 30 days.
118    /// Only supports querying within the last month.
119    ///
120    /// # Arguments
121    ///
122    /// * `snapshot_type` - Type of account (Spot, Margin, or Futures)
123    /// * `start_time` - Start timestamp (optional)
124    /// * `end_time` - End timestamp (optional)
125    /// * `limit` - Number of records (default 7, max 30)
126    ///
127    /// # Example
128    ///
129    /// ```rust,ignore
130    /// use binance_api_client::AccountSnapshotType;
131    ///
132    /// let snapshot = client.wallet()
133    ///     .account_snapshot(AccountSnapshotType::Spot, None, None, Some(5))
134    ///     .await?;
135    /// ```
136    pub async fn account_snapshot(
137        &self,
138        snapshot_type: AccountSnapshotType,
139        start_time: Option<u64>,
140        end_time: Option<u64>,
141        limit: Option<u32>,
142    ) -> Result<AccountSnapshot> {
143        let type_str = match snapshot_type {
144            AccountSnapshotType::Spot => "SPOT",
145            AccountSnapshotType::Margin => "MARGIN",
146            AccountSnapshotType::Futures => "FUTURES",
147        };
148
149        let mut params: Vec<(&str, String)> = vec![("type", type_str.to_string())];
150
151        if let Some(st) = start_time {
152            params.push(("startTime", st.to_string()));
153        }
154        if let Some(et) = end_time {
155            params.push(("endTime", et.to_string()));
156        }
157        if let Some(l) = limit {
158            params.push(("limit", l.to_string()));
159        }
160
161        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
162        self.client
163            .get_signed(SAPI_V1_ACCOUNT_SNAPSHOT, &params_ref)
164            .await
165    }
166
167    // Deposit.
168
169    /// Get deposit address for a coin.
170    ///
171    /// # Arguments
172    ///
173    /// * `coin` - Coin symbol (e.g., "BTC")
174    /// * `network` - Network to use (optional, uses default if not specified)
175    ///
176    /// # Example
177    ///
178    /// ```rust,ignore
179    /// let address = client.wallet().deposit_address("BTC", None).await?;
180    /// println!("Deposit to: {}", address.address);
181    /// ```
182    pub async fn deposit_address(
183        &self,
184        coin: &str,
185        network: Option<&str>,
186    ) -> Result<DepositAddress> {
187        let mut params: Vec<(&str, String)> = vec![("coin", coin.to_string())];
188
189        if let Some(n) = network {
190            params.push(("network", n.to_string()));
191        }
192
193        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
194        self.client
195            .get_signed(SAPI_V1_CAPITAL_DEPOSIT_ADDRESS, &params_ref)
196            .await
197    }
198
199    /// Get deposit history.
200    ///
201    /// # Arguments
202    ///
203    /// * `coin` - Filter by coin (optional)
204    /// * `status` - Filter by status: 0=pending, 6=credited, 1=success (optional)
205    /// * `start_time` - Start timestamp (optional)
206    /// * `end_time` - End timestamp (optional)
207    /// * `offset` - Pagination offset (optional)
208    /// * `limit` - Number of records (default 1000, max 1000)
209    ///
210    /// # Example
211    ///
212    /// ```rust,ignore
213    /// let deposits = client.wallet()
214    ///     .deposit_history(Some("BTC"), None, None, None, None, Some(10))
215    ///     .await?;
216    /// for deposit in deposits {
217    ///     println!("{}: {} {}", deposit.tx_id, deposit.amount, deposit.coin);
218    /// }
219    /// ```
220    pub async fn deposit_history(
221        &self,
222        coin: Option<&str>,
223        status: Option<u32>,
224        start_time: Option<u64>,
225        end_time: Option<u64>,
226        offset: Option<u32>,
227        limit: Option<u32>,
228    ) -> Result<Vec<DepositRecord>> {
229        let mut params: Vec<(&str, String)> = vec![];
230
231        if let Some(c) = coin {
232            params.push(("coin", c.to_string()));
233        }
234        if let Some(s) = status {
235            params.push(("status", s.to_string()));
236        }
237        if let Some(st) = start_time {
238            params.push(("startTime", st.to_string()));
239        }
240        if let Some(et) = end_time {
241            params.push(("endTime", et.to_string()));
242        }
243        if let Some(o) = offset {
244            params.push(("offset", o.to_string()));
245        }
246        if let Some(l) = limit {
247            params.push(("limit", l.to_string()));
248        }
249
250        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
251        self.client
252            .get_signed(SAPI_V1_CAPITAL_DEPOSIT_HISREC, &params_ref)
253            .await
254    }
255
256    // Withdrawal.
257
258    /// Submit a withdrawal request.
259    ///
260    /// # Arguments
261    ///
262    /// * `coin` - Coin symbol
263    /// * `address` - Withdrawal address
264    /// * `amount` - Amount to withdraw
265    /// * `network` - Network to use (optional)
266    /// * `address_tag` - Secondary address identifier (memo/tag, optional)
267    /// * `withdraw_order_id` - Client ID for the withdrawal (optional)
268    ///
269    /// # Example
270    ///
271    /// ```rust,ignore
272    /// let response = client.wallet()
273    ///     .withdraw("USDT", "0x1234...", "100.0", Some("ETH"), None, None)
274    ///     .await?;
275    /// println!("Withdrawal ID: {}", response.id);
276    /// ```
277    pub async fn withdraw(
278        &self,
279        coin: &str,
280        address: &str,
281        amount: &str,
282        network: Option<&str>,
283        address_tag: Option<&str>,
284        withdraw_order_id: Option<&str>,
285    ) -> Result<WithdrawResponse> {
286        let mut params: Vec<(&str, String)> = vec![
287            ("coin", coin.to_string()),
288            ("address", address.to_string()),
289            ("amount", amount.to_string()),
290        ];
291
292        if let Some(n) = network {
293            params.push(("network", n.to_string()));
294        }
295        if let Some(tag) = address_tag {
296            params.push(("addressTag", tag.to_string()));
297        }
298        if let Some(id) = withdraw_order_id {
299            params.push(("withdrawOrderId", id.to_string()));
300        }
301
302        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
303        self.client
304            .post_signed(SAPI_V1_CAPITAL_WITHDRAW_APPLY, &params_ref)
305            .await
306    }
307
308    /// Get withdrawal history.
309    ///
310    /// # Arguments
311    ///
312    /// * `coin` - Filter by coin (optional)
313    /// * `withdraw_order_id` - Filter by client withdrawal ID (optional)
314    /// * `status` - Filter by status (optional)
315    /// * `start_time` - Start timestamp (optional)
316    /// * `end_time` - End timestamp (optional)
317    /// * `offset` - Pagination offset (optional)
318    /// * `limit` - Number of records (default 1000, max 1000)
319    ///
320    /// # Example
321    ///
322    /// ```rust,ignore
323    /// let withdrawals = client.wallet()
324    ///     .withdraw_history(None, None, None, None, None, None, Some(10))
325    ///     .await?;
326    /// ```
327    #[allow(clippy::too_many_arguments)]
328    pub async fn withdraw_history(
329        &self,
330        coin: Option<&str>,
331        withdraw_order_id: Option<&str>,
332        status: Option<u32>,
333        start_time: Option<u64>,
334        end_time: Option<u64>,
335        offset: Option<u32>,
336        limit: Option<u32>,
337    ) -> Result<Vec<WithdrawRecord>> {
338        let mut params: Vec<(&str, String)> = vec![];
339
340        if let Some(c) = coin {
341            params.push(("coin", c.to_string()));
342        }
343        if let Some(id) = withdraw_order_id {
344            params.push(("withdrawOrderId", id.to_string()));
345        }
346        if let Some(s) = status {
347            params.push(("status", s.to_string()));
348        }
349        if let Some(st) = start_time {
350            params.push(("startTime", st.to_string()));
351        }
352        if let Some(et) = end_time {
353            params.push(("endTime", et.to_string()));
354        }
355        if let Some(o) = offset {
356            params.push(("offset", o.to_string()));
357        }
358        if let Some(l) = limit {
359            params.push(("limit", l.to_string()));
360        }
361
362        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
363        self.client
364            .get_signed(SAPI_V1_CAPITAL_WITHDRAW_HISTORY, &params_ref)
365            .await
366    }
367
368    // Asset Management.
369
370    /// Get asset detail (deposit/withdraw fees and status).
371    ///
372    /// # Arguments
373    ///
374    /// * `asset` - Asset symbol (optional, returns all if not specified)
375    ///
376    /// # Example
377    ///
378    /// ```rust,ignore
379    /// let details = client.wallet().asset_detail(Some("BTC")).await?;
380    /// ```
381    pub async fn asset_detail(
382        &self,
383        asset: Option<&str>,
384    ) -> Result<std::collections::HashMap<String, AssetDetail>> {
385        let mut params: Vec<(&str, String)> = vec![];
386
387        if let Some(a) = asset {
388            params.push(("asset", a.to_string()));
389        }
390
391        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
392        self.client
393            .get_signed(SAPI_V1_ASSET_ASSET_DETAIL, &params_ref)
394            .await
395    }
396
397    /// Get trade fee for symbols.
398    ///
399    /// # Arguments
400    ///
401    /// * `symbol` - Trading pair symbol (optional, returns all if not specified)
402    ///
403    /// # Example
404    ///
405    /// ```rust,ignore
406    /// let fees = client.wallet().trade_fee(Some("BTCUSDT")).await?;
407    /// for fee in fees {
408    ///     println!("{}: maker={}, taker={}",
409    ///         fee.symbol, fee.maker_commission, fee.taker_commission);
410    /// }
411    /// ```
412    pub async fn trade_fee(&self, symbol: Option<&str>) -> Result<Vec<TradeFee>> {
413        let mut params: Vec<(&str, String)> = vec![];
414
415        if let Some(s) = symbol {
416            params.push(("symbol", s.to_string()));
417        }
418
419        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
420        self.client
421            .get_signed(SAPI_V1_ASSET_TRADE_FEE, &params_ref)
422            .await
423    }
424
425    // Universal Transfer.
426
427    /// Execute a universal transfer between accounts.
428    ///
429    /// # Arguments
430    ///
431    /// * `transfer_type` - Type of transfer
432    /// * `asset` - Asset to transfer
433    /// * `amount` - Amount to transfer
434    /// * `from_symbol` - Required for isolated margin transfers
435    /// * `to_symbol` - Required for isolated margin transfers
436    ///
437    /// # Example
438    ///
439    /// ```rust,ignore
440    /// use binance_api_client::UniversalTransferType;
441    ///
442    /// let response = client.wallet()
443    ///     .universal_transfer(
444    ///         UniversalTransferType::MainFunding,
445    ///         "USDT",
446    ///         "100.0",
447    ///         None,
448    ///         None,
449    ///     )
450    ///     .await?;
451    /// println!("Transfer ID: {}", response.tran_id);
452    /// ```
453    pub async fn universal_transfer(
454        &self,
455        transfer_type: UniversalTransferType,
456        asset: &str,
457        amount: &str,
458        from_symbol: Option<&str>,
459        to_symbol: Option<&str>,
460    ) -> Result<TransferResponse> {
461        let type_str = transfer_type.as_str().to_string();
462
463        let mut params: Vec<(&str, String)> = vec![
464            ("type", type_str),
465            ("asset", asset.to_string()),
466            ("amount", amount.to_string()),
467        ];
468
469        if let Some(from) = from_symbol {
470            params.push(("fromSymbol", from.to_string()));
471        }
472        if let Some(to) = to_symbol {
473            params.push(("toSymbol", to.to_string()));
474        }
475
476        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
477        self.client
478            .post_signed(SAPI_V1_ASSET_TRANSFER, &params_ref)
479            .await
480    }
481
482    /// Get universal transfer history.
483    ///
484    /// # Arguments
485    ///
486    /// * `transfer_type` - Type of transfer to query
487    /// * `start_time` - Start timestamp (optional)
488    /// * `end_time` - End timestamp (optional)
489    /// * `current` - Page number (default 1)
490    /// * `size` - Page size (default 10, max 100)
491    ///
492    /// # Example
493    ///
494    /// ```rust,ignore
495    /// use binance_api_client::UniversalTransferType;
496    ///
497    /// let history = client.wallet()
498    ///     .transfer_history(UniversalTransferType::MainFunding, None, None, None, Some(10))
499    ///     .await?;
500    /// ```
501    pub async fn transfer_history(
502        &self,
503        transfer_type: UniversalTransferType,
504        start_time: Option<u64>,
505        end_time: Option<u64>,
506        current: Option<u32>,
507        size: Option<u32>,
508    ) -> Result<TransferHistory> {
509        let type_str = transfer_type.as_str().to_string();
510
511        let mut params: Vec<(&str, String)> = vec![("type", type_str)];
512
513        if let Some(st) = start_time {
514            params.push(("startTime", st.to_string()));
515        }
516        if let Some(et) = end_time {
517            params.push(("endTime", et.to_string()));
518        }
519        if let Some(c) = current {
520            params.push(("current", c.to_string()));
521        }
522        if let Some(s) = size {
523            params.push(("size", s.to_string()));
524        }
525
526        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
527        self.client
528            .get_signed(SAPI_V1_ASSET_TRANSFER, &params_ref)
529            .await
530    }
531
532    // Wallet Balances.
533
534    /// Get funding wallet balance.
535    ///
536    /// # Arguments
537    ///
538    /// * `asset` - Asset to query (optional, returns all if not specified)
539    /// * `need_btc_valuation` - Whether to include BTC valuation (optional)
540    ///
541    /// # Example
542    ///
543    /// ```rust,ignore
544    /// let assets = client.wallet().funding_wallet(None, Some(true)).await?;
545    /// for asset in assets {
546    ///     println!("{}: {}", asset.asset, asset.free);
547    /// }
548    /// ```
549    pub async fn funding_wallet(
550        &self,
551        asset: Option<&str>,
552        need_btc_valuation: Option<bool>,
553    ) -> Result<Vec<FundingAsset>> {
554        let mut params: Vec<(&str, String)> = vec![];
555
556        if let Some(a) = asset {
557            params.push(("asset", a.to_string()));
558        }
559        if let Some(btc) = need_btc_valuation {
560            params.push(("needBtcValuation", btc.to_string()));
561        }
562
563        let params_ref: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
564        self.client
565            .post_signed(SAPI_V1_ASSET_GET_FUNDING_ASSET, &params_ref)
566            .await
567    }
568
569    /// Get wallet balance for all asset wallets.
570    ///
571    /// # Example
572    ///
573    /// ```rust,ignore
574    /// let balances = client.wallet().wallet_balance().await?;
575    /// for balance in balances {
576    ///     if balance.balance > 0.0 {
577    ///         println!("{}: {}", balance.wallet_name, balance.balance);
578    ///     }
579    /// }
580    /// ```
581    pub async fn wallet_balance(&self) -> Result<Vec<WalletBalance>> {
582        self.client
583            .get_signed(SAPI_V1_ASSET_WALLET_BALANCE, &[])
584            .await
585    }
586
587    // Account Status.
588
589    /// Get account status.
590    ///
591    /// # Example
592    ///
593    /// ```rust,ignore
594    /// let status = client.wallet().account_status().await?;
595    /// println!("Account status: {}", status.data);
596    /// ```
597    pub async fn account_status(&self) -> Result<AccountStatus> {
598        self.client.get_signed(SAPI_V1_ACCOUNT_STATUS, &[]).await
599    }
600
601    /// Get API trading status.
602    ///
603    /// # Example
604    ///
605    /// ```rust,ignore
606    /// let status = client.wallet().api_trading_status().await?;
607    /// if status.data.is_locked {
608    ///     println!("Trading is locked!");
609    /// }
610    /// ```
611    pub async fn api_trading_status(&self) -> Result<ApiTradingStatus> {
612        self.client
613            .get_signed(SAPI_V1_ACCOUNT_API_TRADING_STATUS, &[])
614            .await
615    }
616
617    /// Get API key permissions.
618    ///
619    /// # Example
620    ///
621    /// ```rust,ignore
622    /// let permissions = client.wallet().api_key_permissions().await?;
623    /// println!("Can trade: {}", permissions.enable_spot_and_margin_trading);
624    /// println!("Can withdraw: {}", permissions.enable_withdrawals);
625    /// ```
626    pub async fn api_key_permissions(&self) -> Result<ApiKeyPermissions> {
627        self.client
628            .get_signed(SAPI_V1_ACCOUNT_API_RESTRICTIONS, &[])
629            .await
630    }
631}