From ce21098e9824edf4f8d828f127e4d1aaeefee213 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Mon, 26 Aug 2024 19:06:54 +0200 Subject: [PATCH] Cw 488 seed offset (#1631) * CW-488 minor code cleanup * Add Derivation Path selector for BTC and LTC * CW-488 Initial Passphrase Impl * CW-488 Final Passphrase Impl * Quick Fix of language Service * CW-488 Implement PR Suggestions * CW-488 Implement PR Suggestions * CW-488 Implement Passphrase for Bitcoin Cash * CW-488 Implement Passphrase for Bitcoin Cash * CW-488 Implement Passphrase for Bitcoin Cash * remove monero and wownero support for passphrase until merged [skip ci] * CW-488 Apply requested change * CW-488 Add Passphrase to QR * CW-488 Fix Seed generation * CW-488 Implement Electrum Passphrases * CW-488 Add Seed Length Selector to BIP39 Seeds * CW-488 Minor fix [skip ci] --------- Co-authored-by: Omar Hatem --- cw_bitcoin/lib/bitcoin_mnemonic.dart | 18 +- .../lib/bitcoin_mnemonics_bip39.dart | 3 +- cw_bitcoin/lib/bitcoin_wallet.dart | 4 +- .../bitcoin_wallet_creation_credentials.dart | 16 +- cw_bitcoin/lib/bitcoin_wallet_service.dart | 16 +- cw_bitcoin/lib/electrum_derivations.dart | 3 +- cw_bitcoin/lib/electrum_wallet.dart | 2 + cw_bitcoin/lib/litecoin_wallet.dart | 34 +++- cw_bitcoin/lib/litecoin_wallet_service.dart | 16 +- .../lib/src/bitcoin_cash_base.dart | 1 - .../lib/src/bitcoin_cash_wallet.dart | 14 +- ...coin_cash_wallet_creation_credentials.dart | 18 +- .../lib/src/bitcoin_cash_wallet_service.dart | 30 ++-- cw_core/lib/wallet_base.dart | 2 + lib/bitcoin/cw_bitcoin.dart | 11 +- lib/bitcoin_cash/cw_bitcoin_cash.dart | 8 +- lib/core/wallet_creation_service.dart | 6 +- lib/di.dart | 34 ++-- lib/entities/language_service.dart | 2 +- lib/entities/preferences_key.dart | 1 + lib/entities/seed_type.dart | 46 +++-- lib/router.dart | 97 +++++------ .../dashboard/widgets/verify_form.dart | 16 -- .../advanced_privacy_settings_page.dart | 140 ++++++++++----- .../screens/new_wallet/new_wallet_page.dart | 61 ++++--- .../wallet_restore_from_seed_form.dart | 164 ++++++++++-------- .../screens/restore/wallet_restore_page.dart | 13 +- lib/src/widgets/seed_language_picker.dart | 34 ++-- lib/src/widgets/seed_language_selector.dart | 4 +- lib/store/seed_settings_store.dart | 11 ++ lib/store/settings_store.dart | 77 +++++--- .../advanced_privacy_settings_view_model.dart | 18 +- .../restore/restore_from_qr_vm.dart | 5 +- lib/view_model/seed_settings_view_model.dart | 34 ++++ lib/view_model/seed_type_view_model.dart | 19 -- lib/view_model/wallet_creation_vm.dart | 42 +++-- .../wallet_hardware_restore_view_model.dart | 12 +- lib/view_model/wallet_keys_view_model.dart | 3 +- lib/view_model/wallet_new_vm.dart | 66 ++++--- lib/view_model/wallet_restore_view_model.dart | 40 +++-- res/values/strings_ar.arb | 1 + res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + 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 | 1 + 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 | 7 +- 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 | 6 +- 67 files changed, 741 insertions(+), 438 deletions(-) rename cw_bitcoin_cash/lib/src/mnemonic.dart => cw_bitcoin/lib/bitcoin_mnemonics_bip39.dart (65%) create mode 100644 lib/store/seed_settings_store.dart create mode 100644 lib/view_model/seed_settings_view_model.dart delete mode 100644 lib/view_model/seed_type_view_model.dart diff --git a/cw_bitcoin/lib/bitcoin_mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonic.dart index 905aece28..0749627e9 100644 --- a/cw_bitcoin/lib/bitcoin_mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonic.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'dart:typed_data'; + import 'package:crypto/crypto.dart'; import 'package:cryptography/cryptography.dart' as cryptography; import 'package:cw_core/sec_random_native.dart'; @@ -59,11 +60,7 @@ void maskBytes(Uint8List bytes, int bits) { } } -String bufferToBin(Uint8List data) { - final q1 = data.map((e) => e.toRadixString(2).padLeft(8, '0')); - final q2 = q1.join(''); - return q2; -} +String bufferToBin(Uint8List data) => data.map((e) => e.toRadixString(2).padLeft(8, '0')).join(''); String encode(Uint8List data) { final dataBitLen = data.length * 8; @@ -112,17 +109,18 @@ Future checkIfMnemonicIsElectrum2(String mnemonic) async { Future getMnemonicHash(String mnemonic) async { final hmacSha512 = Hmac(sha512, utf8.encode('Seed version')); final digest = hmacSha512.convert(utf8.encode(normalizeText(mnemonic))); - final hx = digest.toString(); - return hx; + return digest.toString(); } -Future mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async { +Future mnemonicToSeedBytes(String mnemonic, + {String prefix = segwit, String passphrase = ''}) async { final pbkdf2 = cryptography.Pbkdf2(macAlgorithm: cryptography.Hmac.sha512(), iterations: 2048, bits: 512); final text = normalizeText(mnemonic); - // pbkdf2.deriveKey(secretKey: secretKey, nonce: nonce) + final passphraseBytes = utf8.encode(normalizeText(passphrase)); final key = await pbkdf2.deriveKey( - secretKey: cryptography.SecretKey(text.codeUnits), nonce: 'electrum'.codeUnits); + secretKey: cryptography.SecretKey(text.codeUnits), + nonce: [...'electrum'.codeUnits, ...passphraseBytes]); final bytes = await key.extractBytes(); return Uint8List.fromList(bytes); } diff --git a/cw_bitcoin_cash/lib/src/mnemonic.dart b/cw_bitcoin/lib/bitcoin_mnemonics_bip39.dart similarity index 65% rename from cw_bitcoin_cash/lib/src/mnemonic.dart rename to cw_bitcoin/lib/bitcoin_mnemonics_bip39.dart index 7aac1d5c4..ff02e875c 100644 --- a/cw_bitcoin_cash/lib/src/mnemonic.dart +++ b/cw_bitcoin/lib/bitcoin_mnemonics_bip39.dart @@ -7,5 +7,6 @@ class MnemonicBip39 { static String generate({int strength = 128}) => bip39.generateMnemonic(strength: strength); /// Create root seed from mnemonic - static Uint8List toSeed(String mnemonic) => bip39.mnemonicToSeed(mnemonic); + static Uint8List toSeed(String mnemonic, {String? passphrase}) => + bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? ''); } diff --git a/cw_bitcoin/lib/bitcoin_wallet.dart b/cw_bitcoin/lib/bitcoin_wallet.dart index e2e537ee8..30f04667a 100644 --- a/cw_bitcoin/lib/bitcoin_wallet.dart +++ b/cw_bitcoin/lib/bitcoin_wallet.dart @@ -115,7 +115,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { break; case DerivationType.electrum: default: - seedBytes = await mnemonicToSeedBytes(mnemonic); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); break; } return BitcoinWallet( @@ -195,7 +195,7 @@ abstract class BitcoinWalletBase extends ElectrumWallet with Store { if (mnemonic != null) { switch (walletInfo.derivationInfo!.derivationType) { case DerivationType.electrum: - seedBytes = await mnemonicToSeedBytes(mnemonic); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); break; case DerivationType.bip39: default: diff --git a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart index 91b8e4ae2..5c276390a 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_creation_credentials.dart @@ -3,16 +3,18 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class BitcoinNewWalletCredentials extends WalletCredentials { - BitcoinNewWalletCredentials( - {required String name, - WalletInfo? walletInfo, - String? password, - DerivationType? derivationType, - String? derivationPath}) - : super( + BitcoinNewWalletCredentials({ + required String name, + WalletInfo? walletInfo, + String? password, + DerivationType? derivationType, + String? derivationPath, + String? passphrase, + }) : super( name: name, walletInfo: walletInfo, password: password, + passphrase: passphrase, ); } diff --git a/cw_bitcoin/lib/bitcoin_wallet_service.dart b/cw_bitcoin/lib/bitcoin_wallet_service.dart index d6d97f3de..9f67f7807 100644 --- a/cw_bitcoin/lib/bitcoin_wallet_service.dart +++ b/cw_bitcoin/lib/bitcoin_wallet_service.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; import 'package:cw_bitcoin/mnemonic_is_incorrect_exception.dart'; import 'package:cw_bitcoin/bitcoin_wallet_creation_credentials.dart'; import 'package:cw_core/encryption_file_utils.dart'; @@ -35,8 +36,21 @@ class BitcoinWalletService extends WalletService< final network = isTestnet == true ? BitcoinNetwork.testnet : BitcoinNetwork.mainnet; credentials.walletInfo?.network = network.value; + final String mnemonic; + switch ( credentials.walletInfo?.derivationInfo?.derivationType) { + case DerivationType.bip39: + final strength = credentials.seedPhraseLength == 24 ? 256 : 128; + + mnemonic = await MnemonicBip39.generate(strength: strength); + break; + case DerivationType.electrum: + default: + mnemonic = await generateElectrumMnemonic(); + break; + } + final wallet = await BitcoinWalletBase.create( - mnemonic: await generateElectrumMnemonic(), + mnemonic: mnemonic, password: credentials.password!, passphrase: credentials.passphrase, walletInfo: credentials.walletInfo!, diff --git a/cw_bitcoin/lib/electrum_derivations.dart b/cw_bitcoin/lib/electrum_derivations.dart index 749e5c7af..81a3626d2 100644 --- a/cw_bitcoin/lib/electrum_derivations.dart +++ b/cw_bitcoin/lib/electrum_derivations.dart @@ -109,5 +109,4 @@ Map> electrum_derivations = { ], }; - -String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!; \ No newline at end of file +String electrum_path = electrum_derivations[DerivationType.electrum]!.first.derivationPath!; diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index b763b175b..0ce4844c6 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -137,6 +137,8 @@ abstract class ElectrumWalletBase Bip32Slip10Secp256k1 get sideHd => accountHD.childKey(Bip32KeyIndex(1)); final EncryptionFileUtils encryptionFileUtils; + + @override final String? passphrase; @override diff --git a/cw_bitcoin/lib/litecoin_wallet.dart b/cw_bitcoin/lib/litecoin_wallet.dart index 890d98342..12a43dbe6 100644 --- a/cw_bitcoin/lib/litecoin_wallet.dart +++ b/cw_bitcoin/lib/litecoin_wallet.dart @@ -7,6 +7,7 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_mnemonic.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cw_bitcoin/electrum_derivations.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/unspent_coins_info.dart'; @@ -36,6 +37,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { required Box unspentCoinsInfo, required Uint8List seedBytes, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, String? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, @@ -51,6 +53,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialBalance: initialBalance, seedBytes: seedBytes, encryptionFileUtils: encryptionFileUtils, + passphrase: passphrase, currency: CryptoCurrency.ltc) { walletAddresses = LitecoinWalletAddresses( walletInfo, @@ -89,7 +92,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { break; case DerivationType.electrum: default: - seedBytes = await mnemonicToSeedBytes(mnemonic); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); break; } return LitecoinWallet( @@ -100,6 +103,7 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { initialAddresses: initialAddresses, initialBalance: initialBalance, encryptionFileUtils: encryptionFileUtils, + passphrase: passphrase, seedBytes: seedBytes, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, @@ -143,6 +147,31 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { ); } + walletInfo.derivationInfo ??= DerivationInfo(); + + // set the default if not present: + walletInfo.derivationInfo!.derivationPath ??= snp?.derivationPath ?? electrum_path; + walletInfo.derivationInfo!.derivationType ??= snp?.derivationType ?? DerivationType.electrum; + + Uint8List? seedBytes = null; + final mnemonic = keysData.mnemonic; + final passphrase = keysData.passphrase; + + if (mnemonic != null) { + switch (walletInfo.derivationInfo?.derivationType) { + case DerivationType.bip39: + seedBytes = await bip39.mnemonicToSeed( + mnemonic, + passphrase: passphrase ?? "", + ); + break; + case DerivationType.electrum: + default: + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); + break; + } + } + return LitecoinWallet( mnemonic: keysData.mnemonic!, password: password, @@ -150,7 +179,8 @@ abstract class LitecoinWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: snp?.addresses, initialBalance: snp?.balance, - seedBytes: await mnemonicToSeedBytes(keysData.mnemonic!), + seedBytes: seedBytes!, + passphrase: passphrase, encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp?.regularAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex, diff --git a/cw_bitcoin/lib/litecoin_wallet_service.dart b/cw_bitcoin/lib/litecoin_wallet_service.dart index a46b12a2e..c13265934 100644 --- a/cw_bitcoin/lib/litecoin_wallet_service.dart +++ b/cw_bitcoin/lib/litecoin_wallet_service.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:hive/hive.dart'; @@ -30,8 +31,21 @@ class LitecoinWalletService extends WalletService< @override Future create(BitcoinNewWalletCredentials credentials, {bool? isTestnet}) async { + final String mnemonic; + switch ( credentials.walletInfo?.derivationInfo?.derivationType) { + case DerivationType.bip39: + final strength = credentials.seedPhraseLength == 24 ? 256 : 128; + + mnemonic = await MnemonicBip39.generate(strength: strength); + break; + case DerivationType.electrum: + default: + mnemonic = await generateElectrumMnemonic(); + break; + } + final wallet = await LitecoinWalletBase.create( - mnemonic: await generateElectrumMnemonic(), + mnemonic: mnemonic, password: credentials.password!, passphrase: credentials.passphrase, walletInfo: credentials.walletInfo!, diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart index 4699b1649..950b440e7 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_base.dart @@ -3,5 +3,4 @@ export 'bitcoin_cash_wallet_addresses.dart'; export 'bitcoin_cash_wallet_creation_credentials.dart'; export 'bitcoin_cash_wallet_service.dart'; export 'exceptions/exceptions.dart'; -export 'mnemonic.dart'; export 'bitcoin_cash_address_utils.dart'; diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart index 5659528c0..b1e5e7bf6 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet.dart @@ -1,13 +1,14 @@ import 'package:bitbox/bitbox.dart' as bitbox; import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; -import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; import 'package:cw_bitcoin/bitcoin_transaction_priority.dart'; import 'package:cw_bitcoin/electrum_balance.dart'; import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/electrum_wallet_snapshot.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/unspent_coins_info.dart'; import 'package:cw_core/wallet_info.dart'; @@ -30,6 +31,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required Box unspentCoinsInfo, required Uint8List seedBytes, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, BitcoinAddressType? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, @@ -45,7 +47,8 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { initialBalance: initialBalance, seedBytes: seedBytes, currency: CryptoCurrency.bch, - encryptionFileUtils: encryptionFileUtils) { + encryptionFileUtils: encryptionFileUtils, + passphrase: passphrase) { walletAddresses = BitcoinCashWalletAddresses( walletInfo, initialAddresses: initialAddresses, @@ -67,6 +70,7 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { required WalletInfo walletInfo, required Box unspentCoinsInfo, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, String? addressPageType, List? initialAddresses, ElectrumBalance? initialBalance, @@ -79,11 +83,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { unspentCoinsInfo: unspentCoinsInfo, initialAddresses: initialAddresses, initialBalance: initialBalance, - seedBytes: await MnemonicBip39.toSeed(mnemonic), + seedBytes: await MnemonicBip39.toSeed(mnemonic, passphrase: passphrase), encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: initialRegularAddressIndex, initialChangeAddressIndex: initialChangeAddressIndex, addressPageType: P2pkhAddressType.p2pkh, + passphrase: passphrase, ); } @@ -150,11 +155,12 @@ abstract class BitcoinCashWalletBase extends ElectrumWallet with Store { } }).toList(), initialBalance: snp?.balance, - seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!), + seedBytes: await MnemonicBip39.toSeed(keysData.mnemonic!, passphrase: keysData.passphrase), encryptionFileUtils: encryptionFileUtils, initialRegularAddressIndex: snp?.regularAddressIndex, initialChangeAddressIndex: snp?.changeAddressIndex, addressPageType: P2pkhAddressType.p2pkh, + passphrase: keysData.passphrase, ); } diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart index 017040c5d..bb93656f1 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_creation_credentials.dart @@ -2,17 +2,19 @@ import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; class BitcoinCashNewWalletCredentials extends WalletCredentials { - BitcoinCashNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}) - : super(name: name, walletInfo: walletInfo, password: password); + BitcoinCashNewWalletCredentials( + {required String name, WalletInfo? walletInfo, String? password, String? passphrase}) + : super(name: name, walletInfo: walletInfo, password: password, passphrase: passphrase); } class BitcoinCashRestoreWalletFromSeedCredentials extends WalletCredentials { - BitcoinCashRestoreWalletFromSeedCredentials( - {required String name, - required String password, - required this.mnemonic, - WalletInfo? walletInfo}) - : super(name: name, password: password, walletInfo: walletInfo); + BitcoinCashRestoreWalletFromSeedCredentials({ + required String name, + required String password, + required this.mnemonic, + WalletInfo? walletInfo, + String? passphrase, + }) : super(name: name, password: password, walletInfo: walletInfo, passphrase: passphrase); final String mnemonic; } diff --git a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart index a970be261..f004356b5 100644 --- a/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart +++ b/cw_bitcoin_cash/lib/src/bitcoin_cash_wallet_service.dart @@ -1,6 +1,8 @@ import 'dart:io'; import 'package:bip39/bip39.dart'; +import 'package:collection/collection.dart'; +import 'package:cw_bitcoin/bitcoin_mnemonics_bip39.dart'; import 'package:cw_bitcoin_cash/cw_bitcoin_cash.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:cw_core/pathForWallet.dart'; @@ -9,7 +11,6 @@ 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:collection/collection.dart'; import 'package:hive/hive.dart'; class BitcoinCashWalletService extends WalletService< @@ -35,11 +36,12 @@ class BitcoinCashWalletService extends WalletService< final strength = credentials.seedPhraseLength == 24 ? 256 : 128; final wallet = await BitcoinCashWalletBase.create( - mnemonic: await MnemonicBip39.generate(strength: strength), + mnemonic: await MnemonicBip39.generate(strength: strength), password: credentials.password!, walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, encryptionFileUtils: encryptionFileUtilsFor(isDirect), + passphrase: credentials.passphrase, ); await wallet.save(); await wallet.init(); @@ -54,11 +56,11 @@ class BitcoinCashWalletService extends WalletService< try { final wallet = await BitcoinCashWalletBase.open( - password: password, - name: name, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource, - encryptionFileUtils: encryptionFileUtilsFor(isDirect), + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); saveBackup(name); @@ -66,11 +68,11 @@ class BitcoinCashWalletService extends WalletService< } catch (_) { await restoreWalletFilesFromBackup(name); final wallet = await BitcoinCashWalletBase.open( - password: password, - name: name, - walletInfo: walletInfo, - unspentCoinsInfo: unspentCoinsInfoSource, - encryptionFileUtils: encryptionFileUtilsFor(isDirect), + password: password, + name: name, + walletInfo: walletInfo, + unspentCoinsInfo: unspentCoinsInfoSource, + encryptionFileUtils: encryptionFileUtilsFor(isDirect), ); await wallet.init(); return wallet; @@ -130,7 +132,9 @@ class BitcoinCashWalletService extends WalletService< mnemonic: credentials.mnemonic, walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource, - encryptionFileUtils: encryptionFileUtilsFor(isDirect)); + encryptionFileUtils: encryptionFileUtilsFor(isDirect), + passphrase: credentials.passphrase + ); await wallet.save(); await wallet.init(); return wallet; diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index f55816bb7..48ea90f7c 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -46,6 +46,8 @@ abstract class WalletBase null; + String? get passphrase => null; + Object get keys; WalletAddresses get walletAddresses; diff --git a/lib/bitcoin/cw_bitcoin.dart b/lib/bitcoin/cw_bitcoin.dart index d979f9281..c016c1f2a 100644 --- a/lib/bitcoin/cw_bitcoin.dart +++ b/lib/bitcoin/cw_bitcoin.dart @@ -29,8 +29,9 @@ class CWBitcoin extends Bitcoin { @override WalletCredentials createBitcoinNewWalletCredentials( - {required String name, WalletInfo? walletInfo, String? password}) => - BitcoinNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); + {required String name, WalletInfo? walletInfo, String? password, String? passphrase}) => + BitcoinNewWalletCredentials( + name: name, walletInfo: walletInfo, password: password, passphrase: passphrase); @override WalletCredentials createBitcoinHardwareWalletCredentials( @@ -202,8 +203,8 @@ class CWBitcoin extends Bitcoin { await bitcoinWallet.updateAllUnspents(); } - WalletService createBitcoinWalletService( - Box walletInfoSource, Box unspentCoinSource, bool alwaysScan, bool isDirect) { + WalletService createBitcoinWalletService(Box walletInfoSource, + Box unspentCoinSource, bool alwaysScan, bool isDirect) { return BitcoinWalletService(walletInfoSource, unspentCoinSource, alwaysScan, isDirect); } @@ -315,7 +316,7 @@ class CWBitcoin extends Bitcoin { for (DerivationType dType in electrum_derivations.keys) { late Uint8List seedBytes; if (dType == DerivationType.electrum) { - seedBytes = await mnemonicToSeedBytes(mnemonic); + seedBytes = await mnemonicToSeedBytes(mnemonic, passphrase: passphrase ?? ""); } else if (dType == DerivationType.bip39) { seedBytes = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase ?? ''); } diff --git a/lib/bitcoin_cash/cw_bitcoin_cash.dart b/lib/bitcoin_cash/cw_bitcoin_cash.dart index fcb34a286..62ec1539b 100644 --- a/lib/bitcoin_cash/cw_bitcoin_cash.dart +++ b/lib/bitcoin_cash/cw_bitcoin_cash.dart @@ -15,14 +15,16 @@ class CWBitcoinCash extends BitcoinCash { required String name, WalletInfo? walletInfo, String? password, + String? passphrase, }) => - BitcoinCashNewWalletCredentials(name: name, walletInfo: walletInfo, password: password); + BitcoinCashNewWalletCredentials( + name: name, walletInfo: walletInfo, password: password, passphrase: passphrase); @override WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials( - {required String name, required String mnemonic, required String password}) => + {required String name, required String mnemonic, required String password, String? passphrase}) => BitcoinCashRestoreWalletFromSeedCredentials( - name: name, mnemonic: mnemonic, password: password); + name: name, mnemonic: mnemonic, password: password, passphrase: passphrase); @override TransactionPriority deserializeBitcoinCashTransactionPriority(int raw) => diff --git a/lib/core/wallet_creation_service.dart b/lib/core/wallet_creation_service.dart index 1e9299282..3ee630b33 100644 --- a/lib/core/wallet_creation_service.dart +++ b/lib/core/wallet_creation_service.dart @@ -75,8 +75,10 @@ class WalletCreationService { bool get _hasSeedPhraseLengthOption { switch (type) { - case WalletType.ethereum: + case WalletType.bitcoin: + case WalletType.litecoin: case WalletType.bitcoinCash: + case WalletType.ethereum: case WalletType.polygon: case WalletType.solana: case WalletType.tron: @@ -84,8 +86,6 @@ class WalletCreationService { case WalletType.monero: case WalletType.wownero: case WalletType.none: - case WalletType.bitcoin: - case WalletType.litecoin: case WalletType.haven: case WalletType.nano: case WalletType.banano: diff --git a/lib/di.dart b/lib/di.dart index 1967c9227..8d8c5a368 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -146,7 +146,7 @@ import 'package:cake_wallet/view_model/cake_pay/cake_pay_purchase_view_model.dar import 'package:cake_wallet/view_model/nano_account_list/nano_account_edit_or_create_view_model.dart'; import 'package:cake_wallet/view_model/nano_account_list/nano_account_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; @@ -179,6 +179,7 @@ import 'package:cake_wallet/store/dashboard/trades_store.dart'; import 'package:cake_wallet/store/dashboard/transaction_filter_store.dart'; import 'package:cake_wallet/store/node_list_store.dart'; import 'package:cake_wallet/store/secret_store.dart'; +import 'package:cake_wallet/store/seed_settings_store.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/store/templates/exchange_template_store.dart'; import 'package:cake_wallet/store/templates/send_template_store.dart'; @@ -331,6 +332,7 @@ Future setup({ YatStore(appStore: getIt.get(), secureStorage: getIt.get())..init()); getIt.registerSingleton( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); + getIt.registerSingleton(SeedSettingsStore()); getIt.registerLazySingleton(() => LedgerViewModel()); @@ -361,6 +363,7 @@ Future setup({ getIt.get(param1: type), _walletInfoSource, getIt.get(param1: type), + getIt.get(), type: type)); getIt.registerFactoryParam((args, closable) { @@ -422,14 +425,21 @@ Future setup({ walletType: args.walletType ?? currentWalletType); }); - getIt.registerFactoryParam((WalletType type, _) { - return WalletRestorationFromQRVM(getIt.get(), - getIt.get(param1: type), _walletInfoSource, type); - }); + getIt.registerFactoryParam((WalletType type, _) => + WalletRestorationFromQRVM( + getIt.get(), + getIt.get(param1: type), + _walletInfoSource, + type, + getIt.get())); getIt.registerFactoryParam((type, _) => - WalletHardwareRestoreViewModel(getIt.get(), getIt.get(), - getIt.get(param1: type), _walletInfoSource, + WalletHardwareRestoreViewModel( + getIt.get(), + getIt.get(), + getIt.get(param1: type), + _walletInfoSource, + getIt.get(), type: type)); getIt.registerFactory(() => WalletAddressListViewModel( @@ -833,7 +843,7 @@ Future setup({ getIt.registerFactory(() => WalletSeedViewModel(getIt.get().wallet!)); - getIt.registerFactory(() => SeedTypeViewModel(getIt.get())); + getIt.registerFactory(() => SeedSettingsViewModel(getIt.get(), getIt.get())); getIt.registerFactoryParam((bool isWalletCreated, _) => WalletSeedPage(getIt.get(), isNewWalletCreated: isWalletCreated)); @@ -1018,12 +1028,12 @@ Future setup({ getIt.registerFactory(() => FaqPage(getIt.get())); getIt.registerFactoryParam((type, _) => - WalletRestoreViewModel( - getIt.get(), getIt.get(param1: type), _walletInfoSource, + WalletRestoreViewModel(getIt.get(), getIt.get(param1: type), + _walletInfoSource, getIt.get(), type: type)); getIt.registerFactoryParam((type, _) => WalletRestorePage( - getIt.get(param1: type), getIt.get())); + getIt.get(param1: type), getIt.get())); getIt.registerFactoryParam, void>( (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); @@ -1275,7 +1285,7 @@ Future setup({ getIt.registerFactory( () => WalletConnectConnectionsView(web3walletService: getIt.get())); - + getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); getIt.registerFactory(() => TorPage(getIt.get())); diff --git a/lib/entities/language_service.dart b/lib/entities/language_service.dart index 260c8a27f..4a6b358e1 100644 --- a/lib/entities/language_service.dart +++ b/lib/entities/language_service.dart @@ -60,7 +60,7 @@ class LanguageService { 'yo': 'nga', 'ha': 'hau', 'tl': 'phl', - 'hy': 'arm' + 'hy': 'arm', }; static final list = {}; diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index e1ee0ada3..743357f92 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -77,6 +77,7 @@ class PreferencesKey { static const exchangeProvidersSelection = 'exchange-providers-selection'; static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status'; static const moneroSeedType = 'monero_seed_type'; + static const bitcoinSeedType = 'bitcoin_seed_type'; static const clearnetDonationLink = 'clearnet_donation_link'; static const onionDonationLink = 'onion_donation_link'; static const donationLinkWalletName = 'donation_link_wallet_name'; diff --git a/lib/entities/seed_type.dart b/lib/entities/seed_type.dart index bc2f6cff7..f941c8a2d 100644 --- a/lib/entities/seed_type.dart +++ b/lib/entities/seed_type.dart @@ -1,18 +1,19 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cw_core/enumerable_item.dart'; +import 'package:cw_core/wallet_info.dart'; -class SeedType extends EnumerableItem with Serializable { - const SeedType({required String title, required int raw}) : super(title: title, raw: raw); +class MoneroSeedType extends EnumerableItem with Serializable { + const MoneroSeedType({required String title, required int raw}) : super(title: title, raw: raw); - static const all = [SeedType.legacy, SeedType.polyseed]; + static const all = [MoneroSeedType.legacy, MoneroSeedType.polyseed]; static const defaultSeedType = polyseed; - static const legacy = SeedType(raw: 0, title: 'Legacy (25 words)'); - static const polyseed = SeedType(raw: 1, title: 'Polyseed (16 words)'); - static const wowneroSeed = SeedType(raw: 2, title: 'Wownero (14 words)'); + static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)'); + static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)'); + static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero (14 words)'); - static SeedType deserialize({required int raw}) { + static MoneroSeedType deserialize({required int raw}) { switch (raw) { case 0: return legacy; @@ -28,14 +29,39 @@ class SeedType extends EnumerableItem with Serializable { @override String toString() { switch (this) { - case SeedType.legacy: + case MoneroSeedType.legacy: return S.current.seedtype_legacy; - case SeedType.polyseed: + case MoneroSeedType.polyseed: return S.current.seedtype_polyseed; - case SeedType.wowneroSeed: + case MoneroSeedType.wowneroSeed: return S.current.seedtype_wownero; default: return ''; } } } + +class BitcoinSeedType extends EnumerableItem with Serializable { + const BitcoinSeedType(this.type, {required String title, required int raw}) + : super(title: title, raw: raw); + + final DerivationType type; + + static const all = [BitcoinSeedType.electrum, BitcoinSeedType.bip39]; + + static const defaultDerivationType = bip39; + + static const electrum = BitcoinSeedType(DerivationType.electrum, raw: 0, title: 'Electrum'); + static const bip39 = BitcoinSeedType(DerivationType.bip39, raw: 1, title: 'BIP39'); + + static BitcoinSeedType deserialize({required int raw}) { + switch (raw) { + case 0: + return electrum; + case 1: + return bip39; + default: + throw Exception('Unexpected token: $raw for SeedType deserialize'); + } + } +} diff --git a/lib/router.dart b/lib/router.dart index 25af39043..281d50b69 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -17,6 +17,8 @@ import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_options_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; +import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart'; +import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart'; import 'package:cake_wallet/src/screens/connect_device/connect_device_page.dart'; import 'package:cake_wallet/src/screens/connect_device/select_hardware_wallet_account_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; @@ -27,8 +29,8 @@ import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/address_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; -import 'package:cake_wallet/src/screens/dashboard/sign_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/sign_page.dart'; import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; @@ -43,10 +45,9 @@ import 'package:cake_wallet/src/screens/new_wallet/new_wallet_page.dart'; import 'package:cake_wallet/src/screens/new_wallet/new_wallet_type_page.dart'; import 'package:cake_wallet/src/screens/nodes/node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; -import 'package:cake_wallet/src/screens/receive/address_list_page.dart'; -import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; +import 'package:cake_wallet/src/screens/receive/address_list_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; import 'package:cake_wallet/src/screens/receive/fullscreen_qr_page.dart'; @@ -69,11 +70,9 @@ import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart'; import 'package:cake_wallet/src/screens/settings/other_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/privacy_page.dart'; import 'package:cake_wallet/src/screens/settings/security_backup_page.dart'; -import 'package:cake_wallet/src/screens/cake_pay/auth/cake_pay_account_page.dart'; import 'package:cake_wallet/src/screens/settings/silent_payments_settings.dart'; import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart'; -import 'package:cake_wallet/src/screens/settings/tor_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart'; import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_enter_code_page.dart'; @@ -85,19 +84,18 @@ import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/src/screens/support_chat/support_chat_page.dart'; import 'package:cake_wallet/src/screens/support_other_links/support_other_links_page.dart'; import 'package:cake_wallet/src/screens/trade_details/trade_details_page.dart'; -import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart'; +import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart'; import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart'; import 'package:cake_wallet/src/screens/wallet/wallet_edit_page.dart'; import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart'; import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; +import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_page.dart'; import 'package:cake_wallet/src/screens/welcome/create_welcome_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/src/screens/wallet_unlock/wallet_unlock_arguments.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/payment_request.dart'; import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; @@ -106,7 +104,7 @@ import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_new_vm.dart'; import 'package:cake_wallet/wallet_type_utils.dart'; @@ -120,7 +118,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:cake_wallet/src/screens/cake_pay/cake_pay.dart'; + import 'src/screens/dashboard/pages/nft_import_page.dart'; late RouteSettings currentRouteSettings; @@ -135,7 +133,8 @@ Route createRoute(RouteSettings settings) { case Routes.newWalletFromWelcome: if (SettingsStoreBase.walletPasswordDirectInput) { if (availableWalletTypes.length == 1) { - return createRoute(RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first)); + return createRoute( + RouteSettings(name: Routes.newWallet, arguments: availableWalletTypes.first)); } else { return createRoute(RouteSettings(name: Routes.newWalletType)); } @@ -162,10 +161,10 @@ Route createRoute(RouteSettings settings) { case Routes.newWallet: final type = settings.arguments as WalletType; final walletNewVM = getIt.get(param1: type); - final seedTypeViewModel = getIt.get(); + final seedSettingsViewModel = getIt.get(); return CupertinoPageRoute( - builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel)); + builder: (_) => NewWalletPage(walletNewVM, seedSettingsViewModel)); case Routes.chooseHardwareWalletAccount: final arguments = settings.arguments as List; @@ -348,16 +347,14 @@ Route createRoute(RouteSettings settings) { case Routes.auth: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) - => SettingsStoreBase.walletPasswordDirectInput - ? getIt.get( - param1: WalletUnlockArguments( + builder: (_) => SettingsStoreBase.walletPasswordDirectInput + ? getIt.get( + param1: WalletUnlockArguments( callback: settings.arguments as OnAuthenticationFinished), - instanceName: 'wallet_unlock_verifiable', - param2: true) - : getIt.get( - param1: settings.arguments as OnAuthenticationFinished, - param2: true)); + instanceName: 'wallet_unlock_verifiable', + param2: true) + : getIt.get( + param1: settings.arguments as OnAuthenticationFinished, param2: true)); case Routes.totpAuthCodePage: final args = settings.arguments as TotpAuthArgumentsModel; @@ -371,28 +368,25 @@ Route createRoute(RouteSettings settings) { case Routes.walletUnlockLoadable: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) - => getIt.get( + builder: (_) => getIt.get( param1: settings.arguments as WalletUnlockArguments, - instanceName: 'wallet_unlock_loadable', + instanceName: 'wallet_unlock_loadable', param2: true)); case Routes.unlock: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) - => SettingsStoreBase.walletPasswordDirectInput - ? WillPopScope( - child: getIt.get( + builder: (_) => SettingsStoreBase.walletPasswordDirectInput + ? WillPopScope( + child: getIt.get( param1: WalletUnlockArguments( - callback: settings.arguments as OnAuthenticationFinished), + callback: settings.arguments as OnAuthenticationFinished), param2: false, instanceName: 'wallet_unlock_verifiable'), - onWillPop: () async => false) - : WillPopScope( - child: getIt.get( - param1: settings.arguments as OnAuthenticationFinished, - param2: false), + onWillPop: () async => false) + : WillPopScope( + child: getIt.get( + param1: settings.arguments as OnAuthenticationFinished, param2: false), onWillPop: () async => false)); case Routes.silentPaymentsSettings: @@ -437,11 +431,12 @@ Route createRoute(RouteSettings settings) { return CupertinoPageRoute( builder: (context) => WillPopScope( child: SettingsStoreBase.walletPasswordDirectInput - ? getIt.get(instanceName: 'wallet_password_login') - : getIt.get(instanceName: 'login'), + ? getIt.get(instanceName: 'wallet_password_login') + : getIt.get(instanceName: 'login'), onWillPop: () async => - // FIX-ME: Additional check does it works correctly - (await SystemChannels.platform.invokeMethod('SystemNavigator.pop') ?? false)), + // FIX-ME: Additional check does it works correctly + (await SystemChannels.platform.invokeMethod('SystemNavigator.pop') ?? + false)), fullscreenDialog: true); case Routes.newPowNode: @@ -537,8 +532,7 @@ Route createRoute(RouteSettings settings) { case Routes.support: return CupertinoPageRoute( - fullscreenDialog: true, - builder: (_) => getIt.get()); + fullscreenDialog: true, builder: (_) => getIt.get()); case Routes.supportLiveChat: return CupertinoPageRoute(builder: (_) => getIt.get()); @@ -567,8 +561,7 @@ Route createRoute(RouteSettings settings) { case Routes.cakePayBuyCardPage: final args = settings.arguments as List; - return CupertinoPageRoute( - builder: (_) => getIt.get(param1: args)); + return CupertinoPageRoute(builder: (_) => getIt.get(param1: args)); case Routes.cakePayBuyCardDetailPage: final args = settings.arguments as List; @@ -582,7 +575,8 @@ Route createRoute(RouteSettings settings) { case Routes.cakePayVerifyOtpPage: final args = settings.arguments as List; - return CupertinoPageRoute(builder: (_) => getIt.get(param1: args)); + return CupertinoPageRoute( + builder: (_) => getIt.get(param1: args)); case Routes.cakePayAccountPage: return CupertinoPageRoute(builder: (_) => getIt.get()); @@ -597,16 +591,19 @@ Route createRoute(RouteSettings settings) { case Routes.advancedPrivacySettings: final args = settings.arguments as Map; final type = args['type'] as WalletType; + final isFromRestore = args['isFromRestore'] as bool? ?? false; final useTestnet = args['useTestnet'] as bool; final toggleTestnet = args['toggleTestnet'] as Function(bool? val); return CupertinoPageRoute( builder: (_) => AdvancedPrivacySettingsPage( - useTestnet, - toggleTestnet, - getIt.get(param1: type), - getIt.get(param1: type, param2: false), - getIt.get(), + isFromRestore: isFromRestore, + useTestnet: useTestnet, + toggleUseTestnet: toggleTestnet, + advancedPrivacySettingsViewModel: + getIt.get(param1: type), + nodeViewModel: getIt.get(param1: type, param2: false), + seedSettingsViewModel: getIt.get(), )); case Routes.anonPayInvoicePage: @@ -709,7 +706,7 @@ Route createRoute(RouteSettings settings) { getIt.get(), ), ); - + case Routes.connectDevices: final params = settings.arguments as ConnectDevicePageParams; return MaterialPageRoute( diff --git a/lib/src/screens/dashboard/widgets/verify_form.dart b/lib/src/screens/dashboard/widgets/verify_form.dart index d59261494..bf6809586 100644 --- a/lib/src/screens/dashboard/widgets/verify_form.dart +++ b/lib/src/screens/dashboard/widgets/verify_form.dart @@ -1,23 +1,7 @@ -import 'package:cake_wallet/core/wallet_name_validator.dart'; -import 'package:cake_wallet/entities/generate_name.dart'; -import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/address_text_field.dart'; -import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; -import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart'; -import 'package:cake_wallet/src/widgets/picker.dart'; -import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; -import 'package:cake_wallet/src/widgets/seed_widget.dart'; -import 'package:cake_wallet/themes/extensions/address_theme.dart'; -import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; -import 'package:cake_wallet/utils/show_bar.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:mobx/mobx.dart'; -import 'package:polyseed/polyseed.dart'; class VerifyForm extends StatefulWidget { VerifyForm({ diff --git a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart index 881d9f95a..ff8ec3dd2 100644 --- a/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart +++ b/lib/src/screens/new_wallet/advanced_privacy_settings_page.dart @@ -3,52 +3,61 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; -import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; -import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; -import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; +import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; class AdvancedPrivacySettingsPage extends BasePage { - AdvancedPrivacySettingsPage(this.useTestnet, this.toggleUseTestnet, - this.advancedPrivacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel); + AdvancedPrivacySettingsPage({ + required this.isFromRestore, + required this.useTestnet, + required this.toggleUseTestnet, + required this.advancedPrivacySettingsViewModel, + required this.nodeViewModel, + required this.seedSettingsViewModel, + }); final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel; final NodeCreateOrEditViewModel nodeViewModel; - final SeedTypeViewModel seedTypeViewModel; + final SeedSettingsViewModel seedSettingsViewModel; @override String get title => S.current.privacy_settings; + final bool isFromRestore; final bool useTestnet; final Function(bool? val) toggleUseTestnet; @override - Widget body(BuildContext context) => AdvancedPrivacySettingsBody(useTestnet, toggleUseTestnet, - advancedPrivacySettingsViewModel, nodeViewModel, seedTypeViewModel); + Widget body(BuildContext context) => _AdvancedPrivacySettingsBody(isFromRestore, useTestnet, + toggleUseTestnet, advancedPrivacySettingsViewModel, nodeViewModel, seedSettingsViewModel); } -class AdvancedPrivacySettingsBody extends StatefulWidget { - const AdvancedPrivacySettingsBody(this.useTestnet, this.toggleUseTestnet, +class _AdvancedPrivacySettingsBody extends StatefulWidget { + const _AdvancedPrivacySettingsBody(this.isFromRestore, this.useTestnet, this.toggleUseTestnet, this.privacySettingsViewModel, this.nodeViewModel, this.seedTypeViewModel, {Key? key}) : super(key: key); final AdvancedPrivacySettingsViewModel privacySettingsViewModel; final NodeCreateOrEditViewModel nodeViewModel; - final SeedTypeViewModel seedTypeViewModel; + final SeedSettingsViewModel seedTypeViewModel; + final bool isFromRestore; final bool useTestnet; final Function(bool? val) toggleUseTestnet; @@ -56,15 +65,23 @@ class AdvancedPrivacySettingsBody extends StatefulWidget { _AdvancedPrivacySettingsBodyState createState() => _AdvancedPrivacySettingsBodyState(); } -class _AdvancedPrivacySettingsBodyState extends State { - _AdvancedPrivacySettingsBodyState(); - +class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBody> { + final TextEditingController passphraseController = TextEditingController(); final _formKey = GlobalKey(); bool? testnetValue; + @override + void initState() { + passphraseController.text = widget.seedTypeViewModel.passphrase ?? ''; + + passphraseController + .addListener(() => widget.seedTypeViewModel.setPassphrase(passphraseController.text)); + super.initState(); + } + @override Widget build(BuildContext context) { - if (testnetValue == null && widget.useTestnet != null) { + if (testnetValue == null && widget.useTestnet) { testnetValue = widget.useTestnet; } @@ -97,6 +114,52 @@ class _AdvancedPrivacySettingsBodyState extends State( + title: S.current.seedtype, + items: MoneroSeedType.all, + selectedItem: widget.seedTypeViewModel.moneroSeedType, + onItemSelected: widget.seedTypeViewModel.setMoneroSeedType, + ), + ); + }), + if ([WalletType.bitcoin, WalletType.litecoin] + .contains(widget.privacySettingsViewModel.type)) + Observer(builder: (_) { + return SettingsChoicesCell( + ChoicesListItem( + title: S.current.seedtype, + items: BitcoinSeedType.all, + selectedItem: widget.seedTypeViewModel.bitcoinSeedType, + onItemSelected: widget.seedTypeViewModel.setBitcoinSeedType, + ), + ); + }), + if (!widget.isFromRestore) ...[ + Observer(builder: (_) { + if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption) + return SettingsPickerCell( + title: S.current.seed_phrase_length, + items: SeedPhraseLength.values, + selectedItem: widget.privacySettingsViewModel.seedPhraseLength, + onItemSelected: (SeedPhraseLength length) { + widget.privacySettingsViewModel.setSeedPhraseLength(length); + }, + ); + return Container(); + }), + if (widget.privacySettingsViewModel.hasPassphraseOption) + Padding( + padding: EdgeInsets.all(24), + child: BaseTextFormField( + hintText: S.current.passphrase, + controller: passphraseController, + obscureText: true, + ), + ), + ], Observer(builder: (_) { return Column( children: [ @@ -122,31 +185,9 @@ class _AdvancedPrivacySettingsBodyState extends State( - title: S.current.seed_phrase_length, - items: SeedPhraseLength.values, - selectedItem: widget.privacySettingsViewModel.seedPhraseLength, - onItemSelected: (SeedPhraseLength length) { - widget.privacySettingsViewModel.setSeedPhraseLength(length); - }, - ); - }), - if (widget.privacySettingsViewModel.hasSeedTypeOption) - Observer(builder: (_) { - return SettingsChoicesCell( - ChoicesListItem( - title: S.current.seedtype, - items: SeedType.all, - selectedItem: widget.seedTypeViewModel.moneroSeedType, - onItemSelected: widget.seedTypeViewModel.setMoneroSeedType, - ), - ); - }), if (widget.privacySettingsViewModel.type == WalletType.bitcoin) Builder(builder: (_) { - final val = testnetValue!; + final val = testnetValue ?? false; return SettingsSwitcherCell( title: S.current.use_testnet, value: val, @@ -154,7 +195,7 @@ class _AdvancedPrivacySettingsBodyState extends State widget.seedTypeViewModel.setPassphrase(passphraseController.text)); + super.dispose(); + } } diff --git a/lib/src/screens/new_wallet/new_wallet_page.dart b/lib/src/screens/new_wallet/new_wallet_page.dart index 471240877..8f61ebb38 100644 --- a/lib/src/screens/new_wallet/new_wallet_page.dart +++ b/lib/src/screens/new_wallet/new_wallet_page.dart @@ -1,36 +1,35 @@ +import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/core/wallet_name_validator.dart'; import 'package:cake_wallet/entities/generate_name.dart'; -import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; -import 'package:cake_wallet/src/widgets/picker.dart'; -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/main.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/themes/theme_base.dart'; -import 'package:cake_wallet/utils/responsive_layout_util.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; -import 'package:mobx/mobx.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/core/wallet_name_validator.dart'; -import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; -import 'package:cake_wallet/core/execution_state.dart'; -import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:cake_wallet/src/widgets/seed_language_selector.dart'; +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; -import 'package:cake_wallet/entities/seed_type.dart'; - +import 'package:cake_wallet/themes/theme_base.dart'; +import 'package:cake_wallet/utils/responsive_layout_util.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_new_vm.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; class NewWalletPage extends BasePage { - NewWalletPage(this._walletNewVM, this._seedTypeViewModel); + NewWalletPage(this._walletNewVM, this._seedSettingsViewModel); final WalletNewVM _walletNewVM; - final SeedTypeViewModel _seedTypeViewModel; + final SeedSettingsViewModel _seedSettingsViewModel; final walletNameImage = Image.asset('assets/images/wallet_name.png'); @@ -51,15 +50,15 @@ class NewWalletPage extends BasePage { Widget body(BuildContext context) => WalletNameForm( _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage, - _seedTypeViewModel); + _seedSettingsViewModel); } class WalletNameForm extends StatefulWidget { - WalletNameForm(this._walletNewVM, this.walletImage, this._seedTypeViewModel); + WalletNameForm(this._walletNewVM, this.walletImage, this._seedSettingsViewModel); final WalletNewVM _walletNewVM; final Image walletImage; - final SeedTypeViewModel _seedTypeViewModel; + final SeedSettingsViewModel _seedSettingsViewModel; @override _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM); @@ -110,7 +109,7 @@ class _WalletNameFormState extends State { } }); - _setSeedType(SeedType.defaultSeedType); + _setSeedType(MoneroSeedType.defaultSeedType); super.initState(); } @@ -285,12 +284,12 @@ class _WalletNameFormState extends State { builder: (BuildContext build) => Padding( padding: EdgeInsets.only(top: 24), child: SelectButton( - text: widget._seedTypeViewModel.moneroSeedType.title, + text: widget._seedSettingsViewModel.moneroSeedType.title, onTap: () async { await showPopUp( context: context, builder: (_) => Picker( - items: SeedType.all, + items: MoneroSeedType.all, selectedAtIndex: isPolyseed ? 1 : 0, onItemSelected: _setSeedType, isSeparated: false, @@ -308,8 +307,8 @@ class _WalletNameFormState extends State { key: _languageSelectorKey, initialSelected: defaultSeedLanguage, seedType: _walletNewVM.hasSeedType - ? widget._seedTypeViewModel.moneroSeedType - : SeedType.legacy, + ? widget._seedSettingsViewModel.moneroSeedType + : MoneroSeedType.legacy, ), ), ) @@ -380,10 +379,10 @@ class _WalletNameFormState extends State { _formProcessing = false; } - bool get isPolyseed => widget._seedTypeViewModel.moneroSeedType == SeedType.polyseed; + bool get isPolyseed => widget._seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed; - void _setSeedType(SeedType item) { - widget._seedTypeViewModel.setMoneroSeedType(item); + void _setSeedType(MoneroSeedType item) { + widget._seedSettingsViewModel.setMoneroSeedType(item); _languageSelectorKey.currentState?.selected = defaultSeedLanguage; // Reset Seed language } } 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 ec40eb1c1..f295aab13 100644 --- a/lib/src/screens/restore/wallet_restore_from_seed_form.dart +++ b/lib/src/screens/restore/wallet_restore_from_seed_form.dart @@ -9,35 +9,34 @@ import 'package:cake_wallet/src/widgets/seed_language_picker.dart'; import 'package:cake_wallet/src/widgets/seed_widget.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:mobx/mobx.dart'; import 'package:polyseed/polyseed.dart'; class WalletRestoreFromSeedForm extends StatefulWidget { - WalletRestoreFromSeedForm( - {Key? key, - required this.displayLanguageSelector, - required this.displayBlockHeightSelector, - required this.displayPassphrase, - required this.type, - required this.displayWalletPassword, - required this.seedTypeViewModel, - this.blockHeightFocusNode, - this.onHeightOrDateEntered, - this.onSeedChange, - this.onLanguageChange, - this.onPasswordChange, - this.onRepeatedPasswordChange}) - : super(key: key); + WalletRestoreFromSeedForm({Key? key, + required this.displayLanguageSelector, + required this.displayBlockHeightSelector, + required this.displayPassphrase, + required this.type, + required this.displayWalletPassword, + required this.seedSettingsViewModel, + this.blockHeightFocusNode, + this.onHeightOrDateEntered, + this.onSeedChange, + this.onLanguageChange, + this.onPasswordChange, + this.onRepeatedPasswordChange, + }) : super(key: key); final WalletType type; final bool displayLanguageSelector; final bool displayBlockHeightSelector; final bool displayWalletPassword; final bool displayPassphrase; - final SeedTypeViewModel seedTypeViewModel; + final SeedSettingsViewModel seedSettingsViewModel; final FocusNode? blockHeightFocusNode; final Function(bool)? onHeightOrDateEntered; final void Function(String)? onSeedChange; @@ -58,7 +57,9 @@ class WalletRestoreFromSeedFormState extends State { languageController = TextEditingController(), nameTextEditingController = TextEditingController(), passwordTextEditingController = displayWalletPassword ? TextEditingController() : null, - repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null, + repeatedPasswordTextEditingController = displayWalletPassword + ? TextEditingController() + : null, passphraseController = TextEditingController(), seedTypeController = TextEditingController(); @@ -75,10 +76,11 @@ class WalletRestoreFromSeedFormState extends State { String language; void Function()? passwordListener; void Function()? repeatedPasswordListener; + void Function()? passphraseListener; @override void initState() { - _setSeedType(widget.seedTypeViewModel.moneroSeedType); + _setSeedType(widget.seedSettingsViewModel.moneroSeedType); _setLanguageLabel(language); if (passwordTextEditingController != null) { @@ -87,14 +89,19 @@ class WalletRestoreFromSeedFormState extends State { } if (repeatedPasswordTextEditingController != null) { - repeatedPasswordListener = () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text); + repeatedPasswordListener = + () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text); repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!); } + + passphraseListener = () => widget.seedSettingsViewModel.setPassphrase(passphraseController.text); + passphraseController.addListener(passphraseListener!); + moneroSeedTypeReaction = - reaction((_) => widget.seedTypeViewModel.moneroSeedType, (SeedType item) { - _setSeedType(item); - _changeLanguage('English'); - }); + reaction((_) => widget.seedSettingsViewModel.moneroSeedType, (MoneroSeedType item) { + _setSeedType(item); + _changeLanguage('English'); + }); super.initState(); } @@ -110,6 +117,9 @@ class WalletRestoreFromSeedFormState extends State { if (repeatedPasswordListener != null) { repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); } + + passphraseController.removeListener(passphraseListener!); + super.dispose(); } @@ -118,11 +128,13 @@ class WalletRestoreFromSeedFormState extends State { Polyseed.isValidSeed(seed)) { final lang = PolyseedLang.getByPhrase(seed); - _changeSeedType(SeedType.polyseed); + _changeSeedType(MoneroSeedType.polyseed); _changeLanguage(lang.nameEnglish); } - if (widget.type == WalletType.wownero && seed.split(" ").length == 14) { - _changeSeedType(SeedType.wowneroSeed); + if (widget.type == WalletType.wownero && seed + .split(" ") + .length == 14) { + _changeSeedType(MoneroSeedType.wowneroSeed); _changeLanguage("English"); } widget.onSeedChange?.call(seed); @@ -140,7 +152,9 @@ class WalletRestoreFromSeedFormState extends State { children: [ BaseTextFormField( controller: nameTextEditingController, - hintText: S.of(context).wallet_name, + hintText: S + .of(context) + .wallet_name, suffixIcon: IconButton( onPressed: () async { final rName = await generateName(); @@ -156,7 +170,9 @@ class WalletRestoreFromSeedFormState extends State { padding: const EdgeInsets.all(8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(6.0), - color: Theme.of(context).hintColor, + color: Theme + .of(context) + .hintColor, ), width: 34, height: 34, @@ -183,13 +199,14 @@ class WalletRestoreFromSeedFormState extends State { onTap: () async { await showPopUp( context: context, - builder: (_) => Picker( + builder: (_) => + Picker( items: _getItems(), selectedAtIndex: isPolyseed ? 1 : seedTypeController.value.text.contains("14") - ? 2 - : 0, + ? 2 + : 0, mainAxisAlignment: MainAxisAlignment.start, onItemSelected: _changeSeedType, isSeparated: false, @@ -211,37 +228,43 @@ class WalletRestoreFromSeedFormState extends State { if (widget.displayWalletPassword) ...[BaseTextFormField( controller: passwordTextEditingController, - hintText: S.of(context).password, + hintText: S + .of(context) + .password, obscureText: true), BaseTextFormField( controller: repeatedPasswordTextEditingController, - hintText: S.of(context).repeat_wallet_password, - obscureText: true)], + hintText: S + .of(context) + .repeat_wallet_password, + obscureText: true) + ], if (widget.displayLanguageSelector) - if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) - GestureDetector( - onTap: () async { - await showPopUp( - context: context, - builder: (_) => SeedLanguagePicker( - selected: language, - onItemSelected: _changeLanguage, - seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy, - )); - }, - child: Container( - color: Colors.transparent, - padding: EdgeInsets.only(top: 20.0), - child: IgnorePointer( - child: BaseTextFormField( - controller: languageController, - enableInteractiveSelection: false, - readOnly: true, - suffixIcon: expandIcon, + if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector) + GestureDetector( + onTap: () async { + await showPopUp( + context: context, + builder: (_) => + SeedLanguagePicker( + selected: language, + onItemSelected: _changeLanguage, + seedType: isPolyseed ? MoneroSeedType.polyseed : MoneroSeedType.legacy, + )); + }, + child: Container( + color: Colors.transparent, + padding: EdgeInsets.only(top: 20.0), + child: IgnorePointer( + child: BaseTextFormField( + controller: languageController, + enableInteractiveSelection: false, + readOnly: true, + suffixIcon: expandIcon, + ), ), ), ), - ), if ((!isPolyseed) && widget.displayBlockHeightSelector) BlockchainHeightWidget( focusNode: widget.blockHeightFocusNode, @@ -262,17 +285,20 @@ class WalletRestoreFromSeedFormState extends State { } bool get isPolyseed => - widget.seedTypeViewModel.moneroSeedType == SeedType.polyseed && - (widget.type == WalletType.monero || widget.type == WalletType.wownero); + widget.seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed && + (widget.type == WalletType.monero || widget.type == WalletType.wownero); - Widget get expandIcon => Container( + Widget get expandIcon => + Container( padding: EdgeInsets.all(18), width: 24, height: 24, child: Image.asset( 'assets/images/arrow_bottom_purple_icon.png', height: 8, - color: Theme.of(context).hintColor, + color: Theme + .of(context) + .hintColor, ), ); @@ -280,8 +306,8 @@ class WalletRestoreFromSeedFormState extends State { final setLang = isPolyseed ? "POLYSEED_$language" : seedTypeController.value.text.contains("14") - ? "WOWSEED_" + language - : language; + ? "WOWSEED_" + language + : language; setState(() { this.language = setLang; seedWidgetStateKey.currentState!.changeSeedLanguage(setLang); @@ -293,24 +319,24 @@ class WalletRestoreFromSeedFormState extends State { void _setLanguageLabel(String language) => languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)'; - void _changeSeedType(SeedType item) { + void _changeSeedType(MoneroSeedType item) { _setSeedType(item); _changeLanguage('English'); - widget.seedTypeViewModel.setMoneroSeedType(item); + widget.seedSettingsViewModel.setMoneroSeedType(item); } - void _setSeedType(SeedType item) { + void _setSeedType(MoneroSeedType item) { seedTypeController.text = item.toString(); } - List _getItems() { + List _getItems() { switch (widget.type) { case WalletType.monero: - return [SeedType.legacy, SeedType.polyseed]; + return [MoneroSeedType.legacy, MoneroSeedType.polyseed]; case WalletType.wownero: - return [SeedType.legacy, SeedType.polyseed, SeedType.wowneroSeed]; + return [MoneroSeedType.legacy, MoneroSeedType.polyseed, MoneroSeedType.wowneroSeed]; default: - return [SeedType.legacy]; + return [MoneroSeedType.legacy]; } } } diff --git a/lib/src/screens/restore/wallet_restore_page.dart b/lib/src/screens/restore/wallet_restore_page.dart index c8fa3665e..741befbc0 100644 --- a/lib/src/screens/restore/wallet_restore_page.dart +++ b/lib/src/screens/restore/wallet_restore_page.dart @@ -12,7 +12,7 @@ import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart'; -import 'package:cake_wallet/view_model/seed_type_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; @@ -23,7 +23,7 @@ import 'package:mobx/mobx.dart'; import 'package:smooth_page_indicator/smooth_page_indicator.dart'; class WalletRestorePage extends BasePage { - WalletRestorePage(this.walletRestoreViewModel, this.seedTypeViewModel) + WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel) : walletRestoreFromSeedFormKey = GlobalKey(), walletRestoreFromKeysFormKey = GlobalKey(), _pages = [], @@ -33,7 +33,7 @@ class WalletRestorePage extends BasePage { switch (mode) { case WalletRestoreMode.seed: _pages.add(WalletRestoreFromSeedForm( - seedTypeViewModel: seedTypeViewModel, + seedSettingsViewModel: seedSettingsViewModel, displayBlockHeightSelector: walletRestoreViewModel.hasBlockchainHeightLanguageSelector, displayLanguageSelector: walletRestoreViewModel.hasSeedLanguageSelector, @@ -96,7 +96,7 @@ class WalletRestorePage extends BasePage { )); final WalletRestoreViewModel walletRestoreViewModel; - final SeedTypeViewModel seedTypeViewModel; + final SeedSettingsViewModel seedSettingsViewModel; final PageController _controller; final List _pages; final GlobalKey walletRestoreFromSeedFormKey; @@ -233,6 +233,7 @@ class WalletRestorePage extends BasePage { onTap: () { Navigator.of(context) .pushNamed(Routes.advancedPrivacySettings, arguments: { + 'isFromRestore': true, 'type': walletRestoreViewModel.type, 'useTestnet': walletRestoreViewModel.useTestnet, 'toggleTestnet': walletRestoreViewModel.toggleUseTestnet @@ -322,8 +323,7 @@ class WalletRestorePage extends BasePage { } if (walletRestoreViewModel.hasPassphrase) { - credentials['passphrase'] = - walletRestoreFromSeedFormKey.currentState!.passphraseController.text; + credentials['passphrase'] = seedSettingsViewModel.passphrase; } credentials['name'] = @@ -426,6 +426,7 @@ class WalletRestorePage extends BasePage { } await walletRestoreViewModel.create(options: _credentials()); + seedSettingsViewModel.setPassphrase(null); } catch (e) { _formProcessing = false; rethrow; diff --git a/lib/src/widgets/seed_language_picker.dart b/lib/src/widgets/seed_language_picker.dart index 5cb8f2fd3..4a63e3092 100644 --- a/lib/src/widgets/seed_language_picker.dart +++ b/lib/src/widgets/seed_language_picker.dart @@ -11,36 +11,36 @@ class SeedLanguagePickerOption { final String name; final String nameLocalized; final Image image; - final List supportedSeedTypes; + final List supportedSeedTypes; } final List seedLanguages = [ SeedLanguagePickerOption('English', S.current.seed_language_english, - Image.asset('assets/images/flags/usa.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/usa.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('Chinese (Simplified)', S.current.seed_language_chinese, - Image.asset('assets/images/flags/chn.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/chn.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional, - Image.asset('assets/images/flags/chn.png'), [SeedType.polyseed]), + Image.asset('assets/images/flags/chn.png'), [MoneroSeedType.polyseed]), SeedLanguagePickerOption('Dutch', S.current.seed_language_dutch, - Image.asset('assets/images/flags/nld.png'), [SeedType.legacy]), + Image.asset('assets/images/flags/nld.png'), [MoneroSeedType.legacy]), SeedLanguagePickerOption('German', S.current.seed_language_german, - Image.asset('assets/images/flags/deu.png'), [SeedType.legacy]), + Image.asset('assets/images/flags/deu.png'), [MoneroSeedType.legacy]), SeedLanguagePickerOption('Japanese', S.current.seed_language_japanese, - Image.asset('assets/images/flags/jpn.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/jpn.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('Korean', S.current.seed_language_korean, - Image.asset('assets/images/flags/kor.png'), [SeedType.polyseed]), + Image.asset('assets/images/flags/kor.png'), [MoneroSeedType.polyseed]), SeedLanguagePickerOption('Portuguese', S.current.seed_language_portuguese, - Image.asset('assets/images/flags/prt.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/prt.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('Russian', S.current.seed_language_russian, - Image.asset('assets/images/flags/rus.png'), [SeedType.legacy]), + Image.asset('assets/images/flags/rus.png'), [MoneroSeedType.legacy]), SeedLanguagePickerOption('Czech', S.current.seed_language_czech, - Image.asset('assets/images/flags/czk.png'), [SeedType.polyseed]), + Image.asset('assets/images/flags/czk.png'), [MoneroSeedType.polyseed]), SeedLanguagePickerOption('Spanish', S.current.seed_language_spanish, - Image.asset('assets/images/flags/esp.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/esp.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('French', S.current.seed_language_french, - Image.asset('assets/images/flags/fra.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/fra.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), SeedLanguagePickerOption('Italian', S.current.seed_language_italian, - Image.asset('assets/images/flags/ita.png'), [SeedType.legacy, SeedType.polyseed]), + Image.asset('assets/images/flags/ita.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]), ]; const defaultSeedLanguage = 'English'; @@ -51,11 +51,11 @@ class SeedLanguagePicker extends StatefulWidget { SeedLanguagePicker( {Key? key, this.selected = defaultSeedLanguage, - this.seedType = SeedType.defaultSeedType, + this.seedType = MoneroSeedType.defaultSeedType, required this.onItemSelected}) : super(key: key); - final SeedType seedType; + final MoneroSeedType seedType; final String selected; final Function(String) onItemSelected; @@ -68,7 +68,7 @@ class SeedLanguagePickerState extends State { SeedLanguagePickerState( {required this.selected, required this.onItemSelected, required this.seedType}); - final SeedType seedType; + final MoneroSeedType seedType; final String selected; final Function(String) onItemSelected; diff --git a/lib/src/widgets/seed_language_selector.dart b/lib/src/widgets/seed_language_selector.dart index dde78c58c..87f3aa573 100644 --- a/lib/src/widgets/seed_language_selector.dart +++ b/lib/src/widgets/seed_language_selector.dart @@ -7,11 +7,11 @@ import 'package:flutter/material.dart'; class SeedLanguageSelector extends StatefulWidget { SeedLanguageSelector( - {Key? key, required this.initialSelected, this.seedType = SeedType.defaultSeedType}) + {Key? key, required this.initialSelected, this.seedType = MoneroSeedType.defaultSeedType}) : super(key: key); final String initialSelected; - final SeedType seedType; + final MoneroSeedType seedType; @override SeedLanguageSelectorState createState() => SeedLanguageSelectorState(selected: initialSelected); diff --git a/lib/store/seed_settings_store.dart b/lib/store/seed_settings_store.dart new file mode 100644 index 000000000..90c02ba97 --- /dev/null +++ b/lib/store/seed_settings_store.dart @@ -0,0 +1,11 @@ +import 'package:mobx/mobx.dart'; + +part 'seed_settings_store.g.dart'; + +class SeedSettingsStore = SeedSettingsStoreBase with _$SeedSettingsStore; + +abstract class SeedSettingsStoreBase with Store { + + @observable + String? passphrase; +} diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index ee145195c..ea5f43d98 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,45 +1,46 @@ import 'dart:io'; + import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/core/secure_storage.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/entities/action_list_display_mode.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; -import 'package:cake_wallet/entities/provider_types.dart'; -import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; +import 'package:cake_wallet/entities/balance_display_mode.dart'; +import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/entities/language_service.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; import 'package:cake_wallet/entities/seed_phrase_length.dart'; import 'package:cake_wallet/entities/seed_type.dart'; import 'package:cake_wallet/entities/sort_balance_types.dart'; import 'package:cake_wallet/entities/wallet_list_order_types.dart'; -import 'package:cake_wallet/polygon/polygon.dart'; -import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; -import 'package:cake_wallet/view_model/settings/sync_mode.dart'; -import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; -import 'package:cake_wallet/wallet_type_utils.dart'; -import 'package:cake_wallet/wownero/wownero.dart'; -import 'package:cw_core/transaction_priority.dart'; +import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/themes/theme_base.dart'; import 'package:cake_wallet/themes/theme_list.dart'; +import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/package_info.dart'; +import 'package:cake_wallet/view_model/settings/sync_mode.dart'; +import 'package:cake_wallet/wallet_type_utils.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; +import 'package:cw_core/node.dart'; +import 'package:cw_core/set_app_secure_native.dart'; +import 'package:cw_core/transaction_priority.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/utils/package_info.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cw_core/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:cake_wallet/entities/language_service.dart'; -import 'package:cake_wallet/entities/balance_display_mode.dart'; -import 'package:cake_wallet/entities/fiat_currency.dart'; -import 'package:cw_core/node.dart'; -import 'package:cake_wallet/monero/monero.dart'; -import 'package:cake_wallet/entities/action_list_display_mode.dart'; -import 'package:cake_wallet/entities/fiat_api_mode.dart'; -import 'package:cw_core/set_app_secure_native.dart'; part 'settings_store.g.dart'; @@ -55,7 +56,8 @@ abstract class SettingsStoreBase with Store { required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus, - required SeedType initialMoneroSeedType, + required MoneroSeedType initialMoneroSeedType, + required BitcoinSeedType initialBitcoinSeedType, required bool initialAppSecure, required bool initialDisableBuy, required bool initialDisableSell, @@ -128,6 +130,7 @@ abstract class SettingsStoreBase with Store { shouldSaveRecipientAddress = initialSaveRecipientAddress, autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus, moneroSeedType = initialMoneroSeedType, + bitcoinSeedType = initialBitcoinSeedType, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, selectedCake2FAPreset = initialCake2FAPresetOptions, @@ -329,9 +332,14 @@ abstract class SettingsStoreBase with Store { reaction( (_) => moneroSeedType, - (SeedType moneroSeedType) => + (MoneroSeedType moneroSeedType) => sharedPreferences.setInt(PreferencesKey.moneroSeedType, moneroSeedType.raw)); + reaction( + (_) => bitcoinSeedType, + (BitcoinSeedType bitcoinSeedType) => sharedPreferences.setInt( + PreferencesKey.bitcoinSeedType, bitcoinSeedType.raw)); + reaction( (_) => fiatApiMode, (FiatApiMode mode) => @@ -555,7 +563,8 @@ abstract class SettingsStoreBase with Store { static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized; static final walletPasswordDirectInput = Platform.isLinux; static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords; - static const defaultMoneroSeedType = SeedType.defaultSeedType; + static const defaultMoneroSeedType = MoneroSeedType.defaultSeedType; + static const defaultBitcoinSeedType = BitcoinSeedType.defaultDerivationType; @observable FiatCurrency fiatCurrency; @@ -585,7 +594,10 @@ abstract class SettingsStoreBase with Store { AutoGenerateSubaddressStatus autoGenerateSubaddressStatus; @observable - SeedType moneroSeedType; + MoneroSeedType moneroSeedType; + + @observable + BitcoinSeedType bitcoinSeedType; @observable bool isAppSecure; @@ -945,9 +957,15 @@ abstract class SettingsStoreBase with Store { final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); final moneroSeedType = _moneroSeedType != null - ? SeedType.deserialize(raw: _moneroSeedType) + ? MoneroSeedType.deserialize(raw: _moneroSeedType) : defaultMoneroSeedType; + final _bitcoinSeedType = sharedPreferences.getInt(PreferencesKey.bitcoinSeedType); + + final bitcoinSeedType = _bitcoinSeedType != null + ? BitcoinSeedType.deserialize(raw: _bitcoinSeedType) + : defaultBitcoinSeedType; + final nodes = {}; final powNodes = {}; @@ -1111,6 +1129,7 @@ abstract class SettingsStoreBase with Store { initialSaveRecipientAddress: shouldSaveRecipientAddress, initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus, initialMoneroSeedType: moneroSeedType, + initialBitcoinSeedType: bitcoinSeedType, initialAppSecure: isAppSecure, initialDisableBuy: disableBuy, initialDisableSell: disableSell, @@ -1233,9 +1252,15 @@ abstract class SettingsStoreBase with Store { final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType); moneroSeedType = _moneroSeedType != null - ? SeedType.deserialize(raw: _moneroSeedType) + ? MoneroSeedType.deserialize(raw: _moneroSeedType) : defaultMoneroSeedType; + final _bitcoinSeedType = sharedPreferences.getInt(PreferencesKey.bitcoinSeedType); + + bitcoinSeedType = _bitcoinSeedType != null + ? BitcoinSeedType.deserialize(raw: _bitcoinSeedType) + : defaultBitcoinSeedType; + balanceDisplayMode = BalanceDisplayMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!); shouldSaveRecipientAddress = diff --git a/lib/view_model/advanced_privacy_settings_view_model.dart b/lib/view_model/advanced_privacy_settings_view_model.dart index 73308f15a..da294cdea 100644 --- a/lib/view_model/advanced_privacy_settings_view_model.dart +++ b/lib/view_model/advanced_privacy_settings_view_model.dart @@ -30,6 +30,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { final SettingsStore _settingsStore; + @computed bool get hasSeedPhraseLengthOption { // convert to switch case so that it give a syntax error when adding a new wallet type // thus we don't forget about it @@ -40,11 +41,14 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { case WalletType.solana: case WalletType.tron: return true; + + case WalletType.bitcoin: + case WalletType.litecoin: + return _settingsStore.bitcoinSeedType == BitcoinSeedType.bip39; + case WalletType.monero: case WalletType.wownero: case WalletType.none: - case WalletType.bitcoin: - case WalletType.litecoin: case WalletType.haven: case WalletType.nano: case WalletType.banano: @@ -52,7 +56,13 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { } } - bool get hasSeedTypeOption => type == WalletType.monero || type == WalletType.wownero; + bool get hasSeedTypeOption => [WalletType.monero, WalletType.wownero].contains(type); + + bool get hasPassphraseOption => [ + WalletType.bitcoin, + WalletType.litecoin, + WalletType.bitcoinCash, + ].contains(type); @computed bool get addCustomNode => _addCustomNode; @@ -61,7 +71,7 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store { SeedPhraseLength get seedPhraseLength => _settingsStore.seedPhraseLength; @computed - bool get isPolySeed => _settingsStore.moneroSeedType == SeedType.polyseed; + bool get isPolySeed => _settingsStore.moneroSeedType == MoneroSeedType.polyseed; @action void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; diff --git a/lib/view_model/restore/restore_from_qr_vm.dart b/lib/view_model/restore/restore_from_qr_vm.dart index f5938911b..789d7da52 100644 --- a/lib/view_model/restore/restore_from_qr_vm.dart +++ b/lib/view_model/restore/restore_from_qr_vm.dart @@ -7,6 +7,7 @@ import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cake_wallet/wownero/wownero.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; @@ -26,13 +27,13 @@ class WalletRestorationFromQRVM = WalletRestorationFromQRVMBase with _$WalletRes abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store { WalletRestorationFromQRVMBase(AppStore appStore, WalletCreationService walletCreationService, - Box walletInfoSource, WalletType type) + Box walletInfoSource, WalletType type, SeedSettingsViewModel seedSettingsViewModel) : height = 0, viewKey = '', spendKey = '', wif = '', address = '', - super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true); + super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true); @observable int height; diff --git a/lib/view_model/seed_settings_view_model.dart b/lib/view_model/seed_settings_view_model.dart new file mode 100644 index 000000000..3a9536885 --- /dev/null +++ b/lib/view_model/seed_settings_view_model.dart @@ -0,0 +1,34 @@ +import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/seed_settings_store.dart'; +import 'package:mobx/mobx.dart'; + +part 'seed_settings_view_model.g.dart'; + +class SeedSettingsViewModel = SeedSettingsViewModelBase with _$SeedSettingsViewModel; + +abstract class SeedSettingsViewModelBase with Store { + SeedSettingsViewModelBase(this._appStore, this._seedSettingsStore); + + @computed + MoneroSeedType get moneroSeedType => _appStore.settingsStore.moneroSeedType; + + @action + void setMoneroSeedType(MoneroSeedType seedType) => _appStore.settingsStore.moneroSeedType = seedType; + + @computed + BitcoinSeedType get bitcoinSeedType => _appStore.settingsStore.bitcoinSeedType; + + @action + void setBitcoinSeedType(BitcoinSeedType derivationType) => + _appStore.settingsStore.bitcoinSeedType = derivationType; + + @computed + String? get passphrase => this._seedSettingsStore.passphrase; + + @action + void setPassphrase(String? passphrase) => this._seedSettingsStore.passphrase = passphrase; + + final AppStore _appStore; + final SeedSettingsStore _seedSettingsStore; +} diff --git a/lib/view_model/seed_type_view_model.dart b/lib/view_model/seed_type_view_model.dart deleted file mode 100644 index e3597be1c..000000000 --- a/lib/view_model/seed_type_view_model.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:cake_wallet/entities/seed_type.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:mobx/mobx.dart'; - -part 'seed_type_view_model.g.dart'; - -class SeedTypeViewModel = SeedTypeViewModelBase with _$SeedTypeViewModel; - -abstract class SeedTypeViewModelBase with Store { - SeedTypeViewModelBase(this._appStore); - - @computed - SeedType get moneroSeedType => _appStore.settingsStore.moneroSeedType; - - @action - void setMoneroSeedType(SeedType seedType) => _appStore.settingsStore.moneroSeedType = seedType; - - final AppStore _appStore; -} diff --git a/lib/view_model/wallet_creation_vm.dart b/lib/view_model/wallet_creation_vm.dart index e14934986..5760f5eec 100644 --- a/lib/view_model/wallet_creation_vm.dart +++ b/lib/view_model/wallet_creation_vm.dart @@ -1,20 +1,21 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; +import 'package:cake_wallet/entities/generate_name.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/restore/restore_wallet.dart'; -import 'package:hive/hive.dart'; -import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/core/execution_state.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; +import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/pathForWallet.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:cake_wallet/entities/generate_name.dart'; +import 'package:hive/hive.dart'; +import 'package:mobx/mobx.dart'; import 'package:polyseed/polyseed.dart'; part 'wallet_creation_vm.g.dart'; @@ -23,6 +24,7 @@ class WalletCreationVM = WalletCreationVMBase with _$WalletCreationVM; abstract class WalletCreationVMBase with Store { WalletCreationVMBase(this._appStore, this._walletInfoSource, this.walletCreationService, + this.seedSettingsViewModel, {required this.type, required this.isRecovery}) : state = InitialExecutionState(), name = ''; @@ -44,7 +46,6 @@ abstract class WalletCreationVMBase with Store { @observable String? repeatedWalletPassword; - bool get hasWalletPassword => SettingsStoreBase.walletPasswordDirectInput; WalletType type; @@ -52,6 +53,7 @@ abstract class WalletCreationVMBase with Store { final WalletCreationService walletCreationService; final Box _walletInfoSource; final AppStore _appStore; + final SeedSettingsViewModel seedSettingsViewModel; bool isPolyseed(String seed) => (type == WalletType.monero || type == WalletType.wownero) && @@ -109,17 +111,35 @@ abstract class WalletCreationVMBase with Store { getIt.get().registerSyncTask(); _appStore.authenticationStore.allowed(); state = ExecutedSuccessfullyState(); - } catch (e, s) { + } catch (e, _) { state = FailureState(e.toString()); } } DerivationInfo? getDefaultDerivation() { - switch (this.type) { + final useBip39 = seedSettingsViewModel.bitcoinSeedType.type == DerivationType.bip39; + switch (type) { case WalletType.nano: return DerivationInfo(derivationType: DerivationType.nano); case WalletType.bitcoin: + if (useBip39) { + return DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/0'/0'", + description: "Standard BIP84 native segwit", + scriptType: "p2wpkh", + ); + } + return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first; case WalletType.litecoin: + if (useBip39) { + return DerivationInfo( + derivationType: DerivationType.bip39, + derivationPath: "m/84'/2'/0'", + description: "Default Litecoin", + scriptType: "p2wpkh", + ); + } return bitcoin!.getElectrumDerivations()[DerivationType.electrum]!.first; default: return null; diff --git a/lib/view_model/wallet_hardware_restore_view_model.dart b/lib/view_model/wallet_hardware_restore_view_model.dart index 804ef7e3c..68bc95a00 100644 --- a/lib/view_model/wallet_hardware_restore_view_model.dart +++ b/lib/view_model/wallet_hardware_restore_view_model.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/polygon/polygon.dart'; import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; import 'package:cw_core/hardware/hardware_account_data.dart'; import 'package:cw_core/wallet_base.dart'; @@ -25,10 +26,15 @@ abstract class WalletHardwareRestoreViewModelBase extends WalletCreationVM with int _nextIndex = 0; - WalletHardwareRestoreViewModelBase(this.ledgerViewModel, AppStore appStore, - WalletCreationService walletCreationService, Box walletInfoSource, + WalletHardwareRestoreViewModelBase( + this.ledgerViewModel, + AppStore appStore, + WalletCreationService walletCreationService, + Box walletInfoSource, + SeedSettingsViewModel seedSettingsViewModel, {required WalletType type}) - : super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true); + : super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, + type: type, isRecovery: true); @observable String name = ""; diff --git a/lib/view_model/wallet_keys_view_model.dart b/lib/view_model/wallet_keys_view_model.dart index 1d5c27fed..9921ae30a 100644 --- a/lib/view_model/wallet_keys_view_model.dart +++ b/lib/view_model/wallet_keys_view_model.dart @@ -276,7 +276,8 @@ abstract class WalletKeysViewModelBase with Store { if (_appStore.wallet!.seed == null && _appStore.wallet!.privateKey != null) 'private_key': _appStore.wallet!.privateKey!, - if (restoreHeightResult != null) ...{'height': restoreHeightResult} + if (restoreHeightResult != null) ...{'height': restoreHeightResult}, + if (_appStore.wallet!.passphrase != null) 'passphrase': _appStore.wallet!.passphrase! }; } diff --git a/lib/view_model/wallet_new_vm.dart b/lib/view_model/wallet_new_vm.dart index a618695b1..8c0f4f95f 100644 --- a/lib/view_model/wallet_new_vm.dart +++ b/lib/view_model/wallet_new_vm.dart @@ -1,35 +1,42 @@ -import 'package:cake_wallet/ethereum/ethereum.dart'; +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; -import 'package:cake_wallet/solana/solana.dart'; -import 'package:cake_wallet/tron/tron.dart'; -import 'package:cake_wallet/wownero/wownero.dart'; -import 'package:hive/hive.dart'; -import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/core/wallet_creation_service.dart'; +import 'package:cake_wallet/entities/seed_type.dart'; +import 'package:cake_wallet/ethereum/ethereum.dart'; +import 'package:cake_wallet/haven/haven.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cake_wallet/nano/nano.dart'; +import 'package:cake_wallet/solana/solana.dart'; import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cake_wallet/core/wallet_creation_service.dart'; import 'package:cw_core/wallet_credentials.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; -import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; -import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/haven/haven.dart'; -import 'advanced_privacy_settings_view_model.dart'; +import 'package:hive/hive.dart'; +import 'package:mobx/mobx.dart'; import '../polygon/polygon.dart'; +import 'advanced_privacy_settings_view_model.dart'; part 'wallet_new_vm.g.dart'; class WalletNewVM = WalletNewVMBase with _$WalletNewVM; abstract class WalletNewVMBase extends WalletCreationVM with Store { - WalletNewVMBase(AppStore appStore, WalletCreationService walletCreationService, - Box walletInfoSource, this.advancedPrivacySettingsViewModel, + WalletNewVMBase( + AppStore appStore, + WalletCreationService walletCreationService, + Box walletInfoSource, + this.advancedPrivacySettingsViewModel, + SeedSettingsViewModel seedSettingsViewModel, {required WalletType type}) : selectedMnemonicLanguage = '', - super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: false); + super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, + type: type, isRecovery: false); final AdvancedPrivacySettingsViewModel advancedPrivacySettingsViewModel; @@ -37,47 +44,58 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store { String selectedMnemonicLanguage; bool get hasLanguageSelector => - type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero; + [WalletType.monero, WalletType.haven, WalletType.wownero].contains(type); int get seedPhraseWordsLength { switch (type) { case WalletType.monero: case WalletType.wownero: - if (advancedPrivacySettingsViewModel.isPolySeed) { - return 16; - } - return 25; + return advancedPrivacySettingsViewModel.isPolySeed ? 16 : 25; case WalletType.tron: case WalletType.solana: case WalletType.polygon: case WalletType.ethereum: case WalletType.bitcoinCash: return advancedPrivacySettingsViewModel.seedPhraseLength.value; + case WalletType.bitcoin: + case WalletType.litecoin: + return seedSettingsViewModel.bitcoinSeedType == BitcoinSeedType.bip39 + ? advancedPrivacySettingsViewModel.seedPhraseLength.value + : 24; default: return 24; } } - bool get hasSeedType => type == WalletType.monero || type == WalletType.wownero; + bool get hasSeedType => [WalletType.monero, WalletType.wownero].contains(type); @override WalletCredentials getCredentials(dynamic _options) { final options = _options as List?; + final passphrase = seedSettingsViewModel.passphrase; + seedSettingsViewModel.setPassphrase(null); + switch (type) { case WalletType.monero: return monero!.createMoneroNewWalletCredentials( - name: name, language: options!.first as String, password: walletPassword, isPolyseed: options.last as bool); + name: name, + language: options!.first as String, + password: walletPassword, + isPolyseed: options.last as bool); case WalletType.bitcoin: - return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); + return bitcoin!.createBitcoinNewWalletCredentials( + name: name, password: walletPassword, passphrase: passphrase); case WalletType.litecoin: - return bitcoin!.createBitcoinNewWalletCredentials(name: name, password: walletPassword); + return bitcoin!.createBitcoinNewWalletCredentials( + name: name, password: walletPassword, passphrase: passphrase); case WalletType.haven: return haven!.createHavenNewWalletCredentials( name: name, language: options!.first as String, password: walletPassword); case WalletType.ethereum: return ethereum!.createEthereumNewWalletCredentials(name: name, password: walletPassword); case WalletType.bitcoinCash: - return bitcoinCash!.createBitcoinCashNewWalletCredentials(name: name, password: walletPassword); + return bitcoinCash!.createBitcoinCashNewWalletCredentials( + name: name, password: walletPassword, passphrase: passphrase); case WalletType.nano: case WalletType.banano: return nano!.createNanoNewWalletCredentials(name: name); diff --git a/lib/view_model/wallet_restore_view_model.dart b/lib/view_model/wallet_restore_view_model.dart index a38baabd8..a365a2040 100644 --- a/lib/view_model/wallet_restore_view_model.dart +++ b/lib/view_model/wallet_restore_view_model.dart @@ -1,25 +1,26 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cake_wallet/nano/nano.dart'; -import 'package:cake_wallet/ethereum/ethereum.dart'; -import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; -import 'package:cake_wallet/polygon/polygon.dart'; -import 'package:cake_wallet/solana/solana.dart'; -import 'package:cake_wallet/tron/tron.dart'; -import 'package:hive/hive.dart'; -import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/store/app_store.dart'; -import 'package:cw_core/wallet_base.dart'; import 'package:cake_wallet/core/generate_wallet_password.dart'; import 'package:cake_wallet/core/wallet_creation_service.dart'; -import 'package:cw_core/wallet_credentials.dart'; -import 'package:cw_core/wallet_type.dart'; -import 'package:cw_core/wallet_info.dart'; -import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; -import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/haven/haven.dart'; +import 'package:cake_wallet/monero/monero.dart'; +import 'package:cake_wallet/nano/nano.dart'; +import 'package:cake_wallet/polygon/polygon.dart'; +import 'package:cake_wallet/solana/solana.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/tron/tron.dart'; import 'package:cake_wallet/view_model/restore/restore_mode.dart'; +import 'package:cake_wallet/view_model/seed_settings_view_model.dart'; +import 'package:cake_wallet/view_model/wallet_creation_vm.dart'; +import 'package:cake_wallet/wownero/wownero.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_credentials.dart'; +import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:hive/hive.dart'; +import 'package:mobx/mobx.dart'; part 'wallet_restore_view_model.g.dart'; @@ -27,7 +28,7 @@ class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreVi abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService, - Box walletInfoSource, + Box walletInfoSource, SeedSettingsViewModel seedSettingsViewModel, {required WalletType type}) : hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero, @@ -41,7 +42,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { type == WalletType.tron, isButtonEnabled = false, mode = WalletRestoreMode.seed, - super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) { + super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel, type: type, isRecovery: true) { switch (type) { case WalletType.monero: availableModes = WalletRestoreMode.values; @@ -76,7 +77,8 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store { final bool hasBlockchainHeightLanguageSelector; final bool hasRestoreFromPrivateKey; - bool get hasPassphrase => [WalletType.bitcoin, WalletType.litecoin].contains(type); + bool get hasPassphrase => + [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(type); @observable WalletRestoreMode mode; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index f84f6102b..89068d2d0 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "هل أنت متأكد أنك تريد حذف محفظة ${wallet_name}؟", "deleteConnectionConfirmationPrompt": "ـﺑ ﻝﺎﺼﺗﻻﺍ ﻑﺬﺣ ﺪﻳﺮﺗ ﻚﻧﺃ ﺪﻛﺄﺘﻣ ﺖﻧﺃ ﻞﻫ", "denominations": "الطوائف", + "derivationpath": "مسار الاشتقاق", "descending": "النزول", "description": "ﻒﺻﻭ", "destination_tag": "علامة الوجهة:", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 0b6b688bb..1ff8fdf85 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Сигурни ли сте, че искате да изтриете протфейла ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Сигурни ли сте, че искате да изтриете връзката към", "denominations": "Деноминации", + "derivationpath": "Пътят на производно", "descending": "Низходящ", "description": "Описание", "destination_tag": "Destination tag:", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index ef225041d..904c6d8e4 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Opravdu chcete smazat ${wallet_name} peněženku?", "deleteConnectionConfirmationPrompt": "Jste si jisti, že chcete smazat připojení k?", "denominations": "Označení", + "derivationpath": "Derivační cesta", "descending": "Klesající", "description": "Popis", "destination_tag": "Destination Tag:", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 393c3d928..860d4114d 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?", "deleteConnectionConfirmationPrompt": "Sind Sie sicher, dass Sie die Verbindung zu löschen möchten?", "denominations": "Konfessionen", + "derivationpath": "Ableitungspfad", "descending": "Absteigend", "description": "Beschreibung", "destination_tag": "Ziel-Tag:", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 4ac7dd8fd..9f89eb6ab 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Are you sure that you want to delete ${wallet_name} wallet?", "deleteConnectionConfirmationPrompt": "Are you sure that you want to delete the connection to", "denominations": "Denominations", + "derivationpath": "Derivation Path", "descending": "Descending", "description": "Description", "destination_tag": "Destination tag:", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index c308f045a..250decfc4 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "¿Está seguro de que desea eliminar la billetera ${wallet_name}?", "deleteConnectionConfirmationPrompt": "¿Está seguro de que desea eliminar la conexión a", "denominations": "Denominaciones", + "derivationpath": "Ruta de derivación", "descending": "Descendente", "description": "Descripción", "destination_tag": "Etiqueta de destino:", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 4bc5c9809..297019aa0 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Êtes-vous sûr de vouloir supprimer le portefeuille (wallet) ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Êtes-vous sûr de vouloir supprimer la connexion à", "denominations": "Dénominations", + "derivationpath": "Chemin de dérivation", "descending": "Descendant", "description": "Description", "destination_tag": "Tag de destination :", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 025a33f6b..05cdf27d4 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Shin kun tabbata cewa kuna son share jakar ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Shin kun tabbata cewa kuna son share haɗin zuwa", "denominations": "Denominations", + "derivationpath": "Hanyar Nasara", "descending": "Saukowa", "description": "Bayani", "destination_tag": "Tambarin makoma:", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 346c420a9..30f86d9ae 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "क्या आप वाकई ${wallet_name} वॉलेट हटाना चाहते हैं?", "deleteConnectionConfirmationPrompt": "क्या आप वाकई कनेक्शन हटाना चाहते हैं?", "denominations": "मूल्यवर्ग", + "derivationpath": "व्युत्पत्ति पथ", "descending": "अवरोही", "description": "विवरण", "destination_tag": "गंतव्य टैग:", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 6f5bc88a6..0d6e9a0d8 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Jeste li sigurni da želite izbrisati ${wallet_name} novčanik?", "deleteConnectionConfirmationPrompt": "Jeste li sigurni da želite izbrisati vezu s", "denominations": "Denominacije", + "derivationpath": "Put derivacije", "descending": "Silazni", "description": "Opis", "destination_tag": "Odredišna oznaka:", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 2bc298aa7..65703e0bc 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Apakah Anda yakin ingin menghapus dompet ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Apakah Anda yakin ingin menghapus koneksi ke", "denominations": "Denominasi", + "derivationpath": "Jalur derivasi", "descending": "Menurun", "description": "Keterangan", "destination_tag": "Tag tujuan:", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 0548b0ad2..c2ba20ce0 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -196,6 +196,7 @@ "delete_wallet_confirm_message": "Sei sicuro di voler eliminare il portafoglio ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Sei sicuro di voler eliminare la connessione a", "denominations": "Denominazioni", + "derivationpath": "Percorso di derivazione", "descending": "Discendente", "description": "Descrizione", "destination_tag": "Tag destinazione:", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index c53a6d001..ab6605257 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "${wallet_name} ウォレットを削除してもよろしいですか?", "deleteConnectionConfirmationPrompt": "への接続を削除してもよろしいですか?", "denominations": "宗派", + "derivationpath": "派生パス", "descending": "下降", "description": "説明", "destination_tag": "宛先タグ:", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index be6757d99..da10162ad 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "${wallet_name} 지갑을 삭제하시겠습니까?", "deleteConnectionConfirmationPrompt": "다음 연결을 삭제하시겠습니까?", "denominations": "교파", + "derivationpath": "파생 경로", "descending": "내림차순", "description": "설명", "destination_tag": "목적지 태그:", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c53dffb6b..e04c75d62 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "${wallet_name} ပိုက်ဆံအိတ်ကို ဖျက်လိုသည်မှာ သေချာပါသလား။", "deleteConnectionConfirmationPrompt": "ချိတ်ဆက်မှုကို ဖျက်လိုသည်မှာ သေချာပါသလား။", "denominations": "ဂိုဏ်းချုပ်ပစ္စည်းများ", + "derivationpath": "derivation လမ်းကြောင်း", "descending": "ဆင်း", "description": "ဖော်ပြချက်", "destination_tag": "ခရီးဆုံးအမှတ်-", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 2d55344f5..dfa7c2d8f 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Weet u zeker dat u de portemonnee van ${wallet_name} wilt verwijderen?", "deleteConnectionConfirmationPrompt": "Weet u zeker dat u de verbinding met", "denominations": "Denominaties", + "derivationpath": "Afleidingspad", "descending": "Aflopend", "description": "Beschrijving", "destination_tag": "Bestemmingstag:", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 833fc0308..397ea2bcb 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Czy na pewno chcesz usunąć portfel ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Czy na pewno chcesz usunąć połączenie z", "denominations": "Wyznaczenia", + "derivationpath": "Ścieżka pochodna", "descending": "Schodzenie", "description": "Opis", "destination_tag": "Tag docelowy:", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index bd6d9b506..cbe66715a 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Tem certeza de que deseja excluir a carteira ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Tem certeza de que deseja excluir a conexão com", "denominations": "Denominações", + "derivationpath": "Caminho de derivação", "descending": "descendente", "description": "Descrição", "destination_tag": "Tag de destino:", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index d43572351..9976de9f8 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Вы уверены, что хотите удалить кошелек ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Вы уверены, что хотите удалить подключение к", "denominations": "Деноминации", + "derivationpath": "Путь получения", "descending": "Нисходящий", "description": "Описание", "destination_tag": "Целевой тег:", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index d948ba6d6..e873fe5eb 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "คุณแน่ใจหรือว่าต้องการลบกระเป๋า${wallet_name}?", "deleteConnectionConfirmationPrompt": "คุณแน่ใจหรือไม่ว่าต้องการลบการเชื่อมต่อไปยัง", "denominations": "นิกาย", + "derivationpath": "เส้นทางที่ได้มา", "descending": "ลงมา", "description": "คำอธิบาย", "destination_tag": "แท็กปลายทาง:", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index a0ab030c8..c1bd2bae7 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Sigurado ka ba na gusto mong tanggalin ang iyong ${wallet_name} wallet?", "deleteConnectionConfirmationPrompt": "Sigurado ka bang gusto mong tanggalin ang koneksyon sa", "denominations": "Mga Denominasyon", + "derivationpath": "Landas ng derivation", "descending": "Pababang", "description": "Paglalarawan", "destination_tag": "Tag ng patutunguhan:", @@ -661,14 +662,14 @@ "show_details": "Ipakita ang mga detalye", "show_keys": "Ipakita ang mga seed/key", "show_market_place": "Ipakita ang Marketplace", + "show_seed": "Ipakita ang seed", "sign_message": "Mag -sign Message", + "sign_up": "Mag-sign Up", "sign_verify_message": "Mag -sign o i -verify ang mensahe", "sign_verify_message_sub": "Mag -sign o i -verify ang isang mensahe gamit ang iyong pribadong key", "sign_verify_title": "Mag -sign / Mag -verify", "signature": "Lagda", "signature_invalid_error": "Ang lagda ay hindi wasto para sa ibinigay na mensahe", - "show_seed": "Ipakita ang seed", - "sign_up": "Mag-sign Up", "signTransaction": "Mag-sign ang Transaksyon", "signup_for_card_accept_terms": "Mag-sign up para sa card at tanggapin ang mga tuntunin.", "silent_payments": "Tahimik na pagbabayad", @@ -827,9 +828,9 @@ "use_testnet": "Gumamit ng testnet", "value": "Halaga", "value_type": "Uri ng halaga", - "verify_message": "I -verify ang mensahe", "variable_pair_not_supported": "Ang variable na pares na ito ay hindi suportado sa mga napiling exchange", "verification": "Pag-verify", + "verify_message": "I -verify ang mensahe", "verify_with_2fa": "Mag-verify sa Cake 2FA", "version": "Bersyon ${currentVersion}", "view_all": "Tingnan lahat", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index e7cdc5b12..ff7d9a120 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "${wallet_name} isimli cüzdanını silmek istediğinden emin misin?", "deleteConnectionConfirmationPrompt": "Bağlantıyı silmek istediğinizden emin misiniz?", "denominations": "Mezhepler", + "derivationpath": "Türev yolu", "descending": "Azalan", "description": "Tanım", "destination_tag": "Hedef Etiketi:", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 91b5b5266..95a3a38d8 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Ви впевнені, що хочете видалити гаманець ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Ви впевнені, що хочете видалити з’єднання з", "denominations": "Конфесія", + "derivationpath": "Шлях виведення", "descending": "Низхідний", "description": "опис", "destination_tag": "Тег призначення:", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index e54a0db34..5be1cb7a0 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "کیا آپ واقعی ${wallet_name} والیٹ کو حذف کرنا چاہتے ہیں؟", "deleteConnectionConfirmationPrompt": "۔ﮟﯿﮨ ﮯﺘﮨﺎﭼ ﺎﻧﺮﮐ ﻑﺬﺣ ﻮﮐ ﻦﺸﮑﻨﮐ ﭖﺁ ﮧﮐ ﮯﮨ ﻦﯿﻘﯾ ﻮﮐ ﭖﺁ ﺎﯿﮐ", "denominations": "فرق", + "derivationpath": "مشتق راستہ", "descending": "اترتے ہوئے", "description": "ﻞﯿﺼﻔﺗ", "destination_tag": "منزل کا ٹیگ:", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 8f47d8543..81d784d7f 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "Ṣó dá ẹ lójú pé ẹ fẹ́ pa àpamọ́wọ́ ${wallet_name}?", "deleteConnectionConfirmationPrompt": "Ṣe o da ọ loju pe o fẹ paarẹ asopọ si", "denominations": "Awọn ede", + "derivationpath": "Ọna Deriji", "descending": "Sọkalẹ", "description": "Apejuwe", "destination_tag": "Orúkọ tí ìbí tó a ránṣẹ́ sí:", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index c864a529b..876c3bbfb 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -195,6 +195,7 @@ "delete_wallet_confirm_message": "您确定要删除 ${wallet_name} 钱包吗?", "deleteConnectionConfirmationPrompt": "您确定要删除与", "denominations": "教派", + "derivationpath": "推导路径", "descending": "下降", "description": "描述", "destination_tag": "目标Tag:", diff --git a/tool/configure.dart b/tool/configure.dart index c9b6bbdda..b3194c4c8 100644 --- a/tool/configure.dart +++ b/tool/configure.dart @@ -151,7 +151,7 @@ abstract class Bitcoin { String? passphrase, }); WalletCredentials createBitcoinRestoreWalletFromWIFCredentials({required String name, required String password, required String wif, WalletInfo? walletInfo}); - WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password}); + WalletCredentials createBitcoinNewWalletCredentials({required String name, WalletInfo? walletInfo, String? password, String? passphrase}); WalletCredentials createBitcoinHardwareWalletCredentials({required String name, required HardwareAccountData accountData, WalletInfo? walletInfo}); List getWordList(); Map getWalletKeys(Object wallet); @@ -1021,10 +1021,10 @@ abstract class BitcoinCash { Box walletInfoSource, Box unspentCoinSource, bool isDirect); WalletCredentials createBitcoinCashNewWalletCredentials( - {required String name, WalletInfo? walletInfo, String? password}); + {required String name, WalletInfo? walletInfo, String? password, String? passphrase}); WalletCredentials createBitcoinCashRestoreWalletFromSeedCredentials( - {required String name, required String mnemonic, required String password}); + {required String name, required String mnemonic, required String password, String? passphrase}); TransactionPriority deserializeBitcoinCashTransactionPriority(int raw);