CW-1157-Use-correct-derivation-paths-for-other-address-types (#2825)
What changed, and why it matters
This commit changes how Cake Wallet derives Bitcoin-style addresses from a user's recovery seed. Previously, the app used a single derivation scheme for all address types. Now it uses the standard BIP44/49/84/86 paths per address type (legacy, SegWit, Taproot, etc.) while keeping an older 'legacy' path for compatibility with existing wallets. The change is a feature/fix for correctness and interoperability, not an obvious security vulnerability. However, any change to key derivation is sensitive because mistakes can make funds inaccessible or cause users to share/scan the wrong addresses.
Treat this as a high-risk correctness change rather than a malicious patch. Reviewers should verify that: (1) each address type maps to the correct BIP purpose and coin type; (2) legacy derivation fallback exactly matches the previous behavior for existing wallets; (3) signing and PSBT derivation paths use the same keys as address generation; (4) hardware wallet flows receive correct derivation paths; (5) the restore auto-skip cannot accidentally select a non-user path. Regression tests should cover seed-based address equality against known vectors and cross-check restored balances against previous app version.
Security signals we found
Change to BIP32/SLIP-10 key derivation paths for multiple UTXO coin wallets
New per-address-type HD key maps and legacy fallback key maps introduced
Address records now carry and persist an isLegacyDerivation flag
Signing, PSBT signing, key export, and message signing now select HD trees based on address record metadata
Address discovery and initial address generation logic changed to produce both legacy and standard derivation sets
Restore flow now auto-selects derivation when only standard scan paths have history
No explicit security advisory, CVE, or researcher attribution in commit or supplied references
Evidence from the diff
The patch refactors Electrum/Bitcoin/Litecoin/Bitcoin Cash/Dogecoin wallet classes to maintain separate HD key trees per address type (mainHdByType/sideHdByType) plus legacy main/side HDs. It adds an isLegacyDerivation flag to address records, persists it in JSON, and uses _hdFor() and _accountDerivationPathForRecord() to select the correct key tree and derivation path when signing, exporting keys, building PSBTs, and scanning. It also skips the derivation chooser during restore when only standard BIP scan paths have history. The change is large (+421/-135 across 18 files) and touches key derivation, signing, address discovery, and UI display logic.
Changed components
cw_bitcoin/lib/electrum_wallet.dartcw_bitcoin/lib/electrum_wallet_addresses.dartcw_bitcoin/lib/bitcoin_wallet.dartcw_bitcoin/lib/bitcoin_wallet_addresses.dartcw_bitcoin/lib/litecoin_wallet.dartcw_bitcoin/lib/litecoin_wallet_addresses.dartcw_bitcoin/lib/psbt/signer.dartcw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dartcw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dartcw_dogecoin/lib/src/dogecoin_wallet.dartcw_dogecoin/lib/src/dogecoin_wallet_addresses.dartlib/bitcoin/cw_bitcoin.dartlib/src/screens/restore/wallet_restore_page.dartlib/view_model/wallet_address_list/wallet_address_list_view_model.dartInspect captured patch +421 / −135
diff --git a/cw_bitcoin/lib/bitcoin_address_record.dart b/cw_bitcoin/lib/bitcoin_address_record.dart
index 37413dc8..400e325b 100644
--- a/cw_bitcoin/lib/bitcoin_address_record.dart
+++ b/cw_bitcoin/lib/bitcoin_address_record.dart
@@ -10,6 +10,7 @@ abstract class BaseBitcoinAddressRecord {
this.address, {
required this.index,
this.isHidden = false,
+ this.isLegacyDerivation = false,
int txCount = 0,
int balance = 0,
String name = '',
@@ -26,6 +27,7 @@ abstract class BaseBitcoinAddressRecord {
final String address;
bool isHidden;
+ bool isLegacyDerivation;
final int index;
int _txCount;
int _balance;
@@ -60,6 +62,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
super.address, {
required super.index,
super.isHidden = false,
+ super.isLegacyDerivation = false,
super.txCount = 0,
super.balance = 0,
super.name = '',
@@ -76,21 +79,38 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
}
}
+ static bool _legacyDefaultForType(BitcoinAddressType type) {
+
+ // Some address types (p2wpkh, p2wsh) were historically derived from the same account/path as our new standard
+ // but using legacy formats. For these, we default to legacy = false.
+ if (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh) return false;
+
+ // For other types (p2pkh, p2wpkh-in-p2sh, p2tr, etc.), old wallets used the non-standard
+ return true;
+ }
+
factory BitcoinAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
final decoded = json.decode(jsonSource) as Map;
+ final parsedType = decoded['type'] != null && decoded['type'] != ''
+ ? BitcoinAddressType.values
+ .firstWhere((type) => type.toString() == decoded['type'] as String)
+ : SegwitAddresType.p2wpkh;
+
+ final parsedIsLegacy = decoded.containsKey('isLegacyDerivation')
+ ? (decoded['isLegacyDerivation'] as bool? ?? false)
+ : _legacyDefaultForType(parsedType);
+
return BitcoinAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
isHidden: decoded['isHidden'] as bool? ?? false,
+ isLegacyDerivation: parsedIsLegacy,
isUsed: decoded['isUsed'] as bool? ?? false,
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0,
- type: decoded['type'] != null && decoded['type'] != ''
- ? BitcoinAddressType.values
- .firstWhere((type) => type.toString() == decoded['type'] as String)
- : SegwitAddresType.p2wpkh,
+ type: parsedType,
scriptHash: decoded['scriptHash'] as String?,
network: network,
);
@@ -113,6 +133,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
'address': address,
'index': index,
'isHidden': isHidden,
+ 'isLegacyDerivation': isLegacyDerivation,
'isUsed': isUsed,
'txCount': txCount,
'name': name,
diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart
index a75de4d4..0616f340 100644
--- a/cw_bitcoin/lib/bitcoin_wallet.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet.dart
@@ -123,8 +123,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
initialChangeAddressIndex: initialChangeAddressIndex,
initialSilentAddresses: initialSilentAddresses,
initialSilentAddressIndex: initialSilentAddressIndex,
- mainHd: hd,
- sideHd: accountHD.childKey(Bip32KeyIndex(1)),
+ mainHdByType: mainHdByType,
+ sideHdByType: sideHdByType,
+ legacyMainHd: mainHd,
+ legacySideHd: sideHd,
network: networkParam ?? network,
masterHd: seedBytes != null ? Bip32Slip10Secp256k1.fromSeed(seedBytes) : null,
isHardwareWallet: walletInfo.isHardwareWallet,
diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
index 5d051827..40dd15d0 100644
--- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart
@@ -23,8 +23,10 @@ class BitcoinWalletAddresses = BitcoinWalletAddressesBase with _$BitcoinWalletAd
abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
BitcoinWalletAddressesBase(
WalletInfo walletInfo, {
- required super.mainHd,
- required super.sideHd,
+ required super.mainHdByType,
+ required super.sideHdByType,
+ required super.legacySideHd,
+ required super.legacyMainHd,
required super.network,
required super.isHardwareWallet,
required this.payjoinManager,
diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart
index 603571d3..f3e2b7f1 100644
--- a/cw_bitcoin/lib/electrum_wallet.dart
+++ b/cw_bitcoin/lib/electrum_wallet.dart
@@ -115,6 +115,104 @@ abstract class ElectrumWalletBase
reaction((_) => syncStatus, _syncStatusReaction);
sharedPrefs.complete(SharedPreferences.getInstance());
+
+ final supportedTypes = supportedAddressTypes(walletInfo.type);
+ mainHdByType = <BitcoinAddressType, Bip32Slip10Secp256k1>{};
+ sideHdByType = <BitcoinAddressType, Bip32Slip10Secp256k1>{};
+
+ final isElectrumDerivation = derivationInfo.derivationType == DerivationType.electrum;
+
+ final canDeriveFromSeed = _masterHD != null && currency != null;
+
+ if (isElectrumDerivation) {
+ // Electrum derivation does not follow BIP44/49/84 etc. standards
+ for (final type in supportedTypes) {
+ mainHdByType[type] = mainHd; // accountHD.child(0)
+ sideHdByType[type] = sideHd; // accountHD.child(1)
+ }
+ } else if (canDeriveFromSeed) {
+ final coinType = _coinTypeFor(currency);
+
+ for (final type in supportedTypes) {
+ final purpose = _purposeForType(type);
+ final accountPath = "m/$purpose'/$coinType'/0'";
+
+ mainHdByType[type] = _masterHD!.derivePath("$accountPath/0") as Bip32Slip10Secp256k1;
+ sideHdByType[type] = _masterHD!.derivePath("$accountPath/1") as Bip32Slip10Secp256k1;
+ }
+ } else {
+ // View-only wallet (xpub only)
+ for (final type in supportedTypes) {
+ mainHdByType[type] = mainHd;
+ sideHdByType[type] = sideHd;
+ }
+ }
+
+ }
+
+ int _purposeForType(BitcoinAddressType type) {
+ switch (type.value) {
+ case 'P2PKH':
+ return 44;
+ case 'P2SH/P2WPKH':
+ return 49;
+ case 'P2WPKH':
+ return 84;
+ case 'P2TR':
+ return 86;
+ default:
+ return 84;
+ }
+ }
+
+ int _coinTypeFor(CryptoCurrency cur) {
+ if (!network.isMainnet) return 1;
+ switch (cur) {
+ case CryptoCurrency.btc:
+ case CryptoCurrency.tbtc:
+ return 0;
+ case CryptoCurrency.ltc:
+ return 2;
+ case CryptoCurrency.bch:
+ return 145;
+ case CryptoCurrency.doge:
+ return 3;
+ default:
+ return 0;
+ }
+ }
+
+ /// Returns the BIP32 account derivation path (m/purpose'/coinType'/0') for STANDARD addresses.
+ /// For LEGACY addresses, returns the wallet's legacy derivation base (derivationInfo.derivationPath)
+ /// which is already the account path used historically (e.g. m/0' or m/84'/0'/0').
+ String _accountDerivationPathForRecord(BaseBitcoinAddressRecord record) {
+
+ if (derivationInfo.derivationType == DerivationType.electrum) {
+ return derivationInfo.derivationPath ?? electrum_path; // m/0'
+ }
+
+ if (record.isLegacyDerivation) {
+ return derivationInfo.derivationPath ?? electrum_path;
+ }
+
+ final coinType = _coinTypeFor(currency);
+ final purpose = _purposeForType(record.type);
+ return "m/$purpose'/$coinType'/0'";
+ }
+
+ List<BitcoinAddressType> supportedAddressTypes(WalletType type) {
+ switch (type) {
+ case WalletType.bitcoin:
+ return BITCOIN_ADDRESS_TYPES;
+ case WalletType.bitcoinCash:
+ return BITCOIN_CASH_ADDRESS_TYPES;
+ case WalletType.dogecoin:
+ return DOGECOIN_ADDRESS_TYPES;
+ case WalletType.litecoin:
+ return LITECOIN_ADDRESS_TYPES;
+ default:
+ return BITCOIN_ADDRESS_TYPES;
+ }
}
static Bip32Slip10Secp256k1 getAccountHDWallet(
@@ -191,7 +289,10 @@ abstract class ElectrumWalletBase
final Bip32Slip10Secp256k1 accountHD;
final String? _mnemonic;
- Bip32Slip10Secp256k1 get hd => accountHD.childKey(Bip32KeyIndex(0));
+ late final Map<BitcoinAddressType, Bip32Slip10Secp256k1> mainHdByType;
+ late final Map<BitcoinAddressType, Bip32Slip10Secp256k1> sideHdByType;
+
+ Bip32Slip10Secp256k1 get mainHd => accountHD.childKey(Bip32KeyIndex(0));
Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1));
@@ -321,6 +422,9 @@ abstract class ElectrumWalletBase
String? wif;
String? privateKey;
String? publicKey;
+
+ final hd = mainHdByType[SegwitAddresType.p2wpkh] ?? mainHd;
+
try {
wif = WifEncoder.encode(hd.privateKey.raw, netVer: network.wifNetVer);
} catch (_) {}
@@ -330,6 +434,7 @@ abstract class ElectrumWalletBase
try {
publicKey = hd.publicKey.toHex();
} catch (_) {}
+
return BitcoinWalletKeys(
wif: wif ?? '',
privateKey: privateKey ?? '',
@@ -757,9 +862,7 @@ abstract class ElectrumWalletBase
final address = RegexUtils.addressTypeFromStr(utx.address, network);
ECPrivate? privkey;
bool? isSilentPayment = false;
-
- final hd =
- utx.bitcoinAddressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd;
+ final hd = _hdFor(record: utx.bitcoinAddressRecord);
if (utx.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
final unspentAddress = utx.bitcoinAddressRecord as BitcoinSilentPaymentAddressRecord;
@@ -790,8 +893,10 @@ abstract class ElectrumWalletBase
pubKeyHex = hd.childKey(Bip32KeyIndex(utx.bitcoinAddressRecord.index)).publicKey.toHex();
}
+ final baseDerivationPath = _accountDerivationPathForRecord(utx.bitcoinAddressRecord);
+
final derivationPath =
- "${_hardenedDerivationPath(derivationInfo.derivationPath ?? electrum_path)}"
+ "${_hardenedDerivationPath(baseDerivationPath)}"
"/${utx.bitcoinAddressRecord.isHidden ? "1" : "0"}"
"/${utx.bitcoinAddressRecord.index}";
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
@@ -1005,9 +1110,11 @@ abstract class ElectrumWalletBase
isChange: true,
));
- // Get Derivation path for change Address since it is needed in Litecoin and BitcoinCash hardware Wallets
+
+ // Must match the address' account root (purpose/coinType) and legacy derivation when applicable.
+ final changeBaseDerivationPath = _accountDerivationPathForRecord(changeAddress);
final changeDerivationPath =
- "${_hardenedDerivationPath(derivationInfo.derivationPath ?? "m/0'")}"
+ "${_hardenedDerivationPath(changeBaseDerivationPath)}"
"/${changeAddress.isHidden ? "1" : "0"}"
"/${changeAddress.index}";
utxoDetails.publicKeys[address.pubKeyHash()] =
@@ -1897,8 +2004,11 @@ abstract class ElectrumWalletBase
final addressRecord =
walletAddresses.allAddresses.firstWhere((element) => element.address == address);
final btcAddress = RegexUtils.addressTypeFromStr(addressRecord.address, network);
+
+ final hd = _hdFor(record: addressRecord);
+
final privkey = generateECPrivate(
- hd: addressRecord.isHidden ? walletAddresses.sideHd : walletAddresses.mainHd,
+ hd: hd,
index: addressRecord.index,
network: network);
@@ -1981,10 +2091,11 @@ abstract class ElectrumWalletBase
for (final utxo in unusedUtxos) {
final address = RegexUtils.addressTypeFromStr(utxo.address, network);
+
+ final hd = _hdFor(record: utxo.bitcoinAddressRecord);
+
final privkey = generateECPrivate(
- hd: utxo.bitcoinAddressRecord.isHidden
- ? walletAddresses.sideHd
- : walletAddresses.mainHd,
+ hd: hd,
index: utxo.bitcoinAddressRecord.index,
network: network,
);
@@ -2302,6 +2413,7 @@ abstract class ElectrumWalletBase
.then((history) => history.isNotEmpty ? address.address : null);
},
type: type,
+ isLegacyDerivation: addressRecord.isLegacyDerivation,
);
final newLength = walletAddresses.allAddresses.length;
@@ -2587,11 +2699,19 @@ abstract class ElectrumWalletBase
@override
Future<String> signMessage(String message, {String? address = null}) async {
- final index = address != null
- ? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
+ final addressRecord = address != null
+ ? walletAddresses.allAddresses.firstWhereOrNull((addr) => addr.address == address)
: null;
- final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
- final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex());
+
+ if (addressRecord != null && addressRecord.type == SegwitAddresType.p2tr) {
+ throw UnsupportedError("Cannot sign message with Taproot address");
+ }
+
+ final hd = addressRecord != null
+ ? _hdFor(record: addressRecord).childKey(Bip32KeyIndex(addressRecord.index))
+ : mainHd;
+
+ final priv = ECPrivate.fromHex(hd.privateKey.privKey.toHex());
String messagePrefix = '\x18Bitcoin Signed Message:\n';
final hexEncoded = priv.signMessage(utf8.encode(message), messagePrefix: messagePrefix);
@@ -2949,6 +3069,24 @@ abstract class ElectrumWalletBase
syncStatus = FailedSyncStatus();
}
}
+
+ Bip32Slip10Secp256k1 _hdFor({required BaseBitcoinAddressRecord record}) {
+ final addrType = record.type;
+
+ if (record.isLegacyDerivation) {
+ if (record.isHidden) {
+ return walletAddresses.legacySideHd;
+ } else {
+ return walletAddresses.legacyMainHd;
+ }
+ }
+
+ if (record.isHidden) {
+ return sideHdByType[addrType] ?? sideHd;
+ } else {
+ return mainHdByType[addrType] ?? mainHd;
+ }
+ }
}
class ScanNode {
diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart
index 0c334385..84ba5be6 100644
--- a/cw_bitcoin/lib/electrum_wallet_addresses.dart
+++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart
@@ -46,8 +46,10 @@ const List<BitcoinAddressType> DOGECOIN_ADDRESS_TYPES = [
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
ElectrumWalletAddressesBase(
WalletInfo walletInfo, {
- required this.mainHd,
- required this.sideHd,
+ required this.mainHdByType,
+ required this.sideHdByType,
+ required this.legacyMainHd,
+ required this.legacySideHd,
required this.network,
required this.isHardwareWallet,
List<BitcoinAddressRecord>? initialAddresses,
@@ -140,8 +142,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
// TODO: add this variable in `litecoin_wallet_addresses` and just add a cast in cw_bitcoin to use it
final ObservableList<BitcoinAddressRecord> mwebAddresses;
final BasedUtxoNetwork network;
- final Bip32Slip10Secp256k1 mainHd;
- final Bip32Slip10Secp256k1 sideHd;
+ Map<BitcoinAddressType, Bip32Slip10Secp256k1> mainHdByType;
+ Map<BitcoinAddressType, Bip32Slip10Secp256k1> sideHdByType;
+ final Bip32Slip10Secp256k1 legacyMainHd;
+ final Bip32Slip10Secp256k1 legacySideHd;
final bool isHardwareWallet;
final LightningWallet? lightningWallet;
@@ -178,14 +182,27 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
if (addressPageType == LightningAddressType.p2l) {
- return lightningAddress ??
- "Error: Unable to fetch your Lightning address, please check your network connection.";
+ return lightningAddress ??
+ "Error: Unable to fetch your Lightning address, please check your network connection.";
}
- final typeMatchingAddresses =
- _addresses.where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr)).toList();
- final typeMatchingReceiveAddresses =
- typeMatchingAddresses.where((addr) => !addr.isUsed).toList();
+ final typeMatchingAddressesAll = _addresses
+ .where((addr) => !addr.isHidden && _isAddressPageTypeMatch(addr))
+ .toList();
+
+ // Prefer standard derivation addresses for the current/active address,
+ // but keep legacy addresses present in the overall address lists.
+ final typeMatchingAddresses = <BitcoinAddressRecord>[
+ ...typeMatchingAddressesAll.where((a) => !a.isLegacyDerivation),
+ ...typeMatchingAddressesAll.where((a) => a.isLegacyDerivation),
+ ];
+
+ final typeMatchingReceiveAddressesAll =
+ typeMatchingAddressesAll.where((addr) => !addr.isUsed).toList();
+ final typeMatchingReceiveAddresses = <BitcoinAddressRecord>[
+ ...typeMatchingReceiveAddressesAll.where((a) => !a.isLegacyDerivation),
+ ...typeMatchingReceiveAddressesAll.where((a) => a.isLegacyDerivation),
+ ];
if (!isEnabledAutoGenerateSubaddress) {
if (previousAddressRecord != null && previousAddressRecord!.type == addressPageType) {
@@ -207,7 +224,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
if (locked != null) return locked;
final prev = previousAddressRecord;
- if (prev != null && prev.type == addressPageType && !prev.isUsed) {
+ if (prev != null && prev.type == addressPageType && !prev.isUsed && !prev.isLegacyDerivation) {
return prev.address;
}
@@ -235,7 +252,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
try {
final addressRecord = _addresses.firstWhere(
- (addressRecord) => addressRecord.address == addr,
+ (addressRecord) => addressRecord.address == addr && !addressRecord.isLegacyDerivation,
+ orElse: () => _addresses.firstWhere((r) => r.address == addr),
);
lockedReceiveAddressByType.remove(addressPageType);
@@ -263,7 +281,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
@override
- String get primaryAddress => getAddress(index: 0, hd: mainHd, addressType: addressPageType);
+ String get primaryAddress {
+ if (addressPageType == SilentPaymentsAddresType.p2sp) {
+ return silentAddress?.toString() ?? '';
+ }
+
+ final mainHd = mainHdByType[addressPageType] ?? mainHdByType.values.first;
+ return getAddress(index: 0, hd: mainHd, addressType: addressPageType);
+ }
Map<String, int> currentReceiveAddressIndexByType;
@@ -302,26 +327,34 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return acc;
});
- @override
- Future<void> init() async {
- if (walletInfo.type == WalletType.bitcoinCash) {
- await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
- } else if (walletInfo.type == WalletType.litecoin) {
- await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
- if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
- await _generateInitialAddresses(type: SegwitAddresType.mweb);
- }
- } else if (walletInfo.type == WalletType.dogecoin) {
- await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
- } else if (walletInfo.type == WalletType.bitcoin) {
- await _generateInitialAddresses();
- if (!isHardwareWallet) {
+ @override
+ Future<void> init() async {
+ if (walletInfo.type == WalletType.bitcoinCash) {
+ await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
+ } else if (walletInfo.type == WalletType.litecoin) {
+ await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
+ if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
+ await _generateInitialAddresses(type: SegwitAddresType.mweb);
+ }
+ } else if (walletInfo.type == WalletType.dogecoin) {
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
- await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
- await _generateInitialAddresses(type: SegwitAddresType.p2tr);
- await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
+ } else if (walletInfo.type == WalletType.bitcoin) {
+ await _generateInitialAddresses(isLegacyDerivation: true);
+ await _generateInitialAddresses();
+ if (!isHardwareWallet) {
+ await _generateInitialAddresses(type: P2pkhAddressType.p2pkh, isLegacyDerivation: true);
+ await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
+
+ await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh, isLegacyDerivation: true);
+ await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
+
+ await _generateInitialAddresses(type: SegwitAddresType.p2tr, isLegacyDerivation: true);
+ await _generateInitialAddresses(type: SegwitAddresType.p2tr);
+
+ await _generateInitialAddresses(type: SegwitAddresType.p2wsh, isLegacyDerivation: true);
+ await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
+ }
}
- }
updateAddressesByMatch();
updateReceiveAddresses();
@@ -409,10 +442,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final newAddressIndex = addressesByReceiveType.fold(
0, (int acc, addressRecord) => addressRecord.isHidden == false ? acc + 1 : acc);
+ final hd = _hdFor(isHidden: false, type: addressPageType, isLegacyDerivation: false);
final address = BitcoinAddressRecord(
- getAddress(index: newAddressIndex, hd: mainHd, addressType: addressPageType),
+ getAddress(index: newAddressIndex, hd: hd, addressType: addressPageType),
index: newAddressIndex,
isHidden: false,
+ isLegacyDerivation: false,
name: label,
type: addressPageType,
network: network,
@@ -610,14 +645,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
addressesByReceiveType.clear();
- addressesByReceiveType.addAll(_addresses.where(_isAddressPageTypeMatch).toList());
+ addressesByReceiveType.addAll(
+ _addresses.where(_isAddressPageTypeMatch).toList(),
+ );
}
@action
void updateReceiveAddresses() {
receiveAddresses.removeRange(0, receiveAddresses.length);
- final newAddresses =
- _addresses.where((addressRecord) => !addressRecord.isHidden && !addressRecord.isUsed);
+ final newAddresses = _addresses.where((addressRecord) =>
+ !addressRecord.isHidden &&
+ !addressRecord.isUsed);
receiveAddresses.addAll(newAddresses);
}
@@ -633,66 +671,96 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
@action
- Future<void> discoverAddresses(List<BitcoinAddressRecord> addressList, bool isHidden,
- Future<String?> Function(BitcoinAddressRecord) getAddressHistory,
- {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
- final newAddresses = await _createNewAddresses(gap,
- startIndex: addressList.length, isHidden: isHidden, type: type);
+ Future<void> discoverAddresses(
+ List<BitcoinAddressRecord> addressList,
+ bool isHidden,
+ Future<String?> Function(BitcoinAddressRecord) getAddressHistory, {
+ BitcoinAddressType type = SegwitAddresType.p2wpkh,
+ required bool isLegacyDerivation,
+ }) async {
+ final newAddresses = await _createNewAddresses(
+ gap,
+ startIndex: addressList.length,
+ isHidden: isHidden,
+ isLegacyDerivation: isLegacyDerivation,
+ type: type,
+ );
+
addAddresses(newAddresses);
+ addressList.addAll(newAddresses);
- final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
- final isLastAddressUsed = addressesWithHistory.last == addressList.last.address;
+ final addressesWithHistory =
+ await Future.wait(newAddresses.map(getAddressHistory));
+ final isLastAddressUsed = addressesWithHistory.last != null;
if (isLastAddressUsed) {
- discoverAddresses(addressList, isHidden, getAddressHistory, type: type);
+ await discoverAddresses(
+ addressList,
+ isHidden,
+ getAddressHistory,
+ type: type,
+ isLegacyDerivation: isLegacyDerivation,
+ );
}
}
- Future<void> _generateInitialAddresses(
- {BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
- var countOfReceiveAddresses = 0;
- var countOfHiddenAddresses = 0;
+ Future<void> _generateInitialAddresses(
+ {BitcoinAddressType type = SegwitAddresType.p2wpkh,
+ bool isLegacyDerivation = false }) async {
+
+ // Legacy derivation produces the same addresses as standard for these types.
+ // Don't generate a legacy set to avoid duplicates.
+ if (isLegacyDerivation && (type == SegwitAddresType.p2wpkh || type == SegwitAddresType.p2wsh)) {
+ return;
+ }
- _addresses.forEach((addr) {
- if (addr.type == type) {
- if (addr.isHidden) {
- countOfHiddenAddresses += 1;
- return;
+ var countOfReceiveAddresses = 0;
+ var countOfHiddenAddresses = 0;
+
+ _addresses.forEach((addr) {
+ if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
+ if (addr.isHidden) {
+ countOfHiddenAddresses += 1;
+ } else {
+ countOfReceiveAddresses += 1;
+ }
}
+ });
- countOfReceiveAddresses += 1;
+ if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
+ final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
+ final newAddresses = await _createNewAddresses(addressesCount,
+ startIndex: countOfReceiveAddresses, isHidden: false, type: type, isLegacyDerivation: isLegacyDerivation);
+ addAddresses(newAddresses);
}
- });
- if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
- final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
- final newAddresses = await _createNewAddresses(addressesCount,
- startIndex: countOfReceiveAddresses, isHidden: false, type: type);
- addAddresses(newAddresses);
+ if (countOfHiddenAddresses < defaultChangeAddressesCount) {
+ final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
+ final newAddresses = await _createNewAddresses(addressesCount,
+ startIndex: countOfHiddenAddresses, isHidden: true, type: type, isLegacyDerivation: isLegacyDerivation);
+ addAddresses(newAddresses);
+ }
}
- if (countOfHiddenAddresses < defaultChangeAddressesCount) {
- final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
- final newAddresses = await _createNewAddresses(addressesCount,
- startIndex: countOfHiddenAddresses, isHidden: true, type: type);
- addAddresses(newAddresses);
- }
- }
+ Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
+ {int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async {
+ final list = <BitcoinAddressRecord>[];
- Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
- {int startIndex = 0, bool isHidden = false, BitcoinAddressType? type}) async {
- final list = <BitcoinAddressRecord>[];
+ for (var i = startIndex; i < count + startIndex; i++) {
- for (var i = startIndex; i < count + startIndex; i++) {
- final address = BitcoinAddressRecord(
- await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
- index: i,
- isHidden: isHidden,
- type: type ?? addressPageType,
- network: network,
- );
- list.add(address);
- }
+ final addrType = type ?? addressPageType;
+ final hd = _hdFor(isHidden: isHidden, type: addrType, isLegacyDerivation: isLegacyDerivation);
+
+ final address = BitcoinAddressRecord(
+ await getAddressAsync(index: i, hd: hd, addressType: addrType),
+ index: i,
+ isHidden: isHidden,
+ isLegacyDerivation: isLegacyDerivation,
+ type: addrType,
+ network: network,
+ );
+ list.add(address);
+ }
return list;
}
@@ -730,6 +798,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
return;
}
+
+ final mainHd = _hdFor(isHidden: false, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
+ final sideHd = _hdFor(isHidden: true, type: element.type, isLegacyDerivation: element.isLegacyDerivation);
if (!element.isHidden &&
element.address !=
await getAddressAsync(index: element.index, hd: mainHd, addressType: element.type)) {
@@ -757,9 +828,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return _isAddressByType(addressRecord, addressPageType);
}
- Bip32Slip10Secp256k1 _getHd(bool isHidden) => isHidden ? sideHd : mainHd;
-
- bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
+ bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
!addr.isHidden && !addr.isUsed && addr.type == type;
@@ -769,10 +838,23 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final addressRecord = silentAddresses.firstWhere((addressRecord) =>
addressRecord.type == SilentPaymentsAddresType.p2sp && addressRecord.address == address);
- silentAddresses.remove(addressRecord);
- updateAddressesByMatch();
- }
+ silentAddresses.remove(addressRecord);
+ updateAddressesByMatch();
+ }
+ Bip32Slip10Secp256k1 _hdFor({
+ required bool isHidden,
+ required BitcoinAddressType type,
+ required bool isLegacyDerivation,
+ }) {
+ if (isLegacyDerivation) return isHidden ? legacySideHd : legacyMainHd;
+
+ final map = isHidden ? sideHdByType : mainHdByType;
+ final hd = map[type];
+ if (hd == null) throw Exception("HD not found for type $type");
+ return hd;
+ }
+
@action
Future<void> setLightningAddress(String walletName, {String newAddress = ""}) async {
if (lightningWallet == null) return;
diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart
index 7d98a52a..3cee6c9d 100644
--- a/cw_bitcoin/lib/litecoin_wallet.dart
+++ b/cw_bitcoin/lib/litecoin_wallet.dart
@@ -123,8 +123,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
initialMwebAddresses: initialMwebAddresses,
- mainHd: hd,
- sideHd: accountHD.childKey(Bip32KeyIndex(1)),
+ mainHdByType: mainHdByType,
+ sideHdByType: sideHdByType,
+ legacyMainHd: mainHd,
+ legacySideHd: sideHd,
network: network,
mwebHd: mwebHd,
mwebEnabled: mwebEnabled,
@@ -1264,8 +1266,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
final key = generateECPrivate(
hd: utxo.bitcoinAddressRecord.isHidden
- ? walletAddresses.sideHd
- : walletAddresses.mainHd,
+ ? sideHd
+ : mainHd,
index: utxo.bitcoinAddressRecord.index,
network: network);
final digest = tx2.getTransactionSegwitDigit(
@@ -1438,7 +1440,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
final index = address != null
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
- final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
+ final HD = index == null ? mainHd : mainHd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromHex(HD.privateKey.privKey.toHex());
final privateKey = ECDSAPrivateKey.fromBytes(
diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart
index 737bd41f..19fed3bc 100644
--- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart
+++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart
@@ -25,8 +25,10 @@ class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalle
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
LitecoinWalletAddressesBase(
WalletInfo walletInfo, {
- required super.mainHd,
- required super.sideHd,
+ required super.mainHdByType,
+ required super.sideHdByType,
+ required super.legacyMainHd,
+ required super.legacySideHd,
required super.network,
required super.isHardwareWallet,
required this.mwebHd,
@@ -147,7 +149,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
if (mwebAddrs.length == 0) {
return "";
}
- return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
+ return hd == legacySideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
return generateP2WPKHAddress(hd: hd, index: index, network: network);
}
diff --git a/cw_bitcoin/lib/psbt/signer.dart b/cw_bitcoin/lib/psbt/signer.dart
index 1d0ceba8..c46517e6 100644
--- a/cw_bitcoin/lib/psbt/signer.dart
+++ b/cw_bitcoin/lib/psbt/signer.dart
@@ -227,7 +227,7 @@ class UtxoWithPrivateKey extends UtxoWithAddress {
RegexUtils.addressTypeFromStr(input.address, BitcoinNetwork.mainnet);
final newHd =
- input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.hd;
+ input.bitcoinAddressRecord.isHidden ? wallet.sideHd : wallet.mainHd;
ECPrivate privkey;
if (input.bitcoinAddressRecord is BitcoinSilentPaymentAddressRecord) {
diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
index 39065722..18b9ea1c 100644
--- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
+++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart
@@ -56,8 +56,10 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
- mainHd: hd,
- sideHd: accountHD.childKey(Bip32KeyIndex(1)),
+ mainHdByType: mainHdByType,
+ sideHdByType: sideHdByType,
+ legacyMainHd: mainHd,
+ legacySideHd: sideHd,
network: network,
initialAddressPageType: addressPageType,
isHardwareWallet: walletInfo.isHardwareWallet,
@@ -221,7 +223,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
} catch (_) {}
- final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
+ final HD = index == null ? mainHd : mainHd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromWif(
WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
netVersion: network.wifNetVer,
diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
index 25c11b76..d45f5293 100644
--- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
+++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_addresses.dart
@@ -13,8 +13,10 @@ class BitcoinCashWalletAddresses = BitcoinCashWalletAddressesBase with _$Bitcoin
abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses with Store {
BitcoinCashWalletAddressesBase(
WalletInfo walletInfo, {
- required super.mainHd,
- required super.sideHd,
+ required super.mainHdByType,
+ required super.sideHdByType,
+ required super.legacyMainHd,
+ required super.legacySideHd,
required super.network,
required super.isHardwareWallet,
super.initialAddresses,
diff --git a/cw_dogecoin/lib/src/dogecoin_wallet.dart b/cw_dogecoin/lib/src/dogecoin_wallet.dart
index 964c34b8..c9db30fa 100644
--- a/cw_dogecoin/lib/src/dogecoin_wallet.dart
+++ b/cw_dogecoin/lib/src/dogecoin_wallet.dart
@@ -54,8 +54,10 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
initialAddresses: initialAddresses,
initialRegularAddressIndex: initialRegularAddressIndex,
initialChangeAddressIndex: initialChangeAddressIndex,
- mainHd: hd,
- sideHd: accountHD.childKey(Bip32KeyIndex(1)),
+ mainHdByType: mainHdByType,
+ sideHdByType: sideHdByType,
+ legacyMainHd: mainHd,
+ legacySideHd: sideHd,
network: network,
initialAddressPageType: addressPageType,
isHardwareWallet: walletInfo.isHardwareWallet,
@@ -173,7 +175,7 @@ abstract class DogeCoinWalletBase extends ElectrumWallet with Store {
? walletAddresses.allAddresses.firstWhere((element) => element.address == address).index
: null;
} catch (_) {}
- final HD = index == null ? hd : hd.childKey(Bip32KeyIndex(index));
+ final HD = index == null ? mainHd : mainHd.childKey(Bip32KeyIndex(index));
final priv = ECPrivate.fromWif(
WifEncoder.encode(HD.privateKey.raw, netVer: network.wifNetVer),
netVersion: network.wifNetVer,
diff --git a/cw_dogecoin/lib/src/dogecoin_wallet_addresses.dart b/cw_dogecoin/lib/src/dogecoin_wallet_addresses.dart
index ed3e56f8..df2ede88 100644
--- a/cw_dogecoin/lib/src/dogecoin_wallet_addresses.dart
+++ b/cw_dogecoin/lib/src/dogecoin_wallet_addresses.dart
@@ -8,13 +8,17 @@ import 'package:mobx/mobx.dart';
part 'dogecoin_wallet_addresses.g.dart';
-class DogeCoinWalletAddresses = DogeCoinWalletAddressesBase with _$DogeCoinWalletAddresses;
+class DogeCoinWalletAddresses = DogeCoinWalletAddressesBase
+ with _$DogeCoinWalletAddresses;
-abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
+abstract class DogeCoinWalletAddressesBase extends ElectrumWalletAddresses
+ with Store {
DogeCoinWalletAddressesBase(
WalletInfo walletInfo, {
- required super.mainHd,
- required super.sideHd,
+ required super.mainHdByType,
+ required super.sideHdByType,
+ required super.legacyMainHd,
+ required super.legacySideHd,
required super.network,
required super.isHardwareWallet,
super.initialAddresses,
diff --git a/cw_wownero/devtools_options.yaml b/cw_wownero/devtools_options.yaml
new file mode 100644
index 00000000..fa0b357c
--- /dev/null
+++ b/cw_wownero/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart
index 5fa7a762..cf061a3f 100644
--- a/lib/bitcoin/cw_bitcoin.dart
+++ b/lib/bitcoin/cw_bitcoin.dart
@@ -182,7 +182,8 @@ class CWBitcoin extends Bitcoin {
address: addr.address,
txCount: addr.txCount,
balance: addr.balance,
- isChange: addr.isHidden))
+ isChange: addr.isHidden,
+ isLegacyDerivation: addr.isLegacyDerivation))
.toList();
}
diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart
index 35c76ba3..8ffd2988 100644
--- a/lib/src/screens/restore/wallet_restore_page.dart
+++ b/lib/src/screens/restore/wallet_restore_page.dart
@@ -278,14 +278,31 @@ class WalletRestorePage extends BasePage {
int derivationsWithHistory = 0;
int derivationWithHistoryIndex = 0;
+ final List<String> derivationPathsWithHistory = [];
+
for (int i = 0; i < derivations.length; i++) {
if (derivations[i].transactionsCount > 0) {
derivationsWithHistory++;
derivationWithHistoryIndex = i;
+ final derivationPath = derivations[i].derivationPath;
+ if (derivationPath != null && derivationPath.isNotEmpty) {
+ derivationPathsWithHistory.add(derivationPath);
+ }
}
}
- if (derivationsWithHistory > 1) {
+ final scanDerivationPaths = {
+ "m/84'/0'/0'",
+ "m/86'/0'/0'",
+ "m/44'/0'/0'",
+ "m/49'/0'/0'",
+ };
+
+ final shouldSkipChooseDerivationScreen =
+ derivationPathsWithHistory.isNotEmpty &&
+ derivationPathsWithHistory.every(scanDerivationPaths.contains);
+
+ if (derivationsWithHistory > 1 && !shouldSkipChooseDerivationScreen) {
dInfo = await Navigator.of(context).pushNamed(
Routes.restoreWalletChooseDerivation,
arguments: derivations,
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_item.dart b/lib/view_model/wallet_address_list/wallet_address_list_item.dart
index 725b1ddb..2c5b0a3d 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_item.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_item.dart
@@ -13,6 +13,7 @@ class WalletAddressListItem extends ListItem {
this.isOneTimeReceiveAddress = false,
this.isHidden = false,
this.isManual = false,
+ this.isLegacyDerivation = false,
}) : super();
final int? id;
@@ -24,6 +25,7 @@ class WalletAddressListItem extends ListItem {
final bool isChange;
bool isHidden;
bool isManual;
+ bool isLegacyDerivation;
final bool? isOneTimeReceiveAddress;
@override
diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
index 8d2d2df3..a517cc24 100644
--- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
@@ -290,7 +290,8 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
txCount: subaddress.txCount,
balance: _appStore.amountParsingProxy
.getDisplayCryptoString(subaddress.balance, walletTypeToCryptoCurrency(type)),
- isChange: subaddress.isChange);
+ isChange: subaddress.isChange,
+ isLegacyDerivation: subaddress.isLegacyDerivation);
});
// don't show all 1000+ mweb addresses:
@@ -352,8 +353,9 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
for (var i = 0; i < addressList.length; i++) {
if (!(addressList[i] is WalletAddressListItem)) continue;
- (addressList[i] as WalletAddressListItem).isHidden = wallet.walletAddresses.hiddenAddresses
- .contains((addressList[i] as WalletAddressListItem).address);
+ final item = addressList[i] as WalletAddressListItem;
+ item.isHidden = wallet.walletAddresses.hiddenAddresses.contains(item.address) ||
+ (isElectrumWallet && item.isLegacyDerivation);
}
for (var i = 0; i < addressList.length; i++) {
diff --git a/tool/configure.dart b/tool/configure.dart
index f54e50ff..d9510028 100644
--- a/tool/configure.dart
+++ b/tool/configure.dart
@@ -167,13 +167,15 @@ import "package:breez_sdk_spark_flutter/src/rust/errors.dart";
required this.address,
required this.txCount,
required this.balance,
- required this.isChange});
+ required this.isChange,
+ this.isLegacyDerivation = false});
final int id;
final String name;
final String address;
final int txCount;
final int balance;
final bool isChange;
+ final bool isLegacyDerivation;
}
abstract class Bitcoin {
Why this scored 61/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.