From ce21098e9824edf4f8d828f127e4d1aaeefee213 Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich <konstantinullrich12@gmail.com> Date: Mon, 26 Aug 2024 19:06:54 +0200 Subject: [PATCH 1/3] 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 <omarh.ismail1@gmail.com> --- 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<bool> checkIfMnemonicIsElectrum2(String mnemonic) async { Future<String> 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<Uint8List> mnemonicToSeedBytes(String mnemonic, {String prefix = segwit}) async { +Future<Uint8List> 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<DerivationType, List<DerivationInfo>> 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> unspentCoinsInfo, required Uint8List seedBytes, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, String? addressPageType, List<BitcoinAddressRecord>? 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<LitecoinWallet> 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> unspentCoinsInfo, required Uint8List seedBytes, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, BitcoinAddressType? addressPageType, List<BitcoinAddressRecord>? 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> unspentCoinsInfo, required EncryptionFileUtils encryptionFileUtils, + String? passphrase, String? addressPageType, List<BitcoinAddressRecord>? 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<BalanceType extends Balance, HistoryType extends Trans String? get hexSeed => 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<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> unspentCoinSource, bool alwaysScan, bool isDirect) { + WalletService createBitcoinWalletService(Box<WalletInfo> walletInfoSource, + Box<UnspentCoinsInfo> 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<void> setup({ YatStore(appStore: getIt.get<AppStore>(), secureStorage: getIt.get<SecureStorage>())..init()); getIt.registerSingleton<AnonpayTransactionsStore>( AnonpayTransactionsStore(anonpayInvoiceInfoSource: _anonpayInvoiceInfoSource)); + getIt.registerSingleton<SeedSettingsStore>(SeedSettingsStore()); getIt.registerLazySingleton(() => LedgerViewModel()); @@ -361,6 +363,7 @@ Future<void> setup({ getIt.get<WalletCreationService>(param1: type), _walletInfoSource, getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), + getIt.get<SeedSettingsViewModel>(), type: type)); getIt.registerFactoryParam<WalletUnlockPage, WalletUnlockArguments, bool>((args, closable) { @@ -422,14 +425,21 @@ Future<void> setup({ walletType: args.walletType ?? currentWalletType); }); - getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) { - return WalletRestorationFromQRVM(getIt.get<AppStore>(), - getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type); - }); + getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) => + WalletRestorationFromQRVM( + getIt.get<AppStore>(), + getIt.get<WalletCreationService>(param1: type), + _walletInfoSource, + type, + getIt.get<SeedSettingsViewModel>())); getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) => - WalletHardwareRestoreViewModel(getIt.get<LedgerViewModel>(), getIt.get<AppStore>(), - getIt.get<WalletCreationService>(param1: type), _walletInfoSource, + WalletHardwareRestoreViewModel( + getIt.get<LedgerViewModel>(), + getIt.get<AppStore>(), + getIt.get<WalletCreationService>(param1: type), + _walletInfoSource, + getIt.get<SeedSettingsViewModel>(), type: type)); getIt.registerFactory<WalletAddressListViewModel>(() => WalletAddressListViewModel( @@ -833,7 +843,7 @@ Future<void> setup({ getIt.registerFactory(() => WalletSeedViewModel(getIt.get<AppStore>().wallet!)); - getIt.registerFactory<SeedTypeViewModel>(() => SeedTypeViewModel(getIt.get<AppStore>())); + getIt.registerFactory<SeedSettingsViewModel>(() => SeedSettingsViewModel(getIt.get<AppStore>(), getIt.get<SeedSettingsStore>())); getIt.registerFactoryParam<WalletSeedPage, bool, void>((bool isWalletCreated, _) => WalletSeedPage(getIt.get<WalletSeedViewModel>(), isNewWalletCreated: isWalletCreated)); @@ -1018,12 +1028,12 @@ Future<void> setup({ getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>())); getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>((type, _) => - WalletRestoreViewModel( - getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource, + WalletRestoreViewModel(getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), + _walletInfoSource, getIt.get<SeedSettingsViewModel>(), type: type)); getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => WalletRestorePage( - getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedTypeViewModel>())); + getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedSettingsViewModel>())); getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>( (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations)); @@ -1275,7 +1285,7 @@ Future<void> setup({ getIt.registerFactory( () => WalletConnectConnectionsView(web3walletService: getIt.get<Web3WalletService>())); - + getIt.registerFactory(() => NFTViewModel(appStore, getIt.get<BottomSheetService>())); getIt.registerFactory<TorPage>(() => TorPage(getIt.get<AppStore>())); 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 = <String, String>{}; 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<int> with Serializable<int> { - const SeedType({required String title, required int raw}) : super(title: title, raw: raw); +class MoneroSeedType extends EnumerableItem<int> with Serializable<int> { + 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<int> with Serializable<int> { @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<int> with Serializable<int> { + 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<dynamic> 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<dynamic> createRoute(RouteSettings settings) { case Routes.newWallet: final type = settings.arguments as WalletType; final walletNewVM = getIt.get<WalletNewVM>(param1: type); - final seedTypeViewModel = getIt.get<SeedTypeViewModel>(); + final seedSettingsViewModel = getIt.get<SeedSettingsViewModel>(); return CupertinoPageRoute<void>( - builder: (_) => NewWalletPage(walletNewVM, seedTypeViewModel)); + builder: (_) => NewWalletPage(walletNewVM, seedSettingsViewModel)); case Routes.chooseHardwareWalletAccount: final arguments = settings.arguments as List<dynamic>; @@ -348,16 +347,14 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.auth: return MaterialPageRoute<void>( fullscreenDialog: true, - builder: (_) - => SettingsStoreBase.walletPasswordDirectInput - ? getIt.get<WalletUnlockPage>( - param1: WalletUnlockArguments( + builder: (_) => SettingsStoreBase.walletPasswordDirectInput + ? getIt.get<WalletUnlockPage>( + param1: WalletUnlockArguments( callback: settings.arguments as OnAuthenticationFinished), - instanceName: 'wallet_unlock_verifiable', - param2: true) - : getIt.get<AuthPage>( - param1: settings.arguments as OnAuthenticationFinished, - param2: true)); + instanceName: 'wallet_unlock_verifiable', + param2: true) + : getIt.get<AuthPage>( + param1: settings.arguments as OnAuthenticationFinished, param2: true)); case Routes.totpAuthCodePage: final args = settings.arguments as TotpAuthArgumentsModel; @@ -371,28 +368,25 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.walletUnlockLoadable: return MaterialPageRoute<void>( fullscreenDialog: true, - builder: (_) - => getIt.get<WalletUnlockPage>( + builder: (_) => getIt.get<WalletUnlockPage>( param1: settings.arguments as WalletUnlockArguments, - instanceName: 'wallet_unlock_loadable', + instanceName: 'wallet_unlock_loadable', param2: true)); case Routes.unlock: return MaterialPageRoute<void>( fullscreenDialog: true, - builder: (_) - => SettingsStoreBase.walletPasswordDirectInput - ? WillPopScope( - child: getIt.get<WalletUnlockPage>( + builder: (_) => SettingsStoreBase.walletPasswordDirectInput + ? WillPopScope( + child: getIt.get<WalletUnlockPage>( 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<AuthPage>( - param1: settings.arguments as OnAuthenticationFinished, - param2: false), + onWillPop: () async => false) + : WillPopScope( + child: getIt.get<AuthPage>( + param1: settings.arguments as OnAuthenticationFinished, param2: false), onWillPop: () async => false)); case Routes.silentPaymentsSettings: @@ -437,11 +431,12 @@ Route<dynamic> createRoute(RouteSettings settings) { return CupertinoPageRoute<void>( builder: (context) => WillPopScope( child: SettingsStoreBase.walletPasswordDirectInput - ? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login') - : getIt.get<AuthPage>(instanceName: 'login'), + ? getIt.get<WalletUnlockPage>(instanceName: 'wallet_password_login') + : getIt.get<AuthPage>(instanceName: 'login'), onWillPop: () async => - // FIX-ME: Additional check does it works correctly - (await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? false)), + // FIX-ME: Additional check does it works correctly + (await SystemChannels.platform.invokeMethod<bool>('SystemNavigator.pop') ?? + false)), fullscreenDialog: true); case Routes.newPowNode: @@ -537,8 +532,7 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.support: return CupertinoPageRoute<void>( - fullscreenDialog: true, - builder: (_) => getIt.get<SupportPage>()); + fullscreenDialog: true, builder: (_) => getIt.get<SupportPage>()); case Routes.supportLiveChat: return CupertinoPageRoute<void>(builder: (_) => getIt.get<SupportChatPage>()); @@ -567,8 +561,7 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.cakePayBuyCardPage: final args = settings.arguments as List; - return CupertinoPageRoute<void>( - builder: (_) => getIt.get<CakePayBuyCardPage>(param1: args)); + return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayBuyCardPage>(param1: args)); case Routes.cakePayBuyCardDetailPage: final args = settings.arguments as List; @@ -582,7 +575,8 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.cakePayVerifyOtpPage: final args = settings.arguments as List; - return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayVerifyOtpPage>(param1: args)); + return CupertinoPageRoute<void>( + builder: (_) => getIt.get<CakePayVerifyOtpPage>(param1: args)); case Routes.cakePayAccountPage: return CupertinoPageRoute<void>(builder: (_) => getIt.get<CakePayAccountPage>()); @@ -597,16 +591,19 @@ Route<dynamic> createRoute(RouteSettings settings) { case Routes.advancedPrivacySettings: final args = settings.arguments as Map<String, dynamic>; 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<void>( builder: (_) => AdvancedPrivacySettingsPage( - useTestnet, - toggleTestnet, - getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), - getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false), - getIt.get<SeedTypeViewModel>(), + isFromRestore: isFromRestore, + useTestnet: useTestnet, + toggleUseTestnet: toggleTestnet, + advancedPrivacySettingsViewModel: + getIt.get<AdvancedPrivacySettingsViewModel>(param1: type), + nodeViewModel: getIt.get<NodeCreateOrEditViewModel>(param1: type, param2: false), + seedSettingsViewModel: getIt.get<SeedSettingsViewModel>(), )); case Routes.anonPayInvoicePage: @@ -709,7 +706,7 @@ Route<dynamic> createRoute(RouteSettings settings) { getIt.get<SignViewModel>(), ), ); - + case Routes.connectDevices: final params = settings.arguments as ConnectDevicePageParams; return MaterialPageRoute<void>( 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<AdvancedPrivacySettingsBody> { - _AdvancedPrivacySettingsBodyState(); - +class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBody> { + final TextEditingController passphraseController = TextEditingController(); final _formKey = GlobalKey<FormState>(); 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<AdvancedPrivacySettingsBod ), ); }), + if (widget.privacySettingsViewModel.hasSeedTypeOption) + Observer(builder: (_) { + return SettingsChoicesCell( + ChoicesListItem<MoneroSeedType>( + 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<BitcoinSeedType>( + 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<SeedPhraseLength>( + 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<AdvancedPrivacySettingsBod ], ); }), - if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption) - Observer(builder: (_) { - return SettingsPickerCell<SeedPhraseLength>( - 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<SeedType>( - 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<AdvancedPrivacySettingsBod setState(() { testnetValue = !val; }); - widget.toggleUseTestnet!.call(testnetValue); + widget.toggleUseTestnet.call(testnetValue); }); }), ], @@ -203,4 +244,11 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod ), ); } + + @override + void dispose() { + passphraseController + .removeListener(() => 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<WalletNameForm> { } }); - _setSeedType(SeedType.defaultSeedType); + _setSeedType(MoneroSeedType.defaultSeedType); super.initState(); } @@ -285,12 +284,12 @@ class _WalletNameFormState extends State<WalletNameForm> { 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<void>( 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<WalletNameForm> { 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<WalletNameForm> { _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<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { } 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<WalletRestoreFromSeedForm> { if (repeatedPasswordListener != null) { repeatedPasswordTextEditingController?.removeListener(repeatedPasswordListener!); } + + passphraseController.removeListener(passphraseListener!); + super.dispose(); } @@ -118,11 +128,13 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { onTap: () async { await showPopUp<void>( 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<WalletRestoreFromSeedForm> { 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<void>( - 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<void>( + 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<WalletRestoreFromSeedForm> { } 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<WalletRestoreFromSeedForm> { 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<WalletRestoreFromSeedForm> { 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<SeedType> _getItems() { + List<MoneroSeedType> _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<WalletRestoreFromSeedFormState>(), walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(), _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<Widget> _pages; final GlobalKey<WalletRestoreFromSeedFormState> 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<SeedType> supportedSeedTypes; + final List<MoneroSeedType> supportedSeedTypes; } final List<SeedLanguagePickerOption> 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<SeedLanguagePicker> { 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 = <WalletType, Node>{}; final powNodes = <WalletType, Node>{}; @@ -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<WalletInfo> walletInfoSource, WalletType type) + Box<WalletInfo> 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<WalletInfo> _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<BackgroundTasks>().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<WalletInfo> walletInfoSource, + WalletHardwareRestoreViewModelBase( + this.ledgerViewModel, + AppStore appStore, + WalletCreationService walletCreationService, + Box<WalletInfo> 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<WalletInfo> walletInfoSource, this.advancedPrivacySettingsViewModel, + WalletNewVMBase( + AppStore appStore, + WalletCreationService walletCreationService, + Box<WalletInfo> 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<dynamic>?; + 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<WalletInfo> walletInfoSource, + Box<WalletInfo> 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<String> getWordList(); Map<String, String> getWalletKeys(Object wallet); @@ -1021,10 +1021,10 @@ abstract class BitcoinCash { Box<WalletInfo> walletInfoSource, Box<UnspentCoinsInfo> 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); From 518bfbe40d1a70f08e9c25b0cf957f2b30eda282 Mon Sep 17 00:00:00 2001 From: Omar Hatem <omarh.ismail1@gmail.com> Date: Mon, 26 Aug 2024 20:07:25 +0300 Subject: [PATCH 2/3] fetch accurate fee rates from Cake's mempool api (#1649) * fetch accurate fee rates from mempool api * remove duplicate exception handler onError call [skip ci] * add the domain name --- cw_bitcoin/lib/electrum_wallet.dart | 69 ++++++++++++++++--- cw_bitcoin/pubspec.lock | 63 ++++++++++++++++- cw_bitcoin/pubspec.yaml | 1 + cw_monero/lib/monero_wallet_service.dart | 38 +--------- lib/entities/preferences_key.dart | 1 + lib/src/screens/settings/privacy_page.dart | 29 ++++++++ lib/store/settings_store.dart | 10 +++ lib/utils/exception_handler.dart | 7 ++ .../settings/privacy_settings_view_model.dart | 11 +++ res/values/strings_ar.arb | 2 + res/values/strings_bg.arb | 2 + res/values/strings_cs.arb | 2 + res/values/strings_de.arb | 2 + res/values/strings_en.arb | 2 + res/values/strings_es.arb | 4 +- res/values/strings_fr.arb | 2 + res/values/strings_ha.arb | 2 + res/values/strings_hi.arb | 2 + res/values/strings_hr.arb | 2 + res/values/strings_hy.arb | 2 + res/values/strings_id.arb | 2 + res/values/strings_it.arb | 2 + res/values/strings_ja.arb | 2 + res/values/strings_ko.arb | 2 + res/values/strings_my.arb | 2 + res/values/strings_nl.arb | 2 + res/values/strings_pl.arb | 2 + res/values/strings_pt.arb | 2 + res/values/strings_ru.arb | 2 + res/values/strings_th.arb | 2 + res/values/strings_tl.arb | 2 + res/values/strings_tr.arb | 2 + res/values/strings_uk.arb | 2 + res/values/strings_ur.arb | 2 + res/values/strings_yo.arb | 2 + res/values/strings_zh.arb | 2 + 36 files changed, 239 insertions(+), 46 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 0ce4844c6..be4cee4d8 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -5,6 +5,7 @@ import 'dart:isolate'; import 'dart:math'; import 'package:bitcoin_base/bitcoin_base.dart'; +import 'package:shared_preferences/shared_preferences.dart'; import 'package:cw_core/encryption_file_utils.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:collection/collection.dart'; @@ -44,6 +45,7 @@ import 'package:mobx/mobx.dart'; import 'package:rxdart/subjects.dart'; import 'package:sp_scanner/sp_scanner.dart'; import 'package:hex/hex.dart'; +import 'package:http/http.dart' as http; part 'electrum_wallet.g.dart'; @@ -101,6 +103,8 @@ abstract class ElectrumWalletBase ); reaction((_) => syncStatus, _syncStatusReaction); + + sharedPrefs.complete(SharedPreferences.getInstance()); } static Bip32Slip10Secp256k1 getAccountHDWallet(CryptoCurrency? currency, BasedUtxoNetwork network, @@ -196,6 +200,13 @@ abstract class ElectrumWalletBase bool _isTryingToConnect = false; + Completer<SharedPreferences> sharedPrefs = Completer(); + + Future<bool> checkIfMempoolAPIIsEnabled() async { + bool isMempoolAPIEnabled = (await sharedPrefs.future).getBool("use_mempool_fee_api") ?? true; + return isMempoolAPIEnabled; + } + @action Future<void> setSilentPaymentsScanning(bool active) async { silentPaymentsScanningActive = active; @@ -448,6 +459,20 @@ abstract class ElectrumWalletBase @action Future<void> updateFeeRates() async { + if (await checkIfMempoolAPIIsEnabled()) { + try { + final response = + await http.get(Uri.parse("http://mempool.cakewallet.com:8999/api/v1/fees/recommended")); + + final result = json.decode(response.body) as Map<String, num>; + final slowFee = result['economyFee']?.toInt() ?? 0; + final mediumFee = result['hourFee']?.toInt() ?? 0; + final fastFee = result['fastestFee']?.toInt() ?? 0; + _feeRates = [slowFee, mediumFee, fastFee]; + return; + } catch (_) {} + } + final feeRates = await electrumClient.feeRates(network: network); if (feeRates != [0, 0, 0]) { _feeRates = feeRates; @@ -1455,7 +1480,6 @@ abstract class ElectrumWalletBase // Create a list of available outputs final outputs = <BitcoinOutput>[]; for (final out in bundle.originalTransaction.outputs) { - // Check if the script contains OP_RETURN final script = out.scriptPubKey.script; if (script.contains('OP_RETURN') && memo == null) { @@ -2058,13 +2082,42 @@ abstract class ElectrumWalletBase tx.inputAddresses!.isEmpty || tx.outputAddresses == null || tx.outputAddresses!.isEmpty) { - tx = ElectrumTransactionInfo.fromElectrumBundle( - bundle, - walletInfo.type, - network, - addresses: addressesSet, - height: tx.height, - ); + List<String> inputAddresses = []; + List<String> outputAddresses = []; + + for (int i = 0; i < bundle.originalTransaction.inputs.length; i++) { + final input = bundle.originalTransaction.inputs[i]; + final inputTransaction = bundle.ins[i]; + final vout = input.txIndex; + final outTransaction = inputTransaction.outputs[vout]; + final address = addressFromOutputScript(outTransaction.scriptPubKey, network); + + if (address.isNotEmpty) inputAddresses.add(address); + } + + for (int i = 0; i < bundle.originalTransaction.outputs.length; i++) { + final out = bundle.originalTransaction.outputs[i]; + final address = addressFromOutputScript(out.scriptPubKey, network); + + if (address.isNotEmpty) outputAddresses.add(address); + + // Check if the script contains OP_RETURN + final script = out.scriptPubKey.script; + if (script.contains('OP_RETURN')) { + final index = script.indexOf('OP_RETURN'); + if (index + 1 <= script.length) { + try { + final opReturnData = script[index + 1].toString(); + final decodedString = utf8.decode(HEX.decode(opReturnData)); + outputAddresses.add('OP_RETURN:$decodedString'); + } catch (_) { + outputAddresses.add('OP_RETURN:'); + } + } + } + } + tx.inputAddresses = inputAddresses; + tx.outputAddresses = outputAddresses; transactionHistory.addOne(tx); } diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index cdb88946e..b5e59cd88 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -350,6 +350,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" frontend_server_client: dependency: transitive description: @@ -736,6 +741,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + url: "https://pub.dev" + source: hosted + version: "2.2.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "1ee8bf911094a1b592de7ab29add6f826a7331fb854273d55918693d5364a1f2" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f + url: "https://pub.dev" + source: hosted + version: "2.5.2" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: @@ -944,4 +1005,4 @@ packages: version: "2.2.1" sdks: dart: ">=3.3.0 <4.0.0" - flutter: ">=3.16.6" + flutter: ">=3.19.0" diff --git a/cw_bitcoin/pubspec.yaml b/cw_bitcoin/pubspec.yaml index 2af1ac54e..84164b884 100644 --- a/cw_bitcoin/pubspec.yaml +++ b/cw_bitcoin/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: mobx: ^2.0.7+4 flutter_mobx: ^2.0.6+1 intl: ^0.18.0 + shared_preferences: ^2.0.15 cw_core: path: ../cw_core bitbox: diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index f9973f430..7f4672d43 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -9,11 +9,9 @@ import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_service.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/get_height_by_date.dart'; -import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart'; import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager; import 'package:cw_monero/api/wallet_manager.dart'; import 'package:cw_monero/monero_wallet.dart'; -import 'package:flutter/widgets.dart'; import 'package:hive/hive.dart'; import 'package:polyseed/polyseed.dart'; import 'package:monero/monero.dart' as monero; @@ -120,7 +118,6 @@ class MoneroWalletService extends WalletService< @override Future<MoneroWallet> openWallet(String name, String password, {bool? retryOnFailure}) async { - MoneroWallet? wallet; try { final path = await pathForWallet(name: name, type: getType()); @@ -147,41 +144,10 @@ class MoneroWalletService extends WalletService< await wallet.init(); return wallet; - } catch (e, s) { + } catch (e) { // TODO: Implement Exception for wallet list service. - final bool isBadAlloc = e.toString().contains('bad_alloc') || - (e is WalletOpeningException && - (e.message == 'std::bad_alloc' || e.message.contains('bad_alloc'))); - - final bool doesNotCorrespond = e.toString().contains('does not correspond') || - (e is WalletOpeningException && e.message.contains('does not correspond')); - - final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') || - (e is WalletOpeningException && e.message.contains('basic_string')); - - final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') || - e.toString().contains('input stream error') || - (e is WalletOpeningException && - (e.message.contains('input_stream') || e.message.contains('input stream error'))); - - final bool invalidSignature = e.toString().contains('invalid signature') || - (e is WalletOpeningException && e.message.contains('invalid signature')); - - final bool invalidPassword = e.toString().contains('invalid password') || - (e is WalletOpeningException && e.message.contains('invalid password')); - - if (!isBadAlloc && - !doesNotCorrespond && - !isMissingCacheFilesIOS && - !isMissingCacheFilesAndroid && - !invalidSignature && - !invalidPassword && - wallet != null && - wallet.onError != null) { - wallet.onError!(FlutterErrorDetails(exception: e, stack: s)); - } - if (invalidPassword || retryOnFailure == false) { + if (retryOnFailure == false) { rethrow; } diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 743357f92..714612ff1 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -61,6 +61,7 @@ class PreferencesKey { static const useEtherscan = 'use_etherscan'; static const usePolygonScan = 'use_polygonscan'; static const useTronGrid = 'use_trongrid'; + static const useMempoolFeeAPI = 'use_mempool_fee_api'; static const defaultNanoRep = 'default_nano_representative'; static const defaultBananoRep = 'default_banano_representative'; static const lookupsTwitter = 'looks_up_twitter'; diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 0eaf3ffbd..7db244460 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -6,7 +6,9 @@ import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/utils/device_info.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:flutter/material.dart'; @@ -111,6 +113,33 @@ class PrivacyPage extends BasePage { _privacySettingsViewModel.setUseTronGrid(value); }, ), + if (_privacySettingsViewModel.canUseMempoolFeeAPI) + SettingsSwitcherCell( + title: S.current.live_fee_rates, + value: _privacySettingsViewModel.useMempoolFeeAPI, + onValueChange: (BuildContext _, bool isEnabled) async { + if (!isEnabled) { + final bool confirmation = await showPopUp<bool>( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).warning, + alertContent: S.of(context).disable_fee_api_warning, + rightButtonText: S.of(context).confirm, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.of(context).pop(true), + actionLeftButton: () => Navigator.of(context).pop(false)); + }) ?? + false; + if (confirmation) { + _privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled); + } + return; + } + + _privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled); + }, + ), SettingsCellWithArrow( title: S.current.domain_looks_up, handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index ea5f43d98..f2738a293 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -101,6 +101,7 @@ abstract class SettingsStoreBase with Store { required this.useEtherscan, required this.usePolygonScan, required this.useTronGrid, + required this.useMempoolFeeAPI, required this.defaultNanoRep, required this.defaultBananoRep, required this.lookupsTwitter, @@ -416,6 +417,9 @@ abstract class SettingsStoreBase with Store { reaction((_) => useTronGrid, (bool useTronGrid) => _sharedPreferences.setBool(PreferencesKey.useTronGrid, useTronGrid)); + reaction((_) => useMempoolFeeAPI, + (bool useMempoolFeeAPI) => _sharedPreferences.setBool(PreferencesKey.useMempoolFeeAPI, useMempoolFeeAPI)); + reaction((_) => defaultNanoRep, (String nanoRep) => _sharedPreferences.setString(PreferencesKey.defaultNanoRep, nanoRep)); @@ -707,6 +711,9 @@ abstract class SettingsStoreBase with Store { @observable bool useTronGrid; + @observable + bool useMempoolFeeAPI; + @observable String defaultNanoRep; @@ -892,6 +899,7 @@ abstract class SettingsStoreBase with Store { final useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; final usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true; final useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true; + final useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true; final defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; final defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; final lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; @@ -1154,6 +1162,7 @@ abstract class SettingsStoreBase with Store { useEtherscan: useEtherscan, usePolygonScan: usePolygonScan, useTronGrid: useTronGrid, + useMempoolFeeAPI: useMempoolFeeAPI, defaultNanoRep: defaultNanoRep, defaultBananoRep: defaultBananoRep, lookupsTwitter: lookupsTwitter, @@ -1307,6 +1316,7 @@ abstract class SettingsStoreBase with Store { useEtherscan = sharedPreferences.getBool(PreferencesKey.useEtherscan) ?? true; usePolygonScan = sharedPreferences.getBool(PreferencesKey.usePolygonScan) ?? true; useTronGrid = sharedPreferences.getBool(PreferencesKey.useTronGrid) ?? true; + useMempoolFeeAPI = sharedPreferences.getBool(PreferencesKey.useMempoolFeeAPI) ?? true; defaultNanoRep = sharedPreferences.getString(PreferencesKey.defaultNanoRep) ?? ""; defaultBananoRep = sharedPreferences.getString(PreferencesKey.defaultBananoRep) ?? ""; lookupsTwitter = sharedPreferences.getBool(PreferencesKey.lookupsTwitter) ?? true; diff --git a/lib/utils/exception_handler.dart b/lib/utils/exception_handler.dart index 5e0c83f88..8fc8a8c0e 100644 --- a/lib/utils/exception_handler.dart +++ b/lib/utils/exception_handler.dart @@ -174,6 +174,13 @@ class ExceptionHandler { "ClientException: Write failed, uri=http", "Connection terminated during handshake", "Corrupted wallets seeds", + "bad_alloc", + "does not correspond", + "basic_string", + "input_stream", + "input stream error", + "invalid signature", + "invalid password", ]; static Future<void> _addDeviceInfo(File file) async { diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 90511af8e..c1e0fb1ce 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; @@ -75,6 +76,9 @@ abstract class PrivacySettingsViewModelBase with Store { @computed bool get useTronGrid => _settingsStore.useTronGrid; + @computed + bool get useMempoolFeeAPI => _settingsStore.useMempoolFeeAPI; + @computed bool get lookupTwitter => _settingsStore.lookupsTwitter; @@ -99,6 +103,8 @@ abstract class PrivacySettingsViewModelBase with Store { bool get canUseTronGrid => _wallet.type == WalletType.tron; + bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin; + @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; @@ -156,4 +162,9 @@ abstract class PrivacySettingsViewModelBase with Store { _settingsStore.useTronGrid = value; tron!.updateTronGridUsageState(_wallet, value); } + + @action + void setUseMempoolFeeAPI(bool value) { + _settingsStore.useMempoolFeeAPI = value; + } } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 89068d2d0..0b238ad23 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -208,6 +208,7 @@ "disable_buy": "تعطيل إجراء الشراء", "disable_cake_2fa": "تعطيل 2 عامل المصادقة", "disable_exchange": "تعطيل التبادل", + "disable_fee_api_warning": "من خلال إيقاف تشغيل هذا ، قد تكون معدلات الرسوم غير دقيقة في بعض الحالات ، لذلك قد ينتهي بك الأمر إلى دفع مبالغ زائدة أو دفع رسوم المعاملات الخاصة بك", "disable_fiat": "تعطيل fiat", "disable_sell": "قم بتعطيل إجراء البيع", "disableBatteryOptimization": "تعطيل تحسين البطارية", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "يرجى التأكد", "ledger_please_enable_bluetooth": "يرجى تمكين البلوتوث للكشف عن دفتر الأستاذ الخاص بك", "light_theme": "فاتح", + "live_fee_rates": "أسعار الرسوم المباشرة عبر API", "load_more": "تحميل المزيد", "loading_your_wallet": "يتم تحميل محفظتك", "login": "تسجيل الدخول", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 1ff8fdf85..c13d6d7d6 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -208,6 +208,7 @@ "disable_buy": "Деактивирайте действието за покупка", "disable_cake_2fa": "Деактивирайте Cake 2FA", "disable_exchange": "Деактивиране на борса", + "disable_fee_api_warning": "Като изключите това, таксите могат да бъдат неточни в някои случаи, така че може да се препланите или да не плащате таксите за вашите транзакции", "disable_fiat": "Деактивиране на fiat", "disable_sell": "Деактивирайте действието за продажба", "disableBatteryOptimization": "Деактивирайте оптимизацията на батерията", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Моля, уверете се, че сте отворили правилното приложение на вашата книга", "ledger_please_enable_bluetooth": "Моля, активирайте Bluetooth да открие вашата книга", "light_theme": "Светло", + "live_fee_rates": "Цени на таксите на живо чрез API", "load_more": "Зареди още", "loading_your_wallet": "Зареждане на портфейл", "login": "Влизане", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 904c6d8e4..49bc856fd 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -208,6 +208,7 @@ "disable_buy": "Zakázat akci nákupu", "disable_cake_2fa": "Zakázat Cake 2FA", "disable_exchange": "Zakázat směnárny", + "disable_fee_api_warning": "Tímto vypnutím by sazby poplatků mohly být v některých případech nepřesné, takže byste mohli skončit přepláváním nebo nedoplatkem poplatků za vaše transakce", "disable_fiat": "Zakázat fiat", "disable_sell": "Zakázat akci prodeje", "disableBatteryOptimization": "Zakázat optimalizaci baterie", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Ujistěte se, že se na své knize otevřete správnou aplikaci", "ledger_please_enable_bluetooth": "Umožněte prosím Bluetooth detekovat vaši knihu", "light_theme": "Světlý", + "live_fee_rates": "Živé sazby poplatků prostřednictvím API", "load_more": "Načíst další", "loading_your_wallet": "Načítám peněženku", "login": "Login", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 860d4114d..19b7fbc90 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -208,6 +208,7 @@ "disable_buy": "Kaufaktion deaktivieren", "disable_cake_2fa": "Cake 2FA deaktivieren", "disable_exchange": "Exchange deaktivieren", + "disable_fee_api_warning": "Wenn dies ausgeschaltet wird, sind die Gebührenquoten in einigen Fällen möglicherweise ungenau, sodass Sie die Gebühren für Ihre Transaktionen möglicherweise überbezahlt oder unterzahlt", "disable_fiat": "Fiat deaktivieren", "disable_sell": "Verkaufsaktion deaktivieren", "disableBatteryOptimization": "Batterieoptimierung deaktivieren", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Bitte stellen Sie sicher, dass Sie die richtige App auf Ihrem Ledger geöffnet haben", "ledger_please_enable_bluetooth": "Bitte aktivieren Sie Bluetooth um sich mit Ihren Ledger zu verbinden.", "light_theme": "Hell", + "live_fee_rates": "Live -Gebührenpreise über API", "load_more": "Mehr laden", "loading_your_wallet": "Wallet wird geladen", "login": "Einloggen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 9f89eb6ab..75c0fc9c0 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -208,6 +208,7 @@ "disable_buy": "Disable buy action", "disable_cake_2fa": "Disable Cake 2FA", "disable_exchange": "Disable exchange", + "disable_fee_api_warning": "By turning this off, the fee rates might be inaccurate in some cases, so you might end up overpaying or underpaying the fees for your transactions", "disable_fiat": "Disable fiat", "disable_sell": "Disable sell action", "disableBatteryOptimization": "Disable Battery Optimization", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Please make sure you opend the right app on your ledger", "ledger_please_enable_bluetooth": "Please enable Bluetooth to detect your Ledger", "light_theme": "Light", + "live_fee_rates": "Live fee rates via API", "load_more": "Load more", "loading_your_wallet": "Loading your wallet", "login": "Login", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 250decfc4..73a2e77b9 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -208,6 +208,7 @@ "disable_buy": "Desactivar acción de compra", "disable_cake_2fa": "Desactivar pastel 2FA", "disable_exchange": "Deshabilitar intercambio", + "disable_fee_api_warning": "Al apagar esto, las tasas de tarifas pueden ser inexactas en algunos casos, por lo que puede terminar pagando en exceso o pagando menos las tarifas por sus transacciones", "disable_fiat": "Deshabilitar fiat", "disable_sell": "Desactivar acción de venta", "disableBatteryOptimization": "Deshabilitar la optimización de la batería", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Por favor, asegúrese de abrir la aplicación correcta en su libro mayor.", "ledger_please_enable_bluetooth": "Habilite Bluetooth para detectar su libro mayor", "light_theme": "Ligera", + "live_fee_rates": "Tasas de tarifas en vivo a través de API", "load_more": "Carga más", "loading_your_wallet": "Cargando tu billetera", "login": "Iniciar sesión", @@ -895,4 +897,4 @@ "you_will_get": "Convertir a", "you_will_send": "Convertir de", "yy": "YY" -} +} \ No newline at end of file diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 297019aa0..e1f557060 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -208,6 +208,7 @@ "disable_buy": "Désactiver l'action d'achat", "disable_cake_2fa": "Désactiver Cake 2FA", "disable_exchange": "Désactiver l'échange", + "disable_fee_api_warning": "En désactivant cela, les taux de frais peuvent être inexacts dans certains cas, vous pourriez donc finir par payer trop ou sous-paiement les frais pour vos transactions", "disable_fiat": "Désactiver les montants en fiat", "disable_sell": "Désactiver l'action de vente", "disableBatteryOptimization": "Désactiver l'optimisation de la batterie", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Veuillez vous assurer d'ouvrir la bonne application sur votre grand livre", "ledger_please_enable_bluetooth": "Veuillez activer Bluetooth pour détecter votre grand livre", "light_theme": "Clair", + "live_fee_rates": "Taux de frais en direct via l'API", "load_more": "Charger plus", "loading_your_wallet": "Chargement de votre portefeuille (wallet)", "login": "Utilisateur", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index 05cdf27d4..4e0eb83f9 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -208,6 +208,7 @@ "disable_buy": "Kashe alama", "disable_cake_2fa": "Musaki Cake 2FA", "disable_exchange": "Kashe musanya", + "disable_fee_api_warning": "Ta hanyar juya wannan kashe, kudaden da zai iya zama ba daidai ba a wasu halaye, saboda haka zaku iya ƙare da overpaying ko a ƙarƙashin kudaden don ma'amaloli", "disable_fiat": "Dakatar da fiat", "disable_sell": "Kashe karbuwa", "disableBatteryOptimization": "Kashe ingantawa baturi", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Da fatan za a tabbata kun yi amfani da app ɗin dama akan dillalarku", "ledger_please_enable_bluetooth": "Da fatan za a kunna Bluetooth don gano Ledger ɗinku", "light_theme": "Haske", + "live_fee_rates": "Kudin Kiɗa ta API", "load_more": "Like more", "loading_your_wallet": "Ana loda walat ɗin ku", "login": "Shiga", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 30f86d9ae..f77547992 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -208,6 +208,7 @@ "disable_buy": "खरीद कार्रवाई अक्षम करें", "disable_cake_2fa": "केक 2FA अक्षम करें", "disable_exchange": "एक्सचेंज अक्षम करें", + "disable_fee_api_warning": "इसे बंद करने से, कुछ मामलों में शुल्क दरें गलत हो सकती हैं, इसलिए आप अपने लेनदेन के लिए फीस को कम कर सकते हैं या कम कर सकते हैं", "disable_fiat": "िएट को अक्षम करें", "disable_sell": "बेचने की कार्रवाई अक्षम करें", "disableBatteryOptimization": "बैटरी अनुकूलन अक्षम करें", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "कृपया सुनिश्चित करें कि आप अपने लेजर पर सही ऐप को खोलते हैं", "ledger_please_enable_bluetooth": "कृपया अपने बहीखाने का पता लगाने के लिए ब्लूटूथ को सक्षम करें", "light_theme": "रोशनी", + "live_fee_rates": "एपीआई के माध्यम से लाइव शुल्क दरें", "load_more": "और लोड करें", "loading_your_wallet": "अपना बटुआ लोड कर रहा है", "login": "लॉग इन करें", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 0d6e9a0d8..db02c5552 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -208,6 +208,7 @@ "disable_buy": "Onemogući kupnju", "disable_cake_2fa": "Onemogući Cake 2FA", "disable_exchange": "Onemogući exchange", + "disable_fee_api_warning": "Isključivanjem ovoga, stope naknade u nekim bi slučajevima mogle biti netočne, tako da biste mogli preplatiti ili predati naknadu za vaše transakcije", "disable_fiat": "Isključi, fiat", "disable_sell": "Onemogući akciju prodaje", "disableBatteryOptimization": "Onemogući optimizaciju baterije", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Obavezno obavezno otvorite pravu aplikaciju na knjizi", "ledger_please_enable_bluetooth": "Omogućite Bluetooth da otkrije svoju knjigu", "light_theme": "Svijetla", + "live_fee_rates": "Stope naknada uživo putem API -ja", "load_more": "Učitaj više", "loading_your_wallet": "Novčanik se učitava", "login": "Prijava", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 826bfa99a..178254bba 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -207,6 +207,7 @@ "disable_buy": "Անջատել գնում գործողությունը", "disable_cake_2fa": "Անջատել Cake 2FA", "disable_exchange": "Անջատել փոխանակումը", + "disable_fee_api_warning": "Դրանից անջատելով, վճարների տեմպերը որոշ դեպքերում կարող են անճիշտ լինել, այնպես որ դուք կարող եք վերջ տալ ձեր գործարքների համար վճարների գերավճարների կամ գերավճարների վրա", "disable_fiat": "Անջատել ֆիատ", "disable_sell": "Անջատել վաճառք գործողությունը", "disableBatteryOptimization": "Անջատել մարտկոցի օպտիմիզացիան", @@ -354,6 +355,7 @@ "ledger_error_wrong_app": "Խնդրում ենք համոզվել, որ դուք բացել եք ճիշտ ծրագիրը ձեր Ledger-ում", "ledger_please_enable_bluetooth": "Խնդրում ենք միացնել Bluetooth-ը ձեր Ledger-ը հայտնաբերելու համար", "light_theme": "Լուսավոր", + "live_fee_rates": "Ապակի վարձավճարներ API- ի միջոցով", "load_more": "Բեռնել ավելին", "loading_your_wallet": "Ձեր հաշվեհամարը բեռնում է", "login": "Մուտք", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 65703e0bc..e67402de2 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -208,6 +208,7 @@ "disable_buy": "Nonaktifkan tindakan beli", "disable_cake_2fa": "Nonaktifkan Kue 2FA", "disable_exchange": "Nonaktifkan pertukaran", + "disable_fee_api_warning": "Dengan mematikan ini, tarif biaya mungkin tidak akurat dalam beberapa kasus, jadi Anda mungkin akan membayar lebih atau membayar biaya untuk transaksi Anda", "disable_fiat": "Nonaktifkan fiat", "disable_sell": "Nonaktifkan aksi jual", "disableBatteryOptimization": "Nonaktifkan optimasi baterai", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Pastikan Anda membuka aplikasi yang tepat di buku besar Anda", "ledger_please_enable_bluetooth": "Harap aktifkan Bluetooth untuk mendeteksi buku besar Anda", "light_theme": "Terang", + "live_fee_rates": "Tarif biaya langsung melalui API", "load_more": "Muat lebih banyak", "loading_your_wallet": "Memuat dompet Anda", "login": "Masuk", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index c2ba20ce0..a764520d8 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -209,6 +209,7 @@ "disable_buy": "Disabilita l'azione di acquisto", "disable_cake_2fa": "Disabilita Cake 2FA", "disable_exchange": "Disabilita scambio", + "disable_fee_api_warning": "Disattivando questo, i tassi delle commissioni potrebbero essere inaccurati in alcuni casi, quindi potresti finire in eccesso o sostenere le commissioni per le transazioni", "disable_fiat": "Disabilita fiat", "disable_sell": "Disabilita l'azione di vendita", "disableBatteryOptimization": "Disabilita l'ottimizzazione della batteria", @@ -356,6 +357,7 @@ "ledger_error_wrong_app": "Assicurati di aprire l'app giusta sul libro mastro", "ledger_please_enable_bluetooth": "Si prega di consentire al Bluetooth di rilevare il libro mastro", "light_theme": "Bianco", + "live_fee_rates": "Tariffe delle commissioni dal vivo tramite API", "load_more": "Carica di più", "loading_your_wallet": "Caricamento portafoglio", "login": "Accedi", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index ab6605257..bda62536e 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -208,6 +208,7 @@ "disable_buy": "購入アクションを無効にする", "disable_cake_2fa": "Cake 2FA を無効にする", "disable_exchange": "交換を無効にする", + "disable_fee_api_warning": "これをオフにすることで、料金金利は場合によっては不正確になる可能性があるため、取引の費用が過払いまたは不足している可能性があります", "disable_fiat": "フィアットを無効にする", "disable_sell": "販売アクションを無効にする", "disableBatteryOptimization": "バッテリーの最適化を無効にします", @@ -356,6 +357,7 @@ "ledger_error_wrong_app": "元帳に適切なアプリを開始するようにしてください", "ledger_please_enable_bluetooth": "Bluetoothが元帳を検出できるようにしてください", "light_theme": "光", + "live_fee_rates": "API経由のライブ料金", "load_more": "もっと読み込む", "loading_your_wallet": "ウォレットをロードしています", "login": "ログイン", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index da10162ad..df339df55 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -208,6 +208,7 @@ "disable_buy": "구매 행동 비활성화", "disable_cake_2fa": "케이크 2FA 비활성화", "disable_exchange": "교환 비활성화", + "disable_fee_api_warning": "이것을 끄면 경우에 따라 수수료가 부정확 할 수 있으므로 거래 수수료를 초과 지불하거나 지불 할 수 있습니다.", "disable_fiat": "법정화폐 비활성화", "disable_sell": "판매 조치 비활성화", "disableBatteryOptimization": "배터리 최적화를 비활성화합니다", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "원장에서 올바른 앱을 반대하는지 확인하십시오.", "ledger_please_enable_bluetooth": "Bluetooth가 원장을 감지 할 수 있도록하십시오", "light_theme": "빛", + "live_fee_rates": "API를 통한 라이브 요금 요금", "load_more": "더로드하십시오", "loading_your_wallet": "지갑 넣기", "login": "로그인", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index e04c75d62..e988c6c8d 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -208,6 +208,7 @@ "disable_buy": "ဝယ်ယူမှု လုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disable_cake_2fa": "ကိတ်မုန့် 2FA ကို ပိတ်ပါ။", "disable_exchange": "လဲလှယ်မှုကို ပိတ်ပါ။", + "disable_fee_api_warning": "ဤအရာကိုဖွင့်ခြင်းအားဖြင့်အချို့သောကိစ္စရပ်များတွင်အခကြေးငွေနှုန်းထားများသည်တိကျမှုရှိနိုင်သည်,", "disable_fiat": "Fiat ကိုပိတ်ပါ။", "disable_sell": "ရောင်းချခြင်းလုပ်ဆောင်ချက်ကို ပိတ်ပါ။", "disableBatteryOptimization": "ဘက်ထရီ optimization ကိုပိတ်ပါ", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "ကျေးဇူးပြု. သင့်လက်ျာအက်ပ်ကိုသင်၏ Ledger တွင်ဖွင့်ရန်သေချာစေပါ", "ledger_please_enable_bluetooth": "သင်၏ Ledger ကိုရှာဖွေရန် Bluetooth ကိုဖွင့်ပါ", "light_theme": "အလင်း", + "live_fee_rates": "API မှတစ်ဆင့် Live အခကြေးငွေနှုန်းထားများ", "load_more": "ပိုပြီး load", "loading_your_wallet": "သင့်ပိုက်ဆံအိတ်ကို ဖွင့်နေသည်။", "login": "လော့ဂ်အင်", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index dfa7c2d8f..d1f6032e5 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -208,6 +208,7 @@ "disable_buy": "Koopactie uitschakelen", "disable_cake_2fa": "Taart 2FA uitschakelen", "disable_exchange": "Uitwisseling uitschakelen", + "disable_fee_api_warning": "Door dit uit te schakelen, kunnen de tarieven in sommige gevallen onnauwkeurig zijn, dus u kunt de vergoedingen voor uw transacties te veel betalen of te weinig betalen", "disable_fiat": "Schakel Fiat uit", "disable_sell": "Verkoopactie uitschakelen", "disableBatteryOptimization": "Schakel de batterijoptimalisatie uit", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Zorg ervoor dat u de juiste app op uw grootboek opent", "ledger_please_enable_bluetooth": "Schakel Bluetooth in staat om uw grootboek te detecteren", "light_theme": "Licht", + "live_fee_rates": "Live -tarieven via API", "load_more": "Meer laden", "loading_your_wallet": "Uw portemonnee laden", "login": "Log in", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 397ea2bcb..3b4e0f81f 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -208,6 +208,7 @@ "disable_buy": "Wyłącz akcję kupna", "disable_cake_2fa": "Wyłącz Cake 2FA", "disable_exchange": "Wyłącz wymianę", + "disable_fee_api_warning": "Wyłączając to, stawki opłaty mogą być w niektórych przypadkach niedokładne, więc możesz skończyć się przepłaceniem lub wynagrodzeniem opłat za transakcje", "disable_fiat": "Wyłącz waluty FIAT", "disable_sell": "Wyłącz akcję sprzedaży", "disableBatteryOptimization": "Wyłącz optymalizację baterii", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Upewnij się, że opisz odpowiednią aplikację na swojej księdze", "ledger_please_enable_bluetooth": "Włącz Bluetooth wykrywanie księgi", "light_theme": "Jasny", + "live_fee_rates": "Stawki opłaty na żywo za pośrednictwem API", "load_more": "Załaduj więcej", "loading_your_wallet": "Ładowanie portfela", "login": "Login", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index cbe66715a..6b9e4bf77 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -208,6 +208,7 @@ "disable_buy": "Desativar ação de compra", "disable_cake_2fa": "Desabilitar o Cake 2FA", "disable_exchange": "Desativar troca", + "disable_fee_api_warning": "Ao desativar isso, as taxas de taxas podem ser imprecisas em alguns casos, para que você possa acabar pagando demais ou pagando as taxas por suas transações", "disable_fiat": "Desativar fiat", "disable_sell": "Desativar ação de venda", "disableBatteryOptimization": "Desative a otimização da bateria", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Por favor, certifique -se de optar pelo aplicativo certo no seu livro", "ledger_please_enable_bluetooth": "Ative o Bluetooth para detectar seu livro", "light_theme": "Luz", + "live_fee_rates": "Taxas de taxas ao vivo via API", "load_more": "Carregue mais", "loading_your_wallet": "Abrindo sua carteira", "login": "Login", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 9976de9f8..a8def9179 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -208,6 +208,7 @@ "disable_buy": "Отключить действие покупки", "disable_cake_2fa": "Отключить торт 2FA", "disable_exchange": "Отключить обмен", + "disable_fee_api_warning": "Выключив это, в некоторых случаях ставки платы могут быть неточными, так что вы можете в конечном итоге переплачивать или недоплачивать сборы за ваши транзакции", "disable_fiat": "Отключить фиат", "disable_sell": "Отключить действие продажи", "disableBatteryOptimization": "Отключить оптимизацию батареи", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Пожалуйста, убедитесь, что вы предлагаете правильное приложение в своей бухгалтерской книге", "ledger_please_enable_bluetooth": "Пожалуйста, включите Bluetooth обнаружить вашу бухгалтерскую книгу", "light_theme": "Светлая", + "live_fee_rates": "Ставки по сбору вживую через API", "load_more": "Загрузи больше", "loading_your_wallet": "Загрузка кошелька", "login": "Логин", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index e873fe5eb..02480d6fd 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -208,6 +208,7 @@ "disable_buy": "ปิดการใช้งานการซื้อ", "disable_cake_2fa": "ปิดการใช้งานเค้ก 2FA", "disable_exchange": "ปิดใช้งานการแลกเปลี่ยน", + "disable_fee_api_warning": "โดยการปิดสิ่งนี้อัตราค่าธรรมเนียมอาจไม่ถูกต้องในบางกรณีดังนั้นคุณอาจจบลงด้วยการจ่ายเงินมากเกินไปหรือจ่ายค่าธรรมเนียมสำหรับการทำธุรกรรมของคุณมากเกินไป", "disable_fiat": "ปิดใช้งานสกุลเงินตรา", "disable_sell": "ปิดการใช้งานการขาย", "disableBatteryOptimization": "ปิดใช้งานการเพิ่มประสิทธิภาพแบตเตอรี่", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "โปรดตรวจสอบให้แน่ใจว่าคุณเปิดแอพที่เหมาะสมในบัญชีแยกประเภทของคุณ", "ledger_please_enable_bluetooth": "โปรดเปิดใช้งานบลูทู ธ ในการตรวจจับบัญชีแยกประเภทของคุณ", "light_theme": "สว่าง", + "live_fee_rates": "อัตราค่าธรรมเนียมสดผ่าน API", "load_more": "โหลดมากขึ้น", "loading_your_wallet": "กำลังโหลดกระเป๋าของคุณ", "login": "เข้าสู่ระบบ", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index c1bd2bae7..2307cc62b 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -208,6 +208,7 @@ "disable_buy": "Huwag paganahin ang pagkilos ng pagbili", "disable_cake_2fa": "Huwag paganahin ang Cake 2FA", "disable_exchange": "Huwag paganahin ang palitan", + "disable_fee_api_warning": "Sa pamamagitan ng pag -off nito, ang mga rate ng bayad ay maaaring hindi tumpak sa ilang mga kaso, kaya maaari mong tapusin ang labis na bayad o pagsuporta sa mga bayarin para sa iyong mga transaksyon", "disable_fiat": "Huwag paganahin ang fiat", "disable_sell": "Huwag paganahin ang pagkilos ng pagbebenta", "disableBatteryOptimization": "Huwag Paganahin ang Pag-optimize ng Baterya", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Mangyaring tiyaking pinipili mo ang tamang app sa iyong Ledger", "ledger_please_enable_bluetooth": "Mangyaring paganahin ang Bluetooth upang makita ang iyong Ledger", "light_theme": "Light", + "live_fee_rates": "Mga rate ng live na bayad sa pamamagitan ng API", "load_more": "Mag-load pa", "loading_your_wallet": "Naglo-load ng iyong wallet", "login": "Mag-login", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index ff7d9a120..8346703bc 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -208,6 +208,7 @@ "disable_buy": "Satın alma işlemini devre dışı bırak", "disable_cake_2fa": "Cake 2FA'yı Devre Dışı Bırak", "disable_exchange": "Borsayı devre dışı bırak", + "disable_fee_api_warning": "Bunu kapatarak, ücret oranları bazı durumlarda yanlış olabilir, bu nedenle işlemleriniz için ücretleri fazla ödeyebilir veya az ödeyebilirsiniz.", "disable_fiat": "İtibari paraları devre dışı bırak", "disable_sell": "Satış işlemini devre dışı bırak", "disableBatteryOptimization": "Pil optimizasyonunu devre dışı bırakın", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Lütfen defterinizde doğru uygulamayı açtığınızdan emin olun", "ledger_please_enable_bluetooth": "Defterinizi algılamak için lütfen Bluetooth'u etkinleştirin", "light_theme": "Aydınlık", + "live_fee_rates": "API üzerinden canlı ücret oranları", "load_more": "Daha fazla yükle", "loading_your_wallet": "Cüzdanın yükleniyor", "login": "Login", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 95a3a38d8..ec2324544 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -208,6 +208,7 @@ "disable_buy": "Вимкнути дію покупки", "disable_cake_2fa": "Вимкнути Cake 2FA", "disable_exchange": "Вимкнути exchange", + "disable_fee_api_warning": "Вимкнувши це, ставки плати в деяких випадках можуть бути неточними, тому ви можете переплатити або недооплатити плату за свої транзакції", "disable_fiat": "Вимкнути фиат", "disable_sell": "Вимкнути дію продажу", "disableBatteryOptimization": "Вимкнути оптимізацію акумулятора", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "Будь ласка, переконайтеся, що ви відкриваєте потрібну програму на своїй книзі", "ledger_please_enable_bluetooth": "Будь ласка, ввімкніть Bluetooth виявити свою книгу", "light_theme": "Світла", + "live_fee_rates": "Ставки плати за живий через API", "load_more": "Завантажити ще", "loading_your_wallet": "Завантаження гаманця", "login": "Логін", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 5be1cb7a0..bc5fc332c 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -208,6 +208,7 @@ "disable_buy": "خرید ایکشن کو غیر فعال کریں۔", "disable_cake_2fa": "کیک 2FA کو غیر فعال کریں۔", "disable_exchange": "تبادلے کو غیر فعال کریں۔", + "disable_fee_api_warning": "اس کو بند کرنے سے ، کچھ معاملات میں فیس کی شرح غلط ہوسکتی ہے ، لہذا آپ اپنے لین دین کے لئے فیسوں کو زیادہ ادائیگی یا ادائیگی ختم کرسکتے ہیں۔", "disable_fiat": "فیاٹ کو غیر فعال کریں۔", "disable_sell": "فروخت کی کارروائی کو غیر فعال کریں۔", "disableBatteryOptimization": "بیٹری کی اصلاح کو غیر فعال کریں", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "براہ کرم یقینی بنائیں کہ آپ اپنے لیجر پر صحیح ایپ کو کھولتے ہیں", "ledger_please_enable_bluetooth": "براہ کرم بلوٹوتھ کو اپنے لیجر کا پتہ لگانے کے لئے اہل بنائیں", "light_theme": "روشنی", + "live_fee_rates": "API کے ذریعے براہ راست فیس کی شرح", "load_more": "مزید لوڈ کریں", "loading_your_wallet": "آپ کا بٹوہ لوڈ ہو رہا ہے۔", "login": "لاگ ان کریں", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 81d784d7f..e13d79b6b 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -208,6 +208,7 @@ "disable_buy": "Ko iṣọrọ ọja", "disable_cake_2fa": "Ko 2FA Cake sii", "disable_exchange": "Pa ilé pàṣípààrọ̀", + "disable_fee_api_warning": "Nipa yiyi eyi kuro, awọn oṣuwọn owo naa le jẹ aiṣe deede ni awọn ọrọ kan, nitorinaa o le pari apọju tabi awọn idiyele ti o ni agbara fun awọn iṣowo rẹ", "disable_fiat": "Pa owó tí ìjọba pàṣẹ wa lò", "disable_sell": "Ko iṣọrọ iṣọrọ", "disableBatteryOptimization": "Mu Ifasi batiri", @@ -356,6 +357,7 @@ "ledger_error_wrong_app": "Jọwọ rii daju pe iwọ yoo sọ app ti o tọ loju omi rẹ", "ledger_please_enable_bluetooth": "Jọwọ jẹ ki Bluetooth lati rii iṣupọ rẹ", "light_theme": "Funfun bí eérú", + "live_fee_rates": "Awọn oṣuwọn Owo laaye laaye nipasẹ API", "load_more": "Ẹru diẹ sii", "loading_your_wallet": "A ń ṣí àpamọ́wọ́ yín", "login": "Orúkọ", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 876c3bbfb..a26edec4e 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -208,6 +208,7 @@ "disable_buy": "禁用购买操作", "disable_cake_2fa": "禁用蛋糕 2FA", "disable_exchange": "禁用交换", + "disable_fee_api_warning": "通过将其关闭,在某些情况下,收费率可能不准确,因此您最终可能会超额付款或支付交易费用", "disable_fiat": "禁用法令", "disable_sell": "禁用卖出操作", "disableBatteryOptimization": "禁用电池优化", @@ -355,6 +356,7 @@ "ledger_error_wrong_app": "请确保您在分类帐中操作正确的应用程序", "ledger_please_enable_bluetooth": "请启用蓝牙来检测您的分类帐", "light_theme": "艳丽", + "live_fee_rates": "通过API的实时费率", "load_more": "装载更多", "loading_your_wallet": "加载您的钱包", "login": "登录", From 4c795ea5c26f2b5ba88317cf6d02fd284bc420fb Mon Sep 17 00:00:00 2001 From: cyan <cyjan@mrcyjanek.net> Date: Tue, 27 Aug 2024 01:40:20 +0200 Subject: [PATCH 3/3] subaddress fix (#1620) * subaddress fix * fix subaddress generation * rewrite usedAddresses for xmr and wow * [skip ci] remove print statements --- cw_monero/lib/api/transaction_history.dart | 27 ++++++++++++++++---- cw_monero/lib/monero_subaddress_list.dart | 9 ++++--- cw_monero/lib/monero_wallet.dart | 5 ++++ cw_monero/lib/monero_wallet_addresses.dart | 20 +++++++++++++++ cw_wownero/lib/api/transaction_history.dart | 23 +++++++++++++---- cw_wownero/lib/wownero_subaddress_list.dart | 4 +++ cw_wownero/lib/wownero_wallet.dart | 6 +++++ cw_wownero/lib/wownero_wallet_addresses.dart | 18 +++++++++++++ 8 files changed, 99 insertions(+), 13 deletions(-) diff --git a/cw_monero/lib/api/transaction_history.dart b/cw_monero/lib/api/transaction_history.dart index b416e1b4e..1f00b082f 100644 --- a/cw_monero/lib/api/transaction_history.dart +++ b/cw_monero/lib/api/transaction_history.dart @@ -45,6 +45,8 @@ List<Transaction> getAllTransactions() { confirmations: 0, blockheight: 0, accountIndex: i, + addressIndex: 0, + addressIndexList: [0], paymentId: "", amount: fullBalance - availBalance, isSpend: false, @@ -245,19 +247,30 @@ Future<PendingTransactionDescription> createTransactionMultDest( class Transaction { final String displayLabel; - String subaddressLabel = monero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); - late final String address = monero.Wallet_address( + String get subaddressLabel => monero.Wallet_getSubaddressLabel( wptr!, - accountIndex: 0, - addressIndex: 0, + accountIndex: accountIndex, + addressIndex: addressIndex, ); + String get address => monero.Wallet_address( + wptr!, + accountIndex: accountIndex, + addressIndex: addressIndex, + ); + List<String> get addressList => List.generate(addressIndexList.length, (index) => + monero.Wallet_address( + wptr!, + accountIndex: accountIndex, + addressIndex: addressIndexList[index], + )); final String description; final int fee; final int confirmations; late final bool isPending = confirmations < 10; final int blockheight; - final int addressIndex = 0; + final int addressIndex; final int accountIndex; + final List<int> addressIndexList; final String paymentId; final int amount; final bool isSpend; @@ -303,6 +316,8 @@ class Transaction { amount = monero.TransactionInfo_amount(txInfo), paymentId = monero.TransactionInfo_paymentId(txInfo), accountIndex = monero.TransactionInfo_subaddrAccount(txInfo), + addressIndex = int.tryParse(monero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0, + addressIndexList = monero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(), blockheight = monero.TransactionInfo_blockHeight(txInfo), confirmations = monero.TransactionInfo_confirmations(txInfo), fee = monero.TransactionInfo_fee(txInfo), @@ -316,6 +331,8 @@ class Transaction { required this.confirmations, required this.blockheight, required this.accountIndex, + required this.addressIndexList, + required this.addressIndex, required this.paymentId, required this.amount, required this.isSpend, diff --git a/cw_monero/lib/monero_subaddress_list.dart b/cw_monero/lib/monero_subaddress_list.dart index c35afb282..fe85bef3b 100644 --- a/cw_monero/lib/monero_subaddress_list.dart +++ b/cw_monero/lib/monero_subaddress_list.dart @@ -1,6 +1,7 @@ import 'package:cw_core/subaddress.dart'; import 'package:cw_monero/api/coins_info.dart'; import 'package:cw_monero/api/subaddress_list.dart' as subaddress_list; +import 'package:cw_monero/api/wallet.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; @@ -103,6 +104,9 @@ abstract class MoneroSubaddressListBase with Store { required List<String> usedAddresses, }) async { _usedAddresses.addAll(usedAddresses); + final _all = _usedAddresses.toSet().toList(); + _usedAddresses.clear(); + _usedAddresses.addAll(_all); if (_isUpdating) { return; } @@ -124,7 +128,7 @@ abstract class MoneroSubaddressListBase with Store { Future<List<Subaddress>> _getAllUnusedAddresses( {required int accountIndex, required String label}) async { final allAddresses = subaddress_list.getAllSubaddresses(); - if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last)) { + if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.first.address)) { final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); if (!isAddressUnused) { return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); @@ -143,8 +147,7 @@ abstract class MoneroSubaddressListBase with Store { label.toLowerCase() == 'Primary account'.toLowerCase() ? 'Primary address' : label); - }) - .toList(); + }).toList().reversed.toList(); } Future<bool> _newSubaddress({required int accountIndex, required String label}) async { diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index f5fa0ec7e..483ee8868 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -88,6 +88,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) { _updateSubAddress(enabled, account: walletAddresses.account); }); + reaction((_) => transactionHistory, (__) { + _updateSubAddress(isEnabledAutoGenerateSubaddress, account: walletAddresses.account); + }); } static const int _autoSaveInterval = 30; @@ -130,6 +133,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, monero_wallet.SyncListener? _listener; ReactionDisposer? _onAccountChangeReaction; + ReactionDisposer? _onTxHistoryChangeReaction; bool _isTransactionUpdating; bool _hasSyncAfterStartup; Timer? _autoSaveTimer; @@ -169,6 +173,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance, void close() async { _listener?.stop(); _onAccountChangeReaction?.reaction.dispose(); + _onTxHistoryChangeReaction?.reaction.dispose(); _autoSaveTimer?.cancel(); } diff --git a/cw_monero/lib/monero_wallet_addresses.dart b/cw_monero/lib/monero_wallet_addresses.dart index d4f22e46f..41e22aef2 100644 --- a/cw_monero/lib/monero_wallet_addresses.dart +++ b/cw_monero/lib/monero_wallet_addresses.dart @@ -3,6 +3,7 @@ import 'package:cw_core/address_info.dart'; import 'package:cw_core/subaddress.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_monero/api/transaction_history.dart'; import 'package:cw_monero/api/wallet.dart'; import 'package:cw_monero/monero_account_list.dart'; import 'package:cw_monero/monero_subaddress_list.dart'; @@ -37,6 +38,25 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { MoneroAccountList accountList; + @override + Set<String> get usedAddresses { + final txs = getAllTransactions(); + final adds = _originalUsedAddresses.toList(); + for (var i = 0; i < txs.length; i++) { + for (var j = 0; j < txs[i].addressList.length; j++) { + adds.add(txs[i].addressList[j]); + } + } + return adds.toSet(); + } + + Set<String> _originalUsedAddresses = Set(); + + @override + set usedAddresses(Set<String> _usedAddresses) { + _originalUsedAddresses = _usedAddresses; + } + @override Future<void> init() async { accountList.update(); diff --git a/cw_wownero/lib/api/transaction_history.dart b/cw_wownero/lib/api/transaction_history.dart index a1e1e3c9b..020c47df6 100644 --- a/cw_wownero/lib/api/transaction_history.dart +++ b/cw_wownero/lib/api/transaction_history.dart @@ -45,6 +45,8 @@ List<Transaction> getAllTransactions() { confirmations: 0, blockheight: 0, accountIndex: i, + addressIndex: 0, + addressIndexList: [0], paymentId: "", amount: fullBalance - availBalance, isSpend: false, @@ -243,19 +245,26 @@ Future<PendingTransactionDescription> createTransactionMultDest( class Transaction { final String displayLabel; - String subaddressLabel = wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); - late final String address = wownero.Wallet_address( + String get subaddressLabel => wownero.Wallet_getSubaddressLabel(wptr!, accountIndex: 0, addressIndex: 0); + String get address => wownero.Wallet_address( wptr!, - accountIndex: 0, - addressIndex: 0, + accountIndex: accountIndex, + addressIndex: addressIndex, ); + List<String> get addressList => List.generate(addressIndexList.length, (index) => + wownero.Wallet_address( + wptr!, + accountIndex: accountIndex, + addressIndex: addressIndexList[index], + )); final String description; final int fee; final int confirmations; late final bool isPending = confirmations < 3; final int blockheight; - final int addressIndex = 0; + final int addressIndex; final int accountIndex; + final List<int> addressIndexList; final String paymentId; final int amount; final bool isSpend; @@ -301,6 +310,8 @@ class Transaction { amount = wownero.TransactionInfo_amount(txInfo), paymentId = wownero.TransactionInfo_paymentId(txInfo), accountIndex = wownero.TransactionInfo_subaddrAccount(txInfo), + addressIndex = int.tryParse(wownero.TransactionInfo_subaddrIndex(txInfo).split(", ")[0]) ?? 0, + addressIndexList = wownero.TransactionInfo_subaddrIndex(txInfo).split(", ").map((e) => int.tryParse(e) ?? 0).toList(), blockheight = wownero.TransactionInfo_blockHeight(txInfo), confirmations = wownero.TransactionInfo_confirmations(txInfo), fee = wownero.TransactionInfo_fee(txInfo), @@ -314,6 +325,8 @@ class Transaction { required this.confirmations, required this.blockheight, required this.accountIndex, + required this.addressIndex, + required this.addressIndexList, required this.paymentId, required this.amount, required this.isSpend, diff --git a/cw_wownero/lib/wownero_subaddress_list.dart b/cw_wownero/lib/wownero_subaddress_list.dart index 61fd09ef9..5c026cc86 100644 --- a/cw_wownero/lib/wownero_subaddress_list.dart +++ b/cw_wownero/lib/wownero_subaddress_list.dart @@ -1,6 +1,7 @@ import 'package:cw_core/subaddress.dart'; import 'package:cw_wownero/api/coins_info.dart'; import 'package:cw_wownero/api/subaddress_list.dart' as subaddress_list; +import 'package:cw_wownero/api/wallet.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; @@ -103,6 +104,9 @@ abstract class WowneroSubaddressListBase with Store { required List<String> usedAddresses, }) async { _usedAddresses.addAll(usedAddresses); + final _all = _usedAddresses.toSet().toList(); + _usedAddresses.clear(); + _usedAddresses.addAll(_all); if (_isUpdating) { return; } diff --git a/cw_wownero/lib/wownero_wallet.dart b/cw_wownero/lib/wownero_wallet.dart index c3f4bcb69..ab7691dd6 100644 --- a/cw_wownero/lib/wownero_wallet.dart +++ b/cw_wownero/lib/wownero_wallet.dart @@ -82,6 +82,10 @@ abstract class WowneroWalletBase reaction((_) => isEnabledAutoGenerateSubaddress, (bool enabled) { _updateSubAddress(enabled, account: walletAddresses.account); }); + + _onTxHistoryChangeReaction = reaction((_) => transactionHistory, (__) { + _updateSubAddress(isEnabledAutoGenerateSubaddress, account: walletAddresses.account); + }); } static const int _autoSaveInterval = 30; @@ -123,6 +127,7 @@ abstract class WowneroWalletBase wownero_wallet.SyncListener? _listener; ReactionDisposer? _onAccountChangeReaction; + ReactionDisposer? _onTxHistoryChangeReaction; bool _isTransactionUpdating; bool _hasSyncAfterStartup; Timer? _autoSaveTimer; @@ -158,6 +163,7 @@ abstract class WowneroWalletBase void close() async { _listener?.stop(); _onAccountChangeReaction?.reaction.dispose(); + _onTxHistoryChangeReaction?.reaction.dispose(); _autoSaveTimer?.cancel(); } diff --git a/cw_wownero/lib/wownero_wallet_addresses.dart b/cw_wownero/lib/wownero_wallet_addresses.dart index 9eeb182eb..b36c0e9ec 100644 --- a/cw_wownero/lib/wownero_wallet_addresses.dart +++ b/cw_wownero/lib/wownero_wallet_addresses.dart @@ -3,6 +3,7 @@ import 'package:cw_core/address_info.dart'; import 'package:cw_core/subaddress.dart'; import 'package:cw_core/wallet_addresses.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_wownero/api/transaction_history.dart'; import 'package:cw_wownero/api/wallet.dart'; import 'package:cw_wownero/wownero_account_list.dart'; import 'package:cw_wownero/wownero_subaddress_list.dart'; @@ -36,7 +37,24 @@ abstract class WowneroWalletAddressesBase extends WalletAddresses with Store { WowneroSubaddressList subaddressList; WowneroAccountList accountList; + @override + Set<String> get usedAddresses { + final txs = getAllTransactions(); + final adds = _originalUsedAddresses.toList(); + for (var i = 0; i < txs.length; i++) { + for (var j = 0; j < txs[i].addressList.length; j++) { + adds.add(txs[i].addressList[j]); + } + } + return adds.toSet(); + } + Set<String> _originalUsedAddresses = Set(); + + @override + set usedAddresses(Set<String> _usedAddresses) { + _originalUsedAddresses = _usedAddresses; + } @override Future<void> init() async { accountList.update();