-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Batch Payments Feature #155
Conversation
Note that this branch was created from add-balance-command. Since that branch has not been merged into the master yet, all the commits from |
let mut reader = csv::ReaderBuilder::new().delimiter(b',').from_path(file)?; | ||
let headers = reader.headers()?.clone(); | ||
|
||
if &headers[0] != "username" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we could make this feature proof and go with lightning address? or maybe lightning address has not been implemented yet from the cli?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently haven't implemented Lightning Address in the CLI, but it's on our to-do list. Once we've got that (probably within this week), we'll tweak the batch payments to work with it.
pub fn is_empty(&self) -> bool { | ||
self.payments.is_empty() | ||
} | ||
let wallet_type = if headers.get(1) == Some("cents") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are we assuming that it we sent sats, it's from the btc wallet, and if we send cents, it's from the usd wallet?
I don't think we should make this assumption. we can send "100 dollar worth of bitcoin" from the bitcoin wallet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think typically people right not send "cents" or "cents equivalent", so we could start with this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Batch payments is built upon intraledger payments, which does not support sending cents from a Bitcoin wallet or sats from a USD wallet. Due to this, our current implementation assumes that if the amount is in cents, it is sent from the USD wallet and vice versa. Once we incorporate the capability for conversions in intraledger payments, we will extend the batch payments feature to allow this.
|
||
Ok(()) | ||
pub fn check_sufficient_balance( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also check the sending limits.
@@ -1,92 +0,0 @@ | |||
use galoy_cli::batch::Batch; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why is the tests deleted?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the tests you're referring to were associated with the previous iteration of batch payments, which are no longer relevant.
Once this PR is approved and the implementation is finalized, I'll be introducing new e2e tests for batch in a subsequent PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Once this PR is approved and the implementation is finalized, I'll be introducing new e2e tests for batch in a subsequent PR
imo, unless exception for good reasons, tests should come with PRs, not after. otherwise it's too easy to move on to other things and never do the tests.
client, | ||
price, | ||
} | ||
// Utility function to read and validate the CSV file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment is redundant as it adds no additional information than the name of the function. Delete it - or alternatively if you want to publish this as a library turn in into a doc style comment (///
) and add something useful.
file: &str, | ||
) -> anyhow::Result<(Vec<csv::StringRecord>, Wallet)> { | ||
let mut reader = csv::ReaderBuilder::new().delimiter(b',').from_path(file)?; | ||
let headers = reader.headers()?.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why clone?
let mut reader = csv::ReaderBuilder::new().delimiter(b',').from_path(file)?; | ||
let headers = reader.headers()?.clone(); | ||
|
||
if &headers[0] != "username" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should not be using magic values... all hard coded values must be declared as constants.
pub fn is_empty(&self) -> bool { | ||
self.payments.is_empty() | ||
} | ||
let wallet_type = if headers.get(1) == Some("cents") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be handled via FromStr
/ parse
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"))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find the syntax:
record.get(1).context("Amount is missing")?;
more concise.
Need to use anyhow::Context
for it to work.
"payment to {username} of sats {amount}, usd {usd}: {:?}", | ||
res | ||
); | ||
match wallet_type { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How are you dealing with partial failures? What if the first 5 out of 10 payments complete but then there is an error - how is this reported to avoid a double payment on re-try?
|
||
pub mod server; | ||
|
||
impl From<&WalletCurrency> for Wallet { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like putting From
implementations into their own file eg src/client/convert.rs
.
batch.execute().context("can't make payment successfully")?; | ||
|
||
Ok(()) | ||
pub fn batch_payment(self, file: String) -> anyhow::Result<String> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having batch as a submodule of client is IMO significantly bloating its responsibility. Typically a client is a thin wrapper around some network calls. More complex coordination that requires multiple round trips needs to be put in a use-case / app layer that uses the client as a dependency.
@@ -15,6 +15,8 @@ use std::fs::{self}; | |||
mod constants; | |||
mod token; | |||
|
|||
use galoy_cli::types::*; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
main.rs
is completely bloated. It should just call into something that gets exposed via lib - which is where the use cases should be defined.
IMO this project is getting large enough that a purely design - improvement / refactoring iteration should be executed to improve things before implementing further complex use cases.
At this stage it should still be fairly simple to clean things up - later that might not be the case anymore.
@@ -0,0 +1,16 @@ | |||
use rust_decimal::Decimal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a types.rs
file is a code smell that indicates a lack of domain oriented design-thinking.
This PR introduces the batch payment feature to our CLI. This feature allows the user to execute multiple payments simultaneously using a CSV file containing the details of the recipients.
The command syntax is as follows:
batch --csv <file-path>
The CSV file needs to adhere to this format: the columns should be - username, {cents or sats}, memo(optional).
Example:
The implementation consists of four key steps, which are carried out by their corresponding functions:
Related issue: #148
As this is a significant feature addition, thorough testing has been performed to ensure its correctness and reliability.