-
Notifications
You must be signed in to change notification settings - Fork 57
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
Prepared Send #596
base: main
Are you sure you want to change the base?
Prepared Send #596
Conversation
@thesimplekid two areas I'm looking for feedback:
|
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.
Great work on this PR.
How should we support CDK users who want to bring their own PreparedSend logic? I made PreparedSend a "private" structure so that users can't modify it, causing issues with our logic.
I think we can just added a PreparedSend::new
that way they can at least create a PreparedSend
to pass to send
. But I think maybe better to not worry to much about this for now to keep it simpler and if that need arises we can revisit.
How should we support memos? I have it in SendOptions but I think I should move it to be a parameter of the send function, possibly with a flag to include it in the Token or keep it internal with the eventual TransactionsDatabase.
I agree I think we can move it to thesend
fn maybe we can have two and internal and an external (included in token) memo and when the former is not set use the external one.
pub fn has_tolerance(&self) -> bool { | ||
matches!(self, Self::OnlineTolerance(_) | Self::OfflineTolerance(_)) | ||
} |
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.
Would it be useful is this returned the tolerance? Option<Amount>
instead of a bool?
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.
Then has_tolerance
could be merged with is_exact
.
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 prefer to use bools instead of checking the state of Options when doing conditional logic, such as how these functions are used. I can add a tolerance function that returns an Option, but I don't see why we shouldn't provide all of these functions in the API.
crates/cdk/src/wallet/proofs.rs
Outdated
selected_proofs.extend(Wallet::select_proofs( | ||
remaining_amount, | ||
remaining_proofs, | ||
active_keyset_id, | ||
&HashMap::new(), // Fees are already calculated | ||
false, | ||
)?); |
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.
Does this handle the edge case where the proof added to pay the fee increased the fee?
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 believe so. Do you have a specific edge case in mind that I can include as a test?
.get_proofs( | ||
Some(self.mint_url.clone()), | ||
Some(self.unit.clone()), | ||
Some(vec![State::Unspent]), | ||
None, | ||
) | ||
.await? | ||
.into_iter() | ||
.filter_map(|p| { | ||
if p.spending_condition.is_none() { | ||
Some(p.proof) | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect(); |
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 filter is required because of what you mentioned previously where the get_proofs
will return proofs with conditions even if None
is passed. I think we should update the get_proofs
fn to handle this correctly to avoid the filter here.
I can do this as a small focused pr ahead of merging this. #611
crates/cdk/src/wallet/proofs.rs
Outdated
amount: Amount, | ||
proofs: Proofs, | ||
active_keyset_id: Id, |
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 this should be a Vec<Id>
the nuts allow for mints to have multiple active keysets. Cdk mint limits this on the implementation level to only have one active keyset per unit, however on the wallet side I do not think we should assume this and should account for multiple as it is in spec.
/// Send kind | ||
pub send_kind: SendKind, | ||
/// Include fee | ||
pub include_fee: bool, |
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 the better ux is the default behavior is to include the fee always. Do we think that should be the default here in SendOptions
or that should be done higher up the stack?
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.
IMO it's better to set the SendOptions
default here to true
.
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? Typically, receivers (merchants) pay for fees.
self.localstore | ||
.update_proofs_state(ys, State::Pending) | ||
.await?; |
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 we should use Reserved
here, and keep Pending
as a state used by the mint ie in a melt operation where the ln payment is taking some time.
Have you done any testing around does this change the proofs that would be selected from a set from previous cdk versions? Do you expect it to? I would be interested to include some integrations tests on this so we don't change proofs selection accidentally. |
pub fn has_tolerance(&self) -> bool { | ||
matches!(self, Self::OnlineTolerance(_) | Self::OfflineTolerance(_)) | ||
} |
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.
Then has_tolerance
could be merged with is_exact
.
async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>; | ||
/// Set proofs as unspent in storage. Proofs are identified by their Y | ||
/// value. | ||
async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err>; |
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 instead of removing the 3 methods, we can keep a default implementation in the trait?
/// Set proofs as pending in storage. Proofs are identified by their Y
/// value.
async fn set_pending_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
self.update_proofs_state(ys, State::Pending).await
}
/// Reserve proofs in storage. Proofs are identified by their Y value.
async fn reserve_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
self.update_proofs_state(ys, State::Reserved).await
}
/// Set proofs as unspent in storage. Proofs are identified by their Y
/// value.
async fn set_unspent_proofs(&self, ys: Vec<PublicKey>) -> Result<(), Self::Err> {
self.update_proofs_state(ys, State::Unspent).await
}
This would avoid the need for wallet DBs to re-implement them (as it's done in cdk-rexie
now) and generally preserve the simpler caller semantics from before.
|
||
/// Total fee | ||
pub fn fee(&self) -> Amount { | ||
self.swap_fee + self.send_fee |
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.
Shouldn't this check the options?
self.swap_fee + self.send_fee | |
match self.options.include_fee { | |
true => self.swap_fee + self.send_fee, | |
false => self.swap_fee, | |
} |
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 send_fee should always be zero if include_fee == false. I think it's safer to keep as is.
/// Send kind | ||
pub send_kind: SendKind, | ||
/// Include fee | ||
pub include_fee: bool, |
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.
IMO it's better to set the SendOptions
default here to true
.
Yes, it should change proof selections to be more efficient. I wrote some unit tests because I made the |
Co-authored-by: thesimplekid <[email protected]>
Co-authored-by: thesimplekid <[email protected]>
Co-authored-by: ok300 <[email protected]>
Description
Prepare sends by selecting proofs and returning a fee amount before finalizing a send into a token.
Notes to the reviewers
Suggested CHANGELOG Updates
CHANGED
Wallet::select_proofs
.Wallet::send
now requires aPreparedSend
.WalletDatabase
proof state update functions have been consolidated intoupdate_proofs_state
.ADDED
Wallet::prepare_send
.SendOptions
struct controls optional functionality for sends.Amount
splitting to target a fee rate amount.Proofs
.SendKind
.Amount
(i.e.,checked_mul
andchecked_div
).REMOVED
FIXED
Checklist
just final-check
before committing