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