mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2025-06-28 12:29:51 +00:00
refactor: improve namings and add utility classes for managing addresses
This commit is contained in:
parent
8bbf568033
commit
e09d4d6f64
16 changed files with 617 additions and 507 deletions
|
@ -68,7 +68,19 @@ class BaseBitcoinAddressRecord {
|
|||
|
||||
final String _derivationPath;
|
||||
|
||||
String get derivationPath => _derivationPath;
|
||||
String get indexedDerivationPath => _derivationPath;
|
||||
|
||||
bool isUnusedReceiveAddress() {
|
||||
return !isChange && !getIsUsed();
|
||||
}
|
||||
|
||||
bool getIsUsed() {
|
||||
return isUsed || txCount != 0 || balance != 0;
|
||||
}
|
||||
|
||||
// An address not yet used for receiving funds
|
||||
bool getIsStillReceiveable(bool autoGenerateAddresses) =>
|
||||
!autoGenerateAddresses || (!getIsUsed() && !isHidden);
|
||||
|
||||
String toJSON() => json.encode({
|
||||
'address': address,
|
||||
|
@ -82,7 +94,7 @@ class BaseBitcoinAddressRecord {
|
|||
'type': type.toString(),
|
||||
'runtimeType': runtimeType.toString(),
|
||||
'seedBytesType': seedBytesType?.value,
|
||||
'derivationPath': derivationPath,
|
||||
'derivationPath': indexedDerivationPath,
|
||||
});
|
||||
|
||||
static BaseBitcoinAddressRecord buildFromJSON(
|
||||
|
@ -146,7 +158,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
String _derivationPath = '';
|
||||
|
||||
@override
|
||||
String get derivationPath => _derivationPath;
|
||||
String get indexedDerivationPath => _derivationPath;
|
||||
|
||||
BitcoinAddressRecord(
|
||||
super.address, {
|
||||
|
@ -177,6 +189,10 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
.addElem(Bip32KeyIndex(isChange ? 1 : 0))
|
||||
.addElem(Bip32KeyIndex(index))
|
||||
.toString();
|
||||
|
||||
if (getShouldHideAddressByDefault()) {
|
||||
isHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
factory BitcoinAddressRecord.fromJSON(
|
||||
|
@ -201,7 +217,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
name: base.name,
|
||||
balance: base.balance,
|
||||
type: base.type,
|
||||
derivationPath: base.derivationPath,
|
||||
derivationPath: base.indexedDerivationPath,
|
||||
scriptHash: decoded['scriptHash'] as String?,
|
||||
derivationInfo: derivationInfoSnp == null
|
||||
? seedBytesType != null && !seedBytesType.isElectrum
|
||||
|
@ -217,6 +233,19 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
|
||||
late String scriptHash;
|
||||
|
||||
// Manages the wrong derivation path addresses
|
||||
bool getShouldHideAddressByDefault() {
|
||||
final path = derivationInfo.derivationPath.toString();
|
||||
if (seedBytesType!.isElectrum) {
|
||||
return path != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
|
||||
}
|
||||
|
||||
// TODO: pass network
|
||||
// return path.toString() != BitcoinDerivationInfos.LITECOIN.derivationPath.toString();
|
||||
// return path.toString() != BitcoinDerivationPaths.BCH;
|
||||
return path != BitcoinAddressUtils.getDerivationFromType(type).derivationPath.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
String toJSON() {
|
||||
final m = json.decode(super.toJSON()) as Map<String, dynamic>;
|
||||
|
@ -252,7 +281,7 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
String _derivationPath;
|
||||
|
||||
@override
|
||||
String get derivationPath => _derivationPath;
|
||||
String get indexedDerivationPath => _derivationPath;
|
||||
|
||||
int get labelIndex => index;
|
||||
final String? labelHex;
|
||||
|
@ -301,7 +330,7 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
balance: base.balance,
|
||||
type: base.type,
|
||||
labelIndex: base.index,
|
||||
derivationPath: base.derivationPath,
|
||||
derivationPath: base.indexedDerivationPath,
|
||||
labelHex: decoded['labelHex'] as String?,
|
||||
);
|
||||
}
|
||||
|
@ -320,7 +349,7 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
|
|||
final String spAddress;
|
||||
|
||||
@override
|
||||
String get derivationPath => '';
|
||||
String get indexedDerivationPath => '';
|
||||
|
||||
BitcoinReceivedSPAddressRecord(
|
||||
super.address, {
|
||||
|
@ -390,7 +419,7 @@ class LitecoinMWEBAddressRecord extends BaseBitcoinAddressRecord {
|
|||
String _derivationPath = '';
|
||||
|
||||
@override
|
||||
String get derivationPath => _derivationPath;
|
||||
String get indexedDerivationPath => _derivationPath;
|
||||
|
||||
LitecoinMWEBAddressRecord(
|
||||
super.address, {
|
||||
|
|
|
@ -99,7 +99,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
|||
}
|
||||
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
this.walletAddresses.isEnabledAutoGenerateNewAddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -121,34 +121,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
|||
// If already loaded, no need to generate/discover all initial addresses
|
||||
// so skip
|
||||
// if (!walletAddresses.loadedFromNewSnapshot) {
|
||||
for (final seedBytesType in hdWallets.keys) {
|
||||
generateInitialAddresses(
|
||||
addressType: SegwitAddressType.p2wpkh,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
for (final walletAddressType in walletAddresses.walletAddressTypes) {
|
||||
if (isHardwareWallet && walletAddressType != SegwitAddressType.p2wpkh) continue;
|
||||
|
||||
if (!isHardwareWallet) {
|
||||
for (final seedBytesType in hdWallets.keys) {
|
||||
generateInitialAddresses(
|
||||
addressType: P2pkhAddressType.p2pkh,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
|
||||
generateInitialAddresses(
|
||||
addressType: P2shAddressType.p2wpkhInP2sh,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
|
||||
generateInitialAddresses(
|
||||
addressType: SegwitAddressType.p2tr,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
|
||||
generateInitialAddresses(
|
||||
addressType: SegwitAddressType.p2wsh,
|
||||
addressType: walletAddressType,
|
||||
seedBytesType: seedBytesType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
|
@ -517,7 +500,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet<BitcoinWalletAddresses>
|
|||
@override
|
||||
@action
|
||||
Future<ElectrumWorkerListUnspentResponse?> updateAllUnspents([
|
||||
Set<String>? scripthashes,
|
||||
List<String>? scripthashes,
|
||||
bool? wait,
|
||||
]) async {
|
||||
scripthashes ??= this.walletAddresses.allScriptHashes;
|
||||
|
|
|
@ -19,10 +19,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
required super.hdWallets,
|
||||
required super.network,
|
||||
required super.isHardwareWallet,
|
||||
super.initialAddresses,
|
||||
super.initialDiscoveredAddresses,
|
||||
super.initialReceiveAddressesMapped,
|
||||
super.initialChangeAddressesMapped,
|
||||
super.initialAddressesRecords,
|
||||
super.initialActiveAddressIndex,
|
||||
super.initialAddressPageType,
|
||||
this.loadedFromNewSnapshot = false,
|
||||
List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
|
||||
List<BitcoinReceivedSPAddressRecord>? initialReceivedSPAddresses,
|
||||
|
@ -41,15 +40,14 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
|
||||
static const _OLD_SP_PATH = "m/352'/1'/0'/#'/0";
|
||||
|
||||
// NOTE: ordered in priority: eg. p2wpkh always first, most used address, etc.
|
||||
@override
|
||||
final walletAddressTypes = BITCOIN_ADDRESS_TYPES;
|
||||
|
||||
static const BITCOIN_ADDRESS_TYPES = [
|
||||
final walletAddressTypes = [
|
||||
SegwitAddressType.p2wpkh,
|
||||
P2pkhAddressType.p2pkh,
|
||||
SegwitAddressType.p2tr,
|
||||
SegwitAddressType.p2wsh,
|
||||
P2shAddressType.p2wpkhInP2sh,
|
||||
P2pkhAddressType.p2pkh,
|
||||
SegwitAddressType.p2wsh,
|
||||
];
|
||||
|
||||
@observable
|
||||
|
@ -139,7 +137,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
|
||||
@override
|
||||
@action
|
||||
void resetActiveChangeAddress() {
|
||||
void resetActiveAddress() {
|
||||
if (activeSilentAddress != null &&
|
||||
(activeSilentAddress!.isChange || activeSilentAddress!.isHidden)) {
|
||||
try {
|
||||
|
@ -154,7 +152,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
} catch (_) {}
|
||||
}
|
||||
|
||||
super.resetActiveChangeAddress();
|
||||
super.resetActiveAddress();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -367,7 +365,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
(addressRecord) =>
|
||||
!addressRecord.isChange &&
|
||||
addressRecord.labelIndex == 0 &&
|
||||
addressRecord.derivationPath != oldSpendPath.toString(),
|
||||
addressRecord.indexedDerivationPath != oldSpendPath.toString(),
|
||||
);
|
||||
|
||||
final list = [primaryAddress.address];
|
||||
|
@ -376,7 +374,7 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
(addressRecord) =>
|
||||
!addressRecord.isChange &&
|
||||
addressRecord.labelIndex == 0 &&
|
||||
addressRecord.derivationPath == oldSpendPath.toString(),
|
||||
addressRecord.indexedDerivationPath == oldSpendPath.toString(),
|
||||
);
|
||||
|
||||
// Do it like this to keep in order,
|
||||
|
@ -466,12 +464,9 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
initialAddresses: electrumJson.allAddresses,
|
||||
initialDiscoveredAddresses: electrumJson.discoveredAddresses,
|
||||
initialReceiveAddressesMapped:
|
||||
electrumJson.receiveAddressesMapped,
|
||||
initialChangeAddressesMapped:
|
||||
electrumJson.changeAddressesMapped,
|
||||
initialAddressesRecords: electrumJson.addressesRecords,
|
||||
initialAddressPageType: electrumJson.addressPageType,
|
||||
initialActiveAddressIndex: electrumJson.activeIndexByType,
|
||||
initialSilentAddresses: initialSilentAddresses,
|
||||
initialReceivedSPAddresses: initialReceivedSPAddresses,
|
||||
loadedFromNewSnapshot: snp['loadedFromNewSnapshot'] as bool? ?? false,
|
||||
|
|
|
@ -318,7 +318,6 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
}
|
||||
|
||||
Future<void> init() async {
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
|
||||
_autoSaveTimer =
|
||||
|
@ -475,7 +474,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
|
||||
privkey = ECPrivate.fromBip32(
|
||||
bip32: hdWallets[addressRecord.seedBytesType]!.derive(
|
||||
Bip32PathParser.parse(addressRecord.derivationPath),
|
||||
Bip32PathParser.parse(addressRecord.indexedDerivationPath),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -495,7 +494,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
}
|
||||
|
||||
if (utx.bitcoinAddressRecord is BitcoinAddressRecord) {
|
||||
final derivationPath = utx.bitcoinAddressRecord.derivationPath;
|
||||
final derivationPath = utx.bitcoinAddressRecord.indexedDerivationPath;
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
}
|
||||
|
||||
|
@ -637,7 +636,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
isChange: true,
|
||||
));
|
||||
|
||||
final changeDerivationPath = changeAddress.derivationPath.toString();
|
||||
final changeDerivationPath = changeAddress.indexedDerivationPath.toString();
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
PublicKeyWithDerivationPath('', changeDerivationPath);
|
||||
|
||||
|
@ -1113,7 +1112,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
|
||||
@action
|
||||
Future<ElectrumWorkerListUnspentResponse?> updateAllUnspents([
|
||||
Set<String>? scripthashes,
|
||||
List<String>? scripthashes,
|
||||
bool? wait,
|
||||
]) async {
|
||||
scripthashes ??= walletAddresses.allScriptHashes;
|
||||
|
@ -1540,10 +1539,10 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
}
|
||||
|
||||
// Identify all change outputs
|
||||
final changeAddresses = walletAddresses.allChangeAddresses;
|
||||
final changeAddresses = walletAddresses.allAddresses.where((element) => element.isChange);
|
||||
final List<BitcoinOutput> changeOutputs = outputs
|
||||
.where((output) => changeAddresses
|
||||
.any((element) => element.address == output.address.toAddress(network)))
|
||||
.where((output) =>
|
||||
changeAddresses.any((addr) => addr.address == output.address.toAddress(network)))
|
||||
.toList();
|
||||
|
||||
int totalChangeAmount =
|
||||
|
@ -1931,20 +1930,7 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
try {
|
||||
final scripthashByAddress = await subscribeForStatuses(addresses, true);
|
||||
|
||||
walletAddresses.discoveredAddresses.putIfAbsent(
|
||||
addresses.first.type,
|
||||
() => {
|
||||
addresses.first.seedBytesType!: {addresses.first.isChange: true}
|
||||
},
|
||||
);
|
||||
walletAddresses.discoveredAddresses[addresses.first.type]!.putIfAbsent(
|
||||
addresses.first.seedBytesType!,
|
||||
() => {
|
||||
addresses.first.isChange: true,
|
||||
},
|
||||
);
|
||||
walletAddresses.discoveredAddresses[addresses.first.type]![addresses.first.seedBytesType!]!
|
||||
.putIfAbsent(addresses.first.isChange, () => true);
|
||||
walletAddresses.addAddresses(addresses);
|
||||
|
||||
if (addresses.first.seedBytesType!.isOldDerivation && scripthashByAddress.length == 1) {
|
||||
// Wrong derivation address with no history, discard and do not add to wallet addresses
|
||||
|
@ -1969,12 +1955,14 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
isElectrum: seedBytesType.isElectrum,
|
||||
);
|
||||
|
||||
final existingReceiveAddresses = (walletAddresses.receiveAddressesMapped[addressType]
|
||||
?[seedBytesType]?[bitcoinDerivationInfo.derivationPath.toString()] ??
|
||||
<BaseBitcoinAddressRecord>[]);
|
||||
final existingReceiveAddresses = walletAddresses.addressesRecords.getRecords(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: false,
|
||||
);
|
||||
|
||||
if (existingReceiveAddresses.length <
|
||||
ElectrumWalletAddressesBase.defaultReceiveAddressesCount) {
|
||||
if (existingReceiveAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
||||
discoverNewAddresses(
|
||||
seedBytesType: seedBytesType,
|
||||
isChange: false,
|
||||
|
@ -1984,11 +1972,14 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
);
|
||||
}
|
||||
|
||||
final existingChangeAddresses = (walletAddresses.receiveAddressesMapped[addressType]
|
||||
?[seedBytesType]?[bitcoinDerivationInfo.derivationPath.toString()] ??
|
||||
<BaseBitcoinAddressRecord>[]);
|
||||
final existingChangeAddresses = walletAddresses.addressesRecords.getRecords(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: bitcoinDerivationInfo.derivationPath.toString(),
|
||||
isChange: true,
|
||||
);
|
||||
|
||||
if (existingChangeAddresses.length < ElectrumWalletAddressesBase.defaultChangeAddressesCount) {
|
||||
if (existingChangeAddresses.length < ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT) {
|
||||
discoverNewAddresses(
|
||||
seedBytesType: seedBytesType,
|
||||
isChange: true,
|
||||
|
@ -2003,48 +1994,45 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
List<BitcoinAddressRecord> addresses,
|
||||
) async {
|
||||
final newAddresses = <BitcoinAddressRecord>[];
|
||||
final discoveredAddresses =
|
||||
<SeedBytesType, Map<BitcoinAddressType, Map<BitcoinDerivationType, List<bool>>>>{};
|
||||
final discoveredAddresses = BitcoinDiscoveredAddressesMap();
|
||||
|
||||
final usedAddresses = addresses.isNotEmpty
|
||||
? addresses
|
||||
: walletAddresses.allAddresses.where(walletAddresses.getIsUsed).toList();
|
||||
: walletAddresses.allAddresses.where((addr) => addr.getIsUsed()).toList();
|
||||
|
||||
for (final usedAddress in usedAddresses) {
|
||||
final isChange = usedAddress.isChange;
|
||||
|
||||
final alreadyDiscoveredSeedType = discoveredAddresses[usedAddress.seedBytesType!];
|
||||
final alreadyDiscoveredAddressType = alreadyDiscoveredSeedType?[usedAddress.type];
|
||||
final alreadyDiscoveredDerivationType =
|
||||
alreadyDiscoveredAddressType?[usedAddress.derivationInfo.derivationType];
|
||||
final isAlreadyDiscovered = alreadyDiscoveredDerivationType?.contains(isChange) ?? false;
|
||||
final isAlreadyDiscovered = discoveredAddresses.getIsDiscovered(
|
||||
addressType: usedAddress.type,
|
||||
seedBytesType: usedAddress.seedBytesType!,
|
||||
derivationPath: usedAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: usedAddress.isChange,
|
||||
);
|
||||
|
||||
if (isAlreadyDiscovered) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final matchingAddressList = walletAddresses.allAddresses.where(
|
||||
(addr) =>
|
||||
addr.seedBytesType! == usedAddress.seedBytesType! &&
|
||||
addr.type == usedAddress.type &&
|
||||
addr.derivationInfo.derivationType == usedAddress.derivationInfo.derivationType &&
|
||||
addr.isChange == isChange,
|
||||
final matchingAddressList = walletAddresses.addressesRecords.getRecords(
|
||||
seedBytesType: usedAddress.seedBytesType!,
|
||||
addressType: usedAddress.type,
|
||||
derivationPath: usedAddress.derivationInfo.derivationPath.toString(),
|
||||
isChange: usedAddress.isChange,
|
||||
);
|
||||
final totalMatchingAddresses = matchingAddressList.length;
|
||||
|
||||
final matchingGapLimit = (isChange
|
||||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount);
|
||||
final matchingGapLimit = usedAddress.isChange
|
||||
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
||||
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
||||
final isAddressUsedAboveGap = usedAddress.index >= totalMatchingAddresses - matchingGapLimit;
|
||||
|
||||
if (isAddressUsedAboveGap) {
|
||||
discoveredAddresses.putIfAbsent(usedAddress.seedBytesType!, () => {});
|
||||
discoveredAddresses[usedAddress.seedBytesType!]!.putIfAbsent(usedAddress.type, () => {});
|
||||
discoveredAddresses[usedAddress.seedBytesType!]![usedAddress.type]!
|
||||
.putIfAbsent(usedAddress.derivationInfo.derivationType, () => []);
|
||||
discoveredAddresses[usedAddress.seedBytesType!]![usedAddress.type]![
|
||||
usedAddress.derivationInfo.derivationType]!
|
||||
.add(isChange);
|
||||
// discoveredAddresses.addDiscovered(
|
||||
// addressType: addressType,
|
||||
// seedBytesType: seedBytesType,
|
||||
// derivationPath: derivationPath,
|
||||
// addressRecord: addressRecord,
|
||||
// discovered: discovered,
|
||||
// );
|
||||
|
||||
// final theseAddresses = discoverNewAddresses(
|
||||
// isChange: isChange,
|
||||
|
@ -2080,31 +2068,36 @@ abstract class ElectrumWalletBase<T extends ElectrumWalletAddresses>
|
|||
required BitcoinDerivationInfo derivationInfo,
|
||||
int? startIndex,
|
||||
}) async {
|
||||
if (walletAddresses.discoveredAddresses[addressType]?[seedBytesType]?[isChange] == true) {
|
||||
final count = isChange
|
||||
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
||||
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
||||
final recordList = walletAddresses.addressesRecords.getRecords(
|
||||
seedBytesType: seedBytesType,
|
||||
addressType: addressType,
|
||||
derivationPath: derivationInfo.derivationPath.toString(),
|
||||
isChange: isChange,
|
||||
);
|
||||
|
||||
startIndex ??= recordList.length;
|
||||
|
||||
final isDiscovered = walletAddresses.discoveredAddresses.getIsDiscovered(
|
||||
addressType: addressType,
|
||||
seedBytesType: seedBytesType,
|
||||
derivationPath: derivationInfo.derivationPath.toString(),
|
||||
isChange: isChange,
|
||||
);
|
||||
|
||||
if (isDiscovered) {
|
||||
return;
|
||||
}
|
||||
|
||||
final count = isChange
|
||||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
|
||||
|
||||
startIndex ??= ((isChange
|
||||
? walletAddresses.changeAddressesMapped[addressType]
|
||||
: walletAddresses.receiveAddressesMapped[addressType])?[seedBytesType]
|
||||
?[derivationInfo.derivationPath.toString()] ??
|
||||
[])
|
||||
.length;
|
||||
|
||||
workerSendPort!.send(
|
||||
ElectrumWorkerDiscoverAddressesRequest(
|
||||
id: _messageId,
|
||||
count: count,
|
||||
walletType: type,
|
||||
startIndex: startIndex,
|
||||
seedBytesType: seedBytesType,
|
||||
shouldHideAddress: walletAddresses.getShouldHideAddress(
|
||||
derivationInfo.derivationPath,
|
||||
addressType,
|
||||
),
|
||||
derivationInfo: derivationInfo,
|
||||
isChange: isChange,
|
||||
addressType: addressType,
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import 'dart:collection';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
|
@ -12,54 +13,51 @@ part 'electrum_wallet_addresses.g.dart';
|
|||
class ElectrumWalletAddresses = ElectrumWalletAddressesBase with _$ElectrumWalletAddresses;
|
||||
|
||||
abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
||||
static const INITIAL_RECEIVE_COUNT = 22;
|
||||
static const INITIAL_CHANGE_COUNT = 17;
|
||||
static const GAP = 20;
|
||||
|
||||
ElectrumWalletAddressesBase(
|
||||
WalletInfo walletInfo, {
|
||||
required this.hdWallets,
|
||||
required this.network,
|
||||
required this.isHardwareWallet,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
Map<String, int>? initialRegularAddressIndex,
|
||||
Map<String, int>? initialChangeAddressIndex,
|
||||
BitcoinAddressRecordMap? initialAddressesRecords,
|
||||
Map<BitcoinAddressType, int>? initialActiveAddressIndex,
|
||||
BitcoinDiscoveredAddressesMap? initialDiscoveredAddresses,
|
||||
BitcoinAddressType? initialAddressPageType,
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>
|
||||
initialReceiveAddressesMapped = const {},
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>
|
||||
initialChangeAddressesMapped = const {},
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<bool, bool>>> initialDiscoveredAddresses =
|
||||
const {},
|
||||
}) : _allAddresses = ObservableList.of(initialAddresses ?? []),
|
||||
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
|
||||
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
|
||||
discoveredAddresses = initialDiscoveredAddresses,
|
||||
receiveAddressesMapped = initialReceiveAddressesMapped,
|
||||
changeAddressesMapped = initialChangeAddressesMapped,
|
||||
}) : addressesRecords = initialAddressesRecords ?? BitcoinAddressRecordMap(),
|
||||
activeIndexByType = initialActiveAddressIndex ?? {},
|
||||
discoveredAddresses = initialDiscoveredAddresses ?? BitcoinDiscoveredAddressesMap(),
|
||||
addressPageType = initialAddressPageType ??
|
||||
(walletInfo.addressPageType != null
|
||||
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
|
||||
: SegwitAddressType.p2wpkh),
|
||||
super(walletInfo);
|
||||
|
||||
static const defaultReceiveAddressesCount = 22;
|
||||
static const defaultChangeAddressesCount = 17;
|
||||
static const gap = 20;
|
||||
super(walletInfo) {
|
||||
updateAllAddresses();
|
||||
}
|
||||
|
||||
final walletAddressTypes = <BitcoinAddressType>[];
|
||||
|
||||
final ObservableList<BitcoinAddressRecord> _allAddresses;
|
||||
@observable
|
||||
BitcoinDiscoveredAddressesMap discoveredAddresses;
|
||||
@observable
|
||||
BitcoinAddressRecordMap addressesRecords;
|
||||
|
||||
@observable
|
||||
// { BitcoinAddressType: { SeedBytesType: { isChange: true = discovered } } }
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<bool, bool>>> discoveredAddresses;
|
||||
List<BitcoinAddressRecord> _allAddresses = [];
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allAddresses => _allAddresses;
|
||||
|
||||
@observable
|
||||
// { BitcoinAddressType: { SeedBytesType: { derivationPath: [BaseBitcoinAddressRecord] } } }
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>
|
||||
receiveAddressesMapped;
|
||||
@action
|
||||
List<BitcoinAddressRecord> updateAllAddresses() {
|
||||
_allAddresses = addressesRecords.allRecords().whereType<BitcoinAddressRecord>().toList();
|
||||
|
||||
@observable
|
||||
// { BitcoinAddressType: { SeedBytesType: { derivationPath: [BaseBitcoinAddressRecord] } } }
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>
|
||||
changeAddressesMapped;
|
||||
updateAllScriptHashes();
|
||||
updateSelectedReceiveAddresses();
|
||||
updateSelectedChangeAddresses();
|
||||
return _allAddresses;
|
||||
}
|
||||
|
||||
final BasedUtxoNetwork network;
|
||||
|
||||
|
@ -81,151 +79,192 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@observable
|
||||
BitcoinAddressType addressPageType;
|
||||
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allChangeAddresses =>
|
||||
_allAddresses.where((addr) => addr.isChange).toList();
|
||||
@action
|
||||
Future<void> setAddressPageType(BitcoinAddressType type) async {
|
||||
addressPageType = type;
|
||||
|
||||
updateSelectedReceiveAddresses();
|
||||
updateSelectedChangeAddresses();
|
||||
|
||||
walletInfo.addressPageType = addressPageType.toString();
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
@computed
|
||||
BitcoinDerivationInfo get _defaultAddressPageDerivationInfo =>
|
||||
BitcoinAddressUtils.getDerivationFromType(
|
||||
addressPageType,
|
||||
isElectrum: walletSeedBytesType.isElectrum,
|
||||
);
|
||||
|
||||
@observable
|
||||
List<BaseBitcoinAddressRecord> _selectedReceiveAddresses = [];
|
||||
@computed
|
||||
List<BaseBitcoinAddressRecord> get selectedReceiveAddresses {
|
||||
return receiveAddressesMapped[addressPageType]?[walletSeedBytesType]
|
||||
?[_defaultAddressPageDerivationInfo.derivationPath.toString()] ??
|
||||
[];
|
||||
List<BaseBitcoinAddressRecord> get selectedReceiveAddresses => _selectedReceiveAddresses;
|
||||
|
||||
@action
|
||||
List<BaseBitcoinAddressRecord> updateSelectedReceiveAddresses() {
|
||||
_selectedReceiveAddresses = addressesRecords.getRecords(
|
||||
addressType: addressPageType,
|
||||
seedBytesType: walletSeedBytesType,
|
||||
derivationPath: _defaultAddressPageDerivationInfo.derivationPath.toString(),
|
||||
isChange: false,
|
||||
);
|
||||
updateNextReceiveAddress();
|
||||
return _selectedReceiveAddresses;
|
||||
}
|
||||
|
||||
@observable
|
||||
BitcoinAddressRecord? _nextReceiveAddress;
|
||||
@computed
|
||||
List<BaseBitcoinAddressRecord> get selectedChangeAddresses =>
|
||||
changeAddressesMapped[addressPageType]?[walletSeedBytesType]
|
||||
?[_defaultAddressPageDerivationInfo.derivationPath.toString()] ??
|
||||
[];
|
||||
BitcoinAddressRecord? get nextReceiveAddress => _nextReceiveAddress;
|
||||
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allAddresses => _allAddresses.toList();
|
||||
@action
|
||||
BitcoinAddressRecord? updateNextReceiveAddress() {
|
||||
final receiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
||||
if (receiveAddresses.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@computed
|
||||
Set<String> get allScriptHashes =>
|
||||
_allAddresses.map((addressRecord) => addressRecord.scriptHash).toSet();
|
||||
|
||||
BitcoinAddressRecord getFromAddresses(String address) {
|
||||
return _allAddresses.firstWhere((element) => element.address == address);
|
||||
_nextReceiveAddress = receiveAddresses.firstWhereOrNull(
|
||||
(addressRecord) =>
|
||||
addressRecord.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) &&
|
||||
!addressRecord.isChange,
|
||||
) ??
|
||||
receiveAddresses.first;
|
||||
return _nextReceiveAddress;
|
||||
}
|
||||
|
||||
@observable
|
||||
List<BaseBitcoinAddressRecord> _selectedChangeAddresses = [];
|
||||
@computed
|
||||
List<BaseBitcoinAddressRecord> get selectedChangeAddresses => _selectedChangeAddresses;
|
||||
|
||||
@action
|
||||
List<BaseBitcoinAddressRecord> updateSelectedChangeAddresses() {
|
||||
_selectedChangeAddresses = addressesRecords.getRecords(
|
||||
addressType: addressPageType,
|
||||
seedBytesType: walletSeedBytesType,
|
||||
derivationPath: _defaultAddressPageDerivationInfo.derivationPath.toString(),
|
||||
isChange: true,
|
||||
);
|
||||
updateNextChangeAddress();
|
||||
return _selectedChangeAddresses;
|
||||
}
|
||||
|
||||
@observable
|
||||
BitcoinAddressRecord? _nextChangeAddress;
|
||||
@computed
|
||||
BitcoinAddressRecord? get nextChangeAddress => _nextChangeAddress;
|
||||
|
||||
@action
|
||||
BitcoinAddressRecord? updateNextChangeAddress() {
|
||||
final changeAddresses = selectedChangeAddresses.whereType<BitcoinAddressRecord>();
|
||||
if (changeAddresses.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_nextChangeAddress = changeAddresses.firstWhereOrNull(
|
||||
(addressRecord) =>
|
||||
addressRecord.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) &&
|
||||
addressRecord.isChange,
|
||||
) ??
|
||||
changeAddresses.first;
|
||||
return _nextChangeAddress;
|
||||
}
|
||||
|
||||
@observable
|
||||
List<String> _allScriptHashes = [];
|
||||
@computed
|
||||
List<String> get allScriptHashes => _allScriptHashes;
|
||||
|
||||
@action
|
||||
List<String> updateAllScriptHashes() {
|
||||
_allScriptHashes.clear();
|
||||
_allScriptHashes.addAll(allAddresses.map((address) => address.scriptHash));
|
||||
return _allScriptHashes;
|
||||
}
|
||||
|
||||
BaseBitcoinAddressRecord getFromAddresses(String address) =>
|
||||
allAddresses.firstWhere((element) => element.address == address);
|
||||
|
||||
// TODO: feature with toggle to switch change address type
|
||||
@observable
|
||||
BitcoinAddressType changeAddressType = SegwitAddressType.p2wpkh;
|
||||
|
||||
@observable
|
||||
BitcoinAddressRecord? activeAddress;
|
||||
BitcoinAddressRecord? activeBitcoinAddress;
|
||||
|
||||
// TODO: map by type
|
||||
@observable
|
||||
int activeAddressIndex = 0;
|
||||
bool isEnabledAutoGenerateNewAddress = true;
|
||||
|
||||
@override
|
||||
@action
|
||||
void resetActiveChangeAddress() {
|
||||
if (isEnabledAutoGenerateSubaddress) {
|
||||
try {
|
||||
activeAddress = _allAddresses.firstWhere(
|
||||
(addressRecord) =>
|
||||
addressRecord.type == addressPageType &&
|
||||
addressRecord.index == activeAddressIndex &&
|
||||
getIsReceive(addressRecord),
|
||||
);
|
||||
void resetActiveAddress() {
|
||||
final activeReceiveAddresses = selectedReceiveAddresses.whereType<BitcoinAddressRecord>();
|
||||
|
||||
return;
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
activeAddress = _allAddresses.firstWhere(
|
||||
(addressRecord) => addressRecord.type == addressPageType && getIsReceive(addressRecord),
|
||||
);
|
||||
return;
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
try {
|
||||
activeAddress = _allAddresses.firstWhere(
|
||||
(addressRecord) =>
|
||||
addressRecord.type == addressPageType &&
|
||||
addressRecord.index == activeAddressIndex &&
|
||||
!addressRecord.isChange &&
|
||||
!addressRecord.isHidden,
|
||||
);
|
||||
} catch (_) {}
|
||||
activeBitcoinAddress = activeReceiveAddresses.firstWhereOrNull(
|
||||
(addressRecord) => addressRecord.index == activeIndexByType[addressPageType],
|
||||
) ??
|
||||
activeReceiveAddresses.first;
|
||||
}
|
||||
|
||||
@override
|
||||
@computed
|
||||
String get address {
|
||||
if (activeAddress != null) {
|
||||
return activeAddress!.address;
|
||||
if (activeBitcoinAddress != null) {
|
||||
return activeBitcoinAddress!.address;
|
||||
}
|
||||
|
||||
String? receiveAddress = "";
|
||||
|
||||
if (isEnabledAutoGenerateSubaddress && selectedReceiveAddresses.isEmpty) {
|
||||
receiveAddress =
|
||||
selectedReceiveAddresses.firstWhereOrNull((addr) => !getIsUsed(addr))?.address;
|
||||
} else {
|
||||
receiveAddress = selectedReceiveAddresses.first.address;
|
||||
}
|
||||
final receiveAddress = selectedReceiveAddresses
|
||||
.firstWhereOrNull(
|
||||
(addr) => addr.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) && !addr.isChange,
|
||||
)
|
||||
?.address;
|
||||
|
||||
return receiveAddress ?? '';
|
||||
}
|
||||
|
||||
@observable
|
||||
bool isEnabledAutoGenerateSubaddress = true;
|
||||
|
||||
@override
|
||||
set address(String addr) {
|
||||
try {
|
||||
final addressRecord = _allAddresses.firstWhere(
|
||||
(addressRecord) => addressRecord.address == addr,
|
||||
);
|
||||
final addressRecord = _allAddresses.firstWhereOrNull(
|
||||
(addressRecord) => addressRecord.address == addr,
|
||||
);
|
||||
|
||||
activeAddress = addressRecord;
|
||||
activeBitcoinAddress = addressRecord;
|
||||
|
||||
if (getIsReceive(addressRecord)) {
|
||||
activeAddressIndex = addressRecord.index;
|
||||
}
|
||||
} catch (e) {
|
||||
// printV("ElectrumWalletAddressBase: set address ($addr): $e");
|
||||
if (addressRecord != null &&
|
||||
addressRecord.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) &&
|
||||
!addressRecord.isChange) {
|
||||
activeAddressIndex = addressRecord.index;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get primaryAddress => _allAddresses.first.address;
|
||||
@computed
|
||||
String get primaryAddress =>
|
||||
selectedReceiveAddresses
|
||||
.firstWhereOrNull(
|
||||
(addressRecord) =>
|
||||
addressRecord.getIsStillReceiveable(isEnabledAutoGenerateNewAddress) &&
|
||||
!addressRecord.isChange,
|
||||
)
|
||||
?.address ??
|
||||
'';
|
||||
|
||||
Map<String, int> currentReceiveAddressIndexByType;
|
||||
Map<BitcoinAddressType, int> activeIndexByType;
|
||||
|
||||
int get currentReceiveAddressIndex =>
|
||||
currentReceiveAddressIndexByType[addressPageType.toString()] ?? 0;
|
||||
int get activeAddressIndex => activeIndexByType[addressPageType] ?? 0;
|
||||
|
||||
void set currentReceiveAddressIndex(int index) =>
|
||||
currentReceiveAddressIndexByType[addressPageType.toString()] = index;
|
||||
|
||||
Map<String, int> currentChangeAddressIndexByType;
|
||||
|
||||
int get currentChangeAddressIndex =>
|
||||
currentChangeAddressIndexByType[addressPageType.toString()] ?? 0;
|
||||
|
||||
void set currentChangeAddressIndex(int index) =>
|
||||
currentChangeAddressIndexByType[addressPageType.toString()] = index;
|
||||
set activeAddressIndex(int index) => activeIndexByType[addressPageType] = index;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
await updateAddressesInBox();
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
Future<BaseBitcoinAddressRecord> getChangeAddress() async {
|
||||
final address = selectedChangeAddresses.firstWhere(
|
||||
(addr) => addr.isChange && !getIsUsed(addr) && addr.type == changeAddressType,
|
||||
(addr) => !addr.getIsUsed() && addr.type == changeAddressType,
|
||||
);
|
||||
return address;
|
||||
}
|
||||
|
@ -250,7 +289,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
name: label,
|
||||
type: addressPageType,
|
||||
network: network,
|
||||
derivationInfo: BitcoinAddressUtils.getDerivationFromType(addressPageType),
|
||||
derivationInfo: derivationInfo,
|
||||
seedBytesType: walletSeedBytesType,
|
||||
);
|
||||
return address;
|
||||
|
@ -362,14 +401,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@action
|
||||
void updateAddress(String address, String label) {
|
||||
BaseBitcoinAddressRecord? foundAddress;
|
||||
_allAddresses.forEach((addressRecord) {
|
||||
|
||||
for (final addressRecord in _allAddresses) {
|
||||
if (addressRecord.address == address) {
|
||||
foundAddress = addressRecord;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: verify this updates and keeps on re-open
|
||||
if (foundAddress != null) {
|
||||
foundAddress!.setNewName(label);
|
||||
foundAddress.setNewName(label);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,82 +433,25 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
@action
|
||||
void addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
||||
this._allAddresses.addAll(addresses);
|
||||
|
||||
final firstAddress = addresses.first;
|
||||
final addressesMap = {
|
||||
...(firstAddress.isChange ? changeAddressesMapped : receiveAddressesMapped)
|
||||
};
|
||||
|
||||
addressesMap.putIfAbsent(
|
||||
firstAddress.type,
|
||||
() => {
|
||||
firstAddress.seedBytesType!: {
|
||||
firstAddress.derivationInfo.derivationPath.toString(): addresses.toList(),
|
||||
},
|
||||
},
|
||||
addressesRecords.addAddress(
|
||||
addressType: firstAddress.type,
|
||||
seedBytesType: firstAddress.seedBytesType!,
|
||||
derivationPath: firstAddress.derivationInfo.derivationPath.toString(),
|
||||
addressRecord: firstAddress,
|
||||
);
|
||||
addressesMap[firstAddress.type]!.putIfAbsent(
|
||||
firstAddress.seedBytesType!,
|
||||
() => {
|
||||
firstAddress.derivationInfo.derivationPath.toString(): addresses.toList(),
|
||||
},
|
||||
);
|
||||
addressesMap[firstAddress.type]![firstAddress.seedBytesType]!.putIfAbsent(
|
||||
firstAddress.derivationInfo.derivationPath.toString(),
|
||||
() => addresses.toList(),
|
||||
);
|
||||
|
||||
if (firstAddress.isChange) {
|
||||
changeAddressesMapped = addressesMap;
|
||||
} else {
|
||||
receiveAddressesMapped = addressesMap;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> setAddressType(BitcoinAddressType type) async {
|
||||
addressPageType = type;
|
||||
walletInfo.addressPageType = addressPageType.toString();
|
||||
await walletInfo.save();
|
||||
}
|
||||
|
||||
bool isUnusedReceiveAddress(BaseBitcoinAddressRecord addr) {
|
||||
return !addr.isChange && !getIsUsed(addr);
|
||||
updateAllAddresses();
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json['allAddresses'] = _allAddresses.map((address) => address.toJSON()).toList();
|
||||
json['addressesRecords'] = addressesRecords.toJson();
|
||||
json['discoveredAddresses'] = discoveredAddresses.toJson();
|
||||
json['addressPageType'] = addressPageType.toString();
|
||||
json['discoveredAddresses'] = discoveredAddresses.map((addressType, v) {
|
||||
return MapEntry(addressType.value, v.map((seedBytesType, v) {
|
||||
return MapEntry(seedBytesType.value, v.map((isChange, v) {
|
||||
return MapEntry(isChange.toString(), v);
|
||||
}));
|
||||
}));
|
||||
});
|
||||
|
||||
json['receiveAddressesMapped'] = receiveAddressesMapped.map((addressType, v) {
|
||||
return MapEntry(addressType.value, v.map((seedBytesType, v) {
|
||||
return MapEntry(seedBytesType.value, v.map((derivationPath, v) {
|
||||
return MapEntry(
|
||||
derivationPath.toString(),
|
||||
v.map((address) => address.toJSON()).toList(),
|
||||
);
|
||||
}));
|
||||
}));
|
||||
});
|
||||
json['changeAddressesMapped'] = receiveAddressesMapped.map((addressType, v) {
|
||||
return MapEntry(addressType.value, v.map((seedBytesType, v) {
|
||||
return MapEntry(seedBytesType.value, v.map((derivationPath, v) {
|
||||
return MapEntry(
|
||||
derivationPath.toString(),
|
||||
v.map((address) => address.toJSON()).toList(),
|
||||
);
|
||||
}));
|
||||
}));
|
||||
});
|
||||
json['activeIndexByType'] = activeIndexByType.map(
|
||||
(key, value) => MapEntry(key.toString(), value),
|
||||
);
|
||||
return json;
|
||||
}
|
||||
|
||||
|
@ -477,32 +462,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
.map((addr) => BitcoinAddressRecord.fromJSON(addr))
|
||||
.toList();
|
||||
|
||||
var receiveAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
||||
var changeAddressIndexByType = {SegwitAddressType.p2wpkh.toString(): 0};
|
||||
|
||||
try {
|
||||
receiveAddressIndexByType = {
|
||||
SegwitAddressType.p2wpkh.toString(): int.parse(data['account_index'] as String? ?? '0')
|
||||
};
|
||||
changeAddressIndexByType = {
|
||||
SegwitAddressType.p2wpkh.toString():
|
||||
int.parse(data['change_address_index'] as String? ?? '0')
|
||||
};
|
||||
} catch (_) {
|
||||
try {
|
||||
receiveAddressIndexByType = data["account_index"] as Map<String, int>? ?? {};
|
||||
changeAddressIndexByType = data["change_address_index"] as Map<String, int>? ?? {};
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
return {
|
||||
'allAddresses': addresses,
|
||||
'addressPageType': data['address_page_type'] as String?,
|
||||
'receiveAddressIndexByType': receiveAddressIndexByType,
|
||||
'changeAddressIndexByType': changeAddressIndexByType,
|
||||
'discoveredAddresses': data['discoveredAddresses'] as Map<String, dynamic>? ?? {},
|
||||
'receiveAddressesMapped': data['receiveAddressesMapped'] as Map<String, dynamic>? ?? {},
|
||||
'changeAddressesMapped': data['changeAddressesMapped'] as Map<String, dynamic>? ?? {},
|
||||
'addressesRecords': data['addressesRecords'] as Map<String, dynamic>?,
|
||||
'discoveredAddresses': data['discoveredAddresses'] as Map<String, dynamic>?,
|
||||
'addressPageType': data['addressPageType'] as String?,
|
||||
'activeIndexByType': (data['activeIndexByType'] as Map<dynamic, dynamic>?) ?? {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -513,97 +478,267 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
required BasedUtxoNetwork network,
|
||||
required bool isHardwareWallet,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
List<BitcoinSilentPaymentAddressRecord>? initialSilentAddresses,
|
||||
List<BitcoinReceivedSPAddressRecord>? initialReceivedSPAddresses,
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>?
|
||||
initialReceiveAddressesMapped,
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<String, List<BaseBitcoinAddressRecord>>>>?
|
||||
initialChangeAddressesMapped,
|
||||
Map<BitcoinAddressType, Map<SeedBytesType, Map<bool, bool>>>? initialDiscoveredAddresses,
|
||||
BitcoinAddressRecordMap? initialAddressesRecords,
|
||||
BitcoinDiscoveredAddressesMap? initialDiscoveredAddresses,
|
||||
}) {
|
||||
initialAddresses ??= (json['allAddresses'] as List)
|
||||
.map((record) => BitcoinAddressRecord.fromJSON(record as String))
|
||||
.toList();
|
||||
// TODO: put this into records map (currently unused)
|
||||
if (json['allAddresses'] != null)
|
||||
initialAddresses ??= (json['allAddresses'] as List)
|
||||
.map((record) => BitcoinAddressRecord.fromJSON(record as String))
|
||||
.toList();
|
||||
|
||||
initialReceiveAddressesMapped ??= (json['receiveAddressesMapped'] as Map).map(
|
||||
(addressType, v) => MapEntry(
|
||||
BitcoinAddressType.fromValue(addressType as String),
|
||||
(v as Map).map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
SeedBytesType.fromValue(seedBytesType as String),
|
||||
(v as Map).map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath as String,
|
||||
(v as List)
|
||||
.map((addr) => BaseBitcoinAddressRecord.fromJSON(addr as String))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
initialChangeAddressesMapped ??= (json['changeAddressesMapped'] as Map).map(
|
||||
(addressType, v) => MapEntry(
|
||||
BitcoinAddressType.fromValue(addressType as String),
|
||||
(v as Map).map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
SeedBytesType.fromValue(seedBytesType as String),
|
||||
(v as Map).map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath as String,
|
||||
(v as List)
|
||||
.map((addr) => BaseBitcoinAddressRecord.fromJSON(addr as String))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (json['addressesRecords'] != null)
|
||||
initialAddressesRecords ??= BitcoinAddressRecordMap.fromJson(
|
||||
json['addressesRecords'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
initialDiscoveredAddresses ??= ((json['discoveredAddresses'] as Map?) ?? {}).map(
|
||||
(addressType, v) => MapEntry(
|
||||
BitcoinAddressType.fromValue(addressType as String),
|
||||
(v as Map).map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
SeedBytesType.fromValue(seedBytesType as String),
|
||||
(v as Map).map(
|
||||
(isChange, v) => MapEntry(isChange == "true", v as bool),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
if (json['discoveredAddresses'] != null)
|
||||
initialDiscoveredAddresses ??= BitcoinDiscoveredAddressesMap.fromJson(
|
||||
json['discoveredAddresses'] as Map<String, dynamic>,
|
||||
);
|
||||
|
||||
return ElectrumWalletAddresses(
|
||||
walletInfo,
|
||||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialAddressesRecords: initialAddressesRecords,
|
||||
initialDiscoveredAddresses: initialDiscoveredAddresses,
|
||||
initialReceiveAddressesMapped: initialReceiveAddressesMapped,
|
||||
initialChangeAddressesMapped: initialChangeAddressesMapped,
|
||||
initialAddressPageType: json['addressPageType'] != null
|
||||
? BitcoinAddressType.fromValue(json['addressPageType'] as String)
|
||||
: null,
|
||||
initialActiveAddressIndex: (json['activeIndexByType'] as Map<dynamic, dynamic>?)?.map(
|
||||
(key, value) => MapEntry(BitcoinAddressType.fromValue(key as String), value as int),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typedef AddressRecords = List<BaseBitcoinAddressRecord>;
|
||||
|
||||
typedef ItemsByIsChange<T> = Map<bool, T>;
|
||||
typedef ItemsByDerivationPath<T> = Map<String, ItemsByIsChange<T>>;
|
||||
|
||||
// Maps by each different property with the final item being a list of addresses
|
||||
typedef AddressRecordsBySeedType = Map<SeedBytesType, ItemsByDerivationPath<AddressRecords>>;
|
||||
|
||||
class ItemsRecordMap<T extends Map<SeedBytesType, ItemsByDerivationPath<dynamic>>>
|
||||
extends MapBase<BitcoinAddressType, T> {
|
||||
final Map<BitcoinAddressType, T> _data = {};
|
||||
|
||||
@override
|
||||
T? operator [](Object? key) => _data[key];
|
||||
|
||||
@override
|
||||
void operator []=(BitcoinAddressType key, T value) {
|
||||
_data[key] = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() => _data.clear();
|
||||
|
||||
@override
|
||||
Iterable<BitcoinAddressType> get keys => _data.keys;
|
||||
|
||||
@override
|
||||
Iterable<T> get values => _data.values;
|
||||
|
||||
@override
|
||||
T? remove(Object? key) => _data.remove(key);
|
||||
}
|
||||
|
||||
class BitcoinAddressRecordMap extends ItemsRecordMap<AddressRecordsBySeedType> {
|
||||
void addAddress({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
required String derivationPath,
|
||||
required BaseBitcoinAddressRecord addressRecord,
|
||||
}) {
|
||||
_data.putIfAbsent(
|
||||
addressType,
|
||||
() => {
|
||||
seedBytesType: {
|
||||
derivationPath: {addressRecord.isChange: []},
|
||||
},
|
||||
},
|
||||
);
|
||||
_data[addressType]!.putIfAbsent(
|
||||
seedBytesType,
|
||||
() => {
|
||||
derivationPath: {addressRecord.isChange: []},
|
||||
},
|
||||
);
|
||||
_data[addressType]![seedBytesType]!.putIfAbsent(
|
||||
derivationPath,
|
||||
() => {addressRecord.isChange: []},
|
||||
);
|
||||
_data[addressType]![seedBytesType]![derivationPath]!
|
||||
.putIfAbsent(addressRecord.isChange, () => []);
|
||||
|
||||
_data[addressType]![seedBytesType]![derivationPath]![addressRecord.isChange]!
|
||||
.add(addressRecord);
|
||||
}
|
||||
|
||||
List<BaseBitcoinAddressRecord> allRecords() {
|
||||
return _data.values
|
||||
.expand((seedTypeMap) => seedTypeMap.values)
|
||||
.expand((derivationMap) => derivationMap.values)
|
||||
.expand((changeMap) => changeMap.values)
|
||||
.fold<List<BaseBitcoinAddressRecord>>(
|
||||
[],
|
||||
(acc, records) => acc..addAll(records),
|
||||
);
|
||||
}
|
||||
|
||||
bool getIsUsed(BaseBitcoinAddressRecord addr) {
|
||||
return addr.isUsed || addr.txCount != 0 || addr.balance != 0;
|
||||
List<BaseBitcoinAddressRecord> getRecords({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
required String derivationPath,
|
||||
required bool isChange,
|
||||
}) {
|
||||
return _data[addressType]?[seedBytesType]?[derivationPath]?[isChange] ?? [];
|
||||
}
|
||||
|
||||
bool getIsReceive(BaseBitcoinAddressRecord addr) {
|
||||
return !getIsUsed(addr) && !addr.isChange && !addr.isHidden;
|
||||
}
|
||||
Map<String, dynamic> toJson() => _data.map(
|
||||
(addressType, v) => MapEntry(
|
||||
addressType.value,
|
||||
v.map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
seedBytesType.value,
|
||||
v.map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath.toString(),
|
||||
v.map(
|
||||
(isChange, v) => MapEntry(
|
||||
isChange.toString(),
|
||||
v.map((address) => address.toJSON()).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
bool getShouldHideAddress(Bip32Path path, BitcoinAddressType addressType) {
|
||||
if (walletSeedBytesType.isElectrum) {
|
||||
return path.toString() != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
|
||||
}
|
||||
static BitcoinAddressRecordMap fromJson(Map<String, dynamic> json) {
|
||||
final res = BitcoinAddressRecordMap();
|
||||
|
||||
return path.toString() !=
|
||||
BitcoinAddressUtils.getDerivationFromType(
|
||||
addressType,
|
||||
).derivationPath.toString();
|
||||
final mapped = json.map(
|
||||
(addressType, v) => MapEntry(
|
||||
BitcoinAddressType.fromValue(addressType),
|
||||
(v as Map<String, dynamic>).map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
SeedBytesType.fromValue(seedBytesType),
|
||||
(v as Map<String, dynamic>).map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath,
|
||||
(v as Map).map(
|
||||
(isChange, v) => MapEntry(
|
||||
isChange == 'true',
|
||||
(v as List<dynamic>)
|
||||
.map((address) => BaseBitcoinAddressRecord.fromJSON(address as String))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
res.addAll(mapped);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
// Maps by each different property with the final item being a boolean indicating addresses discovered
|
||||
typedef DiscoveredAddressRecordsBySeedType = Map<SeedBytesType, ItemsByDerivationPath<bool>>;
|
||||
|
||||
class BitcoinDiscoveredAddressesMap extends ItemsRecordMap<DiscoveredAddressRecordsBySeedType> {
|
||||
void addDiscovered({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
required String derivationPath,
|
||||
required BaseBitcoinAddressRecord addressRecord,
|
||||
required bool discovered,
|
||||
}) {
|
||||
_data.putIfAbsent(
|
||||
addressType,
|
||||
() => {
|
||||
seedBytesType: {
|
||||
derivationPath: {addressRecord.isChange: discovered},
|
||||
},
|
||||
},
|
||||
);
|
||||
_data[addressType]!.putIfAbsent(
|
||||
seedBytesType,
|
||||
() => {
|
||||
derivationPath: {addressRecord.isChange: discovered},
|
||||
},
|
||||
);
|
||||
_data[addressType]![seedBytesType]!.putIfAbsent(
|
||||
derivationPath,
|
||||
() => {addressRecord.isChange: discovered},
|
||||
);
|
||||
_data[addressType]![seedBytesType]![derivationPath]!
|
||||
.putIfAbsent(addressRecord.isChange, () => discovered);
|
||||
|
||||
_data[addressType]![seedBytesType]![derivationPath]![addressRecord.isChange] = discovered;
|
||||
}
|
||||
|
||||
bool getIsDiscovered({
|
||||
required BitcoinAddressType addressType,
|
||||
required SeedBytesType seedBytesType,
|
||||
required String derivationPath,
|
||||
required bool isChange,
|
||||
}) {
|
||||
return _data[addressType]?[seedBytesType]?[derivationPath]?[isChange] ?? false;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => _data.map(
|
||||
(addressType, v) => MapEntry(
|
||||
addressType.value,
|
||||
v.map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
seedBytesType.value,
|
||||
v.map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath.toString(),
|
||||
v.map(
|
||||
(isChange, v) => MapEntry(isChange.toString(), v),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
static BitcoinDiscoveredAddressesMap fromJson(Map<String, dynamic> json) {
|
||||
final res = BitcoinDiscoveredAddressesMap();
|
||||
|
||||
final mapped = json.map(
|
||||
(addressType, v) => MapEntry(
|
||||
BitcoinAddressType.fromValue(addressType),
|
||||
(v as Map<String, dynamic>).map(
|
||||
(seedBytesType, v) => MapEntry(
|
||||
SeedBytesType.fromValue(seedBytesType),
|
||||
(v as Map<String, dynamic>).map(
|
||||
(derivationPath, v) => MapEntry(
|
||||
derivationPath,
|
||||
(v as Map).map(
|
||||
(isChange, v) => MapEntry(isChange == 'true', (v as bool)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
res.addAll(mapped);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1742,7 +1742,6 @@ class ElectrumWorker {
|
|||
).toAddress(request.network),
|
||||
index: i,
|
||||
isChange: request.isChange,
|
||||
isHidden: request.shouldHideAddress || request.isChange,
|
||||
type: request.addressType,
|
||||
network: request.network,
|
||||
derivationInfo: request.derivationInfo,
|
||||
|
|
|
@ -5,7 +5,7 @@ class ElectrumWorkerDiscoverAddressesRequest implements ElectrumWorkerRequest {
|
|||
required this.count,
|
||||
required this.startIndex,
|
||||
required this.seedBytesType,
|
||||
required this.shouldHideAddress,
|
||||
required this.walletType,
|
||||
required this.derivationInfo,
|
||||
required this.isChange,
|
||||
required this.addressType,
|
||||
|
@ -20,8 +20,8 @@ class ElectrumWorkerDiscoverAddressesRequest implements ElectrumWorkerRequest {
|
|||
|
||||
final int count;
|
||||
final int startIndex;
|
||||
final bool shouldHideAddress;
|
||||
|
||||
final WalletType walletType;
|
||||
final SeedBytesType seedBytesType;
|
||||
final BitcoinDerivationInfo derivationInfo;
|
||||
final bool isChange;
|
||||
|
@ -38,7 +38,7 @@ class ElectrumWorkerDiscoverAddressesRequest implements ElectrumWorkerRequest {
|
|||
id: json['id'] as int,
|
||||
count: json['count'] as int,
|
||||
startIndex: json['startIndex'] as int,
|
||||
shouldHideAddress: json['shouldHideAddress'] as bool,
|
||||
walletType: deserializeFromInt(json['walletType'] as int),
|
||||
seedBytesType: SeedBytesType.fromValue(json['seedBytesType'] as String),
|
||||
derivationInfo:
|
||||
BitcoinDerivationInfo.fromJSON(json['derivationInfo'] as Map<String, dynamic>),
|
||||
|
@ -57,7 +57,7 @@ class ElectrumWorkerDiscoverAddressesRequest implements ElectrumWorkerRequest {
|
|||
'completed': completed,
|
||||
'count': count,
|
||||
'startIndex': startIndex,
|
||||
'shouldHideAddress': shouldHideAddress,
|
||||
'walletType': serializeToInt(walletType),
|
||||
'seedBytesType': seedBytesType.value,
|
||||
'isChange': isChange,
|
||||
'addressType': addressType.value,
|
||||
|
|
|
@ -95,7 +95,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet<LitecoinWalletAddresses
|
|||
}
|
||||
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
this.walletAddresses.isEnabledAutoGenerateNewAddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
reaction((_) => mwebSyncStatus, (status) async {
|
||||
if (mwebSyncStatus is FailedSyncStatus) {
|
||||
|
@ -762,7 +762,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet<LitecoinWalletAddresses
|
|||
@override
|
||||
@action
|
||||
Future<ElectrumWorkerListUnspentResponse?> updateAllUnspents([
|
||||
Set<String>? scripthashes,
|
||||
List<String>? scripthashes,
|
||||
bool? wait,
|
||||
]) async {
|
||||
if (!mwebEnabled) {
|
||||
|
@ -961,7 +961,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet<LitecoinWalletAddresses
|
|||
ECPrivate? privkey;
|
||||
|
||||
if (!isHardwareWallet) {
|
||||
final path = Bip32PathParser.parse(utx.bitcoinAddressRecord.derivationPath);
|
||||
final path = Bip32PathParser.parse(utx.bitcoinAddressRecord.indexedDerivationPath);
|
||||
privkey = ECPrivate.fromBip32(bip32: hdWallet.derive(path));
|
||||
}
|
||||
|
||||
|
@ -979,7 +979,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet<LitecoinWalletAddresses
|
|||
}
|
||||
|
||||
if (utx.bitcoinAddressRecord is BitcoinAddressRecord) {
|
||||
final derivationPath = utx.bitcoinAddressRecord.derivationPath;
|
||||
final derivationPath = utx.bitcoinAddressRecord.indexedDerivationPath;
|
||||
publicKeys[address.pubKeyHash()] = PublicKeyWithDerivationPath(pubKeyHex, derivationPath);
|
||||
}
|
||||
|
||||
|
@ -1128,7 +1128,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet<LitecoinWalletAddresses
|
|||
));
|
||||
|
||||
// Get Derivation path for change Address since it is needed in Litecoin and BitcoinCash hardware Wallets
|
||||
final changeDerivationPath = changeAddress.derivationPath;
|
||||
final changeDerivationPath = changeAddress.indexedDerivationPath;
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
PublicKeyWithDerivationPath('', changeDerivationPath);
|
||||
|
||||
|
|
|
@ -27,7 +27,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
required super.network,
|
||||
required super.isHardwareWallet,
|
||||
required this.mwebEnabled,
|
||||
super.initialAddresses,
|
||||
super.initialAddressesRecords,
|
||||
super.initialActiveAddressIndex,
|
||||
super.initialAddressPageType,
|
||||
List<LitecoinMWEBAddressRecord>? initialMwebAddresses,
|
||||
}) : mwebAddresses =
|
||||
ObservableList<LitecoinMWEBAddressRecord>.of((initialMwebAddresses ?? []).toSet()),
|
||||
|
@ -41,9 +43,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
}
|
||||
|
||||
@override
|
||||
final walletAddressTypes = LITECOIN_ADDRESS_TYPES;
|
||||
|
||||
static const LITECOIN_ADDRESS_TYPES = [SegwitAddressType.p2wpkh];
|
||||
final walletAddressTypes = [SegwitAddressType.p2wpkh];
|
||||
|
||||
final ObservableList<LitecoinMWEBAddressRecord> mwebAddresses;
|
||||
|
||||
|
@ -96,7 +96,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
.where((addr) => addr.type == addressType && addr.seedBytesType == seedBytesType)
|
||||
.toList();
|
||||
|
||||
if (existingAddresses.length < ElectrumWalletAddressesBase.defaultReceiveAddressesCount) {
|
||||
if (existingAddresses.length < ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT) {
|
||||
await discoverNewMWEBAddresses(
|
||||
seedBytesType: seedBytesType,
|
||||
isChange: false,
|
||||
|
@ -110,8 +110,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
required bool isChange,
|
||||
}) async {
|
||||
final count = isChange
|
||||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
|
||||
? ElectrumWalletAddressesBase.INITIAL_CHANGE_COUNT
|
||||
: ElectrumWalletAddressesBase.INITIAL_RECEIVE_COUNT;
|
||||
|
||||
final startIndex = this.mwebAddresses.length;
|
||||
|
||||
|
@ -325,7 +325,8 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
String get addressForExchange {
|
||||
// don't use mweb addresses for exchange refund address:
|
||||
final addresses = allAddresses.firstWhere(
|
||||
(element) => element.type == SegwitAddressType.p2wpkh && !getIsUsed(element),
|
||||
(addressRecord) =>
|
||||
addressRecord.type == SegwitAddressType.p2wpkh && !addressRecord.getIsUsed(),
|
||||
);
|
||||
return addresses.address;
|
||||
}
|
||||
|
@ -335,7 +336,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
super.updateAddressesInBox();
|
||||
|
||||
final lastP2wpkh =
|
||||
allAddresses.where((addressRecord) => isUnusedReceiveAddress(addressRecord)).toList().last;
|
||||
allAddresses.where((addressRecord) => addressRecord.isUnusedReceiveAddress()).toList().last;
|
||||
if (lastP2wpkh.address != address) {
|
||||
addressesMap[lastP2wpkh.address] = 'P2WPKH';
|
||||
} else {
|
||||
|
@ -343,7 +344,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
}
|
||||
|
||||
final lastMweb = mwebAddresses.firstWhere(
|
||||
(addressRecord) => isUnusedReceiveAddress(addressRecord),
|
||||
(addressRecord) => addressRecord.isUnusedReceiveAddress(),
|
||||
);
|
||||
if (lastMweb.address != address) {
|
||||
addressesMap[lastMweb.address] = 'MWEB';
|
||||
|
@ -445,9 +446,13 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
required bool isHardwareWallet,
|
||||
required bool mwebEnabled,
|
||||
}) {
|
||||
final initialAddresses = (snp['allAddresses'] as List)
|
||||
.map((record) => BitcoinAddressRecord.fromJSON(record as String))
|
||||
.toList();
|
||||
final electrumJson = ElectrumWalletAddressesBase.fromJson(
|
||||
snp,
|
||||
walletInfo,
|
||||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
);
|
||||
|
||||
final initialMwebAddresses = (snp['mwebAddresses'] as List)
|
||||
.map(
|
||||
|
@ -460,7 +465,9 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
|
|||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialAddressesRecords: electrumJson.addressesRecords,
|
||||
initialAddressPageType: electrumJson.addressPageType,
|
||||
initialActiveAddressIndex: electrumJson.activeIndexByType,
|
||||
initialMwebAddresses: initialMwebAddresses,
|
||||
mwebEnabled: mwebEnabled,
|
||||
);
|
||||
|
|
|
@ -54,7 +54,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
autorun((_) {
|
||||
this.walletAddresses.isEnabledAutoGenerateSubaddress = this.isEnabledAutoGenerateSubaddress;
|
||||
this.walletAddresses.isEnabledAutoGenerateNewAddress = this.isEnabledAutoGenerateSubaddress;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -17,43 +17,20 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
required super.hdWallets,
|
||||
required super.network,
|
||||
required super.isHardwareWallet,
|
||||
super.initialAddresses,
|
||||
super.initialAddressesRecords,
|
||||
super.initialActiveAddressIndex,
|
||||
super.initialAddressPageType,
|
||||
}) : super(walletInfo);
|
||||
|
||||
@override
|
||||
final walletAddressTypes = BITCOIN_CASH_ADDRESS_TYPES;
|
||||
|
||||
static const BITCOIN_CASH_ADDRESS_TYPES = [P2pkhAddressType.p2pkh];
|
||||
final walletAddressTypes = [P2pkhAddressType.p2pkh];
|
||||
|
||||
@override
|
||||
@observable
|
||||
BitcoinAddressType changeAddressType = P2pkhAddressType.p2pkh;
|
||||
|
||||
@override
|
||||
BitcoinAddressType get addressPageType => P2pkhAddressType.p2pkh;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
// for (final seedBytesType in hdWallets.keys) {
|
||||
// await generateInitialAddresses(
|
||||
// addressType: P2pkhAddressType.p2pkh,
|
||||
// seedBytesType: seedBytesType,
|
||||
// bitcoinDerivationInfo: BitcoinDerivationInfos.BCH,
|
||||
// );
|
||||
// }
|
||||
await super.init();
|
||||
}
|
||||
|
||||
@override
|
||||
bool getShouldHideAddress(Bip32Path path, BitcoinAddressType addressType) {
|
||||
if (walletSeedBytesType.isElectrum) {
|
||||
return path.toString() != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
|
||||
}
|
||||
|
||||
return path.toString() != BitcoinDerivationPaths.BCH;
|
||||
}
|
||||
|
||||
static BitcoinCashWalletAddressesBase fromJson(
|
||||
Map<String, dynamic> json,
|
||||
WalletInfo walletInfo, {
|
||||
|
@ -62,6 +39,14 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
required bool isHardwareWallet,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
}) {
|
||||
final electrumJson = ElectrumWalletAddressesBase.fromJson(
|
||||
json,
|
||||
walletInfo,
|
||||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
);
|
||||
|
||||
initialAddresses ??= (json['allAddresses'] as List).map((addr) {
|
||||
try {
|
||||
BitcoinCashAddress(addr.address);
|
||||
|
@ -92,7 +77,9 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
hdWallets: hdWallets,
|
||||
network: network,
|
||||
isHardwareWallet: isHardwareWallet,
|
||||
initialAddresses: initialAddresses,
|
||||
initialAddressesRecords: electrumJson.addressesRecords,
|
||||
initialAddressPageType: electrumJson.addressPageType,
|
||||
initialActiveAddressIndex: electrumJson.activeIndexByType,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,8 +78,4 @@ abstract class WalletAddresses {
|
|||
|
||||
bool containsAddress(String address) =>
|
||||
addressesMap.containsKey(address) || allAddressesMap.containsKey(address);
|
||||
|
||||
void resetActiveChangeAddress() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ class CWBitcoin extends Bitcoin {
|
|||
id: addr.index,
|
||||
name: addr.name,
|
||||
address: addr.address,
|
||||
derivationPath: addr.derivationPath,
|
||||
derivationPath: addr.indexedDerivationPath,
|
||||
txCount: addr.txCount,
|
||||
balance: addr.balance,
|
||||
isChange: addr.isChange,
|
||||
|
@ -291,10 +291,15 @@ class CWBitcoin extends Bitcoin {
|
|||
@override
|
||||
TransactionPriority getLitecoinTransactionPrioritySlow() => ElectrumTransactionPriority.slow;
|
||||
|
||||
@override
|
||||
void resetActiveAddress(Object wallet) {
|
||||
(wallet as ElectrumWallet).walletAddresses.resetActiveAddress();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setAddressType(Object wallet, dynamic option) async {
|
||||
final bitcoinWallet = wallet as ElectrumWallet;
|
||||
await bitcoinWallet.walletAddresses.setAddressType(option as BitcoinAddressType);
|
||||
await bitcoinWallet.walletAddresses.setAddressPageType(option as BitcoinAddressType);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -530,7 +535,7 @@ class CWBitcoin extends Bitcoin {
|
|||
id: addr.index,
|
||||
name: addr.name,
|
||||
address: addr.address,
|
||||
derivationPath: addr.derivationPath,
|
||||
derivationPath: addr.indexedDerivationPath,
|
||||
txCount: addr.txCount,
|
||||
balance: addr.balance,
|
||||
isChange: addr.isChange,
|
||||
|
@ -547,7 +552,7 @@ class CWBitcoin extends Bitcoin {
|
|||
id: addr.index,
|
||||
name: addr.name,
|
||||
address: addr.address,
|
||||
derivationPath: addr.derivationPath,
|
||||
derivationPath: addr.indexedDerivationPath,
|
||||
txCount: addr.txCount,
|
||||
balance: addr.balance,
|
||||
isChange: addr.isChange,
|
||||
|
|
|
@ -98,7 +98,7 @@ class AddressPage extends BasePage {
|
|||
|
||||
@override
|
||||
Widget body(BuildContext context) {
|
||||
addressListViewModel.resetActiveChangeAddress();
|
||||
addressListViewModel.resetActiveAddress();
|
||||
|
||||
_setEffects(context);
|
||||
|
||||
|
@ -162,7 +162,8 @@ class AddressPage extends BasePage {
|
|||
}
|
||||
|
||||
reaction((_) => receiveOptionViewModel.selectedReceiveOption, (ReceivePageOption option) {
|
||||
if (dashboardViewModel.type == WalletType.bitcoin && bitcoin!.isBitcoinReceivePageOption(option)) {
|
||||
if (dashboardViewModel.type == WalletType.bitcoin &&
|
||||
bitcoin!.isBitcoinReceivePageOption(option)) {
|
||||
addressListViewModel.setAddressType(bitcoin!.getOptionToType(option));
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -301,10 +301,10 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo
|
|||
WalletType get type => wallet.type;
|
||||
|
||||
@action
|
||||
void resetActiveChangeAddress() {
|
||||
try {
|
||||
wallet.walletAddresses.resetActiveChangeAddress();
|
||||
} catch (_) {}
|
||||
void resetActiveAddress() {
|
||||
if (isElectrumWallet) {
|
||||
bitcoin!.resetActiveAddress(wallet);
|
||||
}
|
||||
}
|
||||
|
||||
@computed
|
||||
|
|
|
@ -127,23 +127,6 @@ import 'package:mobx/mobx.dart';
|
|||
""";
|
||||
const bitcoinCwPart = "part 'cw_bitcoin.dart';";
|
||||
const bitcoinContent = """
|
||||
const List<BitcoinAddressType> BITCOIN_ADDRESS_TYPES = [
|
||||
SegwitAddressType.p2wpkh,
|
||||
P2pkhAddressType.p2pkh,
|
||||
SegwitAddressType.p2tr,
|
||||
SegwitAddressType.p2wsh,
|
||||
P2shAddressType.p2wpkhInP2sh,
|
||||
];
|
||||
|
||||
const List<BitcoinAddressType> LITECOIN_ADDRESS_TYPES = [
|
||||
SegwitAddressType.p2wpkh,
|
||||
SegwitAddressType.mweb,
|
||||
];
|
||||
|
||||
const List<BitcoinAddressType> BITCOIN_CASH_ADDRESS_TYPES = [
|
||||
P2pkhAddressType.p2pkh,
|
||||
];
|
||||
|
||||
class ElectrumSubAddress {
|
||||
ElectrumSubAddress({
|
||||
required this.id,
|
||||
|
@ -232,6 +215,7 @@ abstract class Bitcoin {
|
|||
Future<List<DerivationType>> compareDerivationMethods(
|
||||
{required String mnemonic, required Node node});
|
||||
Map<DerivationType, List<DerivationInfo>> getElectrumDerivations();
|
||||
void resetActiveAddress(Object wallet);
|
||||
Future<void> setAddressType(Object wallet, dynamic option);
|
||||
ReceivePageOption getSelectedAddressType(Object wallet);
|
||||
List<ReceivePageOption> getBitcoinReceivePageOptions();
|
||||
|
@ -978,15 +962,12 @@ abstract class BitcoinCash {
|
|||
""";
|
||||
|
||||
const bitcoinCashEmptyDefinition = 'BitcoinCash? bitcoinCash;\n';
|
||||
const bitcoinCashCWDefinition =
|
||||
'BitcoinCash? bitcoinCash = CWBitcoinCash();\n';
|
||||
const bitcoinCashCWDefinition = 'BitcoinCash? bitcoinCash = CWBitcoinCash();\n';
|
||||
|
||||
final output = '$bitcoinCashCommonHeaders\n' +
|
||||
(hasImplementation ? '$bitcoinCashCWHeaders\n' : '\n') +
|
||||
(hasImplementation ? '$bitcoinCashCwPart\n\n' : '\n') +
|
||||
(hasImplementation
|
||||
? bitcoinCashCWDefinition
|
||||
: bitcoinCashEmptyDefinition) +
|
||||
(hasImplementation ? bitcoinCashCWDefinition : bitcoinCashEmptyDefinition) +
|
||||
'\n' +
|
||||
bitcoinCashContent;
|
||||
|
||||
|
@ -1121,8 +1102,7 @@ abstract class NanoUtil {
|
|||
""";
|
||||
|
||||
const nanoEmptyDefinition = 'Nano? nano;\nNanoUtil? nanoUtil;\n';
|
||||
const nanoCWDefinition =
|
||||
'Nano? nano = CWNano();\nNanoUtil? nanoUtil = CWNanoUtil();\n';
|
||||
const nanoCWDefinition = 'Nano? nano = CWNano();\nNanoUtil? nanoUtil = CWNanoUtil();\n';
|
||||
|
||||
final output = '$nanoCommonHeaders\n' +
|
||||
(hasImplementation ? '$nanoCWHeaders\n' : '\n') +
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue