payjoin address getter fix (#3191)
What changed, and why it matters
This commit fixes how Cake Wallet picks the Bitcoin address used when receiving a Payjoin transaction. Previously it used the wallet's default 'primary address', which could be a Silent Payment or Lightning address type that Payjoin does not support. The change adds a new getter that falls back to a standard Segwit address when the primary address type is incompatible. If left unfixed, Payjoin receivers might hand the sender an address the Payjoin protocol cannot handle, likely causing the Payjoin session to fail or fall back to a normal payment, potentially leaking privacy or losing Payjoin's fee-bumping benefits.
Review the Payjoin manager to confirm it validates or rejects unsupported address types defensively, and add tests covering Silent Payment and Lightning address wallets to ensure Payjoin receiver derivation always produces a compatible address. Consider documenting the supported address types for Payjoin in user-facing or developer docs.
Security signals we found
Incorrect address type selection for Payjoin receiver initialization
Potential protocol incompatibility between Payjoin and Silent Payment / Lightning address types
Privacy/fee-obfuscation degradation if Payjoin falls back to non-Payjoin transaction
No explicit security disclosure or CVE referenced in commit metadata
Evidence from the diff
The patch introduces payjoinCompatibleAddress in ElectrumWalletAddressesBase. It checks addressPageType: if it is SilentPaymentsAddresType.p2sp or LightningAddressType.p2l, it overrides the type to SegwitAddresType.p2wpkh, then derives the address from the corresponding HD key. BitcoinWalletAddressesBase.initPayjoin() and newPayjoinReceiver() now pass this compatible address to payjoinManager.getUnusedReceiver() instead of primaryAddress. This prevents Payjoin receivers from being initialized with address types the Payjoin manager may not support.
Changed components
cw_bitcoin/lib/electrum_wallet_addresses.dartcw_bitcoin/lib/bitcoin_wallet_addresses.dartPayjoin receiver initialization flowInspect captured patch +12 / −2
diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
index 40dd15d0..be03f26d 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
@@ -74,7 +74,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
Future<void> initPayjoin() async {
try {
await payjoinManager.initPayjoin();
- currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
+ currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(payjoinCompatibleAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
payjoinManager.resumeSessions();
@@ -88,7 +88,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
@action
Future<void> newPayjoinReceiver() async {
try {
- currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(primaryAddress);
+ currentPayjoinReceiver = await payjoinManager.getUnusedReceiver(payjoinCompatibleAddress);
payjoinEndpoint = (await currentPayjoinReceiver?.pjUri())?.pjEndpoint();
payjoinManager.spawnReceiver(receiver: currentPayjoinReceiver!);
diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart
index 2cfad69d..d3763386 100644
--- a/cw_bitcoin/lib/electrum_wallet_addresses.dart
+++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart
@@ -290,6 +290,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return getAddress(index: 0, hd: mainHd, addressType: addressPageType);
}
+ String get payjoinCompatibleAddress {
+ final addrType = (addressPageType == SilentPaymentsAddresType.p2sp ||
+ addressPageType == LightningAddressType.p2l)
+ ? SegwitAddresType.p2wpkh
+ : addressPageType;
+
+ final mainHd = mainHdByType[addrType] ?? mainHdByType.values.first;
+ return getAddress(index: 0, hd: mainHd, addressType: addrType);
+ }
+
Map<String, int> currentReceiveAddressIndexByType;
int get currentReceiveAddressIndex =>
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.