mirror of
https://github.com/cake-tech/cake_wallet.git
synced 2024-12-22 19:49:22 +00:00
fix: restore flow slow, checking unspents
This commit is contained in:
parent
7339b7876f
commit
64caf8479e
23 changed files with 396 additions and 484 deletions
|
@ -12,15 +12,19 @@ abstract class BaseBitcoinAddressRecord {
|
|||
String name = '',
|
||||
bool isUsed = false,
|
||||
required this.type,
|
||||
bool? isHidden,
|
||||
}) : _txCount = txCount,
|
||||
_balance = balance,
|
||||
_name = name,
|
||||
_isUsed = isUsed;
|
||||
_isUsed = isUsed,
|
||||
_isHidden = isHidden ?? isChange;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) => o is BaseBitcoinAddressRecord && address == o.address;
|
||||
|
||||
final String address;
|
||||
final bool _isHidden;
|
||||
bool get isHidden => _isHidden;
|
||||
bool isChange;
|
||||
final int index;
|
||||
int _txCount;
|
||||
|
@ -54,6 +58,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
BitcoinAddressRecord(
|
||||
super.address, {
|
||||
required super.index,
|
||||
super.isHidden,
|
||||
super.isChange = false,
|
||||
super.txCount = 0,
|
||||
super.balance = 0,
|
||||
|
@ -76,6 +81,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
return BitcoinAddressRecord(
|
||||
decoded['address'] as String,
|
||||
index: decoded['index'] as int,
|
||||
isHidden: decoded['isHidden'] as bool? ?? false,
|
||||
isChange: decoded['isChange'] as bool? ?? false,
|
||||
isUsed: decoded['isUsed'] as bool? ?? false,
|
||||
txCount: decoded['txCount'] as int? ?? 0,
|
||||
|
@ -95,6 +101,7 @@ class BitcoinAddressRecord extends BaseBitcoinAddressRecord {
|
|||
String toJSON() => json.encode({
|
||||
'address': address,
|
||||
'index': index,
|
||||
'isHidden': isHidden,
|
||||
'isChange': isChange,
|
||||
'isUsed': isUsed,
|
||||
'txCount': txCount,
|
||||
|
@ -117,6 +124,7 @@ class BitcoinSilentPaymentAddressRecord extends BaseBitcoinAddressRecord {
|
|||
super.name = '',
|
||||
super.isUsed = false,
|
||||
super.type = SilentPaymentsAddresType.p2sp,
|
||||
super.isHidden,
|
||||
this.labelHex,
|
||||
}) : super(index: labelIndex, isChange: labelIndex == 0) {
|
||||
if (labelIndex != 1 && labelHex == null) {
|
||||
|
@ -165,7 +173,7 @@ class BitcoinReceivedSPAddressRecord extends BitcoinSilentPaymentAddressRecord {
|
|||
required this.spendKey,
|
||||
super.type = SegwitAddresType.p2tr,
|
||||
super.labelHex,
|
||||
});
|
||||
}) : super(isHidden: true);
|
||||
|
||||
factory BitcoinReceivedSPAddressRecord.fromJSON(String jsonSource, {BasedUtxoNetwork? network}) {
|
||||
final decoded = json.decode(jsonSource) as Map;
|
||||
|
|
|
@ -22,11 +22,10 @@ class BitcoinHardwareWalletService {
|
|||
for (final i in indexRange) {
|
||||
final derivationPath = "m/84'/0'/$i'";
|
||||
final xpub = await bitcoinLedgerApp.getXPubKey(derivationPath: derivationPath);
|
||||
Bip32Slip10Secp256k1 hd =
|
||||
Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0));
|
||||
final bip32 = Bip32Slip10Secp256k1.fromExtendedKey(xpub).childKey(Bip32KeyIndex(0));
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: P2wpkhAddress.fromBip32(bip32: hd, account: i, index: 0)
|
||||
address: P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: i)
|
||||
.toAddress(BitcoinNetwork.mainnet),
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
|
|
|
@ -29,4 +29,10 @@ class BitcoinUnspent extends Unspent {
|
|||
}
|
||||
|
||||
final BaseBitcoinAddressRecord bitcoinAddressRecord;
|
||||
|
||||
@override
|
||||
bool operator ==(Object o) {
|
||||
print('BitcoinUnspent operator ==');
|
||||
return o is BitcoinUnspent && hash == o.hash && vout == o.vout;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_mnemonic.dart';
|
||||
import 'package:cw_bitcoin/psbt_transaction_builder.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_transaction_priority.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cw_bitcoin/electrum_transaction_info.dart';
|
||||
|
@ -45,7 +46,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
required WalletInfo walletInfo,
|
||||
required Box<UnspentCoinsInfo> unspentCoinsInfo,
|
||||
required EncryptionFileUtils encryptionFileUtils,
|
||||
Uint8List? seedBytes,
|
||||
List<int>? seedBytes,
|
||||
String? mnemonic,
|
||||
String? xpub,
|
||||
String? addressPageType,
|
||||
|
@ -89,6 +90,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
initialSilentAddressIndex: initialSilentAddressIndex,
|
||||
bip32: bip32,
|
||||
network: networkParam ?? network,
|
||||
isHardwareWallet: walletInfo.isHardwareWallet,
|
||||
);
|
||||
|
||||
autorun((_) {
|
||||
|
@ -113,20 +115,18 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
int initialSilentAddressIndex = 0,
|
||||
required bool mempoolAPIEnabled,
|
||||
}) async {
|
||||
late Uint8List seedBytes;
|
||||
late List<int> seedBytes;
|
||||
|
||||
switch (walletInfo.derivationInfo?.derivationType) {
|
||||
case DerivationType.bip39:
|
||||
seedBytes = await bip39.mnemonicToSeed(
|
||||
mnemonic,
|
||||
passphrase: passphrase ?? "",
|
||||
);
|
||||
seedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||
break;
|
||||
case DerivationType.electrum:
|
||||
default:
|
||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||
break;
|
||||
}
|
||||
|
||||
return BitcoinWallet(
|
||||
mnemonic: mnemonic,
|
||||
passphrase: passphrase ?? "",
|
||||
|
@ -199,7 +199,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path;
|
||||
walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum;
|
||||
|
||||
Uint8List? seedBytes = null;
|
||||
List<int>? seedBytes = null;
|
||||
final mnemonic = keysData.mnemonic;
|
||||
final passphrase = keysData.passphrase;
|
||||
|
||||
|
@ -360,7 +360,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
||||
}));
|
||||
|
||||
unspentCoins = updatedUnspentCoins;
|
||||
unspentCoins = updatedUnspentCoins.toSet();
|
||||
|
||||
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
|
||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||
|
@ -642,8 +642,8 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<ElectrumBalance> fetchBalances(List<BitcoinAddressRecord> addresses) async {
|
||||
final balance = await super.fetchBalances(addresses);
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
final balance = await super.fetchBalances();
|
||||
|
||||
int totalFrozen = balance.frozen;
|
||||
int totalConfirmed = balance.confirmed;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/bip/bip/bip32/bip32.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -21,34 +20,47 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S
|
|||
super.initialSilentAddressIndex = 0,
|
||||
}) : super(walletInfo);
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
|
||||
if (!isHardwareWallet) {
|
||||
await generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
await generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
|
||||
await updateAddressesInBox();
|
||||
}
|
||||
|
||||
@override
|
||||
BitcoinBaseAddress generateAddress({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) {
|
||||
switch (addressType) {
|
||||
case P2pkhAddressType.p2pkh:
|
||||
return P2pkhAddress.fromBip32(account: account, bip32: hd, index: index);
|
||||
return P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case SegwitAddresType.p2tr:
|
||||
return P2trAddress.fromBip32(account: account, bip32: hd, index: index);
|
||||
return P2trAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case SegwitAddresType.p2wsh:
|
||||
return P2wshAddress.fromBip32(account: account, bip32: hd, index: index);
|
||||
return P2wshAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
case P2shAddressType.p2wpkhInP2sh:
|
||||
return P2shAddress.fromBip32(
|
||||
account: account,
|
||||
bip32: hd,
|
||||
bip32: bip32,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
type: P2shAddressType.p2wpkhInP2sh,
|
||||
);
|
||||
case SegwitAddresType.p2wpkh:
|
||||
return P2wpkhAddress.fromBip32(
|
||||
account: account,
|
||||
bip32: hd,
|
||||
bip32: bip32,
|
||||
isChange: isChange,
|
||||
index: index,
|
||||
isElectrum: true,
|
||||
); // TODO:
|
||||
isElectrum: false, // TODO:
|
||||
);
|
||||
default:
|
||||
throw ArgumentError('Invalid address type');
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ class BitcoinNewWalletCredentials extends WalletCredentials {
|
|||
required String name,
|
||||
WalletInfo? walletInfo,
|
||||
String? password,
|
||||
DerivationType? derivationType,
|
||||
String? derivationPath,
|
||||
String? passphrase,
|
||||
this.mnemonic,
|
||||
String? parentAddress,
|
||||
|
@ -29,18 +27,13 @@ class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials {
|
|||
required String password,
|
||||
required this.mnemonic,
|
||||
WalletInfo? walletInfo,
|
||||
required DerivationType derivationType,
|
||||
required String derivationPath,
|
||||
String? passphrase,
|
||||
}) : super(
|
||||
name: name,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
derivationInfo: DerivationInfo(
|
||||
derivationType: derivationType,
|
||||
derivationPath: derivationPath,
|
||||
));
|
||||
name: name,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
walletInfo: walletInfo,
|
||||
);
|
||||
|
||||
final String mnemonic;
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ class BitcoinWalletService extends WalletService<
|
|||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
alwaysScan: alwaysScan,
|
||||
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(false),
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
await wallet.init();
|
||||
saveBackup(name);
|
||||
|
@ -103,7 +103,7 @@ class BitcoinWalletService extends WalletService<
|
|||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
alwaysScan: alwaysScan,
|
||||
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(false),
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
);
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
|
@ -189,7 +189,6 @@ class BitcoinWalletService extends WalletService<
|
|||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
return wallet;
|
||||
}
|
||||
|
|
|
@ -56,7 +56,7 @@ abstract class ElectrumWalletBase
|
|||
required this.encryptionFileUtils,
|
||||
String? xpub,
|
||||
String? mnemonic,
|
||||
Uint8List? seedBytes,
|
||||
List<int>? seedBytes,
|
||||
this.passphrase,
|
||||
List<BitcoinAddressRecord>? initialAddresses,
|
||||
ElectrumClient? electrumClient,
|
||||
|
@ -69,7 +69,7 @@ abstract class ElectrumWalletBase
|
|||
_password = password,
|
||||
_isTransactionUpdating = false,
|
||||
isEnabledAutoGenerateSubaddress = true,
|
||||
unspentCoins = [],
|
||||
unspentCoins = {},
|
||||
scripthashesListening = {},
|
||||
balance = ObservableMap<CryptoCurrency, ElectrumBalance>.of(currency != null
|
||||
? {
|
||||
|
@ -99,7 +99,7 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network,
|
||||
Uint8List? seedBytes, String? xpub, DerivationInfo? derivationInfo) {
|
||||
List<int>? seedBytes, String? xpub, DerivationInfo? derivationInfo) {
|
||||
if (seedBytes == null && xpub == null) {
|
||||
throw Exception(
|
||||
"To create a Wallet you need either a seed or an xpub. This should not happen");
|
||||
|
@ -121,7 +121,7 @@ abstract class ElectrumWalletBase
|
|||
return Bip32Slip10Secp256k1.fromExtendedKey(xpub!, getKeyNetVersion(network));
|
||||
}
|
||||
|
||||
static Bip32Slip10Secp256k1 bitcoinCashHDWallet(Uint8List seedBytes) =>
|
||||
static Bip32Slip10Secp256k1 bitcoinCashHDWallet(List<int> seedBytes) =>
|
||||
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/44'/145'/0'") as Bip32Slip10Secp256k1;
|
||||
|
||||
int estimatedTransactionSize(int inputsCount, int outputsCounts) =>
|
||||
|
@ -221,7 +221,8 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
|
||||
String _password;
|
||||
List<BitcoinUnspent> unspentCoins;
|
||||
@observable
|
||||
Set<BitcoinUnspent> unspentCoins;
|
||||
|
||||
@observable
|
||||
TransactionPriorities? feeRates;
|
||||
|
@ -242,7 +243,6 @@ abstract class ElectrumWalletBase
|
|||
Future<void> init() async {
|
||||
await walletAddresses.init();
|
||||
await transactionHistory.init();
|
||||
await save();
|
||||
|
||||
_autoSaveTimer =
|
||||
Timer.periodic(Duration(minutes: _autoSaveInterval), (_) async => await save());
|
||||
|
@ -263,13 +263,15 @@ abstract class ElectrumWalletBase
|
|||
|
||||
// await updateTransactions();
|
||||
// await updateAllUnspents();
|
||||
// await updateBalance();
|
||||
await updateBalance();
|
||||
await updateFeeRates();
|
||||
|
||||
_updateFeeRateTimer ??=
|
||||
Timer.periodic(const Duration(seconds: 5), (timer) async => await updateFeeRates());
|
||||
|
||||
syncStatus = SyncedSyncStatus();
|
||||
|
||||
await save();
|
||||
} catch (e, stacktrace) {
|
||||
print(stacktrace);
|
||||
print("startSync $e");
|
||||
|
@ -459,7 +461,7 @@ abstract class ElectrumWalletBase
|
|||
} else if (!isHardwareWallet) {
|
||||
privkey = ECPrivate.fromBip32(
|
||||
bip32: walletAddresses.bip32,
|
||||
account: utx.bitcoinAddressRecord.isChange ? 1 : 0,
|
||||
account: BitcoinAddressUtils.getAccountFromChange(utx.bitcoinAddressRecord.isChange),
|
||||
index: utx.bitcoinAddressRecord.index,
|
||||
);
|
||||
}
|
||||
|
@ -660,11 +662,11 @@ abstract class ElectrumWalletBase
|
|||
isChange: true,
|
||||
));
|
||||
|
||||
// Get Derivation path for change Address since it is needed in Litecoin and BitcoinCash hardware Wallets
|
||||
final changeDerivationPath =
|
||||
"${_hardenedDerivationPath(walletInfo.derivationInfo?.derivationPath ?? "m/0'")}"
|
||||
"/${changeAddress.isHidden ? "1" : "0"}"
|
||||
"/${changeAddress.index}";
|
||||
final changeDerivationPath = BitcoinAddressUtils.getDerivationPath(
|
||||
type: changeAddress.type,
|
||||
account: changeAddress.isChange ? 1 : 0,
|
||||
index: changeAddress.index,
|
||||
);
|
||||
utxoDetails.publicKeys[address.pubKeyHash()] =
|
||||
PublicKeyWithDerivationPath('', changeDerivationPath);
|
||||
|
||||
|
@ -1105,12 +1107,12 @@ abstract class ElectrumWalletBase
|
|||
Future<void> save() async {
|
||||
if (!(await WalletKeysFile.hasKeysFile(walletInfo.name, walletInfo.type))) {
|
||||
await saveKeysFile(_password, encryptionFileUtils);
|
||||
saveKeysFile(_password, encryptionFileUtils, true);
|
||||
await saveKeysFile(_password, encryptionFileUtils, true);
|
||||
}
|
||||
|
||||
final path = await makePath();
|
||||
await encryptionFileUtils.write(path: path, password: _password, data: toJSON());
|
||||
await transactionHistory.save();
|
||||
// await transactionHistory.save();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1174,7 +1176,7 @@ abstract class ElectrumWalletBase
|
|||
updatedUnspentCoins.addAll(await fetchUnspent(address));
|
||||
}));
|
||||
|
||||
unspentCoins = updatedUnspentCoins;
|
||||
unspentCoins = updatedUnspentCoins.toSet();
|
||||
|
||||
if (unspentCoinsInfo.length != updatedUnspentCoins.length) {
|
||||
unspentCoins.forEach((coin) => addCoinInfo(coin));
|
||||
|
@ -1182,9 +1184,10 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
|
||||
await updateCoins(unspentCoins);
|
||||
await refreshUnspentCoinsInfo();
|
||||
// await refreshUnspentCoinsInfo();
|
||||
}
|
||||
|
||||
@action
|
||||
void updateCoin(BitcoinUnspent coin) {
|
||||
final coinInfoList = unspentCoinsInfo.values.where(
|
||||
(element) =>
|
||||
|
@ -1204,7 +1207,8 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> updateCoins(List<BitcoinUnspent> newUnspentCoins) async {
|
||||
@action
|
||||
Future<void> updateCoins(Set<BitcoinUnspent> newUnspentCoins) async {
|
||||
if (newUnspentCoins.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
@ -1213,8 +1217,26 @@ abstract class ElectrumWalletBase
|
|||
|
||||
@action
|
||||
Future<void> updateUnspentsForAddress(BitcoinAddressRecord addressRecord) async {
|
||||
final newUnspentCoins = await fetchUnspent(addressRecord);
|
||||
final newUnspentCoins = (await fetchUnspent(addressRecord)).toSet();
|
||||
await updateCoins(newUnspentCoins);
|
||||
|
||||
print([1, unspentCoins.containsAll(newUnspentCoins)]);
|
||||
if (!unspentCoins.containsAll(newUnspentCoins)) {
|
||||
newUnspentCoins.forEach((coin) {
|
||||
print(unspentCoins.contains(coin));
|
||||
print([coin.vout, coin.hash]);
|
||||
print([unspentCoins.first.vout, unspentCoins.first.hash]);
|
||||
if (!unspentCoins.contains(coin)) {
|
||||
unspentCoins.add(coin);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// if (unspentCoinsInfo.length != unspentCoins.length) {
|
||||
// unspentCoins.forEach(addCoinInfo);
|
||||
// }
|
||||
|
||||
// await refreshUnspentCoinsInfo();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -1231,11 +1253,6 @@ abstract class ElectrumWalletBase
|
|||
final tx = await fetchTransactionInfo(hash: coin.hash);
|
||||
coin.isChange = address.isChange;
|
||||
coin.confirmations = tx?.confirmations;
|
||||
if (coin.isFrozen) {
|
||||
balance[currency]!.frozen += coin.value;
|
||||
} else {
|
||||
balance[currency]!.confirmed += coin.value;
|
||||
}
|
||||
|
||||
updatedUnspentCoins.add(coin);
|
||||
} catch (_) {}
|
||||
|
@ -1492,64 +1509,65 @@ abstract class ElectrumWalletBase
|
|||
}
|
||||
}
|
||||
|
||||
Future<ElectrumTransactionBundle> getTransactionExpanded(
|
||||
{required String hash, int? height}) async {
|
||||
String transactionHex = '';
|
||||
Future<ElectrumTransactionBundle> getTransactionExpanded({required String hash}) async {
|
||||
int? time;
|
||||
int? confirmations;
|
||||
int? height;
|
||||
|
||||
try {
|
||||
final verboseTransaction = await electrumClient2!.request(
|
||||
ElectrumGetTransactionVerbose(transactionHash: hash),
|
||||
);
|
||||
final transactionHex = await electrumClient2!.request(
|
||||
ElectrumGetTransactionHex(transactionHash: hash),
|
||||
);
|
||||
|
||||
transactionHex = verboseTransaction['hex'] as String;
|
||||
time = verboseTransaction['time'] as int?;
|
||||
confirmations = verboseTransaction['confirmations'] as int?;
|
||||
} catch (e) {
|
||||
if (e is RPCError || e is TimeoutException) {
|
||||
transactionHex = await electrumClient2!.request(
|
||||
ElectrumGetTransactionHex(transactionHash: hash),
|
||||
// TODO:
|
||||
// if (mempoolAPIEnabled) {
|
||||
if (true) {
|
||||
try {
|
||||
final txVerbose = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/tx/$hash/status",
|
||||
),
|
||||
);
|
||||
|
||||
if (height != null && height > 0 && mempoolAPIEnabled) {
|
||||
try {
|
||||
final blockHash = await http.get(
|
||||
if (txVerbose.statusCode == 200 &&
|
||||
txVerbose.body.isNotEmpty &&
|
||||
jsonDecode(txVerbose.body) != null) {
|
||||
height = jsonDecode(txVerbose.body)['block_height'] as int;
|
||||
|
||||
final blockHash = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block-height/$height",
|
||||
),
|
||||
);
|
||||
|
||||
if (blockHash.statusCode == 200 &&
|
||||
blockHash.body.isNotEmpty &&
|
||||
jsonDecode(blockHash.body) != null) {
|
||||
final blockResponse = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block-height/$height",
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block/${blockHash.body}",
|
||||
),
|
||||
);
|
||||
|
||||
if (blockHash.statusCode == 200 &&
|
||||
blockHash.body.isNotEmpty &&
|
||||
jsonDecode(blockHash.body) != null) {
|
||||
final blockResponse = await http.get(
|
||||
Uri.parse(
|
||||
"http://mempool.cakewallet.com:8999/api/v1/block/${blockHash.body}",
|
||||
),
|
||||
);
|
||||
if (blockResponse.statusCode == 200 &&
|
||||
blockResponse.body.isNotEmpty &&
|
||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||
time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString());
|
||||
}
|
||||
if (blockResponse.statusCode == 200 &&
|
||||
blockResponse.body.isNotEmpty &&
|
||||
jsonDecode(blockResponse.body)['timestamp'] != null) {
|
||||
time = int.parse(jsonDecode(blockResponse.body)['timestamp'].toString());
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
int? confirmations;
|
||||
|
||||
if (height != null) {
|
||||
if (time == null && height > 0) {
|
||||
time = (getDateByBitcoinHeight(height).millisecondsSinceEpoch / 1000).round();
|
||||
}
|
||||
|
||||
if (confirmations == null) {
|
||||
final tip = currentChainTip!;
|
||||
if (tip > 0 && height > 0) {
|
||||
// Add one because the block itself is the first confirmation
|
||||
confirmations = tip - height + 1;
|
||||
}
|
||||
final tip = currentChainTip!;
|
||||
if (tip > 0 && height > 0) {
|
||||
// Add one because the block itself is the first confirmation
|
||||
confirmations = tip - height + 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1557,19 +1575,9 @@ abstract class ElectrumWalletBase
|
|||
final ins = <BtcTransaction>[];
|
||||
|
||||
for (final vin in original.inputs) {
|
||||
String inputTransactionHex = "";
|
||||
try {
|
||||
final verboseTransaction = await electrumClient2!.request(
|
||||
ElectrumGetTransactionVerbose(transactionHash: vin.txId),
|
||||
);
|
||||
inputTransactionHex = verboseTransaction['hex'] as String;
|
||||
} catch (e) {
|
||||
if (e is RPCError || e is TimeoutException) {
|
||||
inputTransactionHex = await electrumClient2!.request(
|
||||
ElectrumGetTransactionHex(transactionHash: vin.txId),
|
||||
);
|
||||
}
|
||||
}
|
||||
final inputTransactionHex = await electrumClient2!.request(
|
||||
ElectrumGetTransactionHex(transactionHash: vin.txId),
|
||||
);
|
||||
|
||||
ins.add(BtcTransaction.fromRaw(inputTransactionHex));
|
||||
}
|
||||
|
@ -1585,7 +1593,7 @@ abstract class ElectrumWalletBase
|
|||
Future<ElectrumTransactionInfo?> fetchTransactionInfo({required String hash, int? height}) async {
|
||||
try {
|
||||
return ElectrumTransactionInfo.fromElectrumBundle(
|
||||
await getTransactionExpanded(hash: hash, height: height),
|
||||
await getTransactionExpanded(hash: hash),
|
||||
walletInfo.type,
|
||||
network,
|
||||
addresses: addressesSet,
|
||||
|
@ -1674,7 +1682,6 @@ abstract class ElectrumWalletBase
|
|||
// Got a new transaction fetched, add it to the transaction history
|
||||
// instead of waiting all to finish, and next time it will be faster
|
||||
transactionHistory.addOne(tx);
|
||||
await transactionHistory.save();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1682,34 +1689,26 @@ abstract class ElectrumWalletBase
|
|||
}));
|
||||
|
||||
final totalAddresses = (addressRecord.isChange
|
||||
? walletAddresses.allAddresses
|
||||
.where((addr) => addr.isChange && addr.type == addressRecord.type)
|
||||
? walletAddresses.changeAddresses
|
||||
.where((addr) => addr.type == addressRecord.type)
|
||||
.length
|
||||
: walletAddresses.allAddresses
|
||||
.where((addr) => !addr.isChange && addr.type == addressRecord.type)
|
||||
: walletAddresses.receiveAddresses
|
||||
.where((addr) => addr.type == addressRecord.type)
|
||||
.length);
|
||||
final gapLimit = (addressRecord.isChange
|
||||
? ElectrumWalletAddressesBase.defaultChangeAddressesCount
|
||||
: ElectrumWalletAddressesBase.defaultReceiveAddressesCount);
|
||||
|
||||
print("gapLimit: $gapLimit");
|
||||
print("index: ${addressRecord.index}");
|
||||
final isUsedAddressUnderGap = addressRecord.index >= totalAddresses - gapLimit;
|
||||
print("isUsedAddressAtGapLimit: $isUsedAddressUnderGap");
|
||||
print("total: $totalAddresses");
|
||||
final isUsedAddressUnderGap = addressRecord.index < totalAddresses &&
|
||||
(addressRecord.index >= totalAddresses - gapLimit);
|
||||
|
||||
if (isUsedAddressUnderGap) {
|
||||
// Discover new addresses for the same address type until the gap limit is respected
|
||||
final newAddresses = await walletAddresses.discoverAddresses(
|
||||
walletAddresses.allAddresses
|
||||
.where((addr) =>
|
||||
(addressRecord.isChange ? addr.isChange : !addr.isChange) &&
|
||||
addr.type == addressRecord.type)
|
||||
.toList(),
|
||||
addressRecord.isChange,
|
||||
await walletAddresses.discoverAddresses(
|
||||
isChange: addressRecord.isChange,
|
||||
gap: gapLimit,
|
||||
type: addressRecord.type,
|
||||
);
|
||||
await subscribeForUpdates(newAddresses);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1765,59 +1764,30 @@ abstract class ElectrumWalletBase
|
|||
print("status: $status");
|
||||
|
||||
await _fetchAddressHistory(addressRecord);
|
||||
print("_fetchAddressHistory: ${addressRecord.address}");
|
||||
await updateUnspentsForAddress(addressRecord);
|
||||
print("updateUnspentsForAddress: ${addressRecord.address}");
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@action
|
||||
Future<ElectrumBalance> fetchBalances(List<BitcoinAddressRecord> addresses) async {
|
||||
final balanceFutures = <Future<Map<String, dynamic>>>[];
|
||||
for (var i = 0; i < addresses.length; i++) {
|
||||
final addressRecord = addresses[i];
|
||||
final balanceFuture = electrumClient2!.request(
|
||||
ElectrumGetScriptHashBalance(scriptHash: addressRecord.scriptHash),
|
||||
);
|
||||
balanceFutures.add(balanceFuture);
|
||||
}
|
||||
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
var totalFrozen = 0;
|
||||
var totalConfirmed = 0;
|
||||
var totalUnconfirmed = 0;
|
||||
|
||||
unspentCoinsInfo.values.forEach((info) {
|
||||
unspentCoins.forEach((element) {
|
||||
if (element.hash == info.hash &&
|
||||
element.vout == info.vout &&
|
||||
info.isFrozen &&
|
||||
element.bitcoinAddressRecord.address == info.address &&
|
||||
element.value == info.value) {
|
||||
totalFrozen += element.value;
|
||||
}
|
||||
});
|
||||
unspentCoins.forEach((element) {
|
||||
if (element.isFrozen) {
|
||||
totalFrozen += element.value;
|
||||
}
|
||||
|
||||
if (element.confirmations == 0) {
|
||||
totalUnconfirmed += element.value;
|
||||
} else {
|
||||
totalConfirmed += element.value;
|
||||
}
|
||||
});
|
||||
|
||||
final balances = await Future.wait(balanceFutures);
|
||||
|
||||
for (var i = 0; i < balances.length; i++) {
|
||||
final addressRecord = addresses[i];
|
||||
final balance = balances[i];
|
||||
try {
|
||||
final confirmed = balance['confirmed'] as int? ?? 0;
|
||||
final unconfirmed = balance['unconfirmed'] as int? ?? 0;
|
||||
totalConfirmed += confirmed;
|
||||
totalUnconfirmed += unconfirmed;
|
||||
|
||||
addressRecord.balance = confirmed + unconfirmed;
|
||||
if (confirmed > 0 || unconfirmed > 0) {
|
||||
addressRecord.setAsUsed();
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
return ElectrumBalance(
|
||||
confirmed: totalConfirmed,
|
||||
unconfirmed: totalUnconfirmed,
|
||||
|
@ -1825,22 +1795,9 @@ abstract class ElectrumWalletBase
|
|||
);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> updateBalanceForAddress(BitcoinAddressRecord addressRecord) async {
|
||||
final updatedBalance = await fetchBalances([addressRecord]);
|
||||
if (balance[currency] == null) {
|
||||
balance[currency] = updatedBalance;
|
||||
} else {
|
||||
balance[currency]!.confirmed += updatedBalance.confirmed;
|
||||
balance[currency]!.unconfirmed += updatedBalance.unconfirmed;
|
||||
balance[currency]!.frozen += updatedBalance.frozen;
|
||||
}
|
||||
}
|
||||
|
||||
@action
|
||||
Future<void> updateBalance() async {
|
||||
balance[currency] = await fetchBalances(walletAddresses.allAddresses);
|
||||
await save();
|
||||
balance[currency] = await fetchBalances();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -43,15 +43,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
int initialSilentAddressIndex = 0,
|
||||
List<BitcoinAddressRecord>? initialMwebAddresses,
|
||||
BitcoinAddressType? initialAddressPageType,
|
||||
}) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()),
|
||||
}) : _allAddresses = (initialAddresses ?? []).toSet(),
|
||||
addressesByReceiveType =
|
||||
ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => !addressRecord.isChange && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? [])
|
||||
.where((addressRecord) => addressRecord.isChange && !addressRecord.isUsed)
|
||||
.toSet()),
|
||||
receiveAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).where((addressRecord) => !addressRecord.isChange).toSet()),
|
||||
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
||||
changeAddresses = ObservableList<BitcoinAddressRecord>.of(
|
||||
(initialAddresses ?? []).where((addressRecord) => addressRecord.isChange).toSet()),
|
||||
currentReceiveAddressIndexByType = initialRegularAddressIndex ?? {},
|
||||
currentChangeAddressIndexByType = initialChangeAddressIndex ?? {},
|
||||
_addressPageType = initialAddressPageType ??
|
||||
|
@ -92,7 +91,8 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
static const defaultChangeAddressesCount = 17;
|
||||
static const gap = 20;
|
||||
|
||||
final ObservableList<BitcoinAddressRecord> _addresses;
|
||||
@observable
|
||||
final Set<BitcoinAddressRecord> _allAddresses;
|
||||
final ObservableList<BaseBitcoinAddressRecord> addressesByReceiveType;
|
||||
final ObservableList<BitcoinAddressRecord> receiveAddresses;
|
||||
final ObservableList<BitcoinAddressRecord> changeAddresses;
|
||||
|
@ -102,6 +102,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
final ObservableList<BitcoinAddressRecord> mwebAddresses;
|
||||
final BasedUtxoNetwork network;
|
||||
final Bip32Slip10Secp256k1 bip32;
|
||||
final bool isHardwareWallet;
|
||||
|
||||
@observable
|
||||
SilentPaymentOwner? silentAddress;
|
||||
|
@ -116,7 +117,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
String? activeSilentAddress;
|
||||
|
||||
@computed
|
||||
List<BitcoinAddressRecord> get allAddresses => _addresses;
|
||||
List<BitcoinAddressRecord> get allAddresses => _allAddresses.toList();
|
||||
|
||||
@override
|
||||
@computed
|
||||
|
@ -177,21 +178,18 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return;
|
||||
}
|
||||
try {
|
||||
final addressRecord = _addresses.firstWhere(
|
||||
final addressRecord = _allAddresses.firstWhere(
|
||||
(addressRecord) => addressRecord.address == addr,
|
||||
);
|
||||
|
||||
previousAddressRecord = addressRecord;
|
||||
receiveAddresses.remove(addressRecord);
|
||||
receiveAddresses.insert(0, addressRecord);
|
||||
} catch (e) {
|
||||
print("ElectrumWalletAddressBase: set address ($addr): $e");
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String get primaryAddress =>
|
||||
getAddress(account: 0, index: 0, hd: bip32, addressType: addressPageType);
|
||||
String get primaryAddress => getAddress(isChange: false, index: 0, addressType: addressPageType);
|
||||
|
||||
Map<String, int> currentReceiveAddressIndexByType;
|
||||
|
||||
|
@ -233,19 +231,19 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@override
|
||||
Future<void> init() async {
|
||||
if (walletInfo.type == WalletType.bitcoinCash) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
await generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
} else if (walletInfo.type == WalletType.litecoin) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if ((Platform.isAndroid || Platform.isIOS) && !isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
await generateInitialAddresses(type: SegwitAddresType.mweb);
|
||||
}
|
||||
} else if (walletInfo.type == WalletType.bitcoin) {
|
||||
await _generateInitialAddresses();
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2wpkh);
|
||||
if (!isHardwareWallet) {
|
||||
await _generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
await _generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
await _generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
await generateInitialAddresses(type: P2pkhAddressType.p2pkh);
|
||||
await generateInitialAddresses(type: P2shAddressType.p2wpkhInP2sh);
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2tr);
|
||||
await generateInitialAddresses(type: SegwitAddresType.p2wsh);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -265,14 +263,12 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@action
|
||||
Future<String> getChangeAddress(
|
||||
Future<BitcoinAddressRecord> getChangeAddress(
|
||||
{List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async {
|
||||
updateChangeAddresses();
|
||||
|
||||
if (changeAddresses.isEmpty) {
|
||||
final newAddresses = await _createNewAddresses(gap,
|
||||
startIndex: totalCountOfChangeAddresses > 0 ? totalCountOfChangeAddresses - 1 : 0,
|
||||
isHidden: true);
|
||||
final newAddresses = await _createNewAddresses(gap, isChange: true);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
|
@ -331,47 +327,45 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
0, (int acc, addressRecord) => addressRecord.isChange == false ? acc + 1 : acc);
|
||||
|
||||
final address = BitcoinAddressRecord(
|
||||
getAddress(account: 0, index: newAddressIndex, hd: bip32, addressType: addressPageType),
|
||||
getAddress(isChange: false, index: newAddressIndex, addressType: addressPageType),
|
||||
index: newAddressIndex,
|
||||
isChange: false,
|
||||
name: label,
|
||||
type: addressPageType,
|
||||
network: network,
|
||||
);
|
||||
_addresses.add(address);
|
||||
_allAddresses.add(address);
|
||||
Future.delayed(Duration.zero, () => updateAddressesByMatch());
|
||||
return address;
|
||||
}
|
||||
|
||||
BitcoinBaseAddress generateAddress({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
String getAddress({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) {
|
||||
return generateAddress(account: account, index: index, hd: hd, addressType: addressType)
|
||||
return generateAddress(isChange: isChange, index: index, addressType: addressType)
|
||||
.toAddress(network);
|
||||
}
|
||||
|
||||
Future<String> getAddressAsync({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) async =>
|
||||
getAddress(account: account, index: index, hd: hd, addressType: addressType);
|
||||
getAddress(isChange: isChange, index: index, addressType: addressType);
|
||||
|
||||
@action
|
||||
void addBitcoinAddressTypes() {
|
||||
final lastP2wpkh = _addresses
|
||||
final lastP2wpkh = _allAddresses
|
||||
.where((addressRecord) =>
|
||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||
.toList()
|
||||
|
@ -382,7 +376,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - P2WPKH';
|
||||
}
|
||||
|
||||
final lastP2pkh = _addresses.firstWhere(
|
||||
final lastP2pkh = _allAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh));
|
||||
if (lastP2pkh.address != address) {
|
||||
addressesMap[lastP2pkh.address] = 'P2PKH';
|
||||
|
@ -390,7 +384,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - P2PKH';
|
||||
}
|
||||
|
||||
final lastP2sh = _addresses.firstWhere((addressRecord) =>
|
||||
final lastP2sh = _allAddresses.firstWhere((addressRecord) =>
|
||||
_isUnusedReceiveAddressByType(addressRecord, P2shAddressType.p2wpkhInP2sh));
|
||||
if (lastP2sh.address != address) {
|
||||
addressesMap[lastP2sh.address] = 'P2SH';
|
||||
|
@ -398,7 +392,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - P2SH';
|
||||
}
|
||||
|
||||
final lastP2tr = _addresses.firstWhere(
|
||||
final lastP2tr = _allAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2tr));
|
||||
if (lastP2tr.address != address) {
|
||||
addressesMap[lastP2tr.address] = 'P2TR';
|
||||
|
@ -406,7 +400,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - P2TR';
|
||||
}
|
||||
|
||||
final lastP2wsh = _addresses.firstWhere(
|
||||
final lastP2wsh = _allAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wsh));
|
||||
if (lastP2wsh.address != address) {
|
||||
addressesMap[lastP2wsh.address] = 'P2WSH';
|
||||
|
@ -429,8 +423,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
});
|
||||
}
|
||||
|
||||
@action
|
||||
void addLitecoinAddressTypes() {
|
||||
final lastP2wpkh = _addresses
|
||||
final lastP2wpkh = _allAddresses
|
||||
.where((addressRecord) =>
|
||||
_isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.p2wpkh))
|
||||
.toList()
|
||||
|
@ -441,7 +436,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
addressesMap[address] = 'Active - P2WPKH';
|
||||
}
|
||||
|
||||
final lastMweb = _addresses.firstWhere(
|
||||
final lastMweb = _allAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, SegwitAddresType.mweb));
|
||||
if (lastMweb.address != address) {
|
||||
addressesMap[lastMweb.address] = 'MWEB';
|
||||
|
@ -450,8 +445,9 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
}
|
||||
|
||||
@action
|
||||
void addBitcoinCashAddressTypes() {
|
||||
final lastP2pkh = _addresses.firstWhere(
|
||||
final lastP2pkh = _allAddresses.firstWhere(
|
||||
(addressRecord) => _isUnusedReceiveAddressByType(addressRecord, P2pkhAddressType.p2pkh));
|
||||
if (lastP2pkh.address != address) {
|
||||
addressesMap[lastP2pkh.address] = 'P2PKH';
|
||||
|
@ -461,13 +457,14 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
@action
|
||||
Future<void> updateAddressesInBox() async {
|
||||
try {
|
||||
addressesMap.clear();
|
||||
addressesMap[address] = 'Active';
|
||||
|
||||
allAddressesMap.clear();
|
||||
_addresses.forEach((addressRecord) {
|
||||
_allAddresses.forEach((addressRecord) {
|
||||
allAddressesMap[addressRecord.address] = addressRecord.name;
|
||||
});
|
||||
|
||||
|
@ -494,7 +491,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
@action
|
||||
void updateAddress(String address, String label) {
|
||||
BaseBitcoinAddressRecord? foundAddress;
|
||||
_addresses.forEach((addressRecord) {
|
||||
_allAddresses.forEach((addressRecord) {
|
||||
if (addressRecord.address == address) {
|
||||
foundAddress = addressRecord;
|
||||
}
|
||||
|
@ -513,11 +510,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
if (foundAddress != null) {
|
||||
foundAddress!.setNewName(label);
|
||||
|
||||
if (foundAddress is BitcoinAddressRecord) {
|
||||
final index = _addresses.indexOf(foundAddress);
|
||||
_addresses.remove(foundAddress);
|
||||
_addresses.insert(index, foundAddress as BitcoinAddressRecord);
|
||||
} else {
|
||||
if (foundAddress is! BitcoinAddressRecord) {
|
||||
final index = silentAddresses.indexOf(foundAddress as BitcoinSilentPaymentAddressRecord);
|
||||
silentAddresses.remove(foundAddress);
|
||||
silentAddresses.insert(index, foundAddress as BitcoinSilentPaymentAddressRecord);
|
||||
|
@ -534,88 +527,62 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
addressesByReceiveType.clear();
|
||||
addressesByReceiveType.addAll(_addresses.where(_isAddressPageTypeMatch).toList());
|
||||
addressesByReceiveType.addAll(_allAddresses.where(_isAddressPageTypeMatch).toList());
|
||||
}
|
||||
|
||||
@action
|
||||
void updateReceiveAddresses() {
|
||||
receiveAddresses.removeRange(0, receiveAddresses.length);
|
||||
final newAddresses =
|
||||
_addresses.where((addressRecord) => !addressRecord.isChange && !addressRecord.isUsed);
|
||||
final newAddresses = _allAddresses.where((addressRecord) => !addressRecord.isChange);
|
||||
receiveAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
@action
|
||||
void updateChangeAddresses() {
|
||||
changeAddresses.removeRange(0, changeAddresses.length);
|
||||
final newAddresses = _addresses.where((addressRecord) =>
|
||||
final newAddresses = _allAddresses.where((addressRecord) =>
|
||||
addressRecord.isChange &&
|
||||
!addressRecord.isUsed &&
|
||||
// TODO: feature to change change address type. For now fixed to p2wpkh, the cheapest type
|
||||
(walletInfo.type != WalletType.bitcoin || addressRecord.type == SegwitAddresType.p2wpkh));
|
||||
changeAddresses.addAll(newAddresses);
|
||||
}
|
||||
|
||||
@action
|
||||
Future<List<BitcoinAddressRecord>> discoverAddresses(
|
||||
List<BitcoinAddressRecord> addressList,
|
||||
bool isHidden, {
|
||||
BitcoinAddressType type = SegwitAddresType.p2wpkh,
|
||||
Future<List<BitcoinAddressRecord>> discoverAddresses({
|
||||
required bool isChange,
|
||||
required int gap,
|
||||
required BitcoinAddressType type,
|
||||
}) async {
|
||||
final newAddresses = await _createNewAddresses(
|
||||
gap,
|
||||
startIndex: addressList.length,
|
||||
isHidden: isHidden,
|
||||
type: type,
|
||||
);
|
||||
print("_allAddresses: ${_allAddresses.length}");
|
||||
final newAddresses = await _createNewAddresses(gap, isChange: isChange, type: type);
|
||||
addAddresses(newAddresses);
|
||||
print("_allAddresses: ${_allAddresses.length}");
|
||||
return newAddresses;
|
||||
}
|
||||
|
||||
Future<void> _generateInitialAddresses(
|
||||
{BitcoinAddressType type = SegwitAddresType.p2wpkh}) async {
|
||||
var countOfReceiveAddresses = 0;
|
||||
var countOfHiddenAddresses = 0;
|
||||
|
||||
_addresses.forEach((addr) {
|
||||
if (addr.type == type) {
|
||||
if (addr.isChange) {
|
||||
countOfHiddenAddresses += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
countOfReceiveAddresses += 1;
|
||||
}
|
||||
});
|
||||
|
||||
if (countOfReceiveAddresses < defaultReceiveAddressesCount) {
|
||||
final addressesCount = defaultReceiveAddressesCount - countOfReceiveAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfReceiveAddresses, isHidden: false, type: type);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
|
||||
if (countOfHiddenAddresses < defaultChangeAddressesCount) {
|
||||
final addressesCount = defaultChangeAddressesCount - countOfHiddenAddresses;
|
||||
final newAddresses = await _createNewAddresses(addressesCount,
|
||||
startIndex: countOfHiddenAddresses, isHidden: true, type: type);
|
||||
addAddresses(newAddresses);
|
||||
}
|
||||
@action
|
||||
Future<void> generateInitialAddresses({required BitcoinAddressType type}) async {
|
||||
await discoverAddresses(isChange: false, gap: defaultReceiveAddressesCount, type: type);
|
||||
await discoverAddresses(isChange: true, gap: defaultChangeAddressesCount, type: type);
|
||||
}
|
||||
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(int count,
|
||||
{int startIndex = 0, bool isHidden = false, BitcoinAddressType? type}) async {
|
||||
@action
|
||||
Future<List<BitcoinAddressRecord>> _createNewAddresses(
|
||||
int count, {
|
||||
bool isChange = false,
|
||||
BitcoinAddressType? type,
|
||||
}) async {
|
||||
final list = <BitcoinAddressRecord>[];
|
||||
final startIndex = isChange ? totalCountOfChangeAddresses : totalCountOfReceiveAddresses;
|
||||
|
||||
for (var i = startIndex; i < count + startIndex; i++) {
|
||||
final address = BitcoinAddressRecord(
|
||||
await getAddressAsync(
|
||||
account: _getAccount(isHidden),
|
||||
index: i,
|
||||
hd: bip32,
|
||||
addressType: type ?? addressPageType),
|
||||
isChange: isChange,
|
||||
index: i,
|
||||
addressType: type ?? addressPageType,
|
||||
),
|
||||
index: i,
|
||||
isChange: isHidden,
|
||||
isChange: isChange,
|
||||
type: type ?? addressPageType,
|
||||
network: network,
|
||||
);
|
||||
|
@ -627,11 +594,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
|
||||
@action
|
||||
void addAddresses(Iterable<BitcoinAddressRecord> addresses) {
|
||||
final addressesSet = this._addresses.toSet();
|
||||
addressesSet.addAll(addresses);
|
||||
this._addresses.clear();
|
||||
this._addresses.addAll(addressesSet);
|
||||
this._allAddresses.addAll(addresses);
|
||||
updateAddressesByMatch();
|
||||
updateReceiveAddresses();
|
||||
updateChangeAddresses();
|
||||
}
|
||||
|
||||
@action
|
||||
|
@ -653,7 +619,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
}
|
||||
|
||||
void _validateAddresses() {
|
||||
_addresses.forEach((element) async {
|
||||
_allAddresses.forEach((element) async {
|
||||
if (element.type == SegwitAddresType.mweb) {
|
||||
// this would add a ton of startup lag for mweb addresses since we have 1000 of them
|
||||
return;
|
||||
|
@ -661,18 +627,16 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
if (!element.isChange &&
|
||||
element.address !=
|
||||
await getAddressAsync(
|
||||
account: 0,
|
||||
isChange: false,
|
||||
index: element.index,
|
||||
hd: bip32,
|
||||
addressType: element.type,
|
||||
)) {
|
||||
element.isChange = true;
|
||||
} else if (element.isChange &&
|
||||
element.address !=
|
||||
await getAddressAsync(
|
||||
account: 1,
|
||||
isChange: true,
|
||||
index: element.index,
|
||||
hd: bip32,
|
||||
addressType: element.type,
|
||||
)) {
|
||||
element.isChange = false;
|
||||
|
@ -692,11 +656,11 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
|
|||
return _isAddressByType(addressRecord, addressPageType);
|
||||
}
|
||||
|
||||
int _getAccount(bool isHidden) => isHidden ? 1 : 0;
|
||||
bool _isAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) => addr.type == type;
|
||||
|
||||
bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) =>
|
||||
!addr.isChange && !addr.isUsed && addr.type == type;
|
||||
bool _isUnusedReceiveAddressByType(BitcoinAddressRecord addr, BitcoinAddressType type) {
|
||||
return !addr.isChange && !addr.isUsed && addr.type == type;
|
||||
}
|
||||
|
||||
@action
|
||||
void deleteSilentPaymentAddress(String address) {
|
||||
|
|
|
@ -2,7 +2,6 @@ import 'dart:async';
|
|||
|
||||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_bitcoin/utils.dart';
|
||||
import 'package:cw_core/hardware/hardware_account_data.dart';
|
||||
import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
|
||||
import 'package:ledger_litecoin/ledger_litecoin.dart';
|
||||
|
@ -12,8 +11,7 @@ class LitecoinHardwareWalletService {
|
|||
|
||||
final LedgerConnection ledgerConnection;
|
||||
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts(
|
||||
{int index = 0, int limit = 5}) async {
|
||||
Future<List<HardwareAccountData>> getAvailableAccounts({int index = 0, int limit = 5}) async {
|
||||
final litecoinLedgerApp = LitecoinLedgerApp(ledgerConnection);
|
||||
|
||||
await litecoinLedgerApp.getVersion();
|
||||
|
@ -27,14 +25,13 @@ class LitecoinHardwareWalletService {
|
|||
final xpub = await litecoinLedgerApp.getXPubKey(
|
||||
accountsDerivationPath: derivationPath,
|
||||
xPubVersion: int.parse(hex.encode(xpubVersion.public), radix: 16));
|
||||
final hd = Bip32Slip10Secp256k1.fromExtendedKey(xpub, xpubVersion)
|
||||
.childKey(Bip32KeyIndex(0));
|
||||
final bip32 =
|
||||
Bip32Slip10Secp256k1.fromExtendedKey(xpub, xpubVersion).childKey(Bip32KeyIndex(0));
|
||||
|
||||
final address = generateP2WPKHAddress(
|
||||
hd: hd, index: 0, network: LitecoinNetwork.mainnet);
|
||||
final address = P2wpkhAddress.fromBip32(bip32: bip32, isChange: false, index: 0);
|
||||
|
||||
accounts.add(HardwareAccountData(
|
||||
address: address,
|
||||
address: address.toAddress(LitecoinNetwork.mainnet),
|
||||
accountIndex: i,
|
||||
derivationPath: derivationPath,
|
||||
xpub: xpub,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:convert/convert.dart' as convert;
|
||||
import 'dart:math';
|
||||
|
@ -87,8 +86,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||
) {
|
||||
if (seedBytes != null) {
|
||||
mwebHd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath(
|
||||
"m/1000'") as Bip32Slip10Secp256k1;
|
||||
mwebHd =
|
||||
Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath("m/1000'") as Bip32Slip10Secp256k1;
|
||||
mwebEnabled = alwaysScan ?? false;
|
||||
} else {
|
||||
mwebHd = null;
|
||||
|
@ -772,7 +771,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
});
|
||||
|
||||
// copy coin control attributes to mwebCoins:
|
||||
await updateCoins(mwebUnspentCoins);
|
||||
await updateCoins(mwebUnspentCoins.toSet());
|
||||
// get regular ltc unspents (this resets unspentCoins):
|
||||
await super.updateAllUnspents();
|
||||
// add the mwebCoins:
|
||||
|
@ -810,11 +809,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
}
|
||||
|
||||
@override
|
||||
Future<ElectrumBalance> fetchBalances(List<BitcoinAddressRecord> addresses) async {
|
||||
final nonMwebAddresses = walletAddresses.allAddresses
|
||||
.where((address) => RegexUtils.addressTypeFromStr(address.address, network) is! MwebAddress)
|
||||
.toList();
|
||||
final balance = await super.fetchBalances(nonMwebAddresses);
|
||||
Future<ElectrumBalance> fetchBalances() async {
|
||||
final balance = await super.fetchBalances();
|
||||
|
||||
if (!mwebEnabled) {
|
||||
return balance;
|
||||
|
@ -980,8 +976,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
if (!mwebEnabled) {
|
||||
tx.changeAddressOverride =
|
||||
(await (walletAddresses as LitecoinWalletAddresses)
|
||||
.getChangeAddress(isPegIn: false))
|
||||
(await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(isPegIn: false))
|
||||
.address;
|
||||
return tx;
|
||||
}
|
||||
|
@ -1021,10 +1016,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
|
||||
bool isPegIn = !hasMwebInput && hasMwebOutput;
|
||||
bool isRegular = !hasMwebInput && !hasMwebOutput;
|
||||
tx.changeAddressOverride =
|
||||
(await (walletAddresses as LitecoinWalletAddresses)
|
||||
.getChangeAddress(isPegIn: isPegIn || isRegular))
|
||||
.address;
|
||||
tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses)
|
||||
.getChangeAddress(isPegIn: isPegIn || isRegular))
|
||||
.address;
|
||||
if (!hasMwebInput && !hasMwebOutput) {
|
||||
tx.isMweb = false;
|
||||
return tx;
|
||||
|
@ -1058,7 +1052,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
.firstWhere((utxo) => utxo.hash == e.value.txId && utxo.vout == e.value.txIndex);
|
||||
final key = ECPrivate.fromBip32(
|
||||
bip32: walletAddresses.bip32,
|
||||
account: utxo.bitcoinAddressRecord.isChange ? 1 : 0,
|
||||
account: BitcoinAddressUtils.getAccountFromChange(utxo.bitcoinAddressRecord.isChange),
|
||||
index: utxo.bitcoinAddressRecord.index,
|
||||
);
|
||||
final digest = tx2.getTransactionSegwitDigit(
|
||||
|
@ -1277,8 +1271,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
@override
|
||||
void setLedgerConnection(LedgerConnection connection) {
|
||||
_ledgerConnection = connection;
|
||||
_litecoinLedgerApp =
|
||||
LitecoinLedgerApp(_ledgerConnection!, derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||
_litecoinLedgerApp = LitecoinLedgerApp(_ledgerConnection!,
|
||||
derivationPath: walletInfo.derivationInfo!.derivationPath!);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -1314,19 +1308,17 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store {
|
|||
if (maybeChangePath != null) changePath ??= maybeChangePath.derivationPath;
|
||||
}
|
||||
|
||||
|
||||
final rawHex = await _litecoinLedgerApp!.createTransaction(
|
||||
inputs: readyInputs,
|
||||
outputs: outputs
|
||||
.map((e) => TransactionOutput.fromBigInt(
|
||||
(e as BitcoinOutput).value, Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
.toList(),
|
||||
changePath: changePath,
|
||||
sigHashType: 0x01,
|
||||
additionals: ["bech32"],
|
||||
isSegWit: true,
|
||||
useTrustedInputForSegwit: true
|
||||
);
|
||||
inputs: readyInputs,
|
||||
outputs: outputs
|
||||
.map((e) => TransactionOutput.fromBigInt((e as BitcoinOutput).value,
|
||||
Uint8List.fromList(e.address.toScriptPubKey().toBytes())))
|
||||
.toList(),
|
||||
changePath: changePath,
|
||||
sigHashType: 0x01,
|
||||
additionals: ["bech32"],
|
||||
isSegWit: true,
|
||||
useTrustedInputForSegwit: true);
|
||||
|
||||
return BtcTransaction.fromRaw(rawHex);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import 'package:bitcoin_base/bitcoin_base.dart';
|
|||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_address_record.dart';
|
||||
import 'package:cw_bitcoin/bitcoin_unspent.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:cw_mweb/cw_mweb.dart';
|
||||
|
@ -15,11 +14,9 @@ import 'package:mobx/mobx.dart';
|
|||
|
||||
part 'litecoin_wallet_addresses.g.dart';
|
||||
|
||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase
|
||||
with _$LitecoinWalletAddresses;
|
||||
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
|
||||
|
||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
||||
with Store {
|
||||
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
|
||||
LitecoinWalletAddressesBase(
|
||||
WalletInfo walletInfo, {
|
||||
required super.bip32,
|
||||
|
@ -44,14 +41,13 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
|||
List<String> mwebAddrs = [];
|
||||
bool generating = false;
|
||||
|
||||
List<int> get scanSecret =>
|
||||
mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
|
||||
List<int> get scanSecret => mwebHd!.childKey(Bip32KeyIndex(0x80000000)).privateKey.privKey.raw;
|
||||
List<int> get spendPubkey =>
|
||||
mwebHd!.childKey(Bip32KeyIndex(0x80000001)).publicKey.pubKey.compressed;
|
||||
|
||||
@override
|
||||
Future<void> init() async {
|
||||
if (!isHardwareWallet) await initMwebAddresses();
|
||||
if (!super.isHardwareWallet) await initMwebAddresses();
|
||||
await super.init();
|
||||
}
|
||||
|
||||
|
@ -122,31 +118,29 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses
|
|||
|
||||
@override
|
||||
BitcoinBaseAddress generateAddress({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
return MwebAddress.fromAddress(address: mwebAddrs[0], network: network);
|
||||
}
|
||||
|
||||
return P2wpkhAddress.fromBip32(account: account, bip32: hd, index: index);
|
||||
return P2wpkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<String> getAddressAsync({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) async {
|
||||
if (addressType == SegwitAddresType.mweb) {
|
||||
await ensureMwebAddressUpToIndexExists(index);
|
||||
}
|
||||
|
||||
return getAddress(account: account, index: index, hd: hd, addressType: addressType);
|
||||
return getAddress(isChange: isChange, index: index, addressType: addressType);
|
||||
}
|
||||
|
||||
@action
|
||||
|
|
|
@ -170,6 +170,7 @@ class LitecoinWalletService extends WalletService<
|
|||
walletInfo: credentials.walletInfo!,
|
||||
unspentCoinsInfo: unspentCoinsInfoSource,
|
||||
encryptionFileUtils: encryptionFileUtilsFor(isDirect),
|
||||
mempoolAPIEnabled: mempoolAPIEnabled,
|
||||
);
|
||||
await wallet.save();
|
||||
await wallet.init();
|
||||
|
|
|
@ -564,10 +564,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: ledger_flutter_plus
|
||||
sha256: ea3ed586e1697776dacf42ac979095f1ca3bd143bf007cbe5c78e09cb6943f42
|
||||
sha256: c7b04008553193dbca7e17b430768eecc372a72b0ff3625b5e7fc5e5c8d3231b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.5"
|
||||
version: "1.4.1"
|
||||
ledger_litecoin:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
@ -28,10 +28,6 @@ dependencies:
|
|||
cryptography: ^2.0.5
|
||||
blockchain_utils:
|
||||
path: /home/rafael/Working/blockchain_utils/
|
||||
ledger_flutter: ^1.0.1
|
||||
ledger_bitcoin:
|
||||
git:
|
||||
url: https://github.com/cake-tech/ledger-bitcoin
|
||||
cw_mweb:
|
||||
path: ../cw_mweb
|
||||
grpc: ^3.2.4
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'package:bitcoin_base/bitcoin_base.dart';
|
||||
import 'package:blockchain_utils/blockchain_utils.dart';
|
||||
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
|
||||
import 'package:cw_core/wallet_info.dart';
|
||||
import 'package:mobx/mobx.dart';
|
||||
|
@ -22,10 +21,9 @@ abstract class BitcoinCashWalletAddressesBase extends ElectrumWalletAddresses wi
|
|||
|
||||
@override
|
||||
BitcoinBaseAddress generateAddress({
|
||||
required int account,
|
||||
required bool isChange,
|
||||
required int index,
|
||||
required Bip32Slip10Secp256k1 hd,
|
||||
required BitcoinAddressType addressType,
|
||||
}) =>
|
||||
P2pkhAddress.fromBip32(account: account, bip32: hd, index: index);
|
||||
P2pkhAddress.fromBip32(bip32: bip32, isChange: isChange, index: index);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,10 @@ mixin WalletKeysFile<BalanceType extends Balance, HistoryType extends Transactio
|
|||
final path = "$rootPath${isBackup ? ".backup" : ""}";
|
||||
dev.log("Saving .keys file '$path'");
|
||||
await encryptionFileUtils.write(
|
||||
path: path, password: password, data: walletKeysData.toJSON());
|
||||
path: path,
|
||||
password: password,
|
||||
data: walletKeysData.toJSON(),
|
||||
);
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,16 +5,12 @@ class CWBitcoin extends Bitcoin {
|
|||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
required String derivationPath,
|
||||
String? passphrase,
|
||||
}) =>
|
||||
BitcoinRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: mnemonic,
|
||||
password: password,
|
||||
derivationType: derivationType,
|
||||
derivationPath: derivationPath,
|
||||
passphrase: passphrase,
|
||||
);
|
||||
|
||||
|
@ -373,66 +369,71 @@ class CWBitcoin extends Bitcoin {
|
|||
}
|
||||
|
||||
for (DerivationType dType in electrum_derivations.keys) {
|
||||
late Uint8List seedBytes;
|
||||
if (dType == DerivationType.electrum) {
|
||||
seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? "");
|
||||
} else if (dType == DerivationType.bip39) {
|
||||
seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? '');
|
||||
}
|
||||
|
||||
for (DerivationInfo dInfo in electrum_derivations[dType]!) {
|
||||
try {
|
||||
DerivationInfo dInfoCopy = DerivationInfo(
|
||||
derivationType: dInfo.derivationType,
|
||||
derivationPath: dInfo.derivationPath,
|
||||
description: dInfo.description,
|
||||
scriptType: dInfo.scriptType,
|
||||
);
|
||||
|
||||
String balancePath = dInfoCopy.derivationPath!;
|
||||
int derivationDepth = _countCharOccurrences(balancePath, '/');
|
||||
|
||||
// for BIP44
|
||||
if (derivationDepth == 3 || derivationDepth == 1) {
|
||||
// we add "/0" so that we generate account 0
|
||||
balancePath += "/0";
|
||||
}
|
||||
|
||||
final hd = Bip32Slip10Secp256k1.fromSeed(seedBytes).derivePath(balancePath)
|
||||
as Bip32Slip10Secp256k1;
|
||||
|
||||
// derive address at index 0:
|
||||
String? address;
|
||||
switch (dInfoCopy.scriptType) {
|
||||
case "p2wpkh":
|
||||
address = P2wpkhAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network);
|
||||
break;
|
||||
case "p2pkh":
|
||||
address = P2pkhAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network);
|
||||
break;
|
||||
case "p2wpkh-p2sh":
|
||||
address = P2shAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network);
|
||||
break;
|
||||
case "p2tr":
|
||||
address = P2trAddress.fromBip32(bip32: hd, account: 0, index: 0).toAddress(network);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
final sh = BitcoinAddressUtils.scriptHash(address, network: network);
|
||||
final history = await electrumClient.getHistory(sh);
|
||||
|
||||
final balance = await electrumClient.getBalance(sh);
|
||||
dInfoCopy.balance = balance.entries.firstOrNull?.value.toString() ?? "0";
|
||||
dInfoCopy.address = address;
|
||||
dInfoCopy.transactionsCount = history.length;
|
||||
|
||||
list.add(dInfoCopy);
|
||||
} catch (e, s) {
|
||||
print("derivationInfoError: $e");
|
||||
print("derivationInfoStack: $s");
|
||||
try {
|
||||
late List<int> seedBytes;
|
||||
if (dType == DerivationType.electrum) {
|
||||
seedBytes = ElectrumV2SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||
} else if (dType == DerivationType.bip39) {
|
||||
seedBytes = Bip39SeedGenerator.generateFromString(mnemonic, passphrase);
|
||||
}
|
||||
|
||||
for (DerivationInfo dInfo in electrum_derivations[dType]!) {
|
||||
try {
|
||||
DerivationInfo dInfoCopy = DerivationInfo(
|
||||
derivationType: dInfo.derivationType,
|
||||
derivationPath: dInfo.derivationPath,
|
||||
description: dInfo.description,
|
||||
scriptType: dInfo.scriptType,
|
||||
);
|
||||
|
||||
String balancePath = dInfoCopy.derivationPath!;
|
||||
int derivationDepth = _countCharOccurrences(balancePath, '/');
|
||||
|
||||
// for BIP44
|
||||
if (derivationDepth == 3 || derivationDepth == 1) {
|
||||
// we add "/0" so that we generate account 0
|
||||
balancePath += "/0";
|
||||
}
|
||||
|
||||
final bip32 = Bip32Slip10Secp256k1.fromSeed(seedBytes);
|
||||
final bip32BalancePath = Bip32PathParser.parse(balancePath);
|
||||
|
||||
// derive address at index 0:
|
||||
final path = bip32BalancePath.addElem(Bip32KeyIndex(0));
|
||||
String? address;
|
||||
switch (dInfoCopy.scriptType) {
|
||||
case "p2wpkh":
|
||||
address = P2wpkhAddress.fromPath(bip32: bip32, path: path).toAddress(network);
|
||||
break;
|
||||
case "p2pkh":
|
||||
address = P2pkhAddress.fromPath(bip32: bip32, path: path).toAddress(network);
|
||||
break;
|
||||
case "p2wpkh-p2sh":
|
||||
address = P2shAddress.fromPath(bip32: bip32, path: path).toAddress(network);
|
||||
break;
|
||||
case "p2tr":
|
||||
address = P2trAddress.fromPath(bip32: bip32, path: path).toAddress(network);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
final sh = BitcoinAddressUtils.scriptHash(address, network: network);
|
||||
final history = await electrumClient.getHistory(sh);
|
||||
|
||||
final balance = await electrumClient.getBalance(sh);
|
||||
dInfoCopy.balance = balance.entries.firstOrNull?.value.toString() ?? "0";
|
||||
dInfoCopy.address = address;
|
||||
dInfoCopy.transactionsCount = history.length;
|
||||
|
||||
list.add(dInfoCopy);
|
||||
} catch (e, s) {
|
||||
print("derivationInfoError: $e");
|
||||
print("derivationInfoStack: $s");
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("seed error: $e");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,8 +54,10 @@ class WalletRestorePage extends BasePage {
|
|||
_validateOnChange(isPolyseed: isPolyseed);
|
||||
},
|
||||
displayWalletPassword: walletRestoreViewModel.hasWalletPassword,
|
||||
onPasswordChange: (String password) => walletRestoreViewModel.walletPassword = password,
|
||||
onRepeatedPasswordChange: (String repeatedPassword) => walletRestoreViewModel.repeatedWalletPassword = repeatedPassword));
|
||||
onPasswordChange: (String password) =>
|
||||
walletRestoreViewModel.walletPassword = password,
|
||||
onRepeatedPasswordChange: (String repeatedPassword) =>
|
||||
walletRestoreViewModel.repeatedWalletPassword = repeatedPassword));
|
||||
break;
|
||||
case WalletRestoreMode.keys:
|
||||
_pages.add(WalletRestoreFromKeysFrom(
|
||||
|
@ -69,8 +71,10 @@ class WalletRestorePage extends BasePage {
|
|||
},
|
||||
displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
|
||||
displayWalletPassword: walletRestoreViewModel.hasWalletPassword,
|
||||
onPasswordChange: (String password) => walletRestoreViewModel.walletPassword = password,
|
||||
onRepeatedPasswordChange: (String repeatedPassword) => walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
|
||||
onPasswordChange: (String password) =>
|
||||
walletRestoreViewModel.walletPassword = password,
|
||||
onRepeatedPasswordChange: (String repeatedPassword) =>
|
||||
walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
|
||||
onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
|
||||
break;
|
||||
default:
|
||||
|
@ -379,38 +383,40 @@ class WalletRestorePage extends BasePage {
|
|||
|
||||
walletRestoreViewModel.state = IsExecutingState();
|
||||
|
||||
DerivationInfo? dInfo;
|
||||
if (walletRestoreViewModel.type == WalletType.nano) {
|
||||
DerivationInfo? dInfo;
|
||||
|
||||
// get info about the different derivations:
|
||||
List<DerivationInfo> derivations =
|
||||
await walletRestoreViewModel.getDerivationInfo(_credentials());
|
||||
// get info about the different derivations:
|
||||
List<DerivationInfo> derivations =
|
||||
await walletRestoreViewModel.getDerivationInfo(_credentials());
|
||||
|
||||
int derivationsWithHistory = 0;
|
||||
int derivationWithHistoryIndex = 0;
|
||||
for (int i = 0; i < derivations.length; i++) {
|
||||
if (derivations[i].transactionsCount > 0) {
|
||||
derivationsWithHistory++;
|
||||
derivationWithHistoryIndex = i;
|
||||
int derivationsWithHistory = 0;
|
||||
int derivationWithHistoryIndex = 0;
|
||||
for (int i = 0; i < derivations.length; i++) {
|
||||
if (derivations[i].transactionsCount > 0) {
|
||||
derivationsWithHistory++;
|
||||
derivationWithHistoryIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (derivationsWithHistory > 1) {
|
||||
dInfo = await Navigator.of(context).pushNamed(
|
||||
Routes.restoreWalletChooseDerivation,
|
||||
arguments: derivations,
|
||||
) as DerivationInfo?;
|
||||
} else if (derivationsWithHistory == 1) {
|
||||
dInfo = derivations[derivationWithHistoryIndex];
|
||||
} else if (derivations.length == 1) {
|
||||
// we only return 1 derivation if we're pretty sure we know which one to use:
|
||||
dInfo = derivations.first;
|
||||
} else {
|
||||
// if we have multiple possible derivations, and none (or multiple) have histories
|
||||
// we just default to the most common one:
|
||||
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
||||
}
|
||||
if (derivationsWithHistory > 1) {
|
||||
dInfo = await Navigator.of(context).pushNamed(
|
||||
Routes.restoreWalletChooseDerivation,
|
||||
arguments: derivations,
|
||||
) as DerivationInfo?;
|
||||
} else if (derivationsWithHistory == 1) {
|
||||
dInfo = derivations[derivationWithHistoryIndex];
|
||||
} else if (derivations.length == 1) {
|
||||
// we only return 1 derivation if we're pretty sure we know which one to use:
|
||||
dInfo = derivations.first;
|
||||
} else {
|
||||
// if we have multiple possible derivations, and none (or multiple) have histories
|
||||
// we just default to the most common one:
|
||||
dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
|
||||
}
|
||||
|
||||
this.derivationInfo = dInfo;
|
||||
this.derivationInfo = dInfo;
|
||||
}
|
||||
|
||||
await walletRestoreViewModel.create(options: _credentials());
|
||||
seedSettingsViewModel.setPassphrase(null);
|
||||
|
|
|
@ -602,6 +602,7 @@ abstract class SettingsStoreBase with Store {
|
|||
static const defaultActionsMode = 11;
|
||||
static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenMinutes;
|
||||
static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized;
|
||||
// static final walletPasswordDirectInput = Platform.isLinux;
|
||||
static final walletPasswordDirectInput = false;
|
||||
static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
|
||||
static const defaultMoneroSeedType = MoneroSeedType.defaultSeedType;
|
||||
|
|
|
@ -38,7 +38,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
wif = '',
|
||||
address = '',
|
||||
super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
|
||||
type: type, isRecovery: true);
|
||||
type: type, isRecovery: true);
|
||||
|
||||
@observable
|
||||
int height;
|
||||
|
@ -113,21 +113,11 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
);
|
||||
case WalletType.bitcoin:
|
||||
case WalletType.litecoin:
|
||||
|
||||
final derivationInfoList = await getDerivationInfoFromQRCredentials(restoreWallet);
|
||||
DerivationInfo derivationInfo;
|
||||
if (derivationInfoList.isEmpty) {
|
||||
derivationInfo = getDefaultCreateDerivation()!;
|
||||
} else {
|
||||
derivationInfo = derivationInfoList.first;
|
||||
}
|
||||
return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: restoreWallet.mnemonicSeed ?? '',
|
||||
password: password,
|
||||
passphrase: restoreWallet.passphrase,
|
||||
derivationType: derivationInfo.derivationType!,
|
||||
derivationPath: derivationInfo.derivationPath!,
|
||||
);
|
||||
case WalletType.bitcoinCash:
|
||||
return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
|
||||
|
@ -144,8 +134,7 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
passphrase: restoreWallet.passphrase,
|
||||
);
|
||||
case WalletType.nano:
|
||||
final derivationInfo =
|
||||
(await getDerivationInfoFromQRCredentials(restoreWallet)).first;
|
||||
final derivationInfo = (await getDerivationInfoFromQRCredentials(restoreWallet)).first;
|
||||
return nano!.createNanoRestoreWalletFromSeedCredentials(
|
||||
name: name,
|
||||
mnemonic: restoreWallet.mnemonicSeed ?? '',
|
||||
|
@ -190,8 +179,8 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
|
|||
}
|
||||
|
||||
@override
|
||||
Future<WalletBase> processFromRestoredWallet(WalletCredentials credentials,
|
||||
RestoredWallet restoreWallet) async {
|
||||
Future<WalletBase> processFromRestoredWallet(
|
||||
WalletCredentials credentials, RestoredWallet restoreWallet) async {
|
||||
try {
|
||||
switch (restoreWallet.restoreMode) {
|
||||
case WalletRestoreMode.keys:
|
||||
|
|
|
@ -105,8 +105,6 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
|
|||
mnemonic: seed,
|
||||
password: password,
|
||||
passphrase: passphrase,
|
||||
derivationType: derivationInfo!.derivationType!,
|
||||
derivationPath: derivationInfo.derivationPath!,
|
||||
);
|
||||
case WalletType.haven:
|
||||
return haven!.createHavenRestoreWalletFromSeedCredentials(
|
||||
|
|
|
@ -148,8 +148,6 @@ abstract class Bitcoin {
|
|||
required String name,
|
||||
required String mnemonic,
|
||||
required String password,
|
||||
required DerivationType derivationType,
|
||||
required String derivationPath,
|
||||
String? passphrase,
|
||||
});
|
||||
WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo});
|
||||
|
|
Loading…
Reference in a new issue