diff --git a/swapwallet/credit_projector.go b/swapwallet/credit_projector.go index 3bd0e7ebc..ef4b7e802 100644 --- a/swapwallet/credit_projector.go +++ b/swapwallet/credit_projector.go @@ -191,12 +191,23 @@ func creditEntryFromSummary(op credit.CreditOpSummary) ( return nil, false } + // A SEND is an outflow, so it carries a negative amount to match the + // sign convention of every other outgoing row (normal swap sends are + // normalized to negative by swapEntryFromSummary). Credit-only sends + // reach the feed only through this projector, so without the flip a + // sub-dust pay renders as positive and looks like an incoming transfer + // (issue #829). + amountSat := op.AmountSat + if kind == walletdkrpc.EntryKind_ENTRY_KIND_SEND { + amountSat = -op.AmountSat + } + now := nowUnix() entry := &walletdkrpc.WalletEntry{ Id: id, Kind: kind, Status: walletdkrpc.EntryStatus_ENTRY_STATUS_PENDING, - AmountSat: op.AmountSat, + AmountSat: amountSat, Counterparty: creditCounterparty, UpdatedAtUnix: now, Progress: &walletdkrpc.WalletEntryProgress{ diff --git a/swapwallet/credit_projector_test.go b/swapwallet/credit_projector_test.go index 4392dcec3..0b152bfce 100644 --- a/swapwallet/credit_projector_test.go +++ b/swapwallet/credit_projector_test.go @@ -107,7 +107,7 @@ func TestCreditProjectorProjectsOwnedTerminals(t *testing.T) { t, walletdkrpc.EntryStatus_ENTRY_STATUS_COMPLETE, pay.GetStatus(), ) - require.Equal(t, int64(500), pay.GetAmountSat()) + require.Equal(t, int64(-500), pay.GetAmountSat()) require.Equal( t, walletdkrpc.WalletEntryPhase_WALLET_ENTRY_PHASE_CONFIRMED, pay.GetProgress().GetPhase(), diff --git a/swapwallet/router.go b/swapwallet/router.go index cbf36286d..b842733e6 100644 --- a/swapwallet/router.go +++ b/swapwallet/router.go @@ -332,10 +332,14 @@ func creditPayEntry(intent *preparedSendIntent, paymentHashHex := hex.EncodeToString(paymentHash[:]) return &walletdkrpc.WalletEntry{ - Id: paymentHashHex, - Kind: walletdkrpc.EntryKind_ENTRY_KIND_SEND, - Status: walletdkrpc.EntryStatus_ENTRY_STATUS_PENDING, - AmountSat: int64(intent.amountSat), + Id: paymentHashHex, + Kind: walletdkrpc.EntryKind_ENTRY_KIND_SEND, + Status: walletdkrpc.EntryStatus_ENTRY_STATUS_PENDING, + + // A SEND is an outflow, so the pending credit-pay row carries a + // negative amount to match every other outgoing row; otherwise + // a sub-dust send renders as an incoming transfer (issue #829). + AmountSat: -int64(intent.amountSat), Counterparty: "credit", CreatedAtUnix: now, UpdatedAtUnix: now, diff --git a/swapwallet/router_test.go b/swapwallet/router_test.go index 83008ba16..c8cd1ded5 100644 --- a/swapwallet/router_test.go +++ b/swapwallet/router_test.go @@ -470,6 +470,10 @@ func TestRouterSendInvoiceHandsCreditPayToRegistry(t *testing.T) { t, walletdkrpc.EntryStatus_ENTRY_STATUS_PENDING, resp.GetEntry().GetStatus(), ) + + // A SEND is an outflow, so the pending row carries a negative amount + // (issue #829). + require.Equal(t, int64(-500), resp.GetEntry().GetAmountSat()) } // TestRouterSendOnchainSelectsVTXOsAndCallsLeave confirms that an onchain