only allow sending to payment codes where a notification transaction has previously been sent, even when already linked
What changed, and why it matters
This change tightens the rules for sending bitcoin to a BIP47 reusable payment code (PayNym). Previously, Sparrow would let you send to a payment code if it was already 'linked' in the wallet, even if no notification transaction had been sent. Now it also requires that a notification transaction has been sent. A notification transaction is the on-chain message that tells the recipient which payment code is paying them, so without it the recipient may not be able to derive and detect the incoming payment. The patch reduces the chance of sending funds that the recipient cannot find or spend.
Review the broader BIP47 send flow to ensure notification transaction status is checked consistently across all entry points, and consider adding a user-facing warning that explains why sending is blocked until a notification transaction has been broadcast and stored.
Security signals we found
BIP47 payment code notification requirement enforced
Prevents sending to linked PayNym without prior notification transaction
Reduces risk of recipient being unable to derive/claim funds
UI flow now falls back to unlinked recipient behavior when notification transaction is missing
Evidence from the diff
In PaymentController.java, the code that resolves a recipient payment code into a local child wallet now also calls a new hasNotificationTransaction(paymentCode) guard. The new method checks the master wallet and its non-nested child wallets for any stored notification transaction for the external payment code. If no notification transaction exists, the UI falls back to treating the payment code as an unlinked recipient and prompts the user to send a notification transaction first. This closes a path where an already-linked BIP47 channel could be used to send payments before the notification step was actually completed on-chain.
Changed components
src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.javaBIP47 / PayNym send flowNotification transaction validationInspect captured patch +16 / −1
diff --git a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java
index 76b402d..329425b 100644
--- a/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java
+++ b/src/main/java/com/sparrowwallet/sparrow/wallet/PaymentController.java
@@ -240,7 +240,7 @@ public class PaymentController extends WalletFormController implements Initializ
recipientBip47Wallet = sendController.getWalletForm().getWallet().getChildWallet(paymentCode, ScriptType.P2PKH);
}
- if(recipientBip47Wallet != null) {
+ if(recipientBip47Wallet != null && hasNotificationTransaction(paymentCode)) {
PayNym payNym = PayNym.fromWallet(recipientBip47Wallet);
Platform.runLater(() -> setPayNym(payNym));
} else if(!paymentCode.equals(sendController.getWalletForm().getWallet().getPaymentCode())) {
@@ -585,6 +585,21 @@ public class PaymentController extends WalletFormController implements Initializ
return masterWallet.getChildWallet(new PaymentCode(payNym.paymentCode().toString()), payNym.segwit() ? ScriptType.P2WPKH : ScriptType.P2PKH);
}
+ private boolean hasNotificationTransaction(PaymentCode externalPaymentCode) {
+ Wallet masterWallet = sendController.getWalletForm().getMasterWallet();
+ if(!masterWallet.getNotificationTransaction(externalPaymentCode).isEmpty()) {
+ return true;
+ }
+
+ for(Wallet childWallet : masterWallet.getChildWallets()) {
+ if(!childWallet.isNested() && !childWallet.getNotificationTransaction(externalPaymentCode).isEmpty()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
boolean isSentToSamePayNym(PaymentController paymentController) {
return (this != paymentController && payNymProperty.get() != null && payNymProperty.get().paymentCode().equals(paymentController.payNymProperty.get().paymentCode()));
}
Why this scored 42/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.