feat: fix missing addrs

This commit is contained in:
Rafael Saes 2025-02-14 15:53:45 -03:00
parent 4015d32e97
commit da37e11cfb
10 changed files with 336 additions and 173 deletions

View file

@ -16,6 +16,7 @@ abstract class BaseBitcoinAddressRecord {
String name = '',
bool isUsed = false,
required this.type,
this.seedBytesType,
bool? isHidden,
}) : _txCount = txCount,
_balance = balance,
@ -62,6 +63,10 @@ abstract class BaseBitcoinAddressRecord {
BitcoinAddressType type;
final SeedBytesType? seedBytesType;
String get derivationPath => '';
String toJSON() => json.encode({
'address': address,
'index': index,
@ -73,6 +78,8 @@ abstract class BaseBitcoinAddressRecord {
'balance': balance,
'type': type.toString(),
'runtimeType': runtimeType.toString(),
'seedBytesType': seedBytesType?.value,
'derivationPath': derivationPath,
});
static BaseBitcoinAddressRecord fromJSON(
@ -98,15 +105,19 @@ abstract class BaseBitcoinAddressRecord {
class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
final BitcoinDerivationInfo derivationInfo;
final SeedBytesType seedBytesType;
String _derivationPath = '';
@override
String get derivationPath => _derivationPath;
BitcoinAddressRecord(
super.address, {
required super.index,
required this.derivationInfo,
required this.seedBytesType,
super.seedBytesType,
super.isHidden,
super.isChange = false,
required super.isChange,
super.txCount = 0,
super.balance = 0,
super.name = '',
@ -114,6 +125,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
required super.type,
String? scriptHash,
BasedUtxoNetwork? network,
String? derivationPath,
}) {
if (scriptHash != null) {
this.scriptHash = scriptHash;
@ -122,6 +134,14 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
} else {
throw ArgumentError('either scriptHash or network must be provided');
}
if (derivationPath == null)
_derivationPath = derivationInfo.derivationPath
.addElem(Bip32KeyIndex(isChange ? 1 : 0))
.addElem(Bip32KeyIndex(index))
.toString();
else
_derivationPath = derivationPath;
}
factory BitcoinAddressRecord.fromJSON(
@ -131,18 +151,18 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
]) {
final decoded = json.decode(jsonSource) as Map;
final derivationInfoSnp = decoded['derivationInfo'] as Map<String, dynamic>?;
final derivationTypeSnp = decoded['derivationType'] as int?;
final cwDerivationType = derivationTypeSnp != null
? SeedBytesType.values[derivationTypeSnp]
: derivationInfo!.derivationType == DerivationType.bip39
final seedBytesTypeSnp = decoded['seedBytesType'] as String?;
final seedBytesType = seedBytesTypeSnp == null
? derivationInfo!.derivationType == DerivationType.bip39
? SeedBytesType.old_bip39
: SeedBytesType.old_electrum;
: SeedBytesType.old_electrum
: SeedBytesType.fromValue(seedBytesTypeSnp.toString());
return BitcoinAddressRecord(
decoded['address'] as String,
index: decoded['index'] as int,
derivationInfo: derivationInfoSnp == null
? [SeedBytesType.bip39, SeedBytesType.old_bip39].contains(cwDerivationType)
? !seedBytesType.isElectrum
? BitcoinDerivationInfo.fromDerivationAndAddress(
BitcoinDerivationType.bip39,
decoded['address'] as String,
@ -150,7 +170,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
)
: BitcoinDerivationInfos.ELECTRUM
: BitcoinDerivationInfo.fromJSON(derivationInfoSnp),
seedBytesType: cwDerivationType,
seedBytesType: seedBytesType,
isHidden: decoded['isHidden'] as bool? ?? false,
isChange: decoded['isChange'] as bool? ?? false,
isUsed: decoded['isUsed'] as bool? ?? false,
@ -171,7 +191,6 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
String toJSON() {
final m = json.decode(super.toJSON()) as Map<String, dynamic>;
m['derivationInfo'] = derivationInfo.toJSON();
m['derivationType'] = seedBytesType.index;
m['scriptHash'] = scriptHash;
return json.encode(m);
}
@ -202,6 +221,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
String _derivationPath;
@override
String get derivationPath => _derivationPath;
int get labelIndex => index;
@ -219,6 +239,7 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
super.isUsed = false,
super.type = SilentPaymentsAddresType.p2sp,
required super.isChange,
super.seedBytesType,
super.isHidden,
this.labelHex,
}) : _derivationPath = derivationPath,
@ -247,13 +268,13 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
labelHex: decoded['labelHex'] as String?,
isChange: decoded['isChange'] as bool? ?? false,
isHidden: decoded['isHidden'] as bool?,
seedBytesType: SeedBytesType.fromValue(decoded['seedBytesType'] as String),
);
}
@override
String toJSON() {
final m = json.decode(super.toJSON()) as Map<String, dynamic>;
m['derivationPath'] = _derivationPath;
m['index'] = labelIndex;
m['labelHex'] = labelHex;
return json.encode(m);
@ -264,6 +285,9 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
final String tweak;
final String spAddress;
@override
String get derivationPath => '';
BitcoinReceivedSPAddressRecord(
super.address, {
required super.labelIndex,
@ -274,6 +298,7 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
required this.tweak,
required this.spAddress,
required super.isChange,
super.seedBytesType,
super.labelHex,
}) : super(isHidden: true, type: SegwitAddressType.p2tr);
@ -310,6 +335,9 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
tweak: decoded['tweak'] as String? ?? decoded['silent_payment_tweak'] as String? ?? '',
isChange: decoded['isChange'] as bool? ?? false,
spAddress: decoded['spAddress'] as String? ?? '',
seedBytesType: (decoded['seedBytesType'] as String?) == null
? null
: SeedBytesType.fromValue(decoded['seedBytesType'] as String),
);
}
@ -323,9 +351,15 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
}
class LitecoinMWEBAddressRecord extends BaseBitcoinAddressRecord {
String _derivationPath = '';
@override
String get derivationPath => _derivationPath;
LitecoinMWEBAddressRecord(
super.address, {
required super.index,
super.seedBytesType,
super.isHidden,
super.isChange = false,
super.txCount = 0,
@ -333,7 +367,11 @@ class LitecoinMWEBAddressRecord extends BaseBitcoinAddressRecord {
super.name = '',
super.isUsed = false,
BasedUtxoNetwork? network,
}) : super(type: SegwitAddressType.mweb);
}) : super(type: SegwitAddressType.mweb) {
var mwebPath = BitcoinDerivationInfos.LITECOIN_MWEB.derivationPath;
_derivationPath = mwebPath.addElem(Bip32KeyIndex(index)).toString();
}
factory LitecoinMWEBAddressRecord.fromJSON(String jsonSource) {
final decoded = json.decode(jsonSource) as Map;
@ -347,6 +385,9 @@ class LitecoinMWEBAddressRecord extends BaseBitcoinAddressRecord {
txCount: decoded['txCount'] as int? ?? 0,
name: decoded['name'] as String? ?? '',
balance: decoded['balance'] as int? ?? 0,
seedBytesType: (decoded['seedBytesType'] as String?) == null
? null
: SeedBytesType.fromValue(decoded['seedBytesType'] as String),
);
}

