diff --git a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart index 04a3cae36..1a122ef9e 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_addresses.dart @@ -2,6 +2,7 @@ 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_bitcoin/utils.dart'; +import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:mobx/mobx.dart'; @@ -26,7 +27,10 @@ abstract class BitcoinWalletAddressesBase extends ElectrumWalletAddresses with S @override String getAddress( - {required int index, required Bip32Slip10Secp256k1 hd, BitcoinAddressType? addressType}) { + {required int index, + required Bip32Slip10Secp256k1 hd, + BitcoinAddressType? addressType, + UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) { if (addressType == P2pkhAddressType.p2pkh) return generateP2PKHAddress(hd: hd, index: index, network: network); diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 9a7e45d05..e7b8be156 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -842,6 +842,7 @@ abstract class ElectrumWalletBase final changeAddress = await walletAddresses.getChangeAddress( inputs: utxoDetails.availableInputs, outputs: updatedOutputs, + coinTypeToSpendFrom: coinTypeToSpendFrom, ); final address = RegexUtils.addressTypeFromStr(changeAddress.address, network); updatedOutputs.add(BitcoinOutput( diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index 13a32c68c..35c15e578 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -4,6 +4,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/electrum_wallet.dart'; +import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_core/wallet_addresses.dart'; @@ -47,7 +48,6 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { List<BitcoinAddressRecord>? initialMwebAddresses, Bip32Slip10Secp256k1? masterHd, BitcoinAddressType? initialAddressPageType, - }) : _addresses = ObservableList<BitcoinAddressRecord>.of((initialAddresses ?? []).toSet()), addressesByReceiveType = ObservableList<BaseBitcoinAddressRecord>.of((<BitcoinAddressRecord>[]).toSet()), @@ -187,13 +187,13 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return; } try { - final addressRecord = _addresses.firstWhere( - (addressRecord) => addressRecord.address == addr, - ); + final addressRecord = _addresses.firstWhere( + (addressRecord) => addressRecord.address == addr, + ); - previousAddressRecord = addressRecord; - receiveAddresses.remove(addressRecord); - receiveAddresses.insert(0, addressRecord); + previousAddressRecord = addressRecord; + receiveAddresses.remove(addressRecord); + receiveAddresses.insert(0, addressRecord); } catch (e) { printV("ElectrumWalletAddressBase: set address ($addr): $e"); } @@ -274,7 +274,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { } @action - Future<BitcoinAddressRecord> getChangeAddress({List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async { + Future<BitcoinAddressRecord> getChangeAddress( + {List<BitcoinUnspent>? inputs, + List<BitcoinOutput>? outputs, + UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async { updateChangeAddresses(); if (changeAddresses.isEmpty) { diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 79dcbf415..5bd25785f 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -9,6 +9,7 @@ import 'package:crypto/crypto.dart'; import 'package:cw_bitcoin/bitcoin_transaction_credentials.dart'; import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/mweb_utxo.dart'; +import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/node.dart'; import 'package:cw_mweb/mwebd.pbgrpc.dart'; @@ -394,7 +395,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { // if the confirmations haven't changed, skip updating: if (tx.confirmations == confirmations) continue; - // if an outgoing tx is now confirmed, delete the utxo from the box (delete the unspent coin): if (confirmations >= 2 && tx.direction == TransactionDirection.outgoing && @@ -966,10 +966,19 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { List<ECPrivateInfo>? inputPrivKeyInfos, List<Outpoint>? vinOutpoints, }) async { - final spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); - final paysToMweb = outputs + bool spendsMweb = utxos.any((utxo) => utxo.utxo.scriptType == SegwitAddresType.mweb); + bool paysToMweb = outputs .any((output) => output.toOutput.scriptPubKey.getAddressType() == SegwitAddresType.mweb); - if (!spendsMweb && !paysToMweb) { + + bool isRegular = !spendsMweb && !paysToMweb; + bool isMweb = spendsMweb || paysToMweb; + + if (isMweb && !mwebEnabled) { + throw Exception("MWEB is not enabled! can't calculate fee without starting the mweb server!"); + // TODO: likely the change address is mweb and just not updated + } + + if (isRegular) { return await super.calcFee( utxos: utxos, outputs: outputs, @@ -981,10 +990,6 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } - if (!mwebEnabled) { - throw Exception("MWEB is not enabled! can't calculate fee without starting the mweb server!"); - } - if (outputs.length == 1 && outputs[0].toOutput.amount == BigInt.zero) { outputs = [ BitcoinScriptOutput( @@ -1055,7 +1060,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { if (!mwebEnabled) { tx.changeAddressOverride = - (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(isPegIn: false)) + (await (walletAddresses as LitecoinWalletAddresses).getChangeAddress(coinTypeToSpendFrom: UnspentCoinType.nonMweb)) .address; return tx; } @@ -1103,13 +1108,15 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } } + // could probably be simplified but left for clarity: bool isPegIn = !hasMwebInput && hasMwebOutput; bool isPegOut = hasMwebInput && hasRegularOutput; bool isRegular = !hasMwebInput && !hasMwebOutput; + bool shouldNotUseMwebChange = isPegIn || isRegular || !hasMwebInput; tx.changeAddressOverride = (await (walletAddresses as LitecoinWalletAddresses) - .getChangeAddress(isPegIn: isPegIn || isRegular)) + .getChangeAddress(coinTypeToSpendFrom: shouldNotUseMwebChange ? UnspentCoinType.nonMweb : UnspentCoinType.any)) .address; - if (!hasMwebInput && !hasMwebOutput) { + if (isRegular) { tx.isMweb = false; return tx; } @@ -1212,7 +1219,9 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { } Future<void> setMwebEnabled(bool enabled) async { - if (mwebEnabled == enabled) { + if (mwebEnabled == enabled && + alwaysScan == enabled && + (walletAddresses as LitecoinWalletAddresses).mwebEnabled == enabled) { return; } diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index afe3c75b8..bbb987766 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -9,6 +9,7 @@ import 'package:cw_bitcoin/bitcoin_unspent.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; +import 'package:cw_core/unspent_coin_type.dart'; import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_mweb/cw_mweb.dart'; @@ -148,10 +149,12 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with @action @override Future<BitcoinAddressRecord> getChangeAddress( - {List<BitcoinUnspent>? inputs, List<BitcoinOutput>? outputs, bool isPegIn = false}) async { + {List<BitcoinUnspent>? inputs, + List<BitcoinOutput>? outputs, + UnspentCoinType coinTypeToSpendFrom = UnspentCoinType.any}) async { // use regular change address on peg in, otherwise use mweb for change address: - if (!mwebEnabled || isPegIn) { + if (!mwebEnabled || coinTypeToSpendFrom == UnspentCoinType.nonMweb) { return super.getChangeAddress(); } @@ -178,19 +181,17 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with }); bool isPegIn = !comesFromMweb && outputsToMweb; + bool isNonMweb = !comesFromMweb && !outputsToMweb; - if (isPegIn && mwebEnabled) { - return super.getChangeAddress(); - } - - // use regular change address if it's not an mweb tx: - if (!comesFromMweb && !outputsToMweb) { + // use regular change address if it's not an mweb tx or if it's a peg in: + if (isPegIn || isNonMweb) { return super.getChangeAddress(); } } if (mwebEnabled) { await ensureMwebAddressUpToIndexExists(1); + updateChangeAddresses(); return BitcoinAddressRecord( mwebAddrs[0], index: 0, diff --git a/lib/src/screens/settings/mweb_logs_page.dart b/lib/src/screens/settings/mweb_logs_page.dart index 3c5470214..ae476052e 100644 --- a/lib/src/screens/settings/mweb_logs_page.dart +++ b/lib/src/screens/settings/mweb_logs_page.dart @@ -77,15 +77,12 @@ class MwebLogsPage extends BasePage { return AlertWithTwoActions( alertTitle: S.of(context).export_backup, alertContent: S.of(context).select_destination, - rightButtonText: S.of(context).save_to_downloads, - leftButtonText: S.of(context).share, - actionRightButton: () async { - const downloadDirPath = "/storage/emulated/0/Download"; - final filePath = downloadDirPath + "/debug.log"; - await mwebSettingsViewModelBase.saveLogsLocally(filePath); + rightButtonText: S.of(context).save, + leftButtonText: S.of(context).cancel, + actionLeftButton: () async { Navigator.of(dialogContext).pop(); }, - actionLeftButton: () async { + actionRightButton: () async { Navigator.of(dialogContext).pop(); try { await share(context); @@ -101,11 +98,8 @@ class MwebLogsPage extends BasePage { } Future<void> share(BuildContext context) async { - final filePath = (await getAppDir()).path + "/debug.log"; - bool success = await mwebSettingsViewModelBase.saveLogsLocally(filePath); - if (!success) return; - await ShareUtil.shareFile(filePath: filePath, fileName: "debug.log", context: context); - await mwebSettingsViewModelBase.removeLogsLocally(filePath); + final inAppPath = "${(await getApplicationSupportDirectory()).path}/logs/debug.log"; + await ShareUtil.shareFile(filePath: inAppPath, fileName: "debug.log", context: context); } Future<void> _saveFile() async { diff --git a/lib/view_model/settings/mweb_settings_view_model.dart b/lib/view_model/settings/mweb_settings_view_model.dart index 11e4c8177..151854a2e 100644 --- a/lib/view_model/settings/mweb_settings_view_model.dart +++ b/lib/view_model/settings/mweb_settings_view_model.dart @@ -3,6 +3,8 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cw_core/root_dir.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:flutter/widgets.dart'; import 'package:mobx/mobx.dart'; @@ -47,25 +49,6 @@ abstract class MwebSettingsViewModelBase with Store { _settingsStore.mwebAlwaysScan = value; } - Future<bool> saveLogsLocally(String filePath) async { - try { - final appSupportPath = (await getApplicationSupportDirectory()).path; - final logsFile = File("$appSupportPath/logs/debug.log"); - if (!logsFile.existsSync()) { - throw Exception('Logs file does not exist'); - } - await logsFile.copy(filePath); - return true; - } catch (e, s) { - ExceptionHandler.onError(FlutterErrorDetails( - exception: e, - stack: s, - library: "Export Logs", - )); - return false; - } - } - Future<String> getAbbreviatedLogs() async { final appSupportPath = (await getApplicationSupportDirectory()).path; final logsFile = File("$appSupportPath/logs/debug.log");