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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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, ¶ms_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}