diff --git a/cw_bitcoin/lib/bitcoin_transaction_priority.dart b/cw_bitcoin/lib/bitcoin_transaction_priority.dart index d51775368..7c4dcfd5f 100644 --- a/cw_bitcoin/lib/bitcoin_transaction_priority.dart +++ b/cw_bitcoin/lib/bitcoin_transaction_priority.dart @@ -37,7 +37,7 @@ class BitcoinTransactionPriority extends TransactionPriority { switch (this) { case BitcoinTransactionPriority.slow: - label = 'Slow ~24hrs'; // '${S.current.transaction_priority_slow} ~24hrs'; + label = 'Slow ~24hrs+'; // '${S.current.transaction_priority_slow} ~24hrs'; break; case BitcoinTransactionPriority.medium: label = 'Medium'; // S.current.transaction_priority_medium; diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 9b9597123..e0548771b 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -3,7 +3,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; -import 'package:cw_core/node.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_service.dart'; @@ -127,8 +126,4 @@ class BitcoinWalletService extends WalletService getInfoFromSeed({required String seed, required Node node}) async { - throw UnimplementedError(); - } } diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 655c8ab74..8342e4816 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -206,10 +206,14 @@ abstract class ElectrumWalletBase List privateKeys = []; int allInputsAmount = 0; + bool spendsUnconfirmedTX = false; + for (int i = 0; i < unspentCoins.length; i++) { final utx = unspentCoins[i]; - if (utx.isSending) { + if (utx.isSending && !utx.isFrozen) { + if (!spendsUnconfirmedTX) spendsUnconfirmedTX = utx.confirmations == 0; + allInputsAmount += utx.value; final address = addressTypeFromStr(utx.address, network); @@ -267,6 +271,10 @@ abstract class ElectrumWalletBase // Here, when sending all, the output amount equals to the input value - fee to fully spend every input on the transaction and have no amount left for change int amount = allInputsAmount - fee; + if (amount <= 0) { + throw BitcoinTransactionWrongBalanceException(); + } + // Attempting to send less than the dust limit if (_isBelowDust(amount)) { throw BitcoinTransactionNoDustException(); @@ -291,6 +299,7 @@ abstract class ElectrumWalletBase isSendAll: true, hasChange: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -300,17 +309,25 @@ abstract class ElectrumWalletBase int feeRate, { int? inputsCount, String? memo, + bool? useUnconfirmed, }) async { final utxos = []; List privateKeys = []; int allInputsAmount = 0; + bool spendsUnconfirmedTX = false; int leftAmount = credentialsAmount; - final sendingCoins = unspentCoins.where((utx) => utx.isSending).toList(); + final sendingCoins = unspentCoins.where((utx) => utx.isSending && !utx.isFrozen).toList(); + final unconfirmedCoins = sendingCoins.where((utx) => utx.confirmations == 0).toList(); for (int i = 0; i < sendingCoins.length; i++) { final utx = sendingCoins[i]; + final isUncormirmed = utx.confirmations == 0; + if (useUnconfirmed != true && isUncormirmed) continue; + + if (!spendsUnconfirmedTX) spendsUnconfirmedTX = isUncormirmed; + allInputsAmount += utx.value; leftAmount = leftAmount - utx.value; @@ -348,11 +365,23 @@ abstract class ElectrumWalletBase } final spendingAllCoins = sendingCoins.length == utxos.length; + final spendingAllConfirmedCoins = + !spendsUnconfirmedTX && utxos.length == sendingCoins.length - unconfirmedCoins.length; // How much is being spent - how much is being sent int amountLeftForChangeAndFee = allInputsAmount - credentialsAmount; if (amountLeftForChangeAndFee <= 0) { + if (!spendingAllCoins) { + return estimateTxForAmount( + credentialsAmount, + outputs, + feeRate, + inputsCount: utxos.length + 1, + memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, + ); + } throw BitcoinTransactionWrongBalanceException(); } @@ -406,6 +435,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } @@ -452,6 +482,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } } @@ -464,6 +495,7 @@ abstract class ElectrumWalletBase hasChange: true, isSendAll: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -534,7 +566,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } else { txb = BitcoinTransactionBuilder( @@ -544,7 +576,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } @@ -727,6 +759,7 @@ abstract class ElectrumWalletBase final tx = await fetchTransactionInfo( hash: coin.hash, height: 0, myAddresses: addressesSet); coin.isChange = tx?.direction == TransactionDirection.outgoing; + coin.confirmations = tx?.confirmations; updatedUnspentCoins.add(coin); } catch (_) {} })))); @@ -751,6 +784,7 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1043,9 +1077,11 @@ abstract class ElectrumWalletBase return Future.wait(addressesByType.map((addressRecord) async { final history = await _fetchAddressHistory(addressRecord, addressesSet, currentHeight); + final balance = await electrumClient.getBalance(addressRecord.scriptHash!); if (history.isNotEmpty) { addressRecord.txCount = history.length; + addressRecord.balance = balance['confirmed'] as int? ?? 0; historiesWithDetails.addAll(history); final matchedAddresses = @@ -1276,6 +1312,7 @@ class EstimatedTxResult { required this.hasChange, required this.isSendAll, this.memo, + required this.spendsUnconfirmedTX, }); final List utxos; @@ -1285,6 +1322,7 @@ class EstimatedTxResult { final bool hasChange; final bool isSendAll; final String? memo; + final bool spendsUnconfirmedTX; } BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) { diff --git a/cw_bitcoin/lib/exceptions.dart b/cw_bitcoin/lib/exceptions.dart index 4b03eb922..979c1a433 100644 --- a/cw_bitcoin/lib/exceptions.dart +++ b/cw_bitcoin/lib/exceptions.dart @@ -15,7 +15,9 @@ class BitcoinTransactionNoDustOnChangeException extends TransactionNoDustOnChang BitcoinTransactionNoDustOnChangeException(super.max, super.min); } -class BitcoinTransactionCommitFailed extends TransactionCommitFailed {} +class BitcoinTransactionCommitFailed extends TransactionCommitFailed { + BitcoinTransactionCommitFailed({super.errorMessage}); +} class BitcoinTransactionCommitFailedDustChange extends TransactionCommitFailedDustChange {} diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index d68aa5216..be2afeba4 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -11,6 +11,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:collection/collection.dart'; +import 'package:bip39/bip39.dart' as bip39; class LitecoinWalletService extends WalletService< BitcoinNewWalletCredentials, @@ -100,7 +101,7 @@ class LitecoinWalletService extends WalletService< @override Future restoreFromSeed( BitcoinRestoreWalletFromSeedCredentials credentials, {bool? isTestnet}) async { - if (!validateMnemonic(credentials.mnemonic)) { + if (!validateMnemonic(credentials.mnemonic) && !bip39.validateMnemonic(credentials.mnemonic)) { throw LitecoinMnemonicIsIncorrectException(); } diff --git a/cw_bitcoin/lib/pending_bitcoin_transaction.dart b/cw_bitcoin/lib/pending_bitcoin_transaction.dart index 529ac61da..a59b4f429 100644 --- a/cw_bitcoin/lib/pending_bitcoin_transaction.dart +++ b/cw_bitcoin/lib/pending_bitcoin_transaction.dart @@ -73,7 +73,9 @@ class PendingBitcoinTransaction with PendingTransaction { if (error.contains("bad-txns-vout-negative")) { throw BitcoinTransactionCommitFailedVoutNegative(); } + throw BitcoinTransactionCommitFailed(errorMessage: error); } + throw BitcoinTransactionCommitFailed(); } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index 3d828243c..86d58b9b1 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: args - sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted - version: "2.4.2" + version: "2.5.0" asn1lib: dependency: transitive description: @@ -80,7 +80,7 @@ packages: description: path: "." ref: cake-update-v2 - resolved-ref: "3fd81d238b990bb767fc7a4fdd5053a22a142e2e" + resolved-ref: "01d844a5f5a520a31df5254e34169af4664aa769" url: "https://github.com/cake-tech/bitcoin_base.git" source: git version: "4.2.0" @@ -153,10 +153,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + sha256: "3ac61a79bfb6f6cc11f693591063a7f19a7af628dc52f141743edac5c16e8c22" url: "https://pub.dev" source: hosted - version: "2.4.8" + version: "2.4.9" build_runner_core: dependency: transitive description: @@ -177,10 +177,10 @@ packages: dependency: transitive description: name: built_value - sha256: a3ec2e0f967bc47f69f95009bb93db936288d61d5343b9436e378b28a2f830c6 + sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb url: "https://pub.dev" source: hosted - version: "8.9.0" + version: "8.9.2" characters: dependency: transitive description: @@ -309,10 +309,10 @@ packages: dependency: "direct main" description: name: flutter_mobx - sha256: "4a5d062ff85ed3759f4aac6410ff0ffae32e324b2e71ca722ae1b37b32e865f4" + sha256: "859fbf452fa9c2519d2700b125dd7fb14c508bbdd7fb65e26ca8ff6c92280e2e" url: "https://pub.dev" source: hosted - version: "2.2.0+2" + version: "2.2.1+1" flutter_test: dependency: "direct dev" description: flutter @@ -322,10 +322,10 @@ packages: dependency: transitive description: name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "4.0.0" glob: dependency: transitive description: @@ -466,10 +466,10 @@ packages: dependency: "direct main" description: name: mobx - sha256: "74ee54012dc7c1b3276eaa960a600a7418ef5f9997565deb8fca1fd88fb36b78" + sha256: "63920b27b32ad1910adfe767ab1750e4c212e8923232a1f891597b362074ea5e" url: "https://pub.dev" source: hosted - version: "2.3.0+1" + version: "2.3.3+2" mobx_codegen: dependency: "direct dev" description: @@ -570,10 +570,10 @@ packages: dependency: transitive description: name: pointycastle - sha256: "43ac87de6e10afabc85c445745a7b799e04de84cebaa4fd7bf55a5e1e9604d29" + sha256: "70fe966348fe08c34bf929582f1d8247d9d9408130723206472b4687227e4333" url: "https://pub.dev" source: hosted - version: "3.7.4" + version: "3.8.0" pool: dependency: transitive description: @@ -586,10 +586,10 @@ packages: dependency: transitive description: name: provider - sha256: "9a96a0a19b594dbc5bf0f1f27d2bc67d5f95957359b461cd9feb44ed6ae75096" + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" pub_semver: dependency: transitive description: diff --git a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart index da4710a8b..6d2ab4696 100644 --- a/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart +++ b/cw_bitcoin_cash/lib/src/pending_bitcoin_cash_transaction.dart @@ -62,7 +62,9 @@ class PendingBitcoinCashTransaction with PendingTransaction { if (error.contains("bad-txns-vout-negative")) { throw BitcoinTransactionCommitFailedVoutNegative(); } + throw BitcoinTransactionCommitFailed(errorMessage: error); } + throw BitcoinTransactionCommitFailed(); } diff --git a/cw_core/lib/exceptions.dart b/cw_core/lib/exceptions.dart index 848ac40e6..d07da8109 100644 --- a/cw_core/lib/exceptions.dart +++ b/cw_core/lib/exceptions.dart @@ -19,7 +19,11 @@ class TransactionNoDustOnChangeException implements Exception { final String min; } -class TransactionCommitFailed implements Exception {} +class TransactionCommitFailed implements Exception { + final String? errorMessage; + + TransactionCommitFailed({this.errorMessage}); +} class TransactionCommitFailedDustChange implements Exception {} diff --git a/cw_core/lib/unspent_transaction_output.dart b/cw_core/lib/unspent_transaction_output.dart index b52daf43c..595df18f4 100644 --- a/cw_core/lib/unspent_transaction_output.dart +++ b/cw_core/lib/unspent_transaction_output.dart @@ -14,6 +14,7 @@ class Unspent { bool isChange; bool isSending; bool isFrozen; + int? confirmations; String note; bool get isP2wpkh => address.startsWith('bc') || address.startsWith('ltc'); diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index e29332bc4..29ef5ffdd 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -416,13 +416,14 @@ class CWBitcoin extends Bitcoin { } @override - int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, - {int? size}) { + int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, + {int? outputsCount, int? size}) { final bitcoinWallet = wallet as ElectrumWallet; - return bitcoinWallet.feeAmountWithFeeRate( + return bitcoinWallet.calculateEstimatedFeeWithFeeRate( feeRate, - inputsCount, - outputsCount, + amount, + outputsCount: outputsCount, + size: size, ); } diff --git a/lib/core/wallet_loading_service.dart b/lib/core/wallet_loading_service.dart index 3323e7831..1f17a7a1c 100644 --- a/lib/core/wallet_loading_service.dart +++ b/lib/core/wallet_loading_service.dart @@ -1,27 +1,28 @@ import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cw_core/cake_hive.dart'; import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class WalletLoadingService { - WalletLoadingService( - this.sharedPreferences, this.keyService, this.walletServiceFactory); + WalletLoadingService(this.sharedPreferences, this.keyService, this.walletServiceFactory); final SharedPreferences sharedPreferences; final KeyService keyService; final WalletService Function(WalletType type) walletServiceFactory; - Future renameWallet( - WalletType type, String name, String newName) async { + Future renameWallet(WalletType type, String name, String newName) async { final walletService = walletServiceFactory.call(type); final password = await keyService.getWalletPassword(walletName: name); // Save the current wallet's password to the new wallet name's key - await keyService.saveWalletPassword( - walletName: newName, password: password); + await keyService.saveWalletPassword(walletName: newName, password: password); // Delete previous wallet name from keyService to keep only new wallet's name // otherwise keeps duplicate (old and new names) await keyService.deleteWalletPassword(walletName: name); @@ -38,15 +39,43 @@ class WalletLoadingService { } Future load(WalletType type, String name) async { - final walletService = walletServiceFactory.call(type); - final password = await keyService.getWalletPassword(walletName: name); - final wallet = await walletService.openWallet(name, password); + try { + final walletService = walletServiceFactory.call(type); + final password = await keyService.getWalletPassword(walletName: name); + final wallet = await walletService.openWallet(name, password); - if (type == WalletType.monero) { - await updateMoneroWalletPassword(wallet); + if (type == WalletType.monero) { + await updateMoneroWalletPassword(wallet); + } + + return wallet; + } catch (error, stack) { + ExceptionHandler.onError(FlutterErrorDetails(exception: error, stack: stack)); + + // try opening another wallet that is not corrupted to give user access to the app + final walletInfoSource = await CakeHive.openBox(WalletInfo.boxName); + + for (var walletInfo in walletInfoSource.values) { + try { + final walletService = walletServiceFactory.call(walletInfo.type); + final password = await keyService.getWalletPassword(walletName: walletInfo.name); + final wallet = await walletService.openWallet(walletInfo.name, password); + + if (walletInfo.type == WalletType.monero) { + await updateMoneroWalletPassword(wallet); + } + + await sharedPreferences.setString(PreferencesKey.currentWalletName, wallet.name); + await sharedPreferences.setInt( + PreferencesKey.currentWalletType, serializeToInt(wallet.type)); + + return wallet; + } catch (_) {} + } + + // if all user's wallets are corrupted throw exception + throw error; } - - return wallet; } Future updateMoneroWalletPassword(WalletBase wallet) async { @@ -61,11 +90,9 @@ class WalletLoadingService { // Save new generated password with backup key for case where // wallet will change password, but it will fail to update in secure storage final bakWalletName = '#__${wallet.name}_bak__#'; - await keyService.saveWalletPassword( - walletName: bakWalletName, password: password); + await keyService.saveWalletPassword(walletName: bakWalletName, password: password); await wallet.changePassword(password); - await keyService.saveWalletPassword( - walletName: wallet.name, password: password); + await keyService.saveWalletPassword(walletName: wallet.name, password: password); isPasswordUpdated = true; await sharedPreferences.setBool(key, isPasswordUpdated); } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index ac24d92de..f44da653f 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -379,20 +379,24 @@ class WalletRestorePage extends BasePage { dInfo = derivations[derivationWithHistoryIndex]; } + // get the default derivation for this wallet type: if (dInfo == null) { - walletRestoreViewModel.state = InitialExecutionState(); - return; + // we only return 1 derivation if we're pretty sure we know which one to use: + if (derivations.length == 1) { + dInfo = derivations.first; + } else { + // if we have multiple possible derivations, and none have histories + // we just default to the most common one: + dInfo = walletRestoreViewModel.getMostCommonDerivation(); + } } this.derivationInfo = dInfo; - - // get the default derivation for this wallet type: if (this.derivationInfo == null) { - this.derivationInfo = walletRestoreViewModel.getMostCommonDerivation(); + walletRestoreViewModel.state = InitialExecutionState(); + return; } - walletRestoreViewModel.state = InitialExecutionState(); - walletRestoreViewModel.create(options: _credentials()); } diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 970bb31f2..93fadea72 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -100,7 +100,7 @@ class SendPage extends BasePage { AppBarStyle get appBarStyle => AppBarStyle.transparent; double _sendCardHeight(BuildContext context) { - double initialHeight = 450; + double initialHeight = 480; if (sendViewModel.hasCoinControl) { initialHeight += 35; } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index b57473cba..81c78b1ab 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -343,7 +343,9 @@ class WalletListBodyState extends State { }); } } catch (e) { - changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + if (this.mounted) { + changeProcessText(S.of(context).wallet_list_failed_to_load(wallet.name, e.toString())); + } } }, conditionToDetermineIfToUse2FA: diff --git a/lib/view_model/send/output.dart b/lib/view_model/send/output.dart index 6bb3fbb31..07d98ff32 100644 --- a/lib/view_model/send/output.dart +++ b/lib/view_model/send/output.dart @@ -126,8 +126,8 @@ abstract class OutputBase with Store { if (_wallet.type == WalletType.bitcoin) { if (_settingsStore.priority[_wallet.type] == bitcoin!.getBitcoinTransactionPriorityCustom()) { - fee = bitcoin!.getFeeAmountWithFeeRate( - _settingsStore.customBitcoinFeeRate, formattedCryptoAmount, 1, 1); + fee = bitcoin!.getEstimatedFeeWithFeeRate(_wallet, + _settingsStore.customBitcoinFeeRate,formattedCryptoAmount); } return bitcoin!.formatterBitcoinAmountToDouble(amount: fee); diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 78a0d7125..6c0c3870b 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -562,7 +562,7 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor return S.current.tx_no_dust_exception; } if (error is TransactionCommitFailed) { - return S.current.tx_commit_failed; + return "${S.current.tx_commit_failed}${error.errorMessage != null ? "\n\n${error.errorMessage}" : ""}"; } if (error is TransactionCommitFailedDustChange) { return S.current.tx_rejected_dust_change; diff --git a/lib/view_model/transaction_details_view_model.dart b/lib/view_model/transaction_details_view_model.dart index be2ebc545..faa49dfc4 100644 --- a/lib/view_model/transaction_details_view_model.dart +++ b/lib/view_model/transaction_details_view_model.dart @@ -390,16 +390,12 @@ abstract class TransactionDetailsViewModelBase with Store { String setNewFee({double? value, required TransactionPriority priority}) { newFee = priority == bitcoin!.getBitcoinTransactionPriorityCustom() && value != null - ? bitcoin!.getFeeAmountWithFeeRate( - wallet, - value.round(), - transactionInfo.inputAddresses?.length ?? 1, - transactionInfo.outputAddresses?.length ?? 1) + ? bitcoin!.getEstimatedFeeWithFeeRate(wallet, value.round(), transactionInfo.amount) : bitcoin!.getFeeAmountForPriority( - wallet, - priority, - transactionInfo.inputAddresses?.length ?? 1, - transactionInfo.outputAddresses?.length ?? 1); + wallet, + priority, + transactionInfo.inputAddresses?.length ?? 1, + transactionInfo.outputAddresses?.length ?? 1); return bitcoin!.formatterBitcoinAmountToString(amount: newFee); } diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index 87666a298..b508e2f81 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -99,14 +99,14 @@ abstract class WalletCreationVMBase with Store { case WalletType.litecoin: return DerivationInfo( derivationType: DerivationType.electrum, - derivationPath: "m/0'/0", + derivationPath: "m/0'", ); default: return null; } } - DerivationInfo? getMostCommonDerivation() { + DerivationInfo? getCommonRestoreDerivation() { switch (this.type) { case WalletType.nano: return DerivationInfo( @@ -115,8 +115,10 @@ abstract class WalletCreationVMBase with Store { case WalletType.bitcoin: case WalletType.litecoin: return DerivationInfo( - derivationType: DerivationType.electrum, + derivationType: DerivationType.bip39, derivationPath: "m/84'/0'/0'/0", + description: "Standard BIP84 native segwit", + scriptType: "p2wpkh", ); default: return null; diff --git a/tool/configure.dart b/tool/configure.dart index f025d71ef..c637aebc3 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -180,7 +180,7 @@ abstract class Bitcoin { Future canReplaceByFee(Object wallet, String transactionHash); Future isChangeSufficientForFee(Object wallet, String txId, String newFee); int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size}); - int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size}); + int getEstimatedFeeWithFeeRate(Object wallet, int feeRate, int? amount, {int? outputsCount, int? size}); int getMaxCustomFeeRate(Object wallet); } """;