Cw 773 restoring from qr gives no visual feedback of progress (#1944)

* init commit * fix for passphrase * update restore from keys flow * remove WalletRestorationFromQRVM * update creation vm * fix for blockchainHeightKey * fix merge conflicts * Update wallet_restore_page.dart --------- Co-authored-by: Omar Hatem <omarh.ismail1@gmail.com>

Serhii committed Mar 15, 2025 at 01:45 UTC 6abefc25a7fb75e1d3958c7a5b3e737645d6d775
11 files changed +131 -323
lib/di.dart
+10 -13
@@ -54,6 +54,7 @@ import 'package:cake_wallet/view_model/link_view_model.dart';
54 import 'package:cake_wallet/tron/tron.dart';
55 import 'package:cake_wallet/src/screens/transaction_details/rbf_details_page.dart';
56 import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
57 +import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
58 import 'package:cake_wallet/view_model/send/fees_view_model.dart';
59 import 'package:cw_core/receive_page_option.dart';
60 import 'package:cake_wallet/entities/preferences_key.dart';
@@ -173,7 +174,6 @@ import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart';
174 import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
175 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
176 import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart';
176 -import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
177 import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart';
178 import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart';
179 import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
@@ -475,14 +475,6 @@ Future<void> setup({
475 walletType: args.walletType ?? currentWalletType);
476 });
477
478 - getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) =>
479 - WalletRestorationFromQRVM(
480 - getIt.get<AppStore>(),
481 - getIt.get<WalletCreationService>(param1: type),
482 - _walletInfoSource,
483 - type,
484 - getIt.get<SeedSettingsViewModel>()));
485 -
478 getIt.registerFactoryParam<WalletHardwareRestoreViewModel, WalletType, void>((type, _) =>
479 WalletHardwareRestoreViewModel(
480 getIt.get<LedgerViewModel>(),
@@ -1144,13 +1136,18 @@ Future<void> setup({
1136
1137 getIt.registerFactory(() => FaqPage(getIt.get<SettingsStore>()));
1138
1147 - getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, void>((type, _) =>
1139 + getIt.registerFactoryParam<WalletRestoreViewModel, WalletType, RestoredWallet?>((WalletType type,
1140 + restoredWallet) =>
1141 WalletRestoreViewModel(getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type),
1142 _walletInfoSource, getIt.get<SeedSettingsViewModel>(),
1150 - type: type));
1143 + type: type, restoredWallet: restoredWallet));
1144
1152 - getIt.registerFactoryParam<WalletRestorePage, WalletType, void>((type, _) => WalletRestorePage(
1153 - getIt.get<WalletRestoreViewModel>(param1: type), getIt.get<SeedSettingsViewModel>()));
1145 + getIt.registerFactoryParam<WalletRestorePage, WalletType, RestoredWallet?>((WalletType type,
1146 + restoredWallet) {
1147 + return WalletRestorePage(
1148 + getIt.get<WalletRestoreViewModel>(param1: type, param2: restoredWallet),
1149 + getIt.get<SeedSettingsViewModel>());
1150 + });
1151
1152 getIt.registerFactoryParam<WalletRestoreChooseDerivationViewModel, List<DerivationInfo>, void>(
1153 (derivations, _) => WalletRestoreChooseDerivationViewModel(derivationInfos: derivations));
lib/router.dart
+12 -5
@@ -120,6 +120,7 @@ import 'package:cake_wallet/view_model/dashboard/sign_view_model.dart';
120 import 'package:cake_wallet/view_model/hardware_wallet/ledger_view_model.dart';
121 import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart';
122 import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart';
123 +import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
124 import 'package:cake_wallet/view_model/wallet_groups_display_view_model.dart';
125 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
126 import 'package:cake_wallet/view_model/wallet_hardware_restore_view_model.dart';
@@ -235,8 +236,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
236 return CupertinoPageRoute<void>(
237 builder: (_) => getIt.get<NewWalletTypePage>(
238 param1: NewWalletTypeArguments(
238 - onTypeSelected: (BuildContext context, WalletType type) =>
239 - Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
239 + onTypeSelected: (BuildContext context, WalletType type) {
240 + final arg = {'walletType': type};
241 + Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: arg);},
242 isCreate: false,
243 isHardwareWallet: false,
244 ),
@@ -261,8 +263,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
263 return CupertinoPageRoute<void>(
264 builder: (_) => getIt.get<NewWalletTypePage>(
265 param1: NewWalletTypeArguments(
264 - onTypeSelected: (BuildContext context, WalletType type) =>
265 - Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: type),
266 + onTypeSelected: (BuildContext context, WalletType type) {
267 + final arg = {'walletType': type};
268 + Navigator.of(context).pushNamed(Routes.restoreWallet, arguments: arg);},
269 isCreate: false,
270 isHardwareWallet: false,
271 ),
@@ -318,8 +321,11 @@ Route<dynamic> createRoute(RouteSettings settings) {
321 builder: (_) => getIt.get<WalletSeedPage>(param1: settings.arguments as bool));
322
323 case Routes.restoreWallet:
324 + final args = settings.arguments as Map<String, dynamic>?;
325 + final walletType = args?['walletType'] as WalletType;
326 + final restoredWallet = args?['restoredWallet'] as RestoredWallet?;
327 return MaterialPageRoute<void>(
322 - builder: (_) => getIt.get<WalletRestorePage>(param1: settings.arguments as WalletType));
328 + builder: (_) => getIt.get<WalletRestorePage>(param1: walletType, param2: restoredWallet));
329
330 case Routes.restoreWalletChooseDerivation:
331 return MaterialPageRoute<void>(
@@ -677,6 +683,7 @@ Route<dynamic> createRoute(RouteSettings settings) {
683 final isChildWallet = args['isChildWallet'] as bool? ?? false;
684 final useTestnet = args['useTestnet'] as bool;
685 final toggleTestnet = args['toggleTestnet'] as Function(bool? val);
686 + final restoredWallet = args['restoredWallet'] as RestoredWallet?;
687
688 return CupertinoPageRoute<void>(
689 builder: (_) => AdvancedPrivacySettingsPage(
lib/src/screens/restore/restore_options_page.dart
+18 -29
@@ -1,18 +1,14 @@
1 import 'dart:io';
2
3 -import 'package:cake_wallet/core/execution_state.dart';
4 -import 'package:cake_wallet/di.dart';
3 import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/routes.dart';
5 import 'package:cake_wallet/src/screens/base_page.dart';
8 -import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
6 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
7 import 'package:cake_wallet/src/widgets/option_tile.dart';
8 import 'package:cake_wallet/utils/device_info.dart';
9 import 'package:cake_wallet/utils/permission_handler.dart';
10 import 'package:cake_wallet/utils/responsive_layout_util.dart';
11 import 'package:cake_wallet/utils/show_pop_up.dart';
15 -import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
12 import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart';
13 import 'package:cake_wallet/wallet_type_utils.dart';
14 import 'package:cw_core/hardware/device_connection_type.dart';
@@ -131,10 +127,8 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
127 );
128 }
129
134 - void _onWalletCreateFailure(BuildContext context, String error) {
135 - setState(() {
136 - isRestoring = false;
137 - });
130 + void _showQRScanError(BuildContext context, String error) {
131 + setState(() => isRestoring = false);
132
133 WidgetsBinding.instance.addPostFrameCallback((_) {
134 showPopUp<void>(
@@ -151,28 +145,23 @@ class _RestoreOptionsBodyState extends State<_RestoreOptionsBody> {
145
146 Future<void> _onScanQRCode(BuildContext context) async {
147 final isCameraPermissionGranted =
154 - await PermissionHandler.checkPermission(Permission.camera, context);
148 + await PermissionHandler.checkPermission(Permission.camera, context);
149
150 if (!isCameraPermissionGranted) return;
157 - try {
158 - if (isRestoring) {
159 - return;
160 - }
161 - setState(() {
162 - isRestoring = true;
163 - });
164 - final restoreWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
165 -
166 - final restoreFromQRViewModel =
167 - getIt.get<WalletRestorationFromQRVM>(param1: restoreWallet.type);
168 -
169 - await restoreFromQRViewModel.create(restoreWallet: restoreWallet);
170 - if (restoreFromQRViewModel.state is FailureState) {
171 - _onWalletCreateFailure(context,
172 - 'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}');
173 - }
174 - } catch (e) {
175 - _onWalletCreateFailure(context, e.toString());
176 - }
151 + try {
152 + if (isRestoring) return;
153 +
154 + setState(() => isRestoring = true);
155 +
156 + final restoredWallet = await WalletRestoreFromQRCode.scanQRCodeForRestoring(context);
157 +
158 + final params = {'walletType': restoredWallet.type, 'restoredWallet': restoredWallet};
159 +
160 + Navigator.pushNamed(context, Routes.restoreWallet, arguments: params).then((_) {
161 + if (mounted) setState(() => isRestoring = false);
162 + });
163 + } catch (e) {
164 + _showQRScanError(context, e.toString());
165 }
166 + }
167 }
lib/src/screens/restore/wallet_restore_from_keys_form.dart
+27 -10
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/src/widgets/address_text_field.dart';
2 +import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
3 import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
4 import 'package:cw_core/wallet_type.dart';
5 import 'package:flutter/material.dart';
@@ -10,14 +11,15 @@ import 'package:cake_wallet/entities/generate_name.dart';
11 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
12 import 'package:flutter/services.dart';
13
13 -class WalletRestoreFromKeysFrom extends StatefulWidget {
14 - WalletRestoreFromKeysFrom({
14 +class WalletRestoreFromKeysForm extends StatefulWidget {
15 + WalletRestoreFromKeysForm({
16 required this.walletRestoreViewModel,
17 required this.onPrivateKeyChange,
18 required this.displayPrivateKeyField,
19 required this.onHeightOrDateEntered,
20 required this.displayWalletPassword,
21 required this.onRepeatedPasswordChange,
22 + this.restoredWallet,
23 this.onPasswordChange,
24 Key? key,
25 }) : super(key: key);
@@ -27,23 +29,32 @@ class WalletRestoreFromKeysFrom extends StatefulWidget {
29 final void Function(String)? onPrivateKeyChange;
30 final bool displayPrivateKeyField;
31 final bool displayWalletPassword;
32 + final RestoredWallet? restoredWallet;
33 final void Function(String)? onPasswordChange;
34 final void Function(String)? onRepeatedPasswordChange;
35
36 @override
34 - WalletRestoreFromKeysFromState createState() =>
35 - WalletRestoreFromKeysFromState(displayWalletPassword: displayWalletPassword);
37 + WalletRestoreFromKeysFormState createState() =>
38 + WalletRestoreFromKeysFormState(displayWalletPassword: displayWalletPassword, restoredWallet: restoredWallet);
39 }
40
38 -class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
39 - WalletRestoreFromKeysFromState({required bool displayWalletPassword})
41 +class WalletRestoreFromKeysFormState extends State<WalletRestoreFromKeysForm> {
42 + WalletRestoreFromKeysFormState({required bool displayWalletPassword, RestoredWallet? restoredWallet})
43 : formKey = GlobalKey<FormState>(),
44 blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
45 nameController = TextEditingController(),
43 - addressController = TextEditingController(),
44 - viewKeyController = TextEditingController(),
45 - spendKeyController = TextEditingController(),
46 - privateKeyController = TextEditingController(),
46 + addressController = restoredWallet != null
47 + ? TextEditingController(text: restoredWallet.address)
48 + : TextEditingController(),
49 + viewKeyController = restoredWallet != null
50 + ? TextEditingController(text: restoredWallet.viewKey)
51 + : TextEditingController(),
52 + spendKeyController = restoredWallet != null
53 + ? TextEditingController(text: restoredWallet.spendKey)
54 + : TextEditingController(),
55 + privateKeyController = restoredWallet != null
56 + ? TextEditingController(text: restoredWallet.privateKey)
57 + : TextEditingController(),
58 nameTextEditingController = TextEditingController(),
59 passwordTextEditingController = displayWalletPassword ? TextEditingController() : null,
60 repeatedPasswordTextEditingController = displayWalletPassword ? TextEditingController() : null;
@@ -80,6 +91,12 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
91 }
92 widget.onPrivateKeyChange?.call(privateKeyController.text);
93 });
94 +
95 + WidgetsBinding.instance.addPostFrameCallback((_) {
96 + if (widget.restoredWallet?.height != null) {
97 + blockchainHeightKey.currentState?.restoreHeightController.text = widget.restoredWallet!.height.toString();
98 + }
99 + });
100 }
101
102 @override
lib/src/screens/restore/wallet_restore_from_seed_form.dart
+4
@@ -9,6 +9,7 @@ import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
9 import 'package:cake_wallet/src/widgets/seed_widget.dart';
10 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
11 import 'package:cake_wallet/utils/show_pop_up.dart';
12 +import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
13 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
14 import 'package:cw_core/wallet_type.dart';
15 import 'package:flutter/material.dart';
@@ -23,6 +24,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
24 required this.displayWalletPassword,
25 required this.seedSettingsViewModel,
26 this.blockHeightFocusNode,
27 + this.restoredWallet,
28 this.onHeightOrDateEntered,
29 this.onSeedChange,
30 this.onLanguageChange,
@@ -36,6 +38,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
38 final bool displayWalletPassword;
39 final SeedSettingsViewModel seedSettingsViewModel;
40 final FocusNode? blockHeightFocusNode;
41 + final RestoredWallet? restoredWallet;
42 final Function(bool)? onHeightOrDateEntered;
43 final void Function(String)? onSeedChange;
44 final void Function(String)? onLanguageChange;
@@ -184,6 +187,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
187 Container(height: 20),
188 SeedWidget(
189 key: seedWidgetStateKey,
190 + initialSeed: widget.restoredWallet?.mnemonicSeed,
191 language: language,
192 type: widget.type,
193 onSeedChange: onSeedChange,
lib/src/screens/restore/wallet_restore_page.dart
+20 -7
@@ -24,13 +24,13 @@ import 'package:mobx/mobx.dart';
24 class WalletRestorePage extends BasePage {
25 WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel)
26 : walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
27 - walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFromState>(),
27 + walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFormState>(),
28 _blockHeightFocusNode = FocusNode();
29
30 final WalletRestoreViewModel walletRestoreViewModel;
31 final SeedSettingsViewModel seedSettingsViewModel;
32 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
33 - final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
33 + final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
34 final FocusNode _blockHeightFocusNode;
35
36 bool _formProcessing = false;
@@ -52,6 +52,10 @@ class WalletRestorePage extends BasePage {
52 // String? derivationPath = null;
53 DerivationInfo? derivationInfo;
54
55 +
56 + @override
57 + Function(BuildContext)? get popWidget => (context) => seedSettingsViewModel.setPassphrase(null);
58 +
59 @override
60 Function(BuildContext)? get pushToNextWidget => (context) {
61 FocusScopeNode currentFocus = FocusScope.of(context);
@@ -283,7 +287,7 @@ class _WalletRestorePageBody extends StatefulWidget {
287 final WalletRestoreViewModel walletRestoreViewModel;
288 final SeedSettingsViewModel seedSettingsViewModel;
289 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
286 - final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
290 + final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
291 final FocusNode blockHeightFocusNode;
292 final DerivationInfo? derivationInfo;
293 final void Function(DerivationInfo?) onDerivationInfoChanged;
@@ -311,7 +315,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
315 final WalletRestoreViewModel walletRestoreViewModel;
316 final SeedSettingsViewModel seedSettingsViewModel;
317 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
314 - final GlobalKey<WalletRestoreFromKeysFromState> walletRestoreFromKeysFormKey;
318 + final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
319 final FocusNode blockHeightFocusNode;
320 DerivationInfo? derivationInfo;
321
@@ -325,7 +329,14 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
329
330 _hasKeysTab = widget.walletRestoreViewModel.availableModes.contains(WalletRestoreMode.keys);
331 final tabCount = _hasKeysTab ? 2 : 1;
328 - _tabController = TabController(length: tabCount, vsync: this);
332 +
333 + final initialIndex = walletRestoreViewModel.mode == WalletRestoreMode.seed
334 + ? 0
335 + : _hasKeysTab
336 + ? 1
337 + : 0;
338 +
339 + _tabController = TabController(length: tabCount, vsync: this, initialIndex: initialIndex);
340
341 _tabController.addListener(() {
342 if (!_tabController.indexIsChanging) {
@@ -442,9 +453,10 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
453 );
454 }
455
445 - WalletRestoreFromKeysFrom _buildWalletRestoreFromKeysTab() {
446 - return WalletRestoreFromKeysFrom(
456 + WalletRestoreFromKeysForm _buildWalletRestoreFromKeysTab() {
457 + return WalletRestoreFromKeysForm(
458 key: widget.walletRestoreFromKeysFormKey,
459 + restoredWallet: walletRestoreViewModel.restoredWallet,
460 walletRestoreViewModel: widget.walletRestoreViewModel,
461 displayPrivateKeyField: widget.walletRestoreViewModel.hasRestoreFromPrivateKey,
462 displayWalletPassword: widget.walletRestoreViewModel.hasWalletPassword,
@@ -466,6 +478,7 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
478 WalletRestoreFromSeedForm _buildWalletRestoreFromSeedTab() {
479 return WalletRestoreFromSeedForm(
480 key: widget.walletRestoreFromSeedFormKey,
481 + restoredWallet: walletRestoreViewModel.restoredWallet,
482 seedSettingsViewModel: widget.seedSettingsViewModel,
483 displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightLanguageSelector,
484 displayLanguageSelector: widget.walletRestoreViewModel.hasSeedLanguageSelector,
lib/src/widgets/seed_widget.dart
+12 -6
@@ -14,6 +14,7 @@ class SeedWidget extends StatefulWidget {
14 this.onSeedChange,
15 this.pasteButtonKey,
16 this.seedTextFieldKey,
17 + this.initialSeed,
18 super.key,
19 });
20 final Key? seedTextFieldKey;
@@ -21,17 +22,18 @@ class SeedWidget extends StatefulWidget {
22 final String language;
23 final WalletType type;
24 final void Function(String)? onSeedChange;
25 + final String? initialSeed;
26
27 @override
26 - SeedWidgetState createState() => SeedWidgetState(language, type);
28 + SeedWidgetState createState() => SeedWidgetState(language, type, initialSeed);
29 }
30
31 class SeedWidgetState extends State<SeedWidget> {
30 - SeedWidgetState(String language, this.type)
31 - : controller = TextEditingController(),
32 + SeedWidgetState(String language, this.type, String? initialSeed)
33 + : controller = TextEditingController(text: initialSeed ?? ''),
34 focusNode = FocusNode(),
35 words = SeedValidator.getWordList(type: type, language: language),
34 - _showPlaceholder = false {
36 + _showPlaceholder = initialSeed == null || initialSeed.isEmpty {
37 focusNode.addListener(() {
38 setState(() {
39 if (!focusNode.hasFocus && controller.text.isEmpty) {
@@ -57,8 +59,12 @@ class SeedWidgetState extends State<SeedWidget> {
59 @override
60 void initState() {
61 super.initState();
60 - _showPlaceholder = true;
61 - controller.addListener(() => widget.onSeedChange?.call(text));
62 + controller.addListener(() {
63 + setState(() {
64 + _showPlaceholder = controller.text.isEmpty;
65 + });
66 + widget.onSeedChange?.call(text);
67 + });
68 }
69
70 void changeSeedLanguage(String language) {
lib/view_model/restore/restore_from_qr_vm.dart deleted
-219
@@ -1,219 +0,0 @@
1 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 -import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
3 -import 'package:cake_wallet/ethereum/ethereum.dart';
4 -import 'package:cake_wallet/nano/nano.dart';
5 -import 'package:cake_wallet/polygon/polygon.dart';
6 -import 'package:cake_wallet/solana/solana.dart';
7 -import 'package:cake_wallet/tron/tron.dart';
8 -import 'package:cake_wallet/view_model/restore/restore_mode.dart';
9 -import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
10 -import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
11 -import 'package:cake_wallet/wownero/wownero.dart';
12 -import 'package:cake_wallet/zano/zano.dart';
13 -import 'package:hive/hive.dart';
14 -import 'package:mobx/mobx.dart';
15 -import 'package:cake_wallet/monero/monero.dart';
16 -import 'package:cake_wallet/store/app_store.dart';
17 -import 'package:cw_core/wallet_base.dart';
18 -import 'package:cake_wallet/core/generate_wallet_password.dart';
19 -import 'package:cake_wallet/core/wallet_creation_service.dart';
20 -import 'package:cw_core/wallet_credentials.dart';
21 -import 'package:cw_core/wallet_type.dart';
22 -import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
23 -import 'package:cw_core/wallet_info.dart';
24 -
25 -part 'restore_from_qr_vm.g.dart';
26 -
27 -class WalletRestorationFromQRVM = WalletRestorationFromQRVMBase with _$WalletRestorationFromQRVM;
28 -
29 -abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store {
30 - WalletRestorationFromQRVMBase(
31 - AppStore appStore,
32 - WalletCreationService walletCreationService,
33 - Box<WalletInfo> walletInfoSource,
34 - WalletType type,
35 - SeedSettingsViewModel seedSettingsViewModel)
36 - : height = 0,
37 - viewKey = '',
38 - spendKey = '',
39 - wif = '',
40 - address = '',
41 - super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
42 - type: type, isRecovery: true);
43 -
44 - @observable
45 - int height;
46 -
47 - @observable
48 - String viewKey;
49 -
50 - @observable
51 - String spendKey;
52 -
53 - @observable
54 - String wif;
55 -
56 - @observable
57 - String address;
58 -
59 - bool get hasRestorationHeight => type == WalletType.monero || type == WalletType.wownero;
60 -
61 - @override
62 - Future<WalletCredentials> getWalletCredentialsFromQRCredentials(
63 - RestoredWallet restoreWallet) async {
64 - final password = generateWalletPassword();
65 -
66 - switch (restoreWallet.restoreMode) {
67 - case WalletRestoreMode.keys:
68 - switch (restoreWallet.type) {
69 - case WalletType.monero:
70 - return monero!.createMoneroRestoreWalletFromKeysCredentials(
71 - name: name,
72 - password: password,
73 - language: 'English',
74 - address: restoreWallet.address ?? '',
75 - viewKey: restoreWallet.viewKey ?? '',
76 - spendKey: restoreWallet.spendKey ?? '',
77 - height: restoreWallet.height ?? 0);
78 - case WalletType.wownero:
79 - return wownero!.createWowneroRestoreWalletFromKeysCredentials(
80 - name: name,
81 - password: password,
82 - language: 'English',
83 - address: restoreWallet.address ?? '',
84 - viewKey: restoreWallet.viewKey ?? '',
85 - spendKey: restoreWallet.spendKey ?? '',
86 - height: restoreWallet.height ?? 0);
87 - case WalletType.bitcoin:
88 - case WalletType.litecoin:
89 - return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
90 - name: name, password: password, wif: wif);
91 - case WalletType.ethereum:
92 - return ethereum!.createEthereumRestoreWalletFromPrivateKey(
93 - name: name, password: password, privateKey: restoreWallet.privateKey!);
94 - case WalletType.polygon:
95 - return polygon!.createPolygonRestoreWalletFromPrivateKey(
96 - name: name, password: password, privateKey: restoreWallet.privateKey!);
97 - case WalletType.solana:
98 - return solana!.createSolanaRestoreWalletFromPrivateKey(
99 - name: name, password: password, privateKey: restoreWallet.privateKey!);
100 - case WalletType.tron:
101 - return tron!.createTronRestoreWalletFromPrivateKey(
102 - name: name, password: password, privateKey: restoreWallet.privateKey!);
103 - default:
104 - throw Exception('Unexpected type: ${restoreWallet.type.toString()}');
105 - }
106 - case WalletRestoreMode.seed:
107 - switch (restoreWallet.type) {
108 - case WalletType.monero:
109 - return monero!.createMoneroRestoreWalletFromSeedCredentials(
110 - name: name,
111 - height: restoreWallet.height ?? 0,
112 - mnemonic: restoreWallet.mnemonicSeed ?? '',
113 - password: password,
114 - passphrase: restoreWallet.passphrase ?? '',
115 - );
116 - case WalletType.bitcoin:
117 - case WalletType.litecoin:
118 -
119 - final derivationInfoList = await getDerivationInfoFromQRCredentials(restoreWallet);
120 - DerivationInfo derivationInfo;
121 - if (derivationInfoList.isEmpty) {
122 - derivationInfo = getDefaultCreateDerivation()!;
123 - } else {
124 - derivationInfo = derivationInfoList.first;
125 - }
126 - return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
127 - name: name,
128 - mnemonic: restoreWallet.mnemonicSeed ?? '',
129 - password: password,
130 - passphrase: restoreWallet.passphrase,
131 - derivationType: derivationInfo.derivationType!,
132 - derivationPath: derivationInfo.derivationPath!,
133 - );
134 - case WalletType.bitcoinCash:
135 - return bitcoinCash!.createBitcoinCashRestoreWalletFromSeedCredentials(
136 - name: name,
137 - mnemonic: restoreWallet.mnemonicSeed ?? '',
138 - password: password,
139 - passphrase: restoreWallet.passphrase,
140 - );
141 - case WalletType.ethereum:
142 - return ethereum!.createEthereumRestoreWalletFromSeedCredentials(
143 - name: name,
144 - mnemonic: restoreWallet.mnemonicSeed ?? '',
145 - password: password,
146 - passphrase: restoreWallet.passphrase,
147 - );
148 - case WalletType.nano:
149 - final derivationInfo =
150 - (await getDerivationInfoFromQRCredentials(restoreWallet)).first;
151 - return nano!.createNanoRestoreWalletFromSeedCredentials(
152 - name: name,
153 - mnemonic: restoreWallet.mnemonicSeed ?? '',
154 - password: password,
155 - derivationType: derivationInfo.derivationType!,
156 - passphrase: restoreWallet.passphrase,
157 - );
158 - case WalletType.polygon:
159 - return polygon!.createPolygonRestoreWalletFromSeedCredentials(
160 - name: name,
161 - mnemonic: restoreWallet.mnemonicSeed ?? '',
162 - password: password,
163 - passphrase: restoreWallet.passphrase,
164 - );
165 - case WalletType.solana:
166 - return solana!.createSolanaRestoreWalletFromSeedCredentials(
167 - name: name,
168 - mnemonic: restoreWallet.mnemonicSeed ?? '',
169 - password: password,
170 - passphrase: restoreWallet.passphrase,
171 - );
172 - case WalletType.tron:
173 - return tron!.createTronRestoreWalletFromSeedCredentials(
174 - name: name,
175 - mnemonic: restoreWallet.mnemonicSeed ?? '',
176 - password: password,
177 - passphrase: restoreWallet.passphrase,
178 - );
179 - case WalletType.wownero:
180 - return wownero!.createWowneroRestoreWalletFromSeedCredentials(
181 - name: name,
182 - height: restoreWallet.height ?? 0,
183 - mnemonic: restoreWallet.mnemonicSeed ?? '',
184 - password: password,
185 - passphrase: restoreWallet.passphrase ?? '',
186 - );
187 - case WalletType.zano:
188 - return zano!.createZanoRestoreWalletFromSeedCredentials(
189 - name: name,
190 - password: password,
191 - height: height,
192 - mnemonic: restoreWallet.mnemonicSeed ?? '',
193 - passphrase: restoreWallet.passphrase ?? '',
194 - );
195 - default:
196 - throw Exception('Unexpected type: ${type.toString()}');
197 - }
198 - default:
199 - throw Exception('Unexpected type: ${type.toString()}');
200 - }
201 - }
202 -
203 - @override
204 - Future<WalletBase> processFromRestoredWallet(WalletCredentials credentials,
205 - RestoredWallet restoreWallet) async {
206 - try {
207 - switch (restoreWallet.restoreMode) {
208 - case WalletRestoreMode.keys:
209 - return walletCreationService.restoreFromKeys(credentials);
210 - case WalletRestoreMode.seed:
211 - return walletCreationService.restoreFromSeed(credentials);
212 - default:
213 - throw Exception('Unexpected restore mode: ${restoreWallet.restoreMode.toString()}');
214 - }
215 - } catch (e) {
216 - throw Exception('Unexpected restore mode: ${e.toString()}');
217 - }
218 - }
219 -}
lib/view_model/restore/wallet_restore_from_qr_code.dart
+15 -16
@@ -46,8 +46,6 @@ class WalletRestoreFromQRCode {
46 'zano_wallet': WalletType.zano,
47 };
48
49 - static bool _containsAssetSpecifier(String code) => _extractWalletType(code) != null;
50 -
49 static WalletType? _extractWalletType(String code) {
50 final sortedKeys = _walletTypeMap.keys.toList()..sort((a, b) => b.length.compareTo(a.length));
51
@@ -55,15 +53,18 @@ class WalletRestoreFromQRCode {
53
54 if (extracted == null) {
55 // Special case for view-only monero wallet
58 - final codeParsed = json.decode(code);
59 - if (codeParsed["version"] == 0 &&
60 - codeParsed["primaryAddress"] != null &&
61 - codeParsed["privateViewKey"] != null &&
62 - codeParsed["restoreHeight"] != null) {
63 - return WalletType.monero;
56 + try {
57 + final codeParsed = json.decode(code);
58 + if (codeParsed["version"] == 0 &&
59 + codeParsed["primaryAddress"] != null &&
60 + codeParsed["privateViewKey"] != null &&
61 + codeParsed["restoreHeight"] != null) {
62 + return WalletType.monero;
63 + }
64 + } catch (e) {
65 + return null;
66 }
67 }
66 -
68 return _walletTypeMap[extracted];
69 }
70
@@ -93,10 +94,10 @@ class WalletRestoreFromQRCode {
94 if (code == null) throw Exception("Unexpected scan QR code value: aborted");
95 if (code.isEmpty) throw Exception('Unexpected scan QR code value: value is empty');
96
96 - WalletType? walletType;
97 String formattedUri = '';
98 + WalletType? walletType = _extractWalletType(code);
99
99 - if (!_containsAssetSpecifier(code)) {
100 + if (walletType == null) {
101 await _specifyWalletAssets(context, "Can't determine wallet type, please pick it manually");
102 walletType =
103 await Navigator.pushNamed(context, Routes.restoreWalletTypeFromQR) as WalletType?;
@@ -108,7 +109,6 @@ class WalletRestoreFromQRCode {
109 ? '$walletType:?seed=$seedPhrase'
110 : throw Exception('Failed to determine valid seed phrase');
111 } else {
111 - walletType = _extractWalletType(code);
112 final index = code.indexOf(':');
113 final query = code.substring(index + 1).replaceAll('?', '&');
114 formattedUri = '$walletType:?$query';
@@ -118,12 +118,11 @@ class WalletRestoreFromQRCode {
118 Map<String, dynamic> queryParameters = {...uri.queryParameters};
119
120 if (queryParameters['seed'] == null) {
121 - queryParameters['seed'] = _extractSeedPhraseFromUrl(code, walletType!);
121 + queryParameters['seed'] = _extractSeedPhraseFromUrl(code, walletType);
122 }
123 if (queryParameters['address'] == null) {
124 - try {
125 - queryParameters['address'] = _extractAddressFromUrl(code, walletType!);
126 - } catch (_) {}
124 + queryParameters['address'] = _extractAddressFromUrl(code, walletType);
125 +
126 }
127
128 Map<String, dynamic> credentials = {'type': walletType, ...queryParameters, 'raw_qr': code};
lib/view_model/wallet_creation_vm.dart
+4 -16
@@ -67,8 +67,8 @@ abstract class WalletCreationVMBase with Store {
67
68 bool typeExists(WalletType type) => walletCreationService.typeExists(type);
69
70 - Future<void> create({dynamic options, RestoredWallet? restoreWallet}) async {
71 - final type = restoreWallet?.type ?? this.type;
70 + Future<void> create({dynamic options}) async {
71 + final type = this.type;
72 try {
73 state = IsExecutingState();
74 if (name.isEmpty) {
@@ -87,9 +87,7 @@ abstract class WalletCreationVMBase with Store {
87 final dirPath = await pathForWalletDir(name: name, type: type);
88 final path = await pathForWallet(name: name, type: type);
89
90 - final credentials = restoreWallet != null
91 - ? await getWalletCredentialsFromQRCredentials(restoreWallet)
92 - : getCredentials(options);
90 + final credentials = getCredentials(options);
91
92 final walletInfo = WalletInfo.external(
93 id: WalletBase.idFor(name, type),
@@ -107,9 +105,7 @@ abstract class WalletCreationVMBase with Store {
105 );
106
107 credentials.walletInfo = walletInfo;
110 - final wallet = restoreWallet != null
111 - ? await processFromRestoredWallet(credentials, restoreWallet)
112 - : await process(credentials);
108 + final wallet = await process(credentials);
109
110 final isNonSeedWallet = isRecovery ? wallet.seed == null : false;
111 walletInfo.isNonSeedWallet = isNonSeedWallet;
@@ -234,14 +230,6 @@ abstract class WalletCreationVMBase with Store {
230
231 Future<WalletBase> process(WalletCredentials credentials) => throw UnimplementedError();
232
237 - Future<WalletCredentials> getWalletCredentialsFromQRCredentials(
238 - RestoredWallet restoreWallet) async =>
239 - throw UnimplementedError();
240 -
241 - Future<WalletBase> processFromRestoredWallet(
242 - WalletCredentials credentials, RestoredWallet restoreWallet) =>
243 - throw UnimplementedError();
244 -
233 @action
234 void toggleUseTestnet(bool? value) {
235 _useTestnet = value ?? !_useTestnet;
lib/view_model/wallet_restore_view_model.dart
+9 -2
@@ -12,6 +12,7 @@ import 'package:cake_wallet/solana/solana.dart';
12 import 'package:cake_wallet/store/app_store.dart';
13 import 'package:cake_wallet/tron/tron.dart';
14 import 'package:cake_wallet/view_model/restore/restore_mode.dart';
15 +import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
16 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
17 import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
18 import 'package:cake_wallet/wownero/wownero.dart';
@@ -30,7 +31,7 @@ class WalletRestoreViewModel = WalletRestoreViewModelBase with _$WalletRestoreVi
31 abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
32 WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
33 Box<WalletInfo> walletInfoSource, SeedSettingsViewModel seedSettingsViewModel,
33 - {required WalletType type})
34 + {required WalletType type, this.restoredWallet})
35 : hasSeedLanguageSelector =
36 type == WalletType.monero || type == WalletType.haven || type == WalletType.wownero,
37 hasBlockchainHeightLanguageSelector =
@@ -42,7 +43,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
43 type == WalletType.solana ||
44 type == WalletType.tron,
45 isButtonEnabled = false,
45 - mode = WalletRestoreMode.seed,
46 + mode = restoredWallet?.restoreMode ?? WalletRestoreMode.seed,
47 super(appStore, walletInfoSource, walletCreationService, seedSettingsViewModel,
48 type: type, isRecovery: true) {
49 switch (type) {
@@ -68,6 +69,11 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
69 break;
70 }
71 walletCreationService.changeWalletType(type: type);
72 + if (restoredWallet != null) {
73 + if(restoredWallet!.restoreMode == WalletRestoreMode.seed) {
74 + seedSettingsViewModel.setPassphrase(restoredWallet!.passphrase);
75 + }
76 + }
77 }
78
79 static const moneroSeedMnemonicLength = 25;
@@ -76,6 +82,7 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
82 final bool hasSeedLanguageSelector;
83 final bool hasBlockchainHeightLanguageSelector;
84 final bool hasRestoreFromPrivateKey;
85 + final RestoredWallet? restoredWallet;
86
87 @observable
88 WalletRestoreMode mode;