ccxt_exchanges/binance/rest/funding.rs
1//! Binance funding operations.
2//!
3//! This module contains all deposit and withdrawal methods including
4//! deposit addresses, withdrawals, transfers, and transaction history.
5
6use super::super::{Binance, parser};
7use ccxt_core::{
8 Error, Result,
9 types::{DepositAddress, DepositWithdrawFee, Transaction, TransactionType},
10};
11use std::collections::BTreeMap;
12
13impl Binance {
14 // ==================== Deposit Address Methods ====================
15
16 /// Fetch deposit address for a currency.
17 ///
18 /// # Arguments
19 ///
20 /// * `code` - Currency code (e.g., "BTC", "USDT").
21 /// * `params` - Optional parameters:
22 /// - `network`: Network type (e.g., "ETH", "BSC", "TRX").
23 ///
24 /// # Returns
25 ///
26 /// Returns a [`DepositAddress`] with the deposit address and optional tag.
27 ///
28 /// # Errors
29 ///
30 /// Returns an error if authentication fails or the API request fails.
31 ///
32 /// # Notes
33 ///
34 /// - Binance automatically generates a new address if none exists.
35 /// - Some currencies require the network parameter (e.g., USDT can be on ETH/BSC/TRX).
36 ///
37 /// # Example
38 ///
39 /// ```no_run
40 /// # use ccxt_exchanges::binance::Binance;
41 /// # use ccxt_core::ExchangeConfig;
42 /// # use std::collections::BTreeMap;
43 /// # async fn example() -> ccxt_core::Result<()> {
44 /// let binance = Binance::new(ExchangeConfig::default())?;
45 ///
46 /// // Fetch BTC deposit address
47 /// let addr = binance.fetch_deposit_address("BTC", None).await?;
48 /// println!("BTC address: {}", addr.address);
49 ///
50 /// // Fetch USDT deposit address on TRX network
51 /// let mut params = BTreeMap::new();
52 /// params.insert("network".to_string(), "TRX".to_string());
53 /// let addr = binance.fetch_deposit_address("USDT", Some(params)).await?;
54 /// println!("USDT-TRX address: {}", addr.address);
55 /// # Ok(())
56 /// # }
57 /// ```
58 pub async fn fetch_deposit_address(
59 &self,
60 code: &str,
61 params: Option<BTreeMap<String, String>>,
62 ) -> Result<DepositAddress> {
63 let url = format!("{}/capital/deposit/address", self.urls().sapi);
64
65 let data = self
66 .signed_request(url)
67 .param("coin", code.to_uppercase())
68 .params(params.unwrap_or_default())
69 .execute()
70 .await?;
71
72 parser::parse_deposit_address(&data)
73 }
74
75 // ==================== Withdrawal Methods ====================
76
77 /// Withdraw funds to an external address.
78 ///
79 /// # Arguments
80 ///
81 /// * `code` - Currency code (e.g., "BTC", "USDT").
82 /// * `amount` - Withdrawal amount.
83 /// * `address` - Withdrawal address.
84 /// * `params` - Optional parameters:
85 /// - `tag`: Address tag (e.g., XRP tag).
86 /// - `network`: Network type (e.g., "ETH", "BSC", "TRX").
87 /// - `addressTag`: Address tag (alias for `tag`).
88 /// - `name`: Address memo name.
89 /// - `walletType`: Wallet type (0=spot, 1=funding).
90 ///
91 /// # Returns
92 ///
93 /// Returns a [`Transaction`] with withdrawal details.
94 ///
95 /// # Errors
96 ///
97 /// Returns an error if authentication fails or the API request fails.
98 ///
99 /// # Example
100 ///
101 /// ```no_run
102 /// # use ccxt_exchanges::binance::Binance;
103 /// # use ccxt_core::ExchangeConfig;
104 /// # use std::collections::BTreeMap;
105 /// # async fn example() -> ccxt_core::Result<()> {
106 /// let binance = Binance::new(ExchangeConfig::default())?;
107 ///
108 /// // Basic withdrawal
109 /// let tx = binance.withdraw("USDT", "100.0", "TXxxx...", None).await?;
110 /// println!("Withdrawal ID: {}", tx.id);
111 ///
112 /// // Withdrawal with specific network
113 /// let mut params = BTreeMap::new();
114 /// params.insert("network".to_string(), "TRX".to_string());
115 /// let tx = binance.withdraw("USDT", "100.0", "TXxxx...", Some(params)).await?;
116 /// # Ok(())
117 /// # }
118 /// ```
119 pub async fn withdraw(
120 &self,
121 code: &str,
122 amount: &str,
123 address: &str,
124 params: Option<BTreeMap<String, String>>,
125 ) -> Result<Transaction> {
126 let url = format!("{}/capital/withdraw/apply", self.urls().sapi);
127
128 let mut request_params = params.unwrap_or_default();
129
130 // Handle optional tag parameter (supports both 'tag' and 'addressTag' names)
131 if let Some(tag) = request_params.get("tag").cloned() {
132 request_params.insert("addressTag".to_string(), tag);
133 }
134
135 let data = self
136 .signed_request(url)
137 .method(super::super::signed_request::HttpMethod::Post)
138 .param("coin", code.to_uppercase())
139 .param("address", address)
140 .param("amount", amount)
141 .params(request_params)
142 .execute()
143 .await?;
144
145 parser::parse_transaction(&data, TransactionType::Withdrawal)
146 }
147
148 // ==================== Transaction History Methods ====================
149
150 /// Fetch deposit history.
151 ///
152 /// # Arguments
153 ///
154 /// * `code` - Optional currency code (e.g., "BTC", "USDT").
155 /// * `since` - Optional start timestamp in milliseconds.
156 /// * `limit` - Optional quantity limit.
157 /// * `params` - Optional parameters:
158 /// - `coin`: Currency (overrides code parameter).
159 /// - `status`: Status filter (0=pending, 6=credited, 1=success).
160 /// - `startTime`: Start timestamp in milliseconds.
161 /// - `endTime`: End timestamp in milliseconds.
162 /// - `offset`: Offset for pagination.
163 /// - `txId`: Transaction ID filter.
164 ///
165 /// # Returns
166 ///
167 /// Returns a vector of [`Transaction`] records.
168 ///
169 /// # Notes
170 ///
171 /// - Maximum query range: 90 days.
172 /// - Default returns last 90 days if no time range provided.
173 ///
174 /// # Example
175 ///
176 /// ```no_run
177 /// # use ccxt_exchanges::binance::Binance;
178 /// # use ccxt_core::ExchangeConfig;
179 /// # async fn example() -> ccxt_core::Result<()> {
180 /// let binance = Binance::new(ExchangeConfig::default())?;
181 ///
182 /// // Query all deposits
183 /// let deposits = binance.fetch_deposits(None, None, Some(100), None).await?;
184 /// println!("Total deposits: {}", deposits.len());
185 ///
186 /// // Query BTC deposits
187 /// let btc_deposits = binance.fetch_deposits(Some("BTC"), None, None, None).await?;
188 /// # Ok(())
189 /// # }
190 /// ```
191 pub async fn fetch_deposits(
192 &self,
193 code: Option<&str>,
194 since: Option<i64>,
195 limit: Option<i64>,
196 params: Option<BTreeMap<String, String>>,
197 ) -> Result<Vec<Transaction>> {
198 let url = format!("{}/capital/deposit/hisrec", self.urls().sapi);
199
200 let data = self
201 .signed_request(url)
202 .optional_param("coin", code.map(str::to_uppercase))
203 .optional_param("startTime", since)
204 .optional_param("limit", limit)
205 .params(params.unwrap_or_default())
206 .execute()
207 .await?;
208
209 if let Some(arr) = data.as_array() {
210 arr.iter()
211 .map(|item| parser::parse_transaction(item, TransactionType::Deposit))
212 .collect()
213 } else {
214 Err(Error::invalid_request("Expected array response"))
215 }
216 }
217
218 /// Fetch withdrawal history.
219 ///
220 /// # Arguments
221 ///
222 /// * `code` - Optional currency code (e.g., "BTC", "USDT").
223 /// * `since` - Optional start timestamp in milliseconds.
224 /// * `limit` - Optional quantity limit.
225 /// * `params` - Optional parameters:
226 /// - `coin`: Currency (overrides code parameter).
227 /// - `withdrawOrderId`: Withdrawal order ID.
228 /// - `status`: Status filter (0=email sent, 1=cancelled, 2=awaiting approval,
229 /// 3=rejected, 4=processing, 5=failure, 6=completed).
230 /// - `startTime`: Start timestamp in milliseconds.
231 /// - `endTime`: End timestamp in milliseconds.
232 /// - `offset`: Offset for pagination.
233 ///
234 /// # Returns
235 ///
236 /// Returns a vector of [`Transaction`] records.
237 ///
238 /// # Notes
239 ///
240 /// - Maximum query range: 90 days.
241 /// - Default returns last 90 days if no time range provided.
242 ///
243 /// # Example
244 ///
245 /// ```no_run
246 /// # use ccxt_exchanges::binance::Binance;
247 /// # use ccxt_core::ExchangeConfig;
248 /// # async fn example() -> ccxt_core::Result<()> {
249 /// let binance = Binance::new(ExchangeConfig::default())?;
250 ///
251 /// // Query all withdrawals
252 /// let withdrawals = binance.fetch_withdrawals(None, None, Some(100), None).await?;
253 /// println!("Total withdrawals: {}", withdrawals.len());
254 ///
255 /// // Query USDT withdrawals
256 /// let usdt_withdrawals = binance.fetch_withdrawals(Some("USDT"), None, None, None).await?;
257 /// # Ok(())
258 /// # }
259 /// ```
260 pub async fn fetch_withdrawals(
261 &self,
262 code: Option<&str>,
263 since: Option<i64>,
264 limit: Option<i64>,
265 params: Option<BTreeMap<String, String>>,
266 ) -> Result<Vec<Transaction>> {
267 let url = format!("{}/capital/withdraw/history", self.urls().sapi);
268
269 let data = self
270 .signed_request(url)
271 .optional_param("coin", code.map(str::to_uppercase))
272 .optional_param("startTime", since)
273 .optional_param("limit", limit)
274 .params(params.unwrap_or_default())
275 .execute()
276 .await?;
277
278 if let Some(arr) = data.as_array() {
279 arr.iter()
280 .map(|item| parser::parse_transaction(item, TransactionType::Withdrawal))
281 .collect()
282 } else {
283 Err(Error::invalid_request("Expected array response"))
284 }
285 }
286
287 // ==================== Fee Methods ====================
288
289 /// Fetch deposit and withdrawal fees for currencies.
290 ///
291 /// # Arguments
292 ///
293 /// * `currency` - Optional currency code to filter results.
294 /// * `params` - Optional additional parameters.
295 ///
296 /// # Returns
297 ///
298 /// Returns a vector of [`DepositWithdrawFee`] structures.
299 ///
300 /// # Example
301 ///
302 /// ```no_run
303 /// # use ccxt_exchanges::binance::Binance;
304 /// # use ccxt_core::ExchangeConfig;
305 /// # async fn example() -> ccxt_core::Result<()> {
306 /// let binance = Binance::new(ExchangeConfig::default())?;
307 ///
308 /// // Fetch all fees
309 /// let fees = binance.fetch_deposit_withdraw_fees(None, None).await?;
310 ///
311 /// // Fetch fees for specific currency
312 /// let btc_fees = binance.fetch_deposit_withdraw_fees(Some("BTC"), None).await?;
313 /// # Ok(())
314 /// # }
315 /// ```
316 pub async fn fetch_deposit_withdraw_fees(
317 &self,
318 currency: Option<&str>,
319 params: Option<BTreeMap<String, String>>,
320 ) -> Result<Vec<DepositWithdrawFee>> {
321 // Note: Binance /sapi/v1/capital/config/getall endpoint returns all currencies
322 // If currency is specified, client-side filtering is required
323
324 let url = format!("{}/capital/config/getall", self.urls().sapi);
325
326 let data = self
327 .signed_request(url)
328 .params(params.unwrap_or_default())
329 .execute()
330 .await?;
331
332 let all_fees = parser::parse_deposit_withdraw_fees(&data)?;
333
334 // Filter results if currency is specified
335 if let Some(code) = currency {
336 let code_upper = code.to_uppercase();
337 Ok(all_fees
338 .into_iter()
339 .filter(|fee| fee.currency == code_upper)
340 .collect())
341 } else {
342 Ok(all_fees)
343 }
344 }
345}