From 7fcf48f91da3d82121704bf25c530549a24c9322 Mon Sep 17 00:00:00 2001 From: Serhii Date: Fri, 26 Apr 2024 19:18:26 +0300 Subject: [PATCH 1/6] Fix estimated fee calculation for customs fee rate (#1406) * Update output.dart * fix estimated fee calculation * Update bitcoin_transaction_priority.dart --- cw_bitcoin/lib/bitcoin_transaction_priority.dart | 2 +- lib/bitcoin/cw_bitcoin.dart | 11 ++++++----- lib/view_model/send/output.dart | 4 ++-- lib/view_model/transaction_details_view_model.dart | 14 +++++--------- tool/configure.dart | 2 +- 5 files changed, 15 insertions(+), 18 deletions(-) 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/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index 9bdc0f3ac..707f1157b 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -282,13 +282,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/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/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/tool/configure.dart b/tool/configure.dart index 34a39d28b..3b73bfe80 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -158,7 +158,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); } """; From a6dc9bf9d675d27297afbdec616ba5e1d251d566 Mon Sep 17 00:00:00 2001 From: Serhii Date: Fri, 26 Apr 2024 19:18:35 +0300 Subject: [PATCH 2/6] Update electrum_wallet.dart (#1411) --- cw_bitcoin/lib/electrum_wallet.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 5bed6a449..6b305722e 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -1037,9 +1037,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 = From f3160860b1796ef0abe41299eb954ce26c1b899b Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 26 Apr 2024 22:13:44 +0300 Subject: [PATCH 3/6] Better handle corrupted wallets (#1384) * Fix exchanges not showing * Fix button text on Monero receive screen * Temp fix for ERC20 and SPL tokens not having raw value * fallback to other wallets if the current wallet is corrupted so we give user access to the app --------- Co-authored-by: tuxsudo --- lib/core/wallet_loading_service.dart | 61 ++++++++++++++++++++-------- 1 file changed, 44 insertions(+), 17 deletions(-) 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); } From 9e4a7f4331d0ed9a26f5d1c73135a7ae06b4d8c8 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 26 Apr 2024 22:29:31 +0300 Subject: [PATCH 4/6] Enhance bitcoin error message (#1399) * Enhance bitcoin error message * fix: unconfirmed spends, spend confirmed first, wrong balance exception * Minor fixes --------- Co-authored-by: Rafael Saes --- cw_bitcoin/lib/electrum_wallet.dart | 44 +++++++++++++++++-- cw_bitcoin/lib/exceptions.dart | 4 +- .../lib/pending_bitcoin_transaction.dart | 2 + cw_bitcoin/pubspec.lock | 34 +++++++------- .../src/pending_bitcoin_cash_transaction.dart | 2 + cw_core/lib/exceptions.dart | 6 ++- cw_core/lib/unspent_transaction_output.dart | 1 + lib/src/screens/send/send_page.dart | 2 +- lib/view_model/send/send_view_model.dart | 2 +- 9 files changed, 72 insertions(+), 25 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 6b305722e..4a76ee5dd 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -203,10 +203,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); @@ -264,6 +268,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(); @@ -288,6 +296,7 @@ abstract class ElectrumWalletBase isSendAll: true, hasChange: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -297,17 +306,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; @@ -345,11 +362,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(); } @@ -403,6 +432,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } @@ -449,6 +479,7 @@ abstract class ElectrumWalletBase feeRate, inputsCount: utxos.length + 1, memo: memo, + useUnconfirmed: useUnconfirmed ?? spendingAllConfirmedCoins, ); } } @@ -461,6 +492,7 @@ abstract class ElectrumWalletBase hasChange: true, isSendAll: false, memo: memo, + spendsUnconfirmedTX: spendsUnconfirmedTX, ); } @@ -531,7 +563,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } else { txb = BitcoinTransactionBuilder( @@ -541,7 +573,7 @@ abstract class ElectrumWalletBase network: network, memo: estimatedTx.memo, outputOrdering: BitcoinOrdering.none, - enableRBF: true, + enableRBF: !estimatedTx.spendsUnconfirmedTX, ); } @@ -721,6 +753,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 (_) {} })))); @@ -745,6 +778,7 @@ abstract class ElectrumWalletBase coin.isFrozen = coinInfo.isFrozen; coin.isSending = coinInfo.isSending; coin.note = coinInfo.note; + coin.bitcoinAddressRecord.balance += coinInfo.value; } else { _addCoinInfo(coin); } @@ -1272,6 +1306,7 @@ class EstimatedTxResult { required this.hasChange, required this.isSendAll, this.memo, + required this.spendsUnconfirmedTX, }); final List utxos; @@ -1281,6 +1316,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/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 53cf550c8..50cd432c0 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/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/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 2e00d1f0b..cabb723e1 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; From d38ef8a9a823caa1f68c60d1a69746b65e7a3915 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 29 Apr 2024 09:52:53 -0700 Subject: [PATCH 5/6] litecoin mnemonic error fix --- cw_bitcoin/lib/litecoin_wallet_service.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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(); } From 5ecc1a1db61b6b2141c6c7f1c7f84a144f910276 Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 29 Apr 2024 09:57:46 -0700 Subject: [PATCH 6/6] Bip39 passphrase support (#1412) * save * passphrase working * fix for when loading wallets + translation update * minor fix * Fix Nano * minor fix [skip ci] --------- Co-authored-by: OmarHatem --- cw_bitcoin/lib/bitcoin_derivations.dart | 14 ++++++- cw_bitcoin/lib/bitcoin_wallet.dart | 15 +++++++- .../bitcoin_wallet_creation_credentials.dart | 2 + cw_bitcoin/lib/bitcoin_wallet_service.dart | 7 +--- cw_bitcoin/lib/electrum_wallet.dart | 3 ++ cw_bitcoin/lib/electrum_wallet_snapshot.dart | 4 ++ cw_core/lib/wallet_credentials.dart | 2 + cw_core/lib/wallet_info.dart | 37 ++++++++++--------- lib/bitcoin/cw_bitcoin.dart | 22 +++++++---- .../wallet_restore_from_seed_form.dart | 11 ++++++ .../screens/restore/wallet_restore_page.dart | 18 +++++---- .../screens/wallet_list/wallet_list_page.dart | 4 +- .../restore/restore_from_qr_vm.dart | 26 ++++++++----- lib/view_model/wallet_creation_vm.dart | 19 ++++++++++ lib/view_model/wallet_restore_view_model.dart | 18 +++++---- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 5 ++- res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 1 + res/values/strings_ko.arb | 3 +- res/values/strings_my.arb | 1 + res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 1 + res/values/strings_th.arb | 1 + res/values/strings_tl.arb | 1 + res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 1 + res/values/strings_yo.arb | 1 + res/values/strings_zh.arb | 1 + tool/configure.dart | 3 +- 42 files changed, 174 insertions(+), 63 deletions(-) diff --git a/cw_bitcoin/lib/bitcoin_derivations.dart b/cw_bitcoin/lib/bitcoin_derivations.dart index b232dda2c..e2e891dd3 100644 --- a/cw_bitcoin/lib/bitcoin_derivations.dart +++ b/cw_bitcoin/lib/bitcoin_derivations.dart @@ -54,7 +54,19 @@ Map> bitcoin_derivations = { ), DerivationInfo( derivationType: DerivationType.bip39, - derivationPath: "m/44'|49'|84'/0'/0'", + derivationPath: "m/44'/0'/0'", + description: "Samourai Deposit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/49'/0'/0'", + description: "Samourai Deposit", + scriptType: "p2wpkh", + ), + DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/0'", description: "Samourai Deposit", scriptType: "p2wpkh", ), diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index 3bc8ea903..1d29307ca 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -31,8 +31,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { ElectrumBalance? initialBalance, Map? initialRegularAddressIndex, Map? initialChangeAddressIndex, + String? passphrase, }) : super( mnemonic: mnemonic, + passphrase: passphrase, password: password, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, @@ -70,6 +72,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { required String password, required WalletInfo walletInfo, required Box unspentCoinsInfo, + String? passphrase, String? addressPageType, BasedUtxoNetwork? network, List? initialAddresses, @@ -81,7 +84,10 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { switch (walletInfo.derivationInfo?.derivationType) { case DerivationType.bip39: - seedBytes = await bip39.mnemonicToSeed(mnemonic); + seedBytes = await bip39.mnemonicToSeed( + mnemonic, + passphrase: passphrase ?? "", + ); break; case DerivationType.electrum: default: @@ -90,6 +96,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { } return BitcoinWallet( mnemonic: mnemonic, + passphrase: passphrase ?? "", password: password, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, @@ -130,13 +137,17 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { break; case DerivationType.bip39: default: - seedBytes = await bip39.mnemonicToSeed(snp.mnemonic); + seedBytes = await bip39.mnemonicToSeed( + snp.mnemonic, + passphrase: snp.passphrase ?? '', + ); break; } return BitcoinWallet( mnemonic: snp.mnemonic, password: password, + passphrase: snp.passphrase, walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp.addresses, diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index af255f97c..981c7a466 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -21,9 +21,11 @@ class BitcoinRestoreWalletFromSeedCredentials extends WalletCredentials { WalletInfo? walletInfo, required DerivationType derivationType, required String derivationPath, + String? passphrase, }) : super( name: name, password: password, + passphrase: passphrase, walletInfo: walletInfo, derivationInfo: DerivationInfo( derivationType: derivationType, diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index 032a03d75..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'; @@ -33,6 +32,7 @@ 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 226e12c05..8342e4816 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -55,6 +55,7 @@ abstract class ElectrumWalletBase required this.networkType, required this.mnemonic, required Uint8List seedBytes, + this.passphrase, List? initialAddresses, ElectrumClient? electrumClient, ElectrumBalance? initialBalance, @@ -93,6 +94,7 @@ abstract class ElectrumWalletBase final bitcoin.HDWallet hd; final String mnemonic; + final String? passphrase; @override @observable @@ -618,6 +620,7 @@ abstract class ElectrumWalletBase String toJSON() => json.encode({ 'mnemonic': mnemonic, + 'passphrase': passphrase ?? '', 'account_index': walletAddresses.currentReceiveAddressIndexByType, 'change_address_index': walletAddresses.currentChangeAddressIndexByType, 'addresses': walletAddresses.allAddresses.map((addr) => addr.toJSON()).toList(), diff --git a/cw_bitcoin/lib/electrum_wallet_snapshot.dart b/cw_bitcoin/lib/electrum_wallet_snapshot.dart index e8e8c6777..218792e3c 100644 --- a/cw_bitcoin/lib/electrum_wallet_snapshot.dart +++ b/cw_bitcoin/lib/electrum_wallet_snapshot.dart @@ -18,6 +18,7 @@ class ElectrumWalletSnapshot { required this.regularAddressIndex, required this.changeAddressIndex, required this.addressPageType, + this.passphrase, this.derivationType, this.derivationPath, }); @@ -32,6 +33,7 @@ class ElectrumWalletSnapshot { ElectrumBalance balance; Map regularAddressIndex; Map changeAddressIndex; + String? passphrase; DerivationType? derivationType; String? derivationPath; @@ -42,6 +44,7 @@ class ElectrumWalletSnapshot { final data = json.decode(jsonSource) as Map; final addressesTmp = data['addresses'] as List? ?? []; final mnemonic = data['mnemonic'] as String; + final passphrase = data['passphrase'] as String? ?? ''; final addresses = addressesTmp .whereType() .map((addr) => BitcoinAddressRecord.fromJSON(addr, network)) @@ -74,6 +77,7 @@ class ElectrumWalletSnapshot { name: name, type: type, password: password, + passphrase: passphrase, mnemonic: mnemonic, addresses: addresses, balance: balance, diff --git a/cw_core/lib/wallet_credentials.dart b/cw_core/lib/wallet_credentials.dart index 0ce2f80ab..9b28680f9 100644 --- a/cw_core/lib/wallet_credentials.dart +++ b/cw_core/lib/wallet_credentials.dart @@ -7,6 +7,7 @@ abstract class WalletCredentials { this.seedPhraseLength, this.walletInfo, this.password, + this.passphrase, this.derivationInfo, }) { if (this.walletInfo != null && derivationInfo != null) { @@ -18,6 +19,7 @@ abstract class WalletCredentials { final int? height; int? seedPhraseLength; String? password; + String? passphrase; WalletInfo? walletInfo; DerivationInfo? derivationInfo; } diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 381a96068..4892f6d1d 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -19,6 +19,7 @@ enum DerivationType { @HiveField(4) electrum, } + @HiveType(typeId: DerivationInfo.typeId) class DerivationInfo extends HiveObject { DerivationInfo({ @@ -33,11 +34,10 @@ class DerivationInfo extends HiveObject { static const typeId = DERIVATION_INFO_TYPE_ID; - - @HiveField(0) + @HiveField(0, defaultValue: '') String address; - @HiveField(1) + @HiveField(1, defaultValue: '') String balance; @HiveField(2) @@ -90,19 +90,20 @@ class WalletInfo extends HiveObject { DerivationInfo? derivationInfo, }) { return WalletInfo( - id, - name, - type, - isRecovery, - restoreHeight, - date.millisecondsSinceEpoch, - dirPath, - path, - address, - yatEid, - yatLastUsedAddressRaw, - showIntroCakePayCard, - derivationInfo); + id, + name, + type, + isRecovery, + restoreHeight, + date.millisecondsSinceEpoch, + dirPath, + path, + address, + yatEid, + yatLastUsedAddressRaw, + showIntroCakePayCard, + derivationInfo, + ); } static const typeId = WALLET_INFO_TYPE_ID; @@ -154,10 +155,10 @@ class WalletInfo extends HiveObject { List? usedAddresses; @HiveField(16) - DerivationType? derivationType;// no longer used + DerivationType? derivationType; // no longer used @HiveField(17) - String? derivationPath;// no longer used + String? derivationPath; // no longer used @HiveField(18) String? addressPageType; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index b19ea4236..aef36c45e 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -7,13 +7,16 @@ class CWBitcoin extends Bitcoin { required String password, required DerivationType derivationType, required String derivationPath, + String? passphrase, }) => BitcoinRestoreWalletFromSeedCredentials( - name: name, - mnemonic: mnemonic, - password: password, - derivationType: derivationType, - derivationPath: derivationPath); + name: name, + mnemonic: mnemonic, + password: password, + derivationType: derivationType, + derivationPath: derivationPath, + passphrase: passphrase, + ); @override WalletCredentials createBitcoinRestoreWalletFromWIFCredentials( @@ -277,8 +280,11 @@ class CWBitcoin extends Bitcoin { } @override - Future> getDerivationsFromMnemonic( - {required String mnemonic, required Node node}) async { + Future> getDerivationsFromMnemonic({ + required String mnemonic, + required Node node, + String? passphrase, + }) async { List list = []; final electrumClient = ElectrumClient(); @@ -303,7 +309,7 @@ class CWBitcoin extends Bitcoin { if (dType == DerivationType.electrum) { seedBytes = await mnemonicToSeedBytes(mnemonic); } else if (dType == DerivationType.bip39) { - seedBytes = bip39.mnemonicToSeed(mnemonic); + seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? ''); } for (DerivationInfo dInfo in bitcoin_derivations[dType]!) { diff --git a/lib/src/screens/restore/wallet_restore_from_seed_form.dart b/lib/src/screens/restore/wallet_restore_from_seed_form.dart index 6f8f9eb2b..e4af6ec75 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -20,6 +20,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget { {Key? key, required this.displayLanguageSelector, required this.displayBlockHeightSelector, + required this.displayPassphrase, required this.type, required this.seedTypeViewModel, this.blockHeightFocusNode, @@ -31,6 +32,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget { final WalletType type; final bool displayLanguageSelector; final bool displayBlockHeightSelector; + final bool displayPassphrase; final SeedTypeViewModel seedTypeViewModel; final FocusNode? blockHeightFocusNode; final Function(bool)? onHeightOrDateEntered; @@ -48,6 +50,7 @@ class WalletRestoreFromSeedFormState extends State { formKey = GlobalKey(), languageController = TextEditingController(), nameTextEditingController = TextEditingController(), + passphraseController = TextEditingController(), seedTypeController = TextEditingController(); final GlobalKey seedWidgetStateKey; @@ -55,6 +58,7 @@ class WalletRestoreFromSeedFormState extends State { final TextEditingController languageController; final TextEditingController nameTextEditingController; final TextEditingController seedTypeController; + final TextEditingController passphraseController; final GlobalKey formKey; late ReactionDisposer moneroSeedTypeReaction; String language; @@ -194,6 +198,13 @@ class WalletRestoreFromSeedFormState extends State { key: blockchainHeightKey, onHeightOrDateEntered: widget.onHeightOrDateEntered, hasDatePicker: widget.type == WalletType.monero), + if (widget.displayPassphrase) ...[ + const SizedBox(height: 10), + BaseTextFormField( + hintText: S.current.passphrase, + controller: passphraseController, + ), + ] ])); } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index ed51092df..2f0804f98 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -38,6 +38,7 @@ class WalletRestorePage extends BasePage { displayBlockHeightSelector: walletRestoreViewModel.hasBlockchainHeightLanguageSelector, displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, + displayPassphrase: walletRestoreViewModel.hasPassphrase, type: walletRestoreViewModel.type, key: walletRestoreFromSeedFormKey, blockHeightFocusNode: _blockHeightFocusNode, @@ -296,6 +297,11 @@ class WalletRestorePage extends BasePage { -1; } + if (walletRestoreViewModel.hasPassphrase) { + credentials['passphrase'] = + walletRestoreFromSeedFormKey.currentState!.passphraseController.text; + } + credentials['name'] = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text; } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) { @@ -367,18 +373,16 @@ class WalletRestorePage extends BasePage { } } + // dInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation, + // arguments: derivations) as DerivationInfo?; + if (derivationsWithHistory > 1) { dInfo = await Navigator.of(context).pushNamed(Routes.restoreWalletChooseDerivation, arguments: derivations) as DerivationInfo?; } else if (derivationsWithHistory == 1) { dInfo = derivations[derivationWithHistoryIndex]; - } else if (derivationsWithHistory == 0 && derivations.isNotEmpty) { - dInfo = DerivationInfo( - derivationType: DerivationType.bip39, - derivationPath: "m/84'/0'/0'", - description: "Standard BIP84 native segwit", - scriptType: "p2wpkh", - ); + } else { + dInfo = walletRestoreViewModel.getCommonRestoreDerivation(); } if (dInfo == null) { 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/restore/restore_from_qr_vm.dart b/lib/view_model/restore/restore_from_qr_vm.dart index dda79662d..b9b493f04 100644 --- a/lib/view_model/restore/restore_from_qr_vm.dart +++ b/lib/view_model/restore/restore_from_qr_vm.dart @@ -53,9 +53,11 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store WalletCredentials getCredentialsFromRestoredWallet( dynamic options, RestoredWallet restoreWallet) { final password = generateWalletPassword(); + String? passphrase; DerivationInfo? derivationInfo; if (options != null) { derivationInfo = options["derivationInfo"] as DerivationInfo?; + passphrase = options["passphrase"] as String?; } derivationInfo ??= getDefaultDerivation(); @@ -91,31 +93,37 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store switch (restoreWallet.type) { case WalletType.monero: return monero!.createMoneroRestoreWalletFromSeedCredentials( - name: name, - height: restoreWallet.height ?? 0, - mnemonic: restoreWallet.mnemonicSeed ?? '', - password: password); + name: name, + height: restoreWallet.height ?? 0, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + ); case WalletType.bitcoin: case WalletType.litecoin: return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password, + passphrase: passphrase, derivationType: derivationInfo!.derivationType!, derivationPath: derivationInfo.derivationPath!, ); case WalletType.bitcoinCash: return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials( - name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); + name: name, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + ); case WalletType.ethereum: return ethereum!.createEthereumRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); case WalletType.nano: return nano!.createNanoRestoreWalletFromSeedCredentials( - name: name, - mnemonic: restoreWallet.mnemonicSeed ?? '', - password: password, - derivationType: derivationInfo!.derivationType!); + name: name, + mnemonic: restoreWallet.mnemonicSeed ?? '', + password: password, + derivationType: derivationInfo!.derivationType!, + ); case WalletType.polygon: return polygon!.createPolygonRestoreWalletFromSeedCredentials( name: name, mnemonic: restoreWallet.mnemonicSeed ?? '', password: password); diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index a7baf4ffd..1da2c15dc 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -106,6 +106,25 @@ abstract class WalletCreationVMBase with Store { } } + DerivationInfo? getCommonRestoreDerivation() { + switch (this.type) { + case WalletType.nano: + return DerivationInfo( + derivationType: DerivationType.nano, + ); + case WalletType.bitcoin: + case WalletType.litecoin: + return DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/0'/0", + description: "Standard BIP84 native segwit", + scriptType: "p2wpkh", + ); + default: + return null; + } + } + WalletCredentials getCredentials(dynamic options) => throw UnimplementedError(); Future process(WalletCredentials credentials) => throw UnimplementedError(); diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index 15315c935..97c672682 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -67,6 +67,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { final bool hasBlockchainHeightLanguageSelector; final bool hasRestoreFromPrivateKey; + bool get hasPassphrase => [WalletType.bitcoin].contains(type); + @observable WalletRestoreMode mode; @@ -76,6 +78,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { @override WalletCredentials getCredentials(dynamic options) { final password = generateWalletPassword(); + String? passphrase = options['passphrase'] as String?; final height = options['height'] as int? ?? 0; name = options['name'] as String; DerivationInfo? derivationInfo = options["derivationInfo"] as DerivationInfo?; @@ -87,18 +90,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { return monero!.createMoneroRestoreWalletFromSeedCredentials( name: name, height: height, mnemonic: seed, password: password); case WalletType.bitcoin: - return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( - name: name, - mnemonic: seed, - password: password, - derivationType: derivationInfo!.derivationType!, - derivationPath: derivationInfo.derivationPath!, - ); case WalletType.litecoin: return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials( name: name, mnemonic: seed, password: password, + passphrase: passphrase, derivationType: derivationInfo!.derivationType!, derivationPath: derivationInfo.derivationPath!, ); @@ -206,7 +203,12 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { case WalletType.bitcoin: case WalletType.litecoin: String? mnemonic = credentials['seed'] as String?; - return bitcoin!.getDerivationsFromMnemonic(mnemonic: mnemonic!, node: node); + String? passphrase = credentials['passphrase'] as String?; + return bitcoin!.getDerivationsFromMnemonic( + mnemonic: mnemonic!, + node: node, + passphrase: passphrase, + ); case WalletType.nano: String? mnemonic = credentials['seed'] as String?; String? seedKey = credentials['private_key'] as String?; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index cf9959f17..16a87a850 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -411,6 +411,7 @@ "outputs": "المخرجات", "overwrite_amount": "تغير المبلغ", "pairingInvalidEvent": "ﺢﻟﺎﺻ ﺮﻴﻏ ﺙﺪﺣ ﻥﺍﺮﻗﺇ", + "passphrase": "عبارة الممر (اختياري)", "password": "كلمة المرور", "paste": "لصق", "pause_wallet_creation": ".ﺎﻴًﻟﺎﺣ ﺎﺘًﻗﺆﻣ ﺔﻔﻗﻮﺘﻣ Haven Wallet ءﺎﺸﻧﺇ ﻰﻠﻋ ﺓﺭﺪﻘﻟﺍ", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index bd1cd645d..4e92cd707 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -411,6 +411,7 @@ "outputs": "Изходи", "overwrite_amount": "Промени сума", "pairingInvalidEvent": "Невалидно събитие при сдвояване", + "passphrase": "Passphrase (по избор)", "password": "Парола", "paste": "Поставяне", "pause_wallet_creation": "Възможността за създаване на Haven Wallet в момента е на пауза.", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 4d3458bec..95fdc2a93 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -411,6 +411,7 @@ "outputs": "Výstupy", "overwrite_amount": "Přepsat částku", "pairingInvalidEvent": "Neplatná událost párování", + "passphrase": "Passphrase (volitelné)", "password": "Heslo", "paste": "Vložit", "pause_wallet_creation": "Možnost vytvářet Haven Wallet je momentálně pozastavena.", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 0546140eb..d2731d3e7 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -411,6 +411,7 @@ "outputs": "Ausgänge", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Paarung ungültiges Ereignis", + "passphrase": "Passphrase (optional)", "password": "Passwort", "paste": "Einfügen", "pause_wallet_creation": "Die Möglichkeit, Haven Wallet zu erstellen, ist derzeit pausiert.", @@ -425,8 +426,8 @@ "placeholder_transactions": "Ihre Transaktionen werden hier angezeigt", "please_fill_totp": "Bitte geben Sie den 8-stelligen Code ein, der auf Ihrem anderen Gerät vorhanden ist", "please_make_selection": "Bitte treffen Sie unten eine Auswahl zum Erstellen oder Wiederherstellen Ihrer Wallet.", - "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_reference_document": "Bitte verweisen Sie auf die folgenden Dokumente, um weitere Informationen zu erhalten.", + "Please_reference_document": "Weitere Informationen finden Sie in den Dokumenten unten.", "please_select": "Bitte auswählen:", "please_select_backup_file": "Bitte wählen Sie die Sicherungsdatei und geben Sie das Sicherungskennwort ein.", "please_try_to_connect_to_another_node": "Bitte versuchen Sie, sich mit einem anderen Knoten zu verbinden", @@ -822,4 +823,4 @@ "you_will_get": "Konvertieren zu", "you_will_send": "Konvertieren von", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 8f8b753d6..8c302d096 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -411,6 +411,7 @@ "outputs": "Outputs", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Pairing Invalid Event", + "passphrase": "Passphrase (Optional)", "password": "Password", "paste": "Paste", "pause_wallet_creation": "Ability to create Haven Wallet is currently paused.", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 7de9cff53..17c4ff681 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -411,6 +411,7 @@ "outputs": "Salidas", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Evento de emparejamiento no válido", + "passphrase": "Passfrase (opcional)", "password": "Contraseña", "paste": "Pegar", "pause_wallet_creation": "La capacidad para crear Haven Wallet está actualmente pausada.", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 2c76122fc..12716ab33 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -411,6 +411,7 @@ "outputs": "Les sorties", "overwrite_amount": "Remplacer le montant", "pairingInvalidEvent": "Événement de couplage non valide", + "passphrase": "Phrase de passe (facultative)", "password": "Mot de passe", "paste": "Coller", "pause_wallet_creation": "La possibilité de créer Haven Wallet est actuellement suspendue.", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index bac970207..29754cf72 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -413,6 +413,7 @@ "outputs": "Abubuwan fashewa", "overwrite_amount": "Rubuta adadin", "pairingInvalidEvent": "Haɗa Lamarin mara inganci", + "passphrase": "Passphrase (Zabi)", "password": "Kalmar wucewa", "paste": "Manna", "pause_wallet_creation": "A halin yanzu an dakatar da ikon ƙirƙirar Haven Wallet.", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 5a9706bd5..278adde0f 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -411,6 +411,7 @@ "outputs": "आउटपुट", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "अमान्य ईवेंट युग्मित करना", + "passphrase": "पासफ्रेज़ (वैकल्पिक)", "password": "पारण शब्द", "paste": "पेस्ट करें", "pause_wallet_creation": "हेवन वॉलेट बनाने की क्षमता फिलहाल रुकी हुई है।", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 94f675a1d..7940b1add 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -411,6 +411,7 @@ "outputs": "Izlazi", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Nevažeći događaj uparivanja", + "passphrase": "Prolaznica (neobavezno)", "password": "Lozinka", "paste": "Zalijepi", "pause_wallet_creation": "Mogućnost stvaranja novčanika Haven trenutno je pauzirana.", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 69e270d10..8177afdc2 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -413,6 +413,7 @@ "outputs": "Output", "overwrite_amount": "Timpa jumlah", "pairingInvalidEvent": "Menyandingkan Acara Tidak Valid", + "passphrase": "Frasa sandi (opsional)", "password": "Kata Sandi", "paste": "Tempel", "pause_wallet_creation": "Kemampuan untuk membuat Haven Wallet saat ini dijeda.", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 09b3e43bb..4cc08f9b1 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -413,6 +413,7 @@ "outputs": "Output", "overwrite_amount": "Sovrascrivi quantità", "pairingInvalidEvent": "Associazione evento non valido", + "passphrase": "Passphrase (opzionale)", "password": "Password", "paste": "Incolla", "pause_wallet_creation": "La possibilità di creare Haven Wallet è attualmente sospesa.", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index b067c2721..a72bbb0e4 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -412,6 +412,7 @@ "outputs": "出力", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "ペアリング無効イベント", + "passphrase": "パスフレーズ(オプション)", "password": "パスワード", "paste": "ペースト", "pause_wallet_creation": "Haven Wallet を作成する機能は現在一時停止されています。", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index d9881ad04..b80494e80 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -411,6 +411,7 @@ "outputs": "출력", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "잘못된 이벤트 페어링", + "passphrase": "암호화 (선택 사항)", "password": "암호", "paste": "풀", "pause_wallet_creation": "Haven Wallet 생성 기능이 현재 일시 중지되었습니다.", @@ -425,8 +426,8 @@ "placeholder_transactions": "거래가 여기에 표시됩니다", "please_fill_totp": "다른 기기에 있는 8자리 코드를 입력하세요.", "please_make_selection": "아래에서 선택하십시오 지갑 만들기 또는 복구.", - "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "Please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", + "please_reference_document": "자세한 내용은 아래 문서를 참조하십시오.", "please_select": "선택 해주세요:", "please_select_backup_file": "백업 파일을 선택하고 백업 암호를 입력하십시오.", "please_try_to_connect_to_another_node": "다른 노드에 연결을 시도하십시오", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c36f63414..97a22d807 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -411,6 +411,7 @@ "outputs": "ထုတ်လုပ်မှု", "overwrite_amount": "ပမာဏကို ထပ်ရေးပါ။", "pairingInvalidEvent": "မမှန်ကန်သောဖြစ်ရပ်ကို တွဲချိတ်ခြင်း။", + "passphrase": "passphrase (optional)", "password": "စကားဝှက်", "paste": "ငါးပိ", "pause_wallet_creation": "Haven Wallet ဖန်တီးနိုင်မှုကို လောလောဆယ် ခေတ္တရပ်ထားသည်။", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index f258b8f62..a64e264c0 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -411,6 +411,7 @@ "outputs": "Uitgangen", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Koppelen Ongeldige gebeurtenis", + "passphrase": "PassaspHRASE (optioneel)", "password": "Wachtwoord", "paste": "Plakken", "pause_wallet_creation": "De mogelijkheid om Haven Wallet te maken is momenteel onderbroken.", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 4627d0242..109a800ad 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -411,6 +411,7 @@ "outputs": "Wyjścia", "overwrite_amount": "Nadpisz ilość", "pairingInvalidEvent": "Nieprawidłowe zdarzenie parowania", + "passphrase": "PassPhraza (opcjonalnie)", "password": "Hasło", "paste": "Wklej", "pause_wallet_creation": "Możliwość utworzenia Portfela Haven jest obecnie wstrzymana.", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2a781c76b..2d877794a 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -413,6 +413,7 @@ "outputs": "Saídas", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Emparelhamento de evento inválido", + "passphrase": "Senha (opcional)", "password": "Senha", "paste": "Colar", "pause_wallet_creation": "A capacidade de criar a Haven Wallet está atualmente pausada.", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 22bac3e33..e6141c29b 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -412,6 +412,7 @@ "outputs": "Выходы", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Недействительное событие сопряжения", + "passphrase": "Passfrase (необязательно)", "password": "Пароль", "paste": "Вставить", "pause_wallet_creation": "Возможность создания Haven Wallet в настоящее время приостановлена.", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index c0f58495c..ef1a3ea4e 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -411,6 +411,7 @@ "outputs": "เอาต์พุต", "overwrite_amount": "เขียนทับจำนวน", "pairingInvalidEvent": "การจับคู่เหตุการณ์ที่ไม่ถูกต้อง", + "passphrase": "ข้อความรหัสผ่าน (ไม่บังคับ)", "password": "รหัสผ่าน", "paste": "วาง", "pause_wallet_creation": "ขณะนี้ความสามารถในการสร้าง Haven Wallet ถูกหยุดชั่วคราว", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 8c3b13b3b..e0258e38a 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -411,6 +411,7 @@ "outputs": "Mga output", "overwrite_amount": "Overwrite na halaga", "pairingInvalidEvent": "Pagpares ng Di-wastong Kaganapan", + "passphrase": "Passphrase (opsyonal)", "password": "Password", "paste": "I -paste", "pause_wallet_creation": "Kasalukuyang naka-pause ang kakayahang gumawa ng Haven Wallet.", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index 534a36c77..6cacbfd42 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -411,6 +411,7 @@ "outputs": "çıktılar", "overwrite_amount": "Miktarın üzerine yaz", "pairingInvalidEvent": "Geçersiz Etkinliği Eşleştirme", + "passphrase": "Passfrase (isteğe bağlı)", "password": "Parola", "paste": "Yapıştır", "pause_wallet_creation": "Haven Cüzdanı oluşturma yeteneği şu anda duraklatıldı.", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 5d1e2be05..d3f1c5088 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -411,6 +411,7 @@ "outputs": "Виходи", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "Недійсна подія сполучення", + "passphrase": "Пасофрази (необов’язково)", "password": "Пароль", "paste": "Вставити", "pause_wallet_creation": "Можливість створення гаманця Haven зараз призупинено.", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index d98a85753..97851b210 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -413,6 +413,7 @@ "outputs": "نتائج", "overwrite_amount": "رقم کو اوور رائٹ کریں۔", "pairingInvalidEvent": "ﭧﻧﻮﯾﺍ ﻂﻠﻏ ﺎﻧﺎﻨﺑ ﺍﮌﻮﺟ", + "passphrase": "پاسفریز (اختیاری)", "password": "پاس ورڈ", "paste": "چسپاں کریں۔", "pause_wallet_creation": "Haven Wallet ۔ﮯﮨ ﻑﻮﻗﻮﻣ ﻝﺎﺤﻟﺍ ﯽﻓ ﺖﯿﻠﮨﺍ ﯽﮐ ﮯﻧﺎﻨﺑ", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index faaeb8837..acb533536 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -412,6 +412,7 @@ "outputs": "Awọn iṣan", "overwrite_amount": "Pààrọ̀ iye owó", "pairingInvalidEvent": "Pipọpọ Iṣẹlẹ Ti ko tọ", + "passphrase": "Ọrọ kukuru (iyan)", "password": "Ọ̀rọ̀ aṣínà", "paste": "Fikún ẹ̀dà yín", "pause_wallet_creation": "Agbara lati ṣẹda Haven Wallet ti wa ni idaduro lọwọlọwọ.", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 2d5251d86..e17c4a89b 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -411,6 +411,7 @@ "outputs": "输出", "overwrite_amount": "Overwrite amount", "pairingInvalidEvent": "配对无效事件", + "passphrase": "密码(可选)", "password": "密码", "paste": "粘贴", "pause_wallet_creation": "创建 Haven 钱包的功能当前已暂停。", diff --git a/tool/configure.dart b/tool/configure.dart index 3c4d6e474..03d4dbf3e 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -130,6 +130,7 @@ abstract class Bitcoin { required String password, required DerivationType derivationType, required String derivationPath, + String? passphrase, }); WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo}); WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo}); @@ -168,7 +169,7 @@ abstract class Bitcoin { Future> compareDerivationMethods( {required String mnemonic, required Node node}); Future> getDerivationsFromMnemonic( - {required String mnemonic, required Node node}); + {required String mnemonic, required Node node, String? passphrase}); Future setAddressType(Object wallet, dynamic option); ReceivePageOption getSelectedAddressType(Object wallet); List getBitcoinReceivePageOptions();