From 6db69e01de611d86d45ac3e7103333f7c93d7805 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Sun, 11 Jun 2023 01:52:13 +0530 Subject: [PATCH 1/9] refactor: add types to library crate --- src/lib.rs | 2 ++ src/main.rs | 8 ++------ src/types.rs | 5 +++++ 3 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/types.rs diff --git a/src/lib.rs b/src/lib.rs index 610f485..1b404bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,3 +4,5 @@ mod client; pub use client::*; + +pub mod types; diff --git a/src/main.rs b/src/main.rs index 1e1b49d..c6b1d0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,6 +15,8 @@ use std::fs::{self}; mod constants; mod token; +use galoy_cli::types::*; + #[derive(Parser)] #[clap(author, version, about, long_about = None)] struct Cli { @@ -82,12 +84,6 @@ enum Commands { Batch { filename: String, price: Decimal }, } -#[derive(Debug, Clone, clap::ValueEnum, PartialEq, Eq)] -enum Wallet { - Btc, - Usd, -} - fn main() -> anyhow::Result<()> { log::set_max_level(LevelFilter::Warn); diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..7ecec72 --- /dev/null +++ b/src/types.rs @@ -0,0 +1,5 @@ +#[derive(Debug, Clone, clap::ValueEnum, PartialEq, Eq)] +pub enum Wallet { + Btc, + Usd, +} From c4a575de2e758cc16dc70415aa11fdb922892332 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Sun, 11 Jun 2023 02:15:32 +0530 Subject: [PATCH 2/9] feat(balance): add balance command --- src/client/mod.rs | 53 +++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 19 +++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/client/mod.rs b/src/client/mod.rs index 51f5297..b0c3d1a 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -17,6 +17,8 @@ pub use batch::Batch; use self::query_me::WalletCurrency; +use crate::types::Wallet; + pub mod server; pub struct GaloyClient { @@ -97,6 +99,57 @@ impl GaloyClient { Ok(me) } + pub fn fetch_balance(&self, wallet: Option) -> anyhow::Result { + let me = self.me()?; + + let default_wallet_id = me.default_account.default_wallet_id; + + let wallets = &me.default_account.wallets; + let mut wallet_balances = String::new(); + + for wallet_info in wallets { + match &wallet { + Some(Wallet::Usd) if wallet_info.wallet_currency == WalletCurrency::USD => { + return Ok(format!("USD wallet: {} cents", wallet_info.balance)); + } + Some(Wallet::Btc) if wallet_info.wallet_currency == WalletCurrency::BTC => { + return Ok(format!("BTC wallet: {} sats", wallet_info.balance)); + } + None => { + if wallet_info.wallet_currency == WalletCurrency::USD { + wallet_balances += &format!( + "USD wallet{}: {} cents\n", + if wallet_info.id == default_wallet_id { + " (default)" + } else { + "" + }, + wallet_info.balance + ); + } + if wallet_info.wallet_currency == WalletCurrency::BTC { + wallet_balances += &format!( + "BTC wallet{}: {} sats\n", + if wallet_info.id == default_wallet_id { + " (default)" + } else { + "" + }, + wallet_info.balance + ); + } + } + _ => {} + } + } + + if wallet_balances.is_empty() { + Err(anyhow::anyhow!("No matching wallet found")) + } else { + Ok(wallet_balances) + } + } + pub fn request_phone_code(&self, phone: String, nocaptcha: bool) -> std::io::Result<()> { match nocaptcha { false => { diff --git a/src/main.rs b/src/main.rs index c6b1d0c..0a103ae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -67,6 +67,13 @@ enum Commands { }, /// Execute Me query Me, + /// Fetch the balance of a wallet + Balance { + #[clap(long)] + btc: bool, + #[clap(long)] + usd: bool, + }, /// Execute a Payment Pay { #[clap(short, long)] @@ -124,6 +131,18 @@ fn main() -> anyhow::Result<()> { serde_json::to_string_pretty(&result).expect("Can't serialize json") ); } + Commands::Balance { btc, usd } => { + let wallet_type = match (btc, usd) { + (true, true) | (false, false) => None, + (true, false) => Some(Wallet::Btc), + (false, true) => Some(Wallet::Usd), + }; + + let balance = galoy_cli + .fetch_balance(wallet_type) + .context("can't fetch balance")?; + println!("{}", balance); + } Commands::Pay { username, wallet, From 4a65bd3f0ae2fc4c7f87850be882f835bfe012a4 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Mon, 12 Jun 2023 00:27:44 +0530 Subject: [PATCH 3/9] feat: allow input of wallet-ids --- src/client/mod.rs | 73 +++++++++++++++++++++++-------------------- src/client/queries.rs | 2 +- src/lib.rs | 1 + src/main.rs | 10 ++++-- src/utils.rs | 9 ++++++ 5 files changed, 58 insertions(+), 37 deletions(-) create mode 100644 src/utils.rs diff --git a/src/client/mod.rs b/src/client/mod.rs index b0c3d1a..89e5225 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -17,7 +17,8 @@ pub use batch::Batch; use self::query_me::WalletCurrency; -use crate::types::Wallet; +use crate::types::*; +use crate::utils::*; pub mod server; @@ -99,54 +100,58 @@ impl GaloyClient { Ok(me) } - pub fn fetch_balance(&self, wallet: Option) -> anyhow::Result { + pub fn fetch_balance( + &self, + wallet: Option, + wallet_ids: Vec, + ) -> anyhow::Result { let me = self.me()?; let default_wallet_id = me.default_account.default_wallet_id; - let wallets = &me.default_account.wallets; + + let wallet_ids_set: std::collections::HashSet<_> = wallet_ids.iter().collect(); + let mut wallet_balances = String::new(); for wallet_info in wallets { - match &wallet { - Some(Wallet::Usd) if wallet_info.wallet_currency == WalletCurrency::USD => { - return Ok(format!("USD wallet: {} cents", wallet_info.balance)); - } - Some(Wallet::Btc) if wallet_info.wallet_currency == WalletCurrency::BTC => { - return Ok(format!("BTC wallet: {} sats", wallet_info.balance)); + if wallet_ids_set.contains(&wallet_info.id) + || match (&wallet, &wallet_info.wallet_currency) { + (Some(w), wc) if wallet_to_currency(w) == *wc => true, + (None, _) if wallet_ids_set.is_empty() => true, + _ => false, } - None => { + { + let currency = match wallet_info.wallet_currency { + WalletCurrency::USD => "cents", + WalletCurrency::BTC => "sats", + _ => "unknown", + }; + + wallet_balances += &format!( + "{}{}: {} {}\n", if wallet_info.wallet_currency == WalletCurrency::USD { - wallet_balances += &format!( - "USD wallet{}: {} cents\n", - if wallet_info.id == default_wallet_id { - " (default)" - } else { - "" - }, - wallet_info.balance - ); - } - if wallet_info.wallet_currency == WalletCurrency::BTC { - wallet_balances += &format!( - "BTC wallet{}: {} sats\n", - if wallet_info.id == default_wallet_id { - " (default)" - } else { - "" - }, - wallet_info.balance - ); - } - } - _ => {} + "USD wallet" + } else if wallet_info.wallet_currency == WalletCurrency::BTC { + "BTC wallet" + } else { + &wallet_info.id + }, + if wallet_info.id == default_wallet_id { + " (default)" + } else { + "" + }, + wallet_info.balance, + currency, + ); } } if wallet_balances.is_empty() { Err(anyhow::anyhow!("No matching wallet found")) } else { - Ok(wallet_balances) + Ok(wallet_balances.trim().to_string()) } } diff --git a/src/client/queries.rs b/src/client/queries.rs index 0400205..a3463be 100644 --- a/src/client/queries.rs +++ b/src/client/queries.rs @@ -41,7 +41,7 @@ pub use self::query_globals::QueryGlobalsGlobals; query_path = "src/client/graphql/queries/me.graphql", response_derives = "Debug, Serialize, PartialEq" )] -pub(super) struct QueryMe; +pub struct QueryMe; pub use self::query_me::QueryMeMe; // mutations diff --git a/src/lib.rs b/src/lib.rs index 1b404bf..17af812 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,3 +6,4 @@ mod client; pub use client::*; pub mod types; +pub mod utils; diff --git a/src/main.rs b/src/main.rs index 0a103ae..ade4213 100644 --- a/src/main.rs +++ b/src/main.rs @@ -73,6 +73,8 @@ enum Commands { btc: bool, #[clap(long)] usd: bool, + #[clap(long, use_value_delimiter = true)] + wallet_ids: Vec, }, /// Execute a Payment Pay { @@ -131,7 +133,11 @@ fn main() -> anyhow::Result<()> { serde_json::to_string_pretty(&result).expect("Can't serialize json") ); } - Commands::Balance { btc, usd } => { + Commands::Balance { + btc, + usd, + wallet_ids, + } => { let wallet_type = match (btc, usd) { (true, true) | (false, false) => None, (true, false) => Some(Wallet::Btc), @@ -139,7 +145,7 @@ fn main() -> anyhow::Result<()> { }; let balance = galoy_cli - .fetch_balance(wallet_type) + .fetch_balance(wallet_type, wallet_ids) .context("can't fetch balance")?; println!("{}", balance); } diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..93e3618 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,9 @@ +use crate::client::queries::query_me::WalletCurrency; +use crate::types::Wallet; + +pub fn wallet_to_currency(wallet: &Wallet) -> WalletCurrency { + match wallet { + Wallet::Usd => WalletCurrency::USD, + Wallet::Btc => WalletCurrency::BTC, + } +} From 3292bb9e8fcc3418cb86e6f308298920aea41861 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Tue, 13 Jun 2023 22:59:45 +0530 Subject: [PATCH 4/9] refactor: implement From for Wallet conversion --- src/client/mod.rs | 15 ++++++++++++--- src/lib.rs | 1 - src/utils.rs | 9 --------- 3 files changed, 12 insertions(+), 13 deletions(-) delete mode 100644 src/utils.rs diff --git a/src/client/mod.rs b/src/client/mod.rs index 89e5225..89e000f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -15,13 +15,22 @@ pub use error::*; pub mod batch; pub use batch::Batch; -use self::query_me::WalletCurrency; +pub use self::query_me::WalletCurrency; use crate::types::*; -use crate::utils::*; pub mod server; +impl From<&WalletCurrency> for Wallet { + fn from(currency: &WalletCurrency) -> Self { + match currency { + WalletCurrency::USD => Wallet::Usd, + WalletCurrency::BTC => Wallet::Btc, + _ => panic!("Unsupported currency"), + } + } +} + pub struct GaloyClient { graphql_client: Client, api: String, @@ -117,7 +126,7 @@ impl GaloyClient { for wallet_info in wallets { if wallet_ids_set.contains(&wallet_info.id) || match (&wallet, &wallet_info.wallet_currency) { - (Some(w), wc) if wallet_to_currency(w) == *wc => true, + (Some(w), wc) if *w == Wallet::from(wc) => true, (None, _) if wallet_ids_set.is_empty() => true, _ => false, } diff --git a/src/lib.rs b/src/lib.rs index 17af812..1b404bf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,4 +6,3 @@ mod client; pub use client::*; pub mod types; -pub mod utils; diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 93e3618..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::client::queries::query_me::WalletCurrency; -use crate::types::Wallet; - -pub fn wallet_to_currency(wallet: &Wallet) -> WalletCurrency { - match wallet { - Wallet::Usd => WalletCurrency::USD, - Wallet::Btc => WalletCurrency::BTC, - } -} From 626873f802763ef53fbef0cf9b53c8a89a3fe5c5 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Tue, 13 Jun 2023 23:01:00 +0530 Subject: [PATCH 5/9] refactor: rename wallet to wallet_type --- src/client/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 89e000f..ae57960 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -111,7 +111,7 @@ impl GaloyClient { pub fn fetch_balance( &self, - wallet: Option, + wallet_type: Option, wallet_ids: Vec, ) -> anyhow::Result { let me = self.me()?; @@ -125,7 +125,7 @@ impl GaloyClient { for wallet_info in wallets { if wallet_ids_set.contains(&wallet_info.id) - || match (&wallet, &wallet_info.wallet_currency) { + || match (&wallet_type, &wallet_info.wallet_currency) { (Some(w), wc) if *w == Wallet::from(wc) => true, (None, _) if wallet_ids_set.is_empty() => true, _ => false, From b9a2227aabddad86e3691ec389637689c8d1ffa2 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Tue, 13 Jun 2023 23:20:29 +0530 Subject: [PATCH 6/9] refactor(fetch_balance): return type to json --- src/client/mod.rs | 69 +++++++++++++++++++---------------------------- src/main.rs | 6 +++-- src/types.rs | 11 ++++++++ 3 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index ae57960..7d97d7d 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,7 +4,7 @@ use reqwest::blocking::Client; use log::info; use rust_decimal::Decimal; -use std::net::TcpListener; +use std::{collections::HashSet, net::TcpListener}; pub mod queries; pub use queries::*; @@ -113,54 +113,39 @@ impl GaloyClient { &self, wallet_type: Option, wallet_ids: Vec, - ) -> anyhow::Result { + ) -> anyhow::Result> { let me = self.me()?; - let default_wallet_id = me.default_account.default_wallet_id; let wallets = &me.default_account.wallets; - let wallet_ids_set: std::collections::HashSet<_> = wallet_ids.iter().collect(); - - let mut wallet_balances = String::new(); - - for wallet_info in wallets { - if wallet_ids_set.contains(&wallet_info.id) - || match (&wallet_type, &wallet_info.wallet_currency) { - (Some(w), wc) if *w == Wallet::from(wc) => true, - (None, _) if wallet_ids_set.is_empty() => true, - _ => false, - } - { - let currency = match wallet_info.wallet_currency { - WalletCurrency::USD => "cents", - WalletCurrency::BTC => "sats", - _ => "unknown", - }; - - wallet_balances += &format!( - "{}{}: {} {}\n", - if wallet_info.wallet_currency == WalletCurrency::USD { - "USD wallet" - } else if wallet_info.wallet_currency == WalletCurrency::BTC { - "BTC wallet" - } else { - &wallet_info.id - }, - if wallet_info.id == default_wallet_id { - " (default)" - } else { - "" - }, - wallet_info.balance, - currency, - ); - } - } + let wallet_ids_set: HashSet<_> = wallet_ids.into_iter().collect(); - if wallet_balances.is_empty() { + let balances: Vec<_> = wallets + .iter() + .filter(|wallet_info| { + wallet_ids_set.contains(&wallet_info.id) + || wallet_type.as_ref().map_or(wallet_ids_set.is_empty(), |w| { + *w == Wallet::from(&wallet_info.wallet_currency) + }) + }) + .map(|wallet_info| WalletBalance { + currency: format!("{:?}", Wallet::from(&wallet_info.wallet_currency)), + balance: wallet_info.balance, + id: if wallet_info.wallet_currency == WalletCurrency::USD + || wallet_info.wallet_currency == WalletCurrency::BTC + { + None + } else { + Some(wallet_info.id.clone()) + }, + default: wallet_info.id == default_wallet_id, + }) + .collect(); + + if balances.is_empty() { Err(anyhow::anyhow!("No matching wallet found")) } else { - Ok(wallet_balances.trim().to_string()) + Ok(balances) } } diff --git a/src/main.rs b/src/main.rs index ade4213..f0ab6ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -144,10 +144,12 @@ fn main() -> anyhow::Result<()> { (false, true) => Some(Wallet::Usd), }; - let balance = galoy_cli + let balances = galoy_cli .fetch_balance(wallet_type, wallet_ids) .context("can't fetch balance")?; - println!("{}", balance); + let balances_json = + serde_json::to_string_pretty(&balances).context("Can't serialize json")?; + println!("{}", balances_json); } Commands::Pay { username, diff --git a/src/types.rs b/src/types.rs index 7ecec72..065d01d 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,5 +1,16 @@ +use rust_decimal::Decimal; +use serde::Serialize; + #[derive(Debug, Clone, clap::ValueEnum, PartialEq, Eq)] pub enum Wallet { Btc, Usd, } + +#[derive(Debug, Serialize)] +pub struct WalletBalance { + pub currency: String, + pub balance: Decimal, + pub id: Option, + pub default: bool, +} From 141c8dc8d92bb7cc6ae82b0170e16b5a2773afb1 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Thu, 15 Jun 2023 01:58:37 +0530 Subject: [PATCH 7/9] feat: implement batch payments --- src/client/batch.rs | 245 +++++++++++++++----------------------------- src/client/mod.rs | 28 ++--- src/main.rs | 9 +- 3 files changed, 93 insertions(+), 189 deletions(-) diff --git a/src/client/batch.rs b/src/client/batch.rs index 2a02dc3..632d79e 100644 --- a/src/client/batch.rs +++ b/src/client/batch.rs @@ -1,191 +1,106 @@ -use serde::Deserialize; - -use std::fs::File; - -use super::*; - +use csv; use rust_decimal::Decimal; -use rust_decimal_macros::dec; +use std::path::Path; +use std::str::FromStr; -#[derive(Debug, Deserialize)] -pub struct PaymentInput { - pub username: String, - pub usd: Decimal, - pub memo: Option, -} +use crate::client::GaloyClient; +use crate::client::Wallet; -impl From for Payment { - fn from(input: PaymentInput) -> Payment { - Payment { - username: input.username, - usd: input.usd, - sats: None, - wallet_id: None, - memo: input.memo, - } +// Utility function to check if file exists +pub fn check_file_exists(file: &str) -> anyhow::Result<()> { + let file_path = Path::new(&file); + if !file_path.exists() { + return Err(anyhow::anyhow!("File not found: {}", file)); } + Ok(()) } -#[derive(Debug)] -struct Payment { - username: String, - usd: Decimal, - sats: Option, - wallet_id: Option, - memo: Option, -} - -pub struct Batch { - payments: Vec, - client: GaloyClient, - /// price in btc/usd - price: Decimal, -} - -impl Batch { - pub fn new(client: GaloyClient, price: Decimal) -> Self { - let payments: Vec = vec![]; - Self { - payments, - client, - price, - } +// Utility function to read and validate the CSV file +pub fn validate_csv( + galoy_cli: &GaloyClient, + file: &str, +) -> anyhow::Result<(Vec, Wallet)> { + let mut reader = csv::ReaderBuilder::new().delimiter(b',').from_path(file)?; + let headers = reader.headers()?.clone(); + + if &headers[0] != "username" + || (&headers[1] != "cents" && &headers[1] != "sats") + || (headers.len() == 3 && &headers[2] != "memo") + { + return Err(anyhow::anyhow!( + "CSV format not correct, requires: username, (cents or sats), memo(optional)" + )); } - pub fn add(&mut self, input: PaymentInput) { - self.payments.push(input.into()); - } - - pub fn add_csv(&mut self, filename: String) -> anyhow::Result<()> { - let file = File::open(filename)?; - let mut rdr = csv::Reader::from_reader(file); - for result in rdr.deserialize() { - let record: PaymentInput = result?; - self.add(record); - } - - Ok(()) - } - - pub fn len(&self) -> usize { - self.payments.len() - } - - pub fn is_empty(&self) -> bool { - self.payments.is_empty() - } + let wallet_type = if headers.get(1) == Some(&"cents") { + Wallet::Usd + } else { + Wallet::Btc + }; - pub fn populate_wallet_id(&mut self) -> anyhow::Result<()> { - for payment in self.payments.iter_mut() { - let username = payment.username.clone(); - let query = &self.client.default_wallet(username); - match query { - Ok(value) => payment.wallet_id = Some(value.clone()), - Err(error) => bail!("error query {:?}", error), - } - } + let records: Vec = reader.records().collect::>()?; - Ok(()) - } + // Validate each record + for record in &records { + let username = record + .get(0) + .ok_or(anyhow::anyhow!("Username is missing"))?; - pub fn populate_sats(&mut self) -> anyhow::Result<()> { - for payment in self.payments.iter_mut() { - let payment_btc: Decimal = payment.usd / self.price; - payment.sats = Some(payment_btc * dec!(100_000_000)); - } + let amount = record.get(1).ok_or(anyhow::anyhow!("Amount is missing"))?; + amount + .parse::() + .map_err(|_| anyhow::anyhow!("Amount must be a number"))?; - Ok(()) + // Check if the username exists + galoy_cli.default_wallet(username.to_string())?; } - pub fn check_self_payment(&self) -> anyhow::Result<()> { - let me = self.client.me()?; - - #[allow(deprecated)] - let me_username = match me.username { - Some(value) => value, - None => bail!("no username has been set"), - }; - - for payment in self.payments.iter() { - if me_username == payment.username { - println!("{:#?}", (me_username, &payment.username)); - bail!("can't pay to self") - } - } + Ok((records, wallet_type)) +} - Ok(()) +pub fn check_sufficient_balance( + records: &[csv::StringRecord], + wallet_type: Wallet, + galoy_cli: &GaloyClient, +) -> anyhow::Result<()> { + let balance_info = galoy_cli.fetch_balance(Some(wallet_type.clone()), Vec::new())?; + let current_balance: Decimal = balance_info.iter().map(|info| info.balance).sum(); + + let mut total_payment_amount: Decimal = Decimal::new(0, 0); + for record in records { + let amount: Decimal = Decimal::from_str(record.get(1).unwrap_or_default())?; + total_payment_amount += amount; } - pub fn check_limit(&self) -> anyhow::Result<()> { - todo!("Check limit. need API on the backend for it"); + if total_payment_amount > current_balance { + return Err(anyhow::anyhow!("Insufficient balance in the wallet")); } - pub fn check_balance(&self) -> anyhow::Result<()> { - let me = self.client.me()?; - let me_wallet_id = me.default_account.default_wallet_id; - - let mut total_sats = dec!(0); + Ok(()) +} - for payment in self.payments.iter() { - let sats = match payment.sats { - Some(value) => value, - None => bail!("sats needs to be populated first"), - }; - total_sats += sats; - } +pub fn execute_batch_payment( + records: &[csv::StringRecord], + wallet_type: Wallet, + galoy_cli: &GaloyClient, +) -> anyhow::Result<()> { + for record in records { + let username = record + .get(0) + .ok_or(anyhow::anyhow!("Username is missing"))?; - let me_default_wallet = me - .default_account - .wallets - .iter() - .find(|wallet| wallet.id == me_wallet_id); - - let balance_sats = match me_default_wallet { - Some(value) => value.balance, - None => bail!("no balance"), - }; - if total_sats > balance_sats { - bail!( - "not enough balance, got {}, need {}", - balance_sats, - total_sats - ) - } + let amount: Decimal = Decimal::from_str(record.get(1).unwrap_or_default())?; - Ok(()) - } + let memo = record.get(2).map(|s| s.to_string()); - pub fn show(&self) { - println!("{:#?}", &self.payments) - } - - pub fn execute(&mut self) -> anyhow::Result<()> { - self.check_self_payment()?; - self.check_balance()?; - - for Payment { - username, - memo, - usd, - sats, - .. - } in self.payments.drain(..) - { - let amount = match sats { - Some(value) => value, - None => bail!("need sats amount"), - }; - let res = &self - .client - .intraleger_send(username.clone(), amount, memo) - .context("issue sending intraledger")?; - - println!( - "payment to {username} of sats {amount}, usd {usd}: {:?}", - res - ); + match wallet_type { + Wallet::Usd => { + galoy_cli.intraleger_usd_send(username.to_string(), amount, memo)?; + } + Wallet::Btc => { + galoy_cli.intraleger_send(username.to_string(), amount, memo)?; + } } - - Ok(()) } + Ok(()) } diff --git a/src/client/mod.rs b/src/client/mod.rs index 7d97d7d..62a117f 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -13,7 +13,7 @@ pub mod error; pub use error::*; pub mod batch; -pub use batch::Batch; +use crate::client::batch::*; pub use self::query_me::WalletCurrency; @@ -338,26 +338,12 @@ impl GaloyClient { Ok(()) } - // TODO: check if we can do self without & - pub fn batch(self, filename: String, price: Decimal) -> anyhow::Result<()> { - let mut batch = Batch::new(self, price); - - batch.add_csv(filename).context("can't load file")?; - - batch - .populate_wallet_id() - .context("cant get wallet id for all username")?; - - batch - .populate_sats() - .context("cant set sats all payments")?; - - println!("going to execute:"); - batch.show(); - - batch.execute().context("can't make payment successfully")?; - - Ok(()) + pub fn batch_payment(self, file: String) -> anyhow::Result { + check_file_exists(&file)?; + let (mut reader, wallet_type) = validate_csv(&self, &file)?; + check_sufficient_balance(&mut reader, wallet_type.clone(), &self)?; + execute_batch_payment(&mut reader, wallet_type, &self)?; + Ok("Batch Payment Successful".to_string()) } pub fn create_captcha_challenge(&self) -> Result { diff --git a/src/main.rs b/src/main.rs index f0ab6ca..2fd5672 100644 --- a/src/main.rs +++ b/src/main.rs @@ -90,7 +90,10 @@ enum Commands { memo: Option, }, /// execute a batch payment - Batch { filename: String, price: Decimal }, + Batch { + #[clap(short, long = "csv")] + file: String, + }, } fn main() -> anyhow::Result<()> { @@ -203,9 +206,9 @@ fn main() -> anyhow::Result<()> { Ok(_) => println!("Username has been successfully set!"), Err(err) => println!("Error occurred while setting username: {}", err), }, - Commands::Batch { filename, price } => { + Commands::Batch { file } => { let result = galoy_cli - .batch(filename, price) + .batch_payment(file) .context("issue batching payment"); println!("{:#?}", result); } From d06e3af478c3fa4ec62decbfc5b50dc4066f0c29 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Thu, 15 Jun 2023 02:24:18 +0530 Subject: [PATCH 8/9] fix: code check test --- src/client/batch.rs | 4 ++-- src/client/mod.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/batch.rs b/src/client/batch.rs index 632d79e..80ab118 100644 --- a/src/client/batch.rs +++ b/src/client/batch.rs @@ -32,7 +32,7 @@ pub fn validate_csv( )); } - let wallet_type = if headers.get(1) == Some(&"cents") { + let wallet_type = if headers.get(1) == Some("cents") { Wallet::Usd } else { Wallet::Btc @@ -63,7 +63,7 @@ pub fn check_sufficient_balance( wallet_type: Wallet, galoy_cli: &GaloyClient, ) -> anyhow::Result<()> { - let balance_info = galoy_cli.fetch_balance(Some(wallet_type.clone()), Vec::new())?; + let balance_info = galoy_cli.fetch_balance(Some(wallet_type), Vec::new())?; let current_balance: Decimal = balance_info.iter().map(|info| info.balance).sum(); let mut total_payment_amount: Decimal = Decimal::new(0, 0); diff --git a/src/client/mod.rs b/src/client/mod.rs index 62a117f..87c9bd9 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -340,9 +340,9 @@ impl GaloyClient { pub fn batch_payment(self, file: String) -> anyhow::Result { check_file_exists(&file)?; - let (mut reader, wallet_type) = validate_csv(&self, &file)?; - check_sufficient_balance(&mut reader, wallet_type.clone(), &self)?; - execute_batch_payment(&mut reader, wallet_type, &self)?; + let (reader, wallet_type) = validate_csv(&self, &file)?; + check_sufficient_balance(&reader, wallet_type.clone(), &self)?; + execute_batch_payment(&reader, wallet_type, &self)?; Ok("Batch Payment Successful".to_string()) } From 978d13cef789cd76638dac535e310e512d145e00 Mon Sep 17 00:00:00 2001 From: Eshan Vaid Date: Thu, 15 Jun 2023 02:26:21 +0530 Subject: [PATCH 9/9] refactor: remove previous batch tests --- tests/batch.rs | 92 -------------------------------------------------- 1 file changed, 92 deletions(-) delete mode 100644 tests/batch.rs diff --git a/tests/batch.rs b/tests/batch.rs deleted file mode 100644 index 115cd8e..0000000 --- a/tests/batch.rs +++ /dev/null @@ -1,92 +0,0 @@ -use galoy_cli::batch::Batch; -use galoy_cli::GaloyClient; - -use galoy_cli::batch::PaymentInput; -use rust_decimal_macros::dec; - -mod common; - -#[test] -#[ignore] -fn batch_csv() { - let filename = "./tests/fixtures/example.csv".to_string(); - - let galoy_cli = common::unauth_client(); - - let mut batch = Batch::new(galoy_cli, dec!(10_000)); - - batch.add_csv(filename).unwrap(); - assert_eq!(batch.len(), 2); - - assert!(batch.populate_wallet_id().is_ok()); - assert!(batch.populate_sats().is_ok()); - - batch.show(); -} - -#[test] -#[ignore] -fn batch_cant_pay_self() { - let galoy_cli = common::auth_client(); - - let mut batch = Batch::new(galoy_cli, dec!(10_000)); - - batch.add(PaymentInput { - username: "userA".to_string(), - usd: dec!(10), - memo: None, - }); - - assert!(batch.populate_wallet_id().is_ok()); - assert!(batch.populate_sats().is_ok()); - assert!(batch.check_balance().is_ok()); - assert!(batch.check_self_payment().is_err()); -} - -#[test] -#[ignore] -fn batch_balance_too_low() { - let galoy_cli = common::auth_client(); - - let mut batch = Batch::new(galoy_cli, dec!(10_000)); - - batch.add(PaymentInput { - username: "userB".to_string(), - usd: dec!(1_000_000_000), - memo: None, - }); - - assert!(batch.populate_wallet_id().is_ok()); - assert!(batch.populate_sats().is_ok()); - assert!(batch.check_balance().is_err()); - assert!(batch.check_self_payment().is_ok()); -} - -#[test] -#[ignore] -fn execute_batch() { - let galoy_cli = common::auth_client(); - - let mut batch = Batch::new(galoy_cli, dec!(10_000)); - - batch.add(PaymentInput { - username: "userB".to_string(), - usd: dec!(2), - memo: None, - }); - batch.add(PaymentInput { - username: "userB".to_string(), - usd: dec!(5), - memo: Some("memo for second batch tx".to_string()), - }); - - assert!(batch.populate_wallet_id().is_ok()); - assert!(batch.populate_sats().is_ok()); - assert!(batch.check_balance().is_ok()); - assert!(batch.check_self_payment().is_ok()); - - let result = batch.execute().expect("didn't complete batch successfully"); - println!("{:?}", result); - - // TODO: check balance and transactions -}