Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 62 additions & 10 deletions orange-sdk/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,15 +525,24 @@ impl From<NodeError> for WalletError {
}
}

fn should_surface_lightning_payment_without_metadata(status: TxStatus, kind: &PaymentKind) -> bool {
status == TxStatus::Completed || matches!(kind, PaymentKind::Onchain { .. })
fn should_surface_lightning_payment_without_metadata(
status: TxStatus, kind: &PaymentKind, direction: PaymentDirection,
) -> bool {
// Hide only non-completed *inbound* Lightning records (issued-but-unpaid
// invoices are noise). Outbound attempts always surface: a failed or
// still-pending send the user made must appear in their history —
// otherwise a failed payment leaves no record at all.
status == TxStatus::Completed
|| matches!(kind, PaymentKind::Onchain { .. })
|| direction == PaymentDirection::Outbound
}

fn lightning_payment_without_metadata_to_transaction(
payment: &PaymentDetails, fee: Option<Amount>,
) -> Option<Transaction> {
let status = payment.status.into();
if !should_surface_lightning_payment_without_metadata(status, &payment.kind) {
if !should_surface_lightning_payment_without_metadata(status, &payment.kind, payment.direction)
{
return None;
}

Expand Down Expand Up @@ -919,9 +928,10 @@ impl Wallet {
);
}

if payment.status != TxStatus::Completed {
// We don't bother to surface pending inbound transactions (i.e. issued but
// unpaid invoices) in our transaction list.
if !payment.outbound && payment.status != TxStatus::Completed {
// We don't bother to surface pending/expired inbound records
// (i.e. issued but unpaid quotes) in our transaction list.
// Outbound attempts always surface, including failures.
continue;
}

Expand Down Expand Up @@ -1775,7 +1785,11 @@ mod tests {
status: ConfirmationStatus::Unconfirmed,
};

assert!(should_surface_lightning_payment_without_metadata(TxStatus::Pending, &kind));
assert!(should_surface_lightning_payment_without_metadata(
TxStatus::Pending,
&kind,
PaymentDirection::Inbound
));
}

#[test]
Expand Down Expand Up @@ -1805,16 +1819,54 @@ mod tests {
}

#[test]
fn pending_non_onchain_lightning_payments_without_metadata_are_hidden() {
fn pending_inbound_non_onchain_lightning_payments_without_metadata_are_hidden() {
let kind = PaymentKind::Spontaneous { hash: PaymentHash([42; 32]), preimage: None };

assert!(!should_surface_lightning_payment_without_metadata(TxStatus::Pending, &kind));
assert!(!should_surface_lightning_payment_without_metadata(
TxStatus::Pending,
&kind,
PaymentDirection::Inbound
));
}

#[test]
fn completed_lightning_payments_without_metadata_are_listed() {
let kind = PaymentKind::Spontaneous { hash: PaymentHash([42; 32]), preimage: None };

assert!(should_surface_lightning_payment_without_metadata(TxStatus::Completed, &kind));
assert!(should_surface_lightning_payment_without_metadata(
TxStatus::Completed,
&kind,
PaymentDirection::Inbound
));
}

#[test]
fn failed_and_pending_outbound_payments_are_listed() {
// A failed or in-flight send the user made must appear in their
// history — a failed payment that leaves no record erodes trust
// in the send flow (the wallet UI can't show what it never sees).
let kind = PaymentKind::Bolt11 {
hash: PaymentHash([42; 32]),
preimage: None,
secret: None,
counterparty_skimmed_fee_msat: None,
};

assert!(should_surface_lightning_payment_without_metadata(
TxStatus::Failed,
&kind,
PaymentDirection::Outbound
));
assert!(should_surface_lightning_payment_without_metadata(
TxStatus::Pending,
&kind,
PaymentDirection::Outbound
));
// Inbound failures (expired unpaid invoices) stay hidden.
assert!(!should_surface_lightning_payment_without_metadata(
TxStatus::Failed,
&kind,
PaymentDirection::Inbound
));
}
}
16 changes: 9 additions & 7 deletions orange-sdk/src/trusted_wallet/cashu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -730,15 +730,17 @@ impl Cashu {
},
},
None => {
debug_assert!(
false,
"Melt succeeded but no preimage for quote: {quote_id}"
);
log_error!(
// Expected for same-mint payments: when the melt's
// bolt11 destination is a mint quote on this same
// mint, cdk-mintd settles internally — no Lightning
// payment occurs, so there is no preimage to return.
// The success path below already tolerates None
// (hash falls back to the invoice payment_hash).
log_info!(
logger,
"Melt succeeded but no preimage for quote: {quote_id}"
"Melt for quote {quote_id} settled without a preimage (internal/same-mint settlement)"
);
None // Placeholder, should not happen
None
},
};

Expand Down
Loading