fix btc address lookup (#3422)
What changed, and why it matters
This commit fixes how Cake Wallet looks up Bitcoin addresses when checking transaction history. Previously, the code mixed up 'change' (internal) addresses and 'receive' (external) addresses, and did not properly separate newer standard addresses from older legacy addresses. The fix organizes addresses into four clear groups—standard receive, standard change, legacy receive, and legacy change—and checks each group separately. This likely prevents the wallet from missing transactions or incorrectly marking address gaps, which could affect balance accuracy. There is no direct evidence in the commit that this was a security vulnerability or that it could be exploited by an attacker.
Treat as a functional bug fix rather than a security patch. Users relying on accurate Bitcoin balance and transaction history should update, especially if they use both legacy and standard (SegWit) derivation paths. No immediate incident response is indicated by the diff alone.
Security signals we found
Address discovery logic changed to correctly partition change vs receive and legacy vs standard derivation paths
Gap-limit checks now compare against the correct matched address branch
hiddenAddresses persistence updated to include both standard and legacy change addresses
No input validation, cryptographic, or network trust changes visible
Evidence from the diff
The patch refactors ElectrumWalletBase transaction fetching in cw_bitcoin/lib/electrum_wallet.dart. It replaces broad filtering of all addresses by hidden/receive flag with a new getAddressBranchByType() helper that partitions addresses by type, hidden (change vs receive), and legacy derivation. The previous logic treated all hidden addresses as change and all non-hidden as receive, ignoring the isLegacyDerivation flag, and did not sort addresses by index. The new logic fetches history separately for each of the four branches and computes gap limits against the correct matched branch. This corrects address discovery behavior for BIP44/BIP84 wallets.
Changed components
cw_bitcoin/lib/electrum_wallet.dartElectrumWalletBasefetchTransactionsForAddressTypefetchTransactionsForAddressTypeBatchgetAddressBranchByTypeInspect captured patch +28 / −14
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 641feb67..6396abed 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -2507,10 +2507,16 @@ abstract class ElectrumWalletBase
Map<String, ElectrumTransactionInfo> historiesWithDetails,
BitcoinAddressType type,
) async {
- final addressesByType = walletAddresses.allAddresses.where((addr) => addr.type == type);
- final hiddenAddresses = addressesByType.where((addr) => addr.isHidden == true);
- final receiveAddresses = addressesByType.where((addr) => addr.isHidden == false);
- walletAddresses.hiddenAddresses.addAll(hiddenAddresses.map((e) => e.address));
+
+ final addressesByType =
+ walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
+
+ final receiveStandard = getAddressBranchByType(hidden: false, legacy: false, type: type);
+ final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
+ final receiveLegacy = getAddressBranchByType(hidden: false, legacy: true, type: type);
+ final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
+
+ walletAddresses.hiddenAddresses.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
await walletAddresses.saveAddressesInBox();
await Future.wait(addressesByType.map((addressRecord) async {
final history = await _fetchAddressHistory(addressRecord, await getCurrentChainTip());
@@ -2519,7 +2525,9 @@ abstract class ElectrumWalletBase
addressRecord.txCount = history.length;
historiesWithDetails.addAll(history);
- final matchedAddresses = addressRecord.isHidden ? hiddenAddresses : receiveAddresses;
+ final matchedAddresses = addressRecord.isHidden
+ ? (addressRecord.isLegacyDerivation ? changeLegacy : changeStandard)
+ : (addressRecord.isLegacyDerivation ? receiveLegacy : receiveStandard);
final isUsedAddressAboveGap = matchedAddresses.toList().indexOf(addressRecord) >=
matchedAddresses.length -
(addressRecord.isHidden
@@ -2623,18 +2631,19 @@ abstract class ElectrumWalletBase
Future<void> fetchTransactionsForAddressTypeBatch(
Map<String, ElectrumTransactionInfo> historiesWithDetails, BitcoinAddressType type) async {
- final addressesByType =
- walletAddresses.allAddresses.where((addr) => addr.type == type).toList();
- final receiveAddresses = addressesByType.where((addr) => !addr.isHidden).toList();
- final hiddenAddresses = addressesByType.where((addr) => addr.isHidden).toList();
- walletAddresses.hiddenAddresses.addAll(hiddenAddresses.map((e) => e.address));
+ final receiveStandard = getAddressBranchByType(hidden: false, legacy: false, type: type);
+ final changeStandard = getAddressBranchByType(hidden: true, legacy: false, type: type);
+ final receiveLegacy = getAddressBranchByType(hidden: false, legacy: true, type: type);
+ final changeLegacy = getAddressBranchByType(hidden: true, legacy: true, type: type);
+
+ walletAddresses.hiddenAddresses.addAll([...changeStandard, ...changeLegacy].map((e) => e.address));
await walletAddresses.saveAddressesInBox();
await fetchTransactionsForAddressesBranchBatch(
historiesWithDetails,
type,
- receiveAddresses,
+ receiveStandard,
isHidden: false,
isLegacyDerivation: false,
);
@@ -2642,7 +2651,7 @@ abstract class ElectrumWalletBase
await fetchTransactionsForAddressesBranchBatch(
historiesWithDetails,
type,
- hiddenAddresses,
+ changeStandard,
isHidden: true,
isLegacyDerivation: false,
);
@@ -2650,7 +2659,7 @@ abstract class ElectrumWalletBase
await fetchTransactionsForAddressesBranchBatch(
historiesWithDetails,
type,
- receiveAddresses,
+ receiveLegacy,
isHidden: false,
isLegacyDerivation: true,
);
@@ -2658,7 +2667,7 @@ abstract class ElectrumWalletBase
await fetchTransactionsForAddressesBranchBatch(
historiesWithDetails,
type,
- hiddenAddresses,
+ changeLegacy,
isHidden: true,
isLegacyDerivation: true,
);
@@ -2734,6 +2743,11 @@ abstract class ElectrumWalletBase
}
}
+ List<BitcoinAddressRecord> getAddressBranchByType({required bool hidden, required bool legacy, required BitcoinAddressType
+ type}) => walletAddresses.allAddresses.where((addr) => addr.type == type && addr.isHidden == hidden && addr.isLegacyDerivation == legacy)
+ .toList()
+ ..sort((a, b) => a.index.compareTo(b.index));
+
int _highestUsedIndex(List<BitcoinAddressRecord> addresses) {
for (int i = addresses.length - 1; i >= 0; i--) {
if (addresses[i].isUsed) return i;
Why this scored 44/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.