mirror of
https://github.com/Such-Software/hash-wallet.git
synced 2026-10-17 05:41:29 +02:00
CW-1157-Use-correct-derivation-paths-for-other-address-types (#2825)
* feat: use bitcoin standard derivation paths per address type * add support for Electrum derivation type in wallet * fix merge conflict * Use segwit HD for key export and simplify _hdFor * skip derivation chooser for standard scan paths * add legacy and P2SH derivation paths
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
} 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(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);
|
||||
addAddresses(newAddresses);
|
||||
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,
|
||||
);
|
||||
|
||||
final addressesWithHistory = await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final isLastAddressUsed = addressesWithHistory.last == addressList.last.address;
|
||||
addAddresses(newAddresses);
|
||||
addressList.addAll(newAddresses);
|
||||
|
||||
final addressesWithHistory =
|
||||
await Future.wait(newAddresses.map(getAddressHistory));
|
||||
final isLastAddressUsed = addressesWithHistory.last != null;
|
||||
|
||||
if (isLastAddressUsed) {
|
||||
discoverAddresses(addressList, isHidden, getAddressHistory, type: type);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _generateInitialAddresses(
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
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++) {
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(index: i, hd: _getHd(isHidden), addressType: type ?? addressPageType),
|
||||
index: i,
|
||||
isHidden: isHidden,
|
||||
type: type ?? addressPageType,
|
||||
network: network,
|
||||
await discoverAddresses(
|
||||
addressList,
|
||||
isHidden,
|
||||
getAddressHistory,
|
||||
type: type,
|
||||
isLegacyDerivation: isLegacyDerivation,
|
||||
);
|
||||
list.add(address);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type && addr.isLegacyDerivation == isLegacyDerivation) {
|
||||
if (addr.isHidden) {
|
||||
countOfHiddenAddresses += 1;
|
||||
} else {
|
||||
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 (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, isHidden: true, type: type, isLegacyDerivation: isLegacyDerivation);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type, bool isLegacyDerivation = false}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
|
||||
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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
+3
-1
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user