View file

@ -4,12 +4,17 @@ import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_core/wallet_info.dart';
class BitcoinUnspent extends Unspent {
BitcoinUnspent(BaseBitcoinAddressRecord addressRecord, String hash, int value, int vout)
: bitcoinAddressRecord = addressRecord,
super(addressRecord.address, hash, value, vout, null);
BitcoinUnspent(
BaseBitcoinAddressRecord addressRecord,
String hash,
int value,
int vout,
int? height,
) : bitcoinAddressRecord = addressRecord,
super(addressRecord.address, hash, value, vout, null, height: height);
factory BitcoinUnspent.fromUTXO(BaseBitcoinAddressRecord address, ElectrumUtxo utxo) =>
BitcoinUnspent(address, utxo.txId, utxo.value.toInt(), utxo.vout);
BitcoinUnspent(address, utxo.txId, utxo.value.toInt(), utxo.vout, utxo.height);
factory BitcoinUnspent.fromJSON(
BaseBitcoinAddressRecord? address,
@ -27,6 +32,7 @@ class BitcoinUnspent extends Unspent {
json['tx_hash'] as String,
int.parse(json['value'].toString()),
int.parse(json['tx_pos'].toString()),
json['height'] as int?,
);
}
@ -36,6 +42,7 @@ class BitcoinUnspent extends Unspent {
'tx_hash': hash,
'value': value,
'tx_pos': vout,
'height': height,
};
return json;
}

View file

@ -302,58 +302,6 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
super.address = addr;
}
@override
BitcoinBaseAddress generateAddress({
required SeedBytesType seedBytesType,
required bool isChange,
required int index,
required BitcoinAddressType addressType,
required BitcoinDerivationInfo derivationInfo,
}) {
final hdWallet = hdWallets[seedBytesType]!;
switch (addressType) {
case P2pkhAddressType.p2pkh:
return P2pkhAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case SegwitAddressType.p2tr:
return P2trAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case SegwitAddressType.p2wsh:
return P2wshAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case P2shAddressType.p2wpkhInP2sh:
return P2shAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
type: P2shAddressType.p2wpkhInP2sh,
);
case SegwitAddressType.p2wpkh:
return P2wpkhAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
default:
throw ArgumentError('Invalid address type');
}
}
@override
@action
BaseBitcoinAddressRecord generateNewAddress({String label = ''}) {

View file

@ -25,7 +25,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}) : _allAddresses = ObservableList.of(initialAddresses ?? []),
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
_addressPageType = initialAddressPageType ??
addressPageType = initialAddressPageType ??
(walletInfo.addressPageType != null
? BitcoinAddressType.fromValue(walletInfo.addressPageType!)
: SegwitAddressType.p2wpkh),
@ -48,11 +48,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final Map<SeedBytesType, Bip32Slip10Secp256k1> hdWallets;
Bip32Slip10Secp256k1 get hdWallet =>
hdWallets[SeedBytesType.bip39] ?? hdWallets[SeedBytesType.electrum]!;
bool get seedTypeIsElectrum =>
hdWallets[SeedBytesType.bip39] == null && hdWallets[SeedBytesType.electrum] != null;
final bool isHardwareWallet;
@observable
late BitcoinAddressType _addressPageType;
BitcoinAddressType addressPageType;
@computed
List<BitcoinAddressRecord> get allChangeAddresses =>
@ -72,9 +74,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
]) =>
(isChange ? changeAddressesByType[type] : receiveAddressesByType[type]) ?? [];
@computed
BitcoinAddressType get addressPageType => _addressPageType;
@computed
List<BitcoinAddressRecord> get allAddresses => _allAddresses.toList();
@ -176,18 +175,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
Map<String, int> currentReceiveAddressIndexByType;
int get currentReceiveAddressIndex =>
currentReceiveAddressIndexByType[_addressPageType.toString()] ?? 0;
currentReceiveAddressIndexByType[addressPageType.toString()] ?? 0;
void set currentReceiveAddressIndex(int index) =>
currentReceiveAddressIndexByType[_addressPageType.toString()] = index;
currentReceiveAddressIndexByType[addressPageType.toString()] = index;
Map<String, int> currentChangeAddressIndexByType;
int get currentChangeAddressIndex =>
currentChangeAddressIndexByType[_addressPageType.toString()] ?? 0;
currentChangeAddressIndexByType[addressPageType.toString()] ?? 0;
void set currentChangeAddressIndex(int index) =>
currentChangeAddressIndexByType[_addressPageType.toString()] = index;
currentChangeAddressIndexByType[addressPageType.toString()] = index;
SeedBytesType getHDWalletType() {
if (hdWallets.containsKey(SeedBytesType.bip39)) {
@ -246,7 +245,48 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
required BitcoinAddressType addressType,
required BitcoinDerivationInfo derivationInfo,
}) {
throw UnimplementedError();
final hdWallet = hdWallets[seedBytesType]!;
switch (addressType) {
case P2pkhAddressType.p2pkh:
return P2pkhAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case SegwitAddressType.p2tr:
return P2trAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case SegwitAddressType.p2wsh:
return P2wshAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
case P2shAddressType.p2wpkhInP2sh:
return P2shAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
type: P2shAddressType.p2wpkhInP2sh,
);
case SegwitAddressType.p2wpkh:
return P2wpkhAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
default:
throw ArgumentError('Invalid address type');
}
}
String getAddress({
@ -342,11 +382,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final newAddresses = <BitcoinAddressRecord>[];
final isHidden = seedBytesType.isElectrum
? derivationInfo.derivationPath.toString() !=
BitcoinDerivationInfos.ELECTRUM.derivationPath.toString()
: derivationInfo.derivationPath.toString() !=
BitcoinDerivationInfos.BIP84.derivationPath.toString();
final isHidden = getShouldHideAddress(derivationInfo.derivationPath);
for (var i = startIndex; i < count + startIndex; i++) {
final address = BitcoinAddressRecord(
@ -445,15 +481,15 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
@action
void updateHiddenAddresses() {
this.hiddenAddresses.clear();
this.hiddenAddresses.addAll(_allAddresses
hiddenAddresses.clear();
hiddenAddresses.addAll(_allAddresses
.where((addressRecord) => !getIsReceive(addressRecord))
.map((addressRecord) => addressRecord.address));
}
@action
Future<void> setAddressType(BitcoinAddressType type) async {
_addressPageType = type;
addressPageType = type;
updateAddressesByType();
walletInfo.addressPageType = addressPageType.toString();
await walletInfo.save();
@ -549,7 +585,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
for (final usedAddress in usedAddresses) {
final isChange = usedAddress.isChange;
final alreadyDiscoveredSeedType = discoveredAddresses[usedAddress.seedBytesType];
final alreadyDiscoveredSeedType = discoveredAddresses[usedAddress.seedBytesType!];
final alreadyDiscoveredAddressType = alreadyDiscoveredSeedType?[usedAddress.type];
final alreadyDiscoveredDerivationType =
alreadyDiscoveredAddressType?[usedAddress.derivationInfo.derivationType];
@ -561,7 +597,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final matchingAddressList = allAddresses.where(
(addr) =>
addr.seedBytesType == usedAddress.seedBytesType &&
addr.seedBytesType! == usedAddress.seedBytesType! &&
addr.type == usedAddress.type &&
addr.derivationInfo.derivationType == usedAddress.derivationInfo.derivationType &&
addr.isChange == isChange,
@ -573,17 +609,17 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final isAddressUsedAboveGap = usedAddress.index >= totalMatchingAddresses - matchingGapLimit;
if (isAddressUsedAboveGap) {
discoveredAddresses.putIfAbsent(usedAddress.seedBytesType, () => {});
discoveredAddresses[usedAddress.seedBytesType]!.putIfAbsent(usedAddress.type, () => {});
discoveredAddresses[usedAddress.seedBytesType]![usedAddress.type]!
discoveredAddresses.putIfAbsent(usedAddress.seedBytesType!, () => {});
discoveredAddresses[usedAddress.seedBytesType!]!.putIfAbsent(usedAddress.type, () => {});
discoveredAddresses[usedAddress.seedBytesType!]![usedAddress.type]!
.putIfAbsent(usedAddress.derivationInfo.derivationType, () => []);
discoveredAddresses[usedAddress.seedBytesType]![usedAddress.type]![
discoveredAddresses[usedAddress.seedBytesType!]![usedAddress.type]![
usedAddress.derivationInfo.derivationType]!
.add(isChange);
final theseAddresses = await discoverNewAddresses(
isChange: isChange,
seedBytesType: usedAddress.seedBytesType,
seedBytesType: usedAddress.seedBytesType!,
addressType: usedAddress.type,
derivationInfo: usedAddress.derivationInfo,
);
@ -591,7 +627,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
final newMatchingAddressList = allAddresses.where(
(addr) =>
addr.seedBytesType == usedAddress.seedBytesType &&
addr.seedBytesType == usedAddress.seedBytesType! &&
addr.type == usedAddress.type &&
addr.derivationInfo.derivationType == usedAddress.derivationInfo.derivationType &&
addr.isChange == isChange,
@ -599,7 +635,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
printV(
"discovered ${theseAddresses.length} new ${isChange ? "change" : "receive"} addresses");
printV(
"Of type ${usedAddress.type} and derivation type ${usedAddress.seedBytesType}, new total: ${newMatchingAddressList.length}");
"Of type ${usedAddress.type} and derivation type ${usedAddress.seedBytesType!}, new total: ${newMatchingAddressList.length}");
}
}
@ -608,4 +644,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
return newAddresses;
}
bool getShouldHideAddress(Bip32Path path) {
if (seedTypeIsElectrum) {
return path.toString() != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
}
return path.toString() != BitcoinDerivationInfos.BIP84.derivationPath.toString();
}
}

View file

@ -1581,7 +1581,13 @@ class ElectrumWorker {
spAddress: matchingSPWallet.toAddress(scanData.network),
);
final unspent = BitcoinUnspent(receivedAddressRecord, txid, amount, pos);
final unspent = BitcoinUnspent(
receivedAddressRecord,
txid,
amount,
pos,
tweakHeight,
);
unspents.add(unspent);
txInfo.amount += unspent.value;

View file

@ -783,6 +783,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
outputId,
utxo.value.toInt(),
mwebAddrs.indexOf(utxo.address),
utxo.height,
);
if (unspent.vout == 0) {
unspent.isChange = true;
@ -1460,6 +1461,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
return tx;
}
final status = await CwMweb.status(StatusRequest());
// check if any of the inputs of this transaction are hog-ex:
// this list is only non-mweb inputs:
tx2.inputs.forEach((txInput) {
@ -1475,6 +1478,10 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
}
int confirmations = utxo.confirmations ?? 0;
if (confirmations == 0 && utxo.height != null) {
confirmations = status.mwebUtxosHeight - utxo.height!;
}
if (confirmations < 6) {
throw Exception(
"A transaction input has less than 6 confirmations, please try again later.");

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:io' show Platform;
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
import 'package:cw_bitcoin/bitcoin_address_record.dart';
@ -52,6 +53,12 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
List<String> mwebAddrs = [];
bool generating = false;
@observable
int mwebIndex = 0;
@observable
LitecoinMWEBAddressRecord? activeMwebAddress;
List<int> get scanSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
List<int> get spendPubkey =>
mwebHd!.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed;
@ -60,16 +67,19 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
Future<void> init() async {
if (!super.isHardwareWallet) await initMwebAddresses();
for (final derivationType in hdWallets.keys) {
for (final seedBytesType in hdWallets.keys) {
await generateInitialAddresses(
addressType: P2pkhAddressType.p2pkh,
seedBytesType: derivationType,
addressType: SegwitAddressType.p2wpkh,
seedBytesType: seedBytesType,
bitcoinDerivationInfo: seedBytesType.isElectrum
? BitcoinDerivationInfos.ELECTRUM
: BitcoinDerivationInfos.LITECOIN,
);
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
await generateInitialAddresses(
await generateInitialMWEBAddresses(
addressType: SegwitAddressType.mweb,
seedBytesType: derivationType,
seedBytesType: seedBytesType,
);
}
}
@ -77,6 +87,50 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
await super.init();
}
@action
Future<void> generateInitialMWEBAddresses({
required BitcoinAddressType addressType,
required SeedBytesType seedBytesType,
}) async {
final existingAddresses = mwebAddresses
.where((addr) => addr.type == addressType && addr.seedBytesType == seedBytesType)
.toList();
if (existingAddresses.length < ElectrumWalletAddressesBase.defaultReceiveAddressesCount) {
await discoverNewMWEBAddresses(
seedBytesType: seedBytesType,
isChange: false,
);
}
}
@action
Future<List<LitecoinMWEBAddressRecord>> discoverNewMWEBAddresses({
required SeedBytesType seedBytesType,
required bool isChange,
}) async {
final count = isChange
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
final startIndex = this.mwebAddresses.length;
final mwebAddresses = <LitecoinMWEBAddressRecord>[];
for (var i = startIndex; i < count + startIndex; i++) {
final address = LitecoinMWEBAddressRecord(
(await generateMWEBAddress(index: i)).toAddress(network),
index: i,
seedBytesType: seedBytesType,
);
mwebAddresses.add(address);
}
addMwebAddresses(mwebAddresses);
return mwebAddresses;
}
@override
@action
Future<List<BitcoinAddressRecord>> discoverNewAddresses({
required SeedBytesType seedBytesType,
@ -84,52 +138,40 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required BitcoinAddressType addressType,
required BitcoinDerivationInfo derivationInfo,
}) async {
final count = isChange
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
if (addressType == SegwitAddressType.mweb) {
final count = isChange
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount;
final startIndex = getAddressesByType(addressType, isChange)
.where((addr) => (addr as BitcoinAddressRecord).seedBytesType == seedBytesType)
.length;
final startIndex = this.mwebAddresses.length;
final mwebAddresses = <LitecoinMWEBAddressRecord>[];
final newAddresses = <BitcoinAddressRecord>[];
final mwebAddresses = <LitecoinMWEBAddressRecord>[];
final isHidden = seedBytesType.isElectrum
? derivationInfo.derivationPath != BitcoinDerivationInfos.ELECTRUM.derivationPath
: derivationInfo.derivationPath != BitcoinDerivationInfos.BIP84.derivationPath;
for (var i = startIndex; i < count + startIndex; i++) {
final addressString = await getAddressAsync(
derivationType: seedBytesType,
isChange: isChange,
index: i,
addressType: addressType,
derivationInfo: derivationInfo,
);
if (addressType == SegwitAddressType.mweb) {
final address = LitecoinMWEBAddressRecord(addressString, index: i);
mwebAddresses.add(address);
} else {
final address = BitcoinAddressRecord(
addressString,
for (var i = startIndex; i < count + startIndex; i++) {
final address = LitecoinMWEBAddressRecord(
await getAddressAsync(
derivationType: seedBytesType,
isChange: isChange,
index: i,
addressType: addressType,
derivationInfo: derivationInfo,
),
index: i,
isChange: isChange,
isHidden: isHidden || isChange,
type: addressType,
network: network,
derivationInfo: derivationInfo,
seedBytesType: seedBytesType,
);
newAddresses.add(address);
mwebAddresses.add(address);
}
addMwebAddresses(mwebAddresses);
// TODO:
return [];
}
addAddresses(newAddresses);
addMwebAddresses(mwebAddresses);
return newAddresses;
return super.discoverNewAddresses(
seedBytesType: seedBytesType,
isChange: isChange,
addressType: addressType,
derivationInfo: derivationInfo,
);
}
Future<void> ensureMwebAddressUpToIndexExists(int index) async {
@ -189,6 +231,11 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
}
}
Future<BitcoinBaseAddress> generateMWEBAddress({required int index}) async {
await ensureMwebAddressUpToIndexExists(index);
return MwebAddress.fromAddress(address: mwebAddrs[index]);
}
@override
BitcoinBaseAddress generateAddress({
required SeedBytesType seedBytesType,
@ -285,11 +332,46 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
}
@override
@computed
String get address {
if (addressPageType == SegwitAddressType.mweb) {
if (activeMwebAddress != null) {
return activeMwebAddress!.address;
}
return mwebAddresses[0].address;
}
return super.address;
}
@override
set address(String addr) {
if (addressPageType == SegwitAddressType.mweb) {
final selected =
mwebAddresses.firstWhereOrNull((addressRecord) => addressRecord.address == addr) ??
mwebAddresses[0];
activeMwebAddress = selected;
if (!selected.isChange) {
mwebIndex = selected.index;
}
return;
}
super.address = addr;
}
@override
@computed
String get addressForExchange {
// don't use mweb addresses for exchange refund address:
final addresses = selectedReceiveAddresses
.where((element) => element.type == SegwitAddressType.p2wpkh && !element.isUsed);
return addresses.first.address;
final addresses = allAddresses.firstWhere(
(element) => element.type == SegwitAddressType.p2wpkh && getIsUsed(element),
);
return addresses.address;
}
@override
@ -344,10 +426,15 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
@action
void addMwebAddresses(Iterable<LitecoinMWEBAddressRecord> addresses) {
final addressesSet = this.mwebAddresses.toSet();
addressesSet.addAll(addresses);
this.mwebAddresses.clear();
this.mwebAddresses.addAll(addressesSet);
final newMwebAddresses = <LitecoinMWEBAddressRecord>[];
for (final address in addresses) {
if (mwebAddresses.any((existing) => existing.address == address.address)) {
continue;
}
newMwebAddresses.add(address);
}
this.mwebAddresses.addAll(newMwebAddresses);
updateAddressesByType();
}
@ -408,4 +495,20 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
mwebEnabled: true, // TODO
);
}
@override
@action
void updateAddressesByType() {
receiveAddressesByType[SegwitAddressType.mweb] = mwebAddresses.toList();
super.updateAddressesByType();
}
@override
bool getShouldHideAddress(Bip32Path path) {
if (seedTypeIsElectrum) {
return path.toString() != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
}
return path.toString() != BitcoinDerivationInfos.LITECOIN.derivationPath.toString();
}
}

View file

@ -26,32 +26,30 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
static const BITCOIN_CASH_ADDRESS_TYPES = [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: BitcoinDerivationInfo(
derivationType: BitcoinDerivationType.bip39,
derivationPath: "m/44'/145'/0'",
description: "Default Bitcoin Cash",
scriptType: P2pkhAddressType.p2pkh,
),
);
}
await super.init();
}
@override
BitcoinBaseAddress generateAddress({
required SeedBytesType seedBytesType,
required bool isChange,
required int index,
required BitcoinAddressType addressType,
required BitcoinDerivationInfo derivationInfo,
}) =>
P2pkhAddress.fromDerivation(
bip32: hdWallet,
derivationInfo: derivationInfo,
isChange: isChange,
index: index,
);
static BitcoinCashWalletAddressesBase fromJson(
Map<String, dynamic> json,
WalletInfo walletInfo, {
@ -93,4 +91,13 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
initialAddresses: initialAddresses,
);
}
@override
bool getShouldHideAddress(Bip32Path path) {
if (seedTypeIsElectrum) {
return path.toString() != BitcoinDerivationInfos.ELECTRUM.derivationPath.toString();
}
return path.toString() != "m/44'/145'/0'";
}
}

View file

@ -1,8 +1,14 @@
import 'package:cw_core/unspent_comparable_mixin.dart';
class Unspent with UnspentComparable {
Unspent(this.address, this.hash, this.value, this.vout, this.keyImage)
: isSending = true,
Unspent(
this.address,
this.hash,
this.value,
this.vout,
this.keyImage, {
this.height,
}) : isSending = true,
isFrozen = false,
isChange = false,
note = '';
@ -16,6 +22,7 @@ class Unspent with UnspentComparable {
bool isChange;
bool isSending;
bool isFrozen;
int? height;
int? confirmations;
String note;

View file

@ -162,14 +162,7 @@ class CWBitcoin extends Bitcoin {
id: addr.index,
name: addr.name,
address: addr.address,
derivationPath: (addr as BitcoinAddressRecord)
.derivationInfo
.derivationPath
.addElem(
Bip32KeyIndex(addr.isChange ? 1 : 0),
)
.addElem(Bip32KeyIndex(addr.index))
.toString(),
derivationPath: addr.derivationPath,
txCount: addr.txCount,
balance: addr.balance,
isChange: addr.isChange,
@ -560,7 +553,7 @@ class CWBitcoin extends Bitcoin {
id: addr.index,
name: addr.name,
address: addr.address,
derivationPath: "",
derivationPath: addr.derivationPath,
txCount: addr.txCount,
balance: addr.balance,
isChange: addr.isChange,