dev
dart 651 lines 26 KB
Raw
1 import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/generated/i18n.dart';
3 import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/src/screens/base_page.dart';
5 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
6 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
7 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
8 import 'package:cake_wallet/src/widgets/bottom_sheet/add_passphrase_bottom_sheet_widget.dart';
9 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
10 import 'package:cake_wallet/src/widgets/primary_button.dart';
11 import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
12 import 'package:cake_wallet/utils/show_pop_up.dart';
13 import 'package:cake_wallet/view_model/restore/restore_mode.dart';
14 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
15 import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
16 import 'package:cw_core/wallet_info.dart';
17 import 'package:cw_core/wallet_type.dart';
18 import 'package:flutter/material.dart';
19 import 'package:flutter_mobx/flutter_mobx.dart';
20 import 'package:keyboard_actions/keyboard_actions.dart';
21 import 'package:mobx/mobx.dart';
22
23 class WalletRestorePage extends BasePage {
24 WalletRestorePage(this.walletRestoreViewModel, this.seedSettingsViewModel)
25 : walletRestoreFromSeedFormKey = GlobalKey<WalletRestoreFromSeedFormState>(),
26 walletRestoreFromKeysFormKey = GlobalKey<WalletRestoreFromKeysFormState>(),
27 _blockHeightFocusNode = FocusNode();
28
29 final WalletRestoreViewModel walletRestoreViewModel;
30 final SeedSettingsViewModel seedSettingsViewModel;
31 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
32 final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
33 final FocusNode _blockHeightFocusNode;
34
35 bool _formProcessing = false;
36
37 @override
38 Widget middle(BuildContext context) => Observer(
39 builder: (_) => Text(
40 walletRestoreViewModel.mode == WalletRestoreMode.seed
41 ? S.current.restore_title_from_seed
42 : S.current.restore_title_from_keys,
43 style: Theme.of(context).textTheme.titleMedium?.copyWith(
44 fontSize: 18,
45 fontWeight: FontWeight.w600,
46 ),
47 ),
48 );
49
50 // DerivationType derivationType = DerivationType.unknown;
51 // String? derivationPath = null;
52 DerivationInfo? derivationInfo;
53
54 @override
55 Function(BuildContext)? get popWidget => (context) => seedSettingsViewModel.setPassphrase(null);
56
57 @override
58 Function(BuildContext)? get pushToNextWidget => (context) {
59 FocusScopeNode currentFocus = FocusScope.of(context);
60 if (!currentFocus.hasPrimaryFocus) {
61 currentFocus.focusedChild?.unfocus();
62 }
63 };
64
65 @override
66 Widget body(BuildContext context) {
67 return KeyboardActions(
68 config: KeyboardActionsConfig(
69 keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
70 keyboardBarColor: Theme.of(context).colorScheme.surface,
71 nextFocus: false,
72 actions: [
73 KeyboardActionsItem(
74 focusNode: _blockHeightFocusNode,
75 toolbarButtons: [(_) => KeyboardDoneButton()],
76 )
77 ],
78 ),
79 child: Container(
80 height: 0,
81 color: Theme.of(context).colorScheme.surface,
82 child: Center(
83 child: Column(
84 children: [
85 Expanded(
86 child: _WalletRestorePageBody(
87 walletRestoreViewModel: walletRestoreViewModel,
88 seedSettingsViewModel: seedSettingsViewModel,
89 walletRestoreFromSeedFormKey: walletRestoreFromSeedFormKey,
90 walletRestoreFromKeysFormKey: walletRestoreFromKeysFormKey,
91 blockHeightFocusNode: _blockHeightFocusNode,
92 derivationInfo: derivationInfo,
93 onDerivationInfoChanged: (info) => derivationInfo = info,
94 ),
95 ),
96 Padding(
97 padding: EdgeInsets.symmetric(horizontal: 16),
98 child: Column(
99 children: [
100 Observer(
101 builder: (context) {
102 return walletRestoreViewModel.passphraseAvailable
103 ? StandardCheckbox(
104 captionColor: Theme.of(context).colorScheme.onSecondaryContainer,
105 value: walletRestoreViewModel.hasPassphrase,
106 caption: S.of(context).wallet_has_passphrase,
107 onChanged: (value) {
108 walletRestoreViewModel.hasPassphrase = value;
109 },
110 )
111 : SizedBox.shrink();
112 },
113 ),
114 SizedBox(height: 16),
115 PrimaryButton(
116 key: ValueKey('wallet_restore_advanced_settings_button_key'),
117 onPressed: () {
118 Navigator.of(context).pushNamed(
119 Routes.advancedPrivacySettings,
120 arguments: {
121 'isFromRestore': true,
122 'type': walletRestoreViewModel.type,
123 'useTestnet': walletRestoreViewModel.useTestnet,
124 'toggleTestnet': walletRestoreViewModel.toggleUseTestnet,
125 'zcashNetwork': walletRestoreViewModel.zcashNetwork,
126 'setZcashNetwork': walletRestoreViewModel.setZcashNetwork,
127 },
128 );
129 },
130 text: S.of(context).advanced_settings,
131 color: Theme.of(context).colorScheme.surfaceContainer,
132 textColor: Theme.of(context).colorScheme.onSecondaryContainer,
133 ),
134 SizedBox(height: 8),
135 Observer(
136 builder: (context) {
137 return LoadingPrimaryButton(
138 key: ValueKey('wallet_restore_seed_or_key_restore_button_key'),
139 onPressed: () async {
140 if (walletRestoreViewModel.hasPassphrase) {
141 await showModalBottomSheet<void>(
142 context: context,
143 isDismissible: false,
144 isScrollControlled: true,
145 builder: (BuildContext bottomSheetContext) {
146 return Padding(
147 padding: EdgeInsets.only(
148 bottom: MediaQuery.of(bottomSheetContext).viewInsets.bottom,
149 ),
150 child: AddPassphraseBottomSheet(
151 titleText: S.of(bottomSheetContext).add_passphrase,
152 onRestoreButtonPressed: (passphrase) async {
153 await _onPassphraseBottomSheetRestoreButtonPressed(
154 passphrase,
155 context,
156 );
157 },
158 ),
159 );
160 },
161 );
162 } else {
163 await _confirmForm(context);
164 }
165 },
166 text: walletRestoreViewModel.hasPassphrase
167 ? S.of(context).add_passphrase
168 : S.of(context).restore_recover,
169 color: Theme.of(context).colorScheme.primary,
170 textColor: Theme.of(context).colorScheme.onPrimary,
171 isLoading: walletRestoreViewModel.state is IsExecutingState,
172 isDisabled: !walletRestoreViewModel.isButtonEnabled,
173 );
174 },
175 ),
176 const SizedBox(height: 24),
177 ],
178 ),
179 )
180 ],
181 ),
182 ),
183 ),
184 );
185 }
186
187 Future<void> _onPassphraseBottomSheetRestoreButtonPressed(
188 String passphrase,
189 BuildContext context,
190 ) async {
191 walletRestoreViewModel.seedSettingsViewModel.setPassphrase(passphrase);
192 await _confirmForm(context);
193 }
194
195 Map<String, dynamic> _credentials() {
196 final credentials = <String, dynamic>{};
197
198 if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
199 credentials['seed'] =
200 walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
201
202 if (walletRestoreViewModel.hasBlockchainHeightSelector) {
203 credentials['height'] =
204 walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState?.height ??
205 -1;
206 }
207
208 credentials['passphrase'] = seedSettingsViewModel.passphrase;
209
210 credentials['name'] =
211 walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
212 } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
213 if (walletRestoreViewModel.hasRestoreFromPrivateKey) {
214 credentials['private_key'] =
215 walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
216 credentials['name'] =
217 walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
218 if (walletRestoreViewModel.hasBlockchainHeightSelector) {
219 credentials['height'] =
220 walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState?.height;
221 }
222 } else {
223 credentials['name'] =
224 walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
225 credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
226 if (!walletRestoreViewModel.onlyViewKeyRestore) {
227 credentials['address'] =
228 walletRestoreFromKeysFormKey.currentState!.addressController.text;
229 credentials['spendKey'] =
230 walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
231 credentials['scanSecret'] =
232 walletRestoreFromKeysFormKey.currentState!.scanSecretController.text;
233 credentials['spendPubkey'] =
234 walletRestoreFromKeysFormKey.currentState!.spendPubkeyController.text;
235 credentials['height'] =
236 walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState?.height;
237 }
238 }
239 }
240
241 credentials['derivationInfo'] = this.derivationInfo;
242 credentials['walletType'] = walletRestoreViewModel.type;
243 return credentials;
244 }
245
246 Future<void> _confirmForm(BuildContext context) async {
247 if (_formProcessing) return;
248 _formProcessing = true;
249 try {
250 // Dismissing all visible keyboard to provide context for navigation
251 FocusManager.instance.primaryFocus?.unfocus();
252
253 late BuildContext? formContext;
254 late GlobalKey<FormState>? formKey;
255 late String name;
256 if (walletRestoreViewModel.mode == WalletRestoreMode.seed) {
257 formContext = walletRestoreFromSeedFormKey.currentContext;
258 formKey = walletRestoreFromSeedFormKey.currentState!.formKey;
259 name = walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.value.text;
260 } else if (walletRestoreViewModel.mode == WalletRestoreMode.keys) {
261 formContext = walletRestoreFromKeysFormKey.currentContext;
262 formKey = walletRestoreFromKeysFormKey.currentState!.formKey;
263 name = walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.value.text;
264 }
265
266 if (!formKey!.currentState!.validate()) {
267 _formProcessing = false;
268 return;
269 }
270
271 if (await walletRestoreViewModel.nameExists(name)) {
272 showNameExistsAlert(formContext!);
273 _formProcessing = false;
274 return;
275 }
276
277 walletRestoreViewModel.state = IsExecutingState();
278
279 DerivationInfo? dInfo;
280
281 // get info about the different derivations:
282 List<DerivationInfo> derivations =
283 await walletRestoreViewModel.getDerivationInfo(_credentials());
284
285 int derivationsWithHistory = 0;
286 int derivationWithHistoryIndex = 0;
287 final List<String> derivationPathsWithHistory = [];
288
289 for (int i = 0; i < derivations.length; i++) {
290 if (derivations[i].transactionsCount > 0) {
291 derivationsWithHistory++;
292 derivationWithHistoryIndex = i;
293 final derivationPath = derivations[i].derivationPath;
294 if (derivationPath != null && derivationPath.isNotEmpty) {
295 derivationPathsWithHistory.add(derivationPath);
296 }
297 }
298 }
299
300 final scanDerivationPaths = {
301 "m/84'/0'/0'",
302 "m/86'/0'/0'",
303 "m/44'/0'/0'",
304 "m/49'/0'/0'",
305 };
306
307 final shouldSkipChooseDerivationScreen = derivationPathsWithHistory.isNotEmpty &&
308 derivationPathsWithHistory.every(scanDerivationPaths.contains);
309
310 if (derivationsWithHistory > 1 && !shouldSkipChooseDerivationScreen) {
311 dInfo = await Navigator.of(context).pushNamed(
312 Routes.restoreWalletChooseDerivation,
313 arguments: derivations,
314 ) as DerivationInfo?;
315 } else if (derivationsWithHistory == 1) {
316 dInfo = derivations[derivationWithHistoryIndex];
317 } else if (derivations.length == 1) {
318 // we only return 1 derivation if we're pretty sure we know which one to use:
319 dInfo = derivations.first;
320 } else {
321 // if we have multiple possible derivations, and none (or multiple) have histories
322 // we just default to the most common one:
323 dInfo = walletRestoreViewModel.getCommonRestoreDerivation();
324 }
325
326 this.derivationInfo = dInfo;
327
328 await walletRestoreViewModel.create(options: _credentials());
329 seedSettingsViewModel.setPassphrase(null);
330 } catch (e) {
331 _formProcessing = false;
332 rethrow;
333 }
334 _formProcessing = false;
335 }
336
337 Future<void> showNameExistsAlert(BuildContext context) {
338 return showPopUp<void>(
339 context: context,
340 builder: (_) {
341 return AlertWithOneAction(
342 alertTitle: '',
343 alertContent: S.of(context).wallet_name_exists,
344 buttonText: S.of(context).ok,
345 buttonAction: () => Navigator.of(context).pop());
346 });
347 }
348 }
349
350 class _WalletRestorePageBody extends StatefulWidget {
351 const _WalletRestorePageBody({
352 Key? key,
353 required this.walletRestoreViewModel,
354 required this.seedSettingsViewModel,
355 required this.walletRestoreFromSeedFormKey,
356 required this.walletRestoreFromKeysFormKey,
357 required this.blockHeightFocusNode,
358 required this.derivationInfo,
359 required this.onDerivationInfoChanged,
360 }) : super(key: key);
361
362 final WalletRestoreViewModel walletRestoreViewModel;
363 final SeedSettingsViewModel seedSettingsViewModel;
364 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
365 final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
366 final FocusNode blockHeightFocusNode;
367 final DerivationInfo? derivationInfo;
368 final void Function(DerivationInfo?) onDerivationInfoChanged;
369
370 @override
371 State<_WalletRestorePageBody> createState() => _WalletRestorePageBodyState(
372 walletRestoreViewModel: walletRestoreViewModel,
373 seedSettingsViewModel: seedSettingsViewModel,
374 walletRestoreFromSeedFormKey: walletRestoreFromSeedFormKey,
375 walletRestoreFromKeysFormKey: walletRestoreFromKeysFormKey,
376 blockHeightFocusNode: blockHeightFocusNode,
377 derivationInfo: derivationInfo);
378 }
379
380 class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
381 with SingleTickerProviderStateMixin {
382 _WalletRestorePageBodyState(
383 {required this.walletRestoreViewModel,
384 required this.seedSettingsViewModel,
385 required this.walletRestoreFromSeedFormKey,
386 required this.walletRestoreFromKeysFormKey,
387 required this.blockHeightFocusNode,
388 required this.derivationInfo});
389
390 final WalletRestoreViewModel walletRestoreViewModel;
391 final SeedSettingsViewModel seedSettingsViewModel;
392 final GlobalKey<WalletRestoreFromSeedFormState> walletRestoreFromSeedFormKey;
393 final GlobalKey<WalletRestoreFromKeysFormState> walletRestoreFromKeysFormKey;
394 final FocusNode blockHeightFocusNode;
395 DerivationInfo? derivationInfo;
396
397 late TabController _tabController;
398
399 late bool _hasKeysTab;
400
401 @override
402 void initState() {
403 super.initState();
404
405 _hasKeysTab = widget.walletRestoreViewModel.availableModes.contains(WalletRestoreMode.keys);
406 final tabCount = _hasKeysTab ? 2 : 1;
407
408 final initialIndex = walletRestoreViewModel.mode == WalletRestoreMode.seed
409 ? 0
410 : _hasKeysTab
411 ? 1
412 : 0;
413
414 _tabController = TabController(length: tabCount, vsync: this, initialIndex: initialIndex);
415
416 _tabController.addListener(() {
417 if (!_tabController.indexIsChanging) {
418 widget.walletRestoreViewModel.mode =
419 _tabController.index == 0 ? WalletRestoreMode.seed : WalletRestoreMode.keys;
420 }
421 });
422
423 reaction<WalletRestoreMode>(
424 (_) => widget.walletRestoreViewModel.mode,
425 (mode) {
426 final index = mode == WalletRestoreMode.seed ? 0 : 1;
427 if (_tabController.index != index) {
428 _tabController.animateTo(index);
429 }
430 },
431 );
432
433 reaction((_) => walletRestoreViewModel.state, (ExecutionState state) {
434 if (state is FailureState) {
435 WidgetsBinding.instance.addPostFrameCallback((_) {
436 showPopUp<void>(
437 context: context,
438 builder: (_) {
439 return AlertWithOneAction(
440 alertTitle: S.current.new_wallet,
441 alertContent: state.error,
442 buttonText: S.of(context).ok,
443 buttonAction: () => Navigator.of(context).pop());
444 });
445 });
446 }
447 });
448
449 reaction((_) => walletRestoreViewModel.mode, (WalletRestoreMode mode) {
450 walletRestoreViewModel.isButtonEnabled = false;
451 walletRestoreViewModel.walletPassword = null;
452 walletRestoreViewModel.repeatedWalletPassword = null;
453
454 walletRestoreFromSeedFormKey
455 .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
456 walletRestoreFromSeedFormKey
457 .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
458 walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text = '';
459
460 walletRestoreFromKeysFormKey
461 .currentState!.blockchainHeightKey.currentState!.restoreHeightController.text = '';
462 walletRestoreFromKeysFormKey
463 .currentState!.blockchainHeightKey.currentState!.dateController.text = '';
464 walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text = '';
465 });
466 }
467
468 @override
469 void dispose() {
470 _tabController.dispose();
471 super.dispose();
472 }
473
474 @override
475 Widget build(BuildContext context) {
476 return Container(
477 child: Column(
478 children: [
479 Padding(
480 padding: const EdgeInsets.symmetric(horizontal: 22),
481 child: TabBar(
482 controller: _tabController,
483 splashFactory: NoSplash.splashFactory,
484 indicatorSize: TabBarIndicatorSize.label,
485 isScrollable: true,
486 labelStyle: Theme.of(context).textTheme.labelLarge?.copyWith(
487 fontWeight: FontWeight.w600,
488 fontSize: 16,
489 color: Theme.of(context).colorScheme.primary,
490 ),
491 unselectedLabelStyle: Theme.of(context).textTheme.labelLarge?.copyWith(
492 fontWeight: FontWeight.w600,
493 fontSize: 16,
494 color: Theme.of(context).colorScheme.primary.withOpacity(0.5),
495 ),
496 labelColor: Theme.of(context).colorScheme.primary,
497 indicatorColor: Theme.of(context).colorScheme.primary,
498 indicatorPadding: EdgeInsets.zero,
499 labelPadding: const EdgeInsets.only(right: 24),
500 tabAlignment: TabAlignment.start,
501 dividerColor: Colors.transparent,
502 padding: EdgeInsets.zero,
503 tabs: [
504 Tab(text: S.of(context).widgets_seed),
505 if (_hasKeysTab) Tab(text: S.of(context).keys),
506 ],
507 ),
508 ),
509 const SizedBox(height: 12),
510 Expanded(
511 child: TabBarView(
512 controller: _tabController,
513 children: [
514 SingleChildScrollView(
515 child: _buildWalletRestoreFromSeedTab(),
516 ),
517 if (_hasKeysTab)
518 SingleChildScrollView(
519 child: _buildWalletRestoreFromKeysTab(),
520 ),
521 ],
522 ),
523 ),
524 ],
525 ),
526 );
527 }
528
529 WalletRestoreFromKeysForm _buildWalletRestoreFromKeysTab() {
530 return WalletRestoreFromKeysForm(
531 key: widget.walletRestoreFromKeysFormKey,
532 restoredWallet: walletRestoreViewModel.restoredWallet,
533 walletRestoreViewModel: widget.walletRestoreViewModel,
534 displayPrivateKeyField: widget.walletRestoreViewModel.hasRestoreFromPrivateKey,
535 displayWalletPassword: widget.walletRestoreViewModel.hasWalletPassword,
536 onPrivateKeyChange: (String seed) {
537 // For nano/banano, set button state if valid seed key
538 if (widget.walletRestoreViewModel.type == WalletType.nano ||
539 widget.walletRestoreViewModel.type == WalletType.banano) {
540 widget.walletRestoreViewModel.isButtonEnabled = _isValidSeedKey();
541 }
542 },
543 onViewKeyEntered: (bool entered) {
544 if (widget.walletRestoreViewModel.onlyViewKeyRestore ||
545 walletRestoreViewModel.type == WalletType.litecoin) {
546 walletRestoreViewModel.isButtonEnabled = entered;
547 }
548 },
549 onPasswordChange: (String password) =>
550 widget.walletRestoreViewModel.walletPassword = password,
551 onRepeatedPasswordChange: (String repeatedPassword) =>
552 widget.walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
553 onHeightOrDateEntered: (value) => widget.walletRestoreViewModel.isButtonEnabled = value,
554 );
555 }
556
557 WalletRestoreFromSeedForm _buildWalletRestoreFromSeedTab() {
558 return WalletRestoreFromSeedForm(
559 key: widget.walletRestoreFromSeedFormKey,
560 restoredWallet: walletRestoreViewModel.restoredWallet,
561 seedSettingsViewModel: widget.seedSettingsViewModel,
562 displayBlockHeightSelector: widget.walletRestoreViewModel.hasBlockchainHeightSelector,
563 displayLanguageSelector: widget.walletRestoreViewModel.hasSeedLanguageSelector,
564 type: widget.walletRestoreViewModel.type,
565 blockHeightFocusNode: widget.blockHeightFocusNode,
566 onHeightOrDateEntered: (value) {
567 // set button state
568 if (_isValidSeed()) {
569 widget.walletRestoreViewModel.isButtonEnabled = value;
570 }
571 },
572 onSeedChange: (String seed) {
573 final isPolyseed = widget.walletRestoreViewModel.isPolyseed(seed);
574 _validateOnChange(isPolyseed: isPolyseed);
575 },
576 onLanguageChange: (String language) {
577 final isPolyseed = language.startsWith("POLYSEED_");
578 _validateOnChange(isPolyseed: isPolyseed);
579 },
580 displayWalletPassword: widget.walletRestoreViewModel.hasWalletPassword,
581 onPasswordChange: (String password) =>
582 widget.walletRestoreViewModel.walletPassword = password,
583 onRepeatedPasswordChange: (String repeatedPassword) =>
584 widget.walletRestoreViewModel.repeatedWalletPassword = repeatedPassword,
585 );
586 }
587
588 void _validateOnChange({bool isPolyseed = false}) {
589 if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightSelector) {
590 final hasHeight = walletRestoreFromSeedFormKey
591 .currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
592
593 if (hasHeight == true) {
594 walletRestoreViewModel.isButtonEnabled = _isValidSeed();
595 }
596 } else {
597 walletRestoreViewModel.isButtonEnabled = _isValidSeed();
598 }
599 }
600
601 bool _isValidSeed() {
602 final seedPhrase =
603 walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
604 if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true;
605
606 final seedWords = seedPhrase.split(' ');
607
608 if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
609 if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
610
611 if (seedWords.length == 12 && walletRestoreViewModel.type == WalletType.monero) {
612 return walletRestoreFromSeedFormKey.currentState?.blockchainHeightKey.currentState
613 ?.restoreHeightController.text.isNotEmpty ==
614 true;
615 }
616
617 if ([WalletType.monero, WalletType.wownero, WalletType.haven]
618 .contains(walletRestoreViewModel.type) &&
619 seedWords.length == WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
620 return true;
621 }
622
623 // bip39:
624 final validBip39SeedLengths = [12, 18, 24];
625 final nonBip39WalletTypes = [WalletType.wownero, WalletType.haven, WalletType.decred];
626 // if it's a bip39 wallet and the length is not valid return false
627 if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
628 !(validBip39SeedLengths.contains(seedWords.length))) {
629 return false;
630 }
631
632 if ((walletRestoreViewModel.type == WalletType.decred) &&
633 ![12, 15, 24].contains(seedWords.length)) {
634 return false;
635 }
636
637 final words =
638 walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
639 return seedWords.toSet().difference(words).toSet().isEmpty;
640 }
641
642 bool _isValidSeedKey() {
643 final seedKey = walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
644
645 if (seedKey.length != 64 && seedKey.length != 128) {
646 return false;
647 }
648
649 return true;
650 }
651 }