From 83e8f1220fc16343260f1bc2ce7502d57f5a858d Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 20 Mar 2023 12:59:13 +1100 Subject: [PATCH 1/6] add new withdraw API --- examples/binance_endpoints.rs | 22 +++++++++++++- src/api.rs | 30 ++++++++++++++++++ src/lib.rs | 1 + src/model.rs | 31 +++++++++++++++++++ src/withdraw.rs | 57 +++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/withdraw.rs diff --git a/examples/binance_endpoints.rs b/examples/binance_endpoints.rs index 8ea7bc6d..e417a93e 100644 --- a/examples/binance_endpoints.rs +++ b/examples/binance_endpoints.rs @@ -4,6 +4,7 @@ use binance::config::*; use binance::general::*; use binance::account::*; use binance::market::*; +use binance::withdraw::*; use binance::model::KlineSummary; use binance::errors::ErrorKind as BinanceLibErrorKind; @@ -16,10 +17,11 @@ fn main() { // The market data API endpoint market_data(); - // The account data API and savings API endpoint examples need an API key. Change those lines locally + // The account data, withdrawal, and savings API endpoint examples need an API key. Change those lines locally // and uncomment the line below (and do not commit your api key :)). //account(); //savings(); + //withdrawal() } fn general(use_testnet: bool) { @@ -156,6 +158,24 @@ fn savings() { } } +#[allow(dead_code)] +fn withdrawal() { + let api_key = Some("YOUR_API_KEY".into()); + let secret_key = Some("YOUR_SECRET_KEY".into()); + + let wapi: Withdraw = Binance::new(api_key, secret_key); + + match wapi.get_trade_fees() { + Ok(answer) => println!("{:#?}", answer), + Err(e) => println!("Error: {}", e), + } + + match wapi.get_asset_details() { + Ok(answer) => println!("{:#?}", answer), + Err(e) => println!("Error: {}", e), + } +} + #[allow(dead_code)] fn market_data() { let market: Market = Binance::new(None, None); diff --git a/src/api.rs b/src/api.rs index b69c9cc6..a32914a7 100644 --- a/src/api.rs +++ b/src/api.rs @@ -9,12 +9,14 @@ use crate::general::General; use crate::market::Market; use crate::userstream::UserStream; use crate::savings::Savings; +use crate::withdraw::Withdraw; #[allow(clippy::all)] pub enum API { Spot(Spot), Savings(Sapi), Futures(Futures), + Withdraw(Wapi), } /// Endpoint for production and test orders. @@ -95,6 +97,13 @@ pub enum Futures { Income, } +pub enum Wapi { + TradeFee, + AssetDetail, + DepositAddress, + Withdraw, +} + impl From for String { fn from(item: API) -> Self { String::from(match item { @@ -170,6 +179,12 @@ impl From for String { Futures::UserDataStream => "/fapi/v1/listenKey", Futures::Income => "/fapi/v1/income", }, + API::Withdraw(route) => match route { + Wapi::TradeFee => "/wapi/v3/tradeFee", + Wapi::AssetDetail => "/wapi/v3/assetDetail", + Wapi::DepositAddress => "/wapi/v3/depositAddress", + Wapi::Withdraw => "/wapi/v3/withdraw", + }, }) } } @@ -255,6 +270,21 @@ impl Binance for UserStream { } } +impl Binance for Withdraw { + fn new(api_key: Option, secret_key: Option) -> Withdraw { + Self::new_with_config(api_key, secret_key, &Config::default()) + } + + fn new_with_config( + api_key: Option, secret_key: Option, config: &Config, + ) -> Withdraw { + Withdraw { + client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), + recv_window: config.recv_window, + } + } +} + // ***************************************************** // Binance Futures API // ***************************************************** diff --git a/src/lib.rs b/src/lib.rs index 2b8e4396..98a7de03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,5 +29,6 @@ pub mod market; pub mod savings; pub mod userstream; pub mod websockets; +pub mod withdraw; pub mod futures; diff --git a/src/model.rs b/src/model.rs index baedc33f..f7aab196 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1319,6 +1319,14 @@ pub struct AssetDetail { pub deposit_tip: Option, } +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct AssetDetails { + pub success: bool, + #[serde(rename = "assetDetail")] + pub asset_details: std::collections::HashMap, +} + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct DepositAddress { pub address: String, @@ -1327,6 +1335,29 @@ pub struct DepositAddress { pub url: String, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct WithdrawResponse { + pub msg: String, + pub success: bool, + pub id: String, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct TradeFee { + pub symbol: String, + pub maker: f64, + pub taker: f64, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(rename_all = "camelCase")] +pub struct TradeFees { + #[serde(rename = "tradeFee")] + pub fees: Vec, + pub success: bool, +} + pub(crate) mod string_or_float { use std::fmt; diff --git a/src/withdraw.rs b/src/withdraw.rs new file mode 100644 index 00000000..a77f6cbb --- /dev/null +++ b/src/withdraw.rs @@ -0,0 +1,57 @@ +use crate::{ + util::build_signed_request, + model::{TradeFees, AssetDetails, DepositAddress, WithdrawResponse}, + client::Client, + errors::Result, + api::{API, Wapi}, +}; +use std::collections::BTreeMap; + +#[derive(Clone)] +pub struct Withdraw { + pub client: Client, + pub recv_window: u64, +} + +impl Withdraw { + // Maker and Taker trade fees for each asset pair + pub fn get_trade_fees(&self) -> Result { + let parameters: BTreeMap = BTreeMap::new(); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Withdraw(Wapi::TradeFee), Some(request)) + } + + // Fetch asset details: min_withdraw_amount, deposit_status, withdraw_fee, withdraw_status, Option + pub fn get_asset_details(&self) -> Result { + let parameters: BTreeMap = BTreeMap::new(); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Withdraw(Wapi::AssetDetail), Some(request)) + } + + // Depoist Address to given Asset + pub fn get_deposit_address(&self, asset: S) -> Result + where + S: Into, + { + let mut parameters = BTreeMap::new(); + parameters.insert("asset".into(), asset.into()); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Withdraw(Wapi::DepositAddress), Some(request)) + } + + // Withdraw currency + pub fn withdraw_currency(&self, asset: S, address: S, address_tag: Option, amount: f64) -> Result + where + S: Into, + { + let mut parameters = BTreeMap::new(); + parameters.insert("asset".into(), asset.into()); + parameters.insert("address".into(), address.into()); + if address_tag.is_some() { + parameters.insert("addressTag".into(), address_tag.unwrap().to_string()); + } + parameters.insert("amount".into(), amount.to_string()); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Withdraw(Wapi::Withdraw), Some(request)) + } +} \ No newline at end of file From 63728d7b56baf06a1f77e5fd65eec6b383420025 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 20 Mar 2023 14:11:54 +1100 Subject: [PATCH 2/6] add missing withdraw method to Sapi and remove deprecated Wapi --- examples/binance_endpoints.rs | 22 +------------- src/api.rs | 34 +++------------------ src/lib.rs | 1 - src/model.rs | 8 ----- src/savings.rs | 25 ++++++++++++++- src/withdraw.rs | 57 ----------------------------------- 6 files changed, 29 insertions(+), 118 deletions(-) delete mode 100644 src/withdraw.rs diff --git a/examples/binance_endpoints.rs b/examples/binance_endpoints.rs index e417a93e..8ea7bc6d 100644 --- a/examples/binance_endpoints.rs +++ b/examples/binance_endpoints.rs @@ -4,7 +4,6 @@ use binance::config::*; use binance::general::*; use binance::account::*; use binance::market::*; -use binance::withdraw::*; use binance::model::KlineSummary; use binance::errors::ErrorKind as BinanceLibErrorKind; @@ -17,11 +16,10 @@ fn main() { // The market data API endpoint market_data(); - // The account data, withdrawal, and savings API endpoint examples need an API key. Change those lines locally + // The account data API and savings API endpoint examples need an API key. Change those lines locally // and uncomment the line below (and do not commit your api key :)). //account(); //savings(); - //withdrawal() } fn general(use_testnet: bool) { @@ -158,24 +156,6 @@ fn savings() { } } -#[allow(dead_code)] -fn withdrawal() { - let api_key = Some("YOUR_API_KEY".into()); - let secret_key = Some("YOUR_SECRET_KEY".into()); - - let wapi: Withdraw = Binance::new(api_key, secret_key); - - match wapi.get_trade_fees() { - Ok(answer) => println!("{:#?}", answer), - Err(e) => println!("Error: {}", e), - } - - match wapi.get_asset_details() { - Ok(answer) => println!("{:#?}", answer), - Err(e) => println!("Error: {}", e), - } -} - #[allow(dead_code)] fn market_data() { let market: Market = Binance::new(None, None); diff --git a/src/api.rs b/src/api.rs index a32914a7..acb9c8b8 100644 --- a/src/api.rs +++ b/src/api.rs @@ -9,14 +9,12 @@ use crate::general::General; use crate::market::Market; use crate::userstream::UserStream; use crate::savings::Savings; -use crate::withdraw::Withdraw; #[allow(clippy::all)] pub enum API { Spot(Spot), Savings(Sapi), Futures(Futures), - Withdraw(Wapi), } /// Endpoint for production and test orders. @@ -53,6 +51,8 @@ pub enum Sapi { AssetDetail, DepositAddress, SpotFuturesTransfer, + TradeFee, + Withdraw, } pub enum Futures { @@ -97,13 +97,6 @@ pub enum Futures { Income, } -pub enum Wapi { - TradeFee, - AssetDetail, - DepositAddress, - Withdraw, -} - impl From for String { fn from(item: API) -> Self { String::from(match item { @@ -137,6 +130,8 @@ impl From for String { Sapi::AssetDetail => "/sapi/v1/asset/assetDetail", Sapi::DepositAddress => "/sapi/v1/capital/deposit/address", Sapi::SpotFuturesTransfer => "/sapi/v1/futures/transfer", + Sapi::TradeFee => "/sapi/v1/asset/tradeFee", + Sapi::Withdraw => "/sapi/v1/capital/withdraw/apply", }, API::Futures(route) => match route { Futures::Ping => "/fapi/v1/ping", @@ -179,12 +174,6 @@ impl From for String { Futures::UserDataStream => "/fapi/v1/listenKey", Futures::Income => "/fapi/v1/income", }, - API::Withdraw(route) => match route { - Wapi::TradeFee => "/wapi/v3/tradeFee", - Wapi::AssetDetail => "/wapi/v3/assetDetail", - Wapi::DepositAddress => "/wapi/v3/depositAddress", - Wapi::Withdraw => "/wapi/v3/withdraw", - }, }) } } @@ -270,21 +259,6 @@ impl Binance for UserStream { } } -impl Binance for Withdraw { - fn new(api_key: Option, secret_key: Option) -> Withdraw { - Self::new_with_config(api_key, secret_key, &Config::default()) - } - - fn new_with_config( - api_key: Option, secret_key: Option, config: &Config, - ) -> Withdraw { - Withdraw { - client: Client::new(api_key, secret_key, config.rest_api_endpoint.clone()), - recv_window: config.recv_window, - } - } -} - // ***************************************************** // Binance Futures API // ***************************************************** diff --git a/src/lib.rs b/src/lib.rs index 98a7de03..2b8e4396 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,5 @@ pub mod market; pub mod savings; pub mod userstream; pub mod websockets; -pub mod withdraw; pub mod futures; diff --git a/src/model.rs b/src/model.rs index f7aab196..a1c265c8 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1319,14 +1319,6 @@ pub struct AssetDetail { pub deposit_tip: Option, } -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct AssetDetails { - pub success: bool, - #[serde(rename = "assetDetail")] - pub asset_details: std::collections::HashMap, -} - #[derive(Debug, Serialize, Deserialize, Clone)] pub struct DepositAddress { pub address: String, diff --git a/src/savings.rs b/src/savings.rs index fbde1279..ae6dd250 100644 --- a/src/savings.rs +++ b/src/savings.rs @@ -1,5 +1,5 @@ use crate::util::build_signed_request; -use crate::model::{AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TransactionId}; +use crate::model::{AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TradeFees, TransactionId, WithdrawResponse}; use crate::client::Client; use crate::errors::Result; use std::collections::BTreeMap; @@ -31,6 +31,13 @@ impl Savings { .get_signed(API::Savings(Sapi::AssetDetail), Some(request)) } + // Maker and Taker trade fees for each asset pair + pub fn get_trade_fees(&self) -> Result { + let parameters: BTreeMap = BTreeMap::new(); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Savings(Sapi::TradeFee), Some(request)) + } + /// Fetch deposit address with network. /// /// You can get the available networks using `get_all_coins`. @@ -63,4 +70,20 @@ impl Savings { self.client .post_signed(API::Savings(Sapi::SpotFuturesTransfer), request) } + + // Withdraw currency + pub fn withdraw_currency(&self, asset: S, address: S, address_tag: Option, amount: f64) -> Result + where + S: Into, + { + let mut parameters = BTreeMap::new(); + parameters.insert("asset".into(), asset.into()); + parameters.insert("address".into(), address.into()); + if address_tag.is_some() { + parameters.insert("addressTag".into(), address_tag.unwrap().to_string()); + } + parameters.insert("amount".into(), amount.to_string()); + let request = build_signed_request(parameters, self.recv_window)?; + self.client.get_signed(API::Savings(Sapi::Withdraw), Some(request)) + } } diff --git a/src/withdraw.rs b/src/withdraw.rs deleted file mode 100644 index a77f6cbb..00000000 --- a/src/withdraw.rs +++ /dev/null @@ -1,57 +0,0 @@ -use crate::{ - util::build_signed_request, - model::{TradeFees, AssetDetails, DepositAddress, WithdrawResponse}, - client::Client, - errors::Result, - api::{API, Wapi}, -}; -use std::collections::BTreeMap; - -#[derive(Clone)] -pub struct Withdraw { - pub client: Client, - pub recv_window: u64, -} - -impl Withdraw { - // Maker and Taker trade fees for each asset pair - pub fn get_trade_fees(&self) -> Result { - let parameters: BTreeMap = BTreeMap::new(); - let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Withdraw(Wapi::TradeFee), Some(request)) - } - - // Fetch asset details: min_withdraw_amount, deposit_status, withdraw_fee, withdraw_status, Option - pub fn get_asset_details(&self) -> Result { - let parameters: BTreeMap = BTreeMap::new(); - let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Withdraw(Wapi::AssetDetail), Some(request)) - } - - // Depoist Address to given Asset - pub fn get_deposit_address(&self, asset: S) -> Result - where - S: Into, - { - let mut parameters = BTreeMap::new(); - parameters.insert("asset".into(), asset.into()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Withdraw(Wapi::DepositAddress), Some(request)) - } - - // Withdraw currency - pub fn withdraw_currency(&self, asset: S, address: S, address_tag: Option, amount: f64) -> Result - where - S: Into, - { - let mut parameters = BTreeMap::new(); - parameters.insert("asset".into(), asset.into()); - parameters.insert("address".into(), address.into()); - if address_tag.is_some() { - parameters.insert("addressTag".into(), address_tag.unwrap().to_string()); - } - parameters.insert("amount".into(), amount.to_string()); - let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Withdraw(Wapi::Withdraw), Some(request)) - } -} \ No newline at end of file From 9e69d7fa2d823155b55e2f79dca96195d328be68 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 20 Mar 2023 14:22:58 +1100 Subject: [PATCH 3/6] if let --- src/savings.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/savings.rs b/src/savings.rs index ae6dd250..91de92c7 100644 --- a/src/savings.rs +++ b/src/savings.rs @@ -79,8 +79,8 @@ impl Savings { let mut parameters = BTreeMap::new(); parameters.insert("asset".into(), asset.into()); parameters.insert("address".into(), address.into()); - if address_tag.is_some() { - parameters.insert("addressTag".into(), address_tag.unwrap().to_string()); + if let Some(address_tag) = address_tag { + parameters.insert("addressTag".into(), address_tag.to_string()); } parameters.insert("amount".into(), amount.to_string()); let request = build_signed_request(parameters, self.recv_window)?; From ffc5b3f1aebdfa9d0f79946f0468f89a1e72387d Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 20 Mar 2023 14:23:36 +1100 Subject: [PATCH 4/6] fmt --- src/savings.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/savings.rs b/src/savings.rs index 91de92c7..8896006d 100644 --- a/src/savings.rs +++ b/src/savings.rs @@ -1,5 +1,8 @@ use crate::util::build_signed_request; -use crate::model::{AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TradeFees, TransactionId, WithdrawResponse}; +use crate::model::{ + AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TradeFees, TransactionId, + WithdrawResponse, +}; use crate::client::Client; use crate::errors::Result; use std::collections::BTreeMap; @@ -35,7 +38,8 @@ impl Savings { pub fn get_trade_fees(&self) -> Result { let parameters: BTreeMap = BTreeMap::new(); let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Savings(Sapi::TradeFee), Some(request)) + self.client + .get_signed(API::Savings(Sapi::TradeFee), Some(request)) } /// Fetch deposit address with network. @@ -71,8 +75,10 @@ impl Savings { .post_signed(API::Savings(Sapi::SpotFuturesTransfer), request) } - // Withdraw currency - pub fn withdraw_currency(&self, asset: S, address: S, address_tag: Option, amount: f64) -> Result + // Withdraw currency + pub fn withdraw_currency( + &self, asset: S, address: S, address_tag: Option, amount: f64, + ) -> Result where S: Into, { @@ -84,6 +90,7 @@ impl Savings { } parameters.insert("amount".into(), amount.to_string()); let request = build_signed_request(parameters, self.recv_window)?; - self.client.get_signed(API::Savings(Sapi::Withdraw), Some(request)) - } + self.client + .get_signed(API::Savings(Sapi::Withdraw), Some(request)) + } } From 096a3225b6e5bfb49a5b9bba192072278cd16600 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 22 Apr 2024 09:58:22 +1000 Subject: [PATCH 5/6] rebase master From fedce43c04bf60953bd22484f5e0fee6ea50b7f1 Mon Sep 17 00:00:00 2001 From: Joshua Batty Date: Mon, 22 Apr 2024 10:51:50 +1000 Subject: [PATCH 6/6] get trade fee now works --- src/model.rs | 14 ++++---------- src/savings.rs | 11 +++++++---- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/model.rs b/src/model.rs index a1c265c8..14e5c804 100644 --- a/src/model.rs +++ b/src/model.rs @@ -1338,16 +1338,10 @@ pub struct WithdrawResponse { #[serde(rename_all = "camelCase")] pub struct TradeFee { pub symbol: String, - pub maker: f64, - pub taker: f64, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct TradeFees { - #[serde(rename = "tradeFee")] - pub fees: Vec, - pub success: bool, + #[serde(with = "string_or_float")] + pub maker_commission: f64, + #[serde(with = "string_or_float")] + pub taker_commission: f64, } pub(crate) mod string_or_float { diff --git a/src/savings.rs b/src/savings.rs index 8896006d..8f79381e 100644 --- a/src/savings.rs +++ b/src/savings.rs @@ -1,6 +1,6 @@ use crate::util::build_signed_request; use crate::model::{ - AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TradeFees, TransactionId, + AssetDetail, CoinInfo, DepositAddress, SpotFuturesTransferType, TradeFee, TransactionId, WithdrawResponse, }; use crate::client::Client; @@ -34,9 +34,12 @@ impl Savings { .get_signed(API::Savings(Sapi::AssetDetail), Some(request)) } - // Maker and Taker trade fees for each asset pair - pub fn get_trade_fees(&self) -> Result { - let parameters: BTreeMap = BTreeMap::new(); + /// Maker and Taker trade fees for an asset pair + pub fn get_trade_fee(&self, symbol: Option) -> Result> { + let mut parameters: BTreeMap = BTreeMap::new(); + if let Some(symbol) = symbol { + parameters.insert("symbol".into(), symbol); + } let request = build_signed_request(parameters, self.recv_window)?; self.client .get_signed(API::Savings(Sapi::TradeFee), Some(request))