Add restore from private key to Ethereum (#1055)

* Add restore from private key to Ethereum Add restore from QR code for Ethereum in both seeds/keys * Add node network issue to ignored errors [skip ci]

Omar Hatem committed Aug 23, 2023 at 15:33 UTC 3577730de8cd84cec50f0b1722615218f90b1a82
24 files changed +250 -433
cw_core/lib/wallet_base.dart
+3 -1
@@ -42,7 +42,9 @@ abstract class WalletBase<
42
43 set syncStatus(SyncStatus status);
44
45 - String get seed;
45 + String? get seed;
46 +
47 + String? get privateKey => null;
48
49 Object get keys;
50
cw_ethereum/lib/ethereum_wallet.dart
+36 -17
@@ -44,12 +44,14 @@ abstract class EthereumWalletBase
44 with Store {
45 EthereumWalletBase({
46 required WalletInfo walletInfo,
47 - required String mnemonic,
47 + String? mnemonic,
48 + String? privateKey,
49 required String password,
50 ERC20Balance? initialBalance,
51 }) : syncStatus = NotConnectedSyncStatus(),
52 _password = password,
53 _mnemonic = mnemonic,
54 + _hexPrivateKey = privateKey,
55 _isTransactionUpdating = false,
56 _client = EthereumClient(),
57 walletAddresses = EthereumWalletAddresses(walletInfo),
@@ -66,12 +68,13 @@ abstract class EthereumWalletBase
68 _sharedPrefs.complete(SharedPreferences.getInstance());
69 }
70
69 - final String _mnemonic;
71 + final String? _mnemonic;
72 + final String? _hexPrivateKey;
73 final String _password;
74
75 late final Box<Erc20Token> erc20TokensBox;
76
74 - late final EthPrivateKey _privateKey;
77 + late final EthPrivateKey _ethPrivateKey;
78
79 late EthereumClient _client;
80
@@ -99,8 +102,12 @@ abstract class EthereumWalletBase
102 erc20TokensBox = await CakeHive.openBox<Erc20Token>(Erc20Token.boxName);
103 await walletAddresses.init();
104 await transactionHistory.init();
102 - _privateKey = await getPrivateKey(_mnemonic, _password);
103 - walletAddresses.address = _privateKey.address.toString();
105 + _ethPrivateKey = await getPrivateKey(
106 + mnemonic: _mnemonic,
107 + privateKey: _hexPrivateKey,
108 + password: _password,
109 + );
110 + walletAddresses.address = _ethPrivateKey.address.toString();
111 await save();
112 }
113
@@ -108,8 +115,7 @@ abstract class EthereumWalletBase
115 int calculateEstimatedFee(TransactionPriority priority, int? amount) {
116 try {
117 if (priority is EthereumTransactionPriority) {
111 - final priorityFee =
112 - EtherAmount.fromUnitAndValue(EtherUnit.gwei, priority.tip).getInWei.toInt();
118 + final priorityFee = EtherAmount.fromInt(EtherUnit.gwei, priority.tip).getInWei.toInt();
119 return (_gasPrice! + priorityFee) * (_estimatedGas ?? 0);
120 }
121
@@ -142,7 +148,7 @@ abstract class EthereumWalletBase
148 throw Exception("Ethereum Node connection failed");
149 }
150
145 - _client.setListeners(_privateKey.address, _onNewTransaction);
151 + _client.setListeners(_ethPrivateKey.address, _onNewTransaction);
152
153 _setTransactionUpdateTimer();
154
@@ -202,7 +208,7 @@ abstract class EthereumWalletBase
208 }
209
210 final pendingEthereumTransaction = await _client.signTransaction(
205 - privateKey: _privateKey,
211 + privateKey: _ethPrivateKey,
212 toAddress: _credentials.outputs.first.isParsedAddress
213 ? _credentials.outputs.first.extractedAddress!
214 : _credentials.outputs.first.address,
@@ -240,7 +246,7 @@ abstract class EthereumWalletBase
246
247 @override
248 Future<Map<String, EthereumTransactionInfo>> fetchTransactions() async {
243 - final address = _privateKey.address.hex;
249 + final address = _ethPrivateKey.address.hex;
250 final transactions = await _client.fetchTransactions(address);
251
252 final List<Future<List<EthereumTransactionModel>>> erc20TokensTransactions = [];
@@ -300,7 +306,10 @@ abstract class EthereumWalletBase
306 }
307
308 @override
303 - String get seed => _mnemonic;
309 + String? get seed => _mnemonic;
310 +
311 + @override
312 + String get privateKey => HEX.encode(_ethPrivateKey.privateKey);
313
314 @action
315 @override
@@ -327,6 +336,7 @@ abstract class EthereumWalletBase
336
337 String toJSON() => json.encode({
338 'mnemonic': _mnemonic,
339 + 'private_key': privateKey,
340 'balance': balance[currency]!.toJSON(),
341 });
342
@@ -338,13 +348,15 @@ abstract class EthereumWalletBase
348 final path = await pathForWallet(name: name, type: walletInfo.type);
349 final jsonSource = await read(path: path, password: password);
350 final data = json.decode(jsonSource) as Map;
341 - final mnemonic = data['mnemonic'] as String;
351 + final mnemonic = data['mnemonic'] as String?;
352 + final privateKey = data['private_key'] as String?;
353 final balance = ERC20Balance.fromJSON(data['balance'] as String) ?? ERC20Balance(BigInt.zero);
354
355 return EthereumWallet(
356 walletInfo: walletInfo,
357 password: password,
358 mnemonic: mnemonic,
359 + privateKey: privateKey,
360 initialBalance: balance,
361 );
362 }
@@ -357,7 +369,7 @@ abstract class EthereumWalletBase
369 }
370
371 Future<ERC20Balance> _fetchEthBalance() async {
360 - final balance = await _client.getBalance(_privateKey.address);
372 + final balance = await _client.getBalance(_ethPrivateKey.address);
373 return ERC20Balance(balance.getInWei);
374 }
375
@@ -366,7 +378,7 @@ abstract class EthereumWalletBase
378 try {
379 if (token.enabled) {
380 balance[token] = await _client.fetchERC20Balances(
369 - _privateKey.address,
381 + _ethPrivateKey.address,
382 token.contractAddress,
383 );
384 } else {
@@ -376,8 +388,15 @@ abstract class EthereumWalletBase
388 }
389 }
390
379 - Future<EthPrivateKey> getPrivateKey(String mnemonic, String password) async {
380 - final seed = bip39.mnemonicToSeed(mnemonic);
391 + Future<EthPrivateKey> getPrivateKey(
392 + {String? mnemonic, String? privateKey, required String password}) async {
393 + assert(mnemonic != null || privateKey != null);
394 +
395 + if (privateKey != null) {
396 + return EthPrivateKey.fromHex(privateKey);
397 + }
398 +
399 + final seed = bip39.mnemonicToSeed(mnemonic!);
400
401 final root = bip32.BIP32.fromSeed(seed);
402
@@ -413,7 +432,7 @@ abstract class EthereumWalletBase
432
433 if (_token.enabled) {
434 balance[_token] = await _client.fetchERC20Balances(
416 - _privateKey.address,
435 + _ethPrivateKey.address,
436 _token.contractAddress,
437 );
438 } else {
cw_ethereum/lib/ethereum_wallet_creation_credentials.dart
+11 -5
@@ -8,16 +8,22 @@ class EthereumNewWalletCredentials extends WalletCredentials {
8
9 class EthereumRestoreWalletFromSeedCredentials extends WalletCredentials {
10 EthereumRestoreWalletFromSeedCredentials(
11 - {required String name, required String password, required this.mnemonic, WalletInfo? walletInfo})
11 + {required String name,
12 + required String password,
13 + required this.mnemonic,
14 + WalletInfo? walletInfo})
15 : super(name: name, password: password, walletInfo: walletInfo);
16
17 final String mnemonic;
18 }
19
17 -class EthereumRestoreWalletFromWIFCredentials extends WalletCredentials {
18 - EthereumRestoreWalletFromWIFCredentials(
19 - {required String name, required String password, required this.wif, WalletInfo? walletInfo})
20 +class EthereumRestoreWalletFromPrivateKey extends WalletCredentials {
21 + EthereumRestoreWalletFromPrivateKey(
22 + {required String name,
23 + required String password,
24 + required this.privateKey,
25 + WalletInfo? walletInfo})
26 : super(name: name, password: password, walletInfo: walletInfo);
27
22 - final String wif;
28 + final String privateKey;
29 }
cw_ethereum/lib/ethereum_wallet_service.dart
+13 -3
@@ -13,7 +13,7 @@ import 'package:bip39/bip39.dart' as bip39;
13 import 'package:collection/collection.dart';
14
15 class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
16 - EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromWIFCredentials> {
16 + EthereumRestoreWalletFromSeedCredentials, EthereumRestoreWalletFromPrivateKey> {
17 EthereumWalletService(this.walletInfoSource);
18
19 final Box<WalletInfo> walletInfoSource;
@@ -66,8 +66,18 @@ class EthereumWalletService extends WalletService<EthereumNewWalletCredentials,
66 }
67
68 @override
69 - Future<EthereumWallet> restoreFromKeys(credentials) {
70 - throw UnimplementedError();
69 + Future<EthereumWallet> restoreFromKeys(EthereumRestoreWalletFromPrivateKey credentials) async {
70 + final wallet = EthereumWallet(
71 + password: credentials.password!,
72 + privateKey: credentials.privateKey,
73 + walletInfo: credentials.walletInfo!,
74 + );
75 +
76 + await wallet.init();
77 + wallet.addInitialTokens();
78 + await wallet.save();
79 +
80 + return wallet;
81 }
82
83 @override
ios/Podfile.lock
+4 -4
@@ -174,12 +174,12 @@ DEPENDENCIES:
174 - in_app_review (from `.symlinks/plugins/in_app_review/ios`)
175 - local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
176 - package_info (from `.symlinks/plugins/package_info/ios`)
177 - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
177 + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
178 - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
179 - platform_device_id (from `.symlinks/plugins/platform_device_id/ios`)
180 - sensitive_clipboard (from `.symlinks/plugins/sensitive_clipboard/ios`)
181 - share_plus (from `.symlinks/plugins/share_plus/ios`)
182 - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
182 + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
183 - uni_links (from `.symlinks/plugins/uni_links/ios`)
184 - UnstoppableDomainsResolution (~> 4.0.0)
185 - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
@@ -236,7 +236,7 @@ EXTERNAL SOURCES:
236 package_info:
237 :path: ".symlinks/plugins/package_info/ios"
238 path_provider_foundation:
239 - :path: ".symlinks/plugins/path_provider_foundation/ios"
239 + :path: ".symlinks/plugins/path_provider_foundation/darwin"
240 permission_handler_apple:
241 :path: ".symlinks/plugins/permission_handler_apple/ios"
242 platform_device_id:
@@ -246,7 +246,7 @@ EXTERNAL SOURCES:
246 share_plus:
247 :path: ".symlinks/plugins/share_plus/ios"
248 shared_preferences_foundation:
249 - :path: ".symlinks/plugins/shared_preferences_foundation/ios"
249 + :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
250 uni_links:
251 :path: ".symlinks/plugins/uni_links/ios"
252 url_launcher_ios:
lib/di.dart
-21
@@ -177,8 +177,6 @@ import 'package:hive/hive.dart';
177 import 'package:mobx/mobx.dart';
178 import 'package:shared_preferences/shared_preferences.dart';
179 import 'package:flutter_secure_storage/flutter_secure_storage.dart';
180 -import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
181 -import 'package:cake_wallet/view_model/wallet_restoration_from_keys_vm.dart';
180 import 'package:cake_wallet/core/wallet_creation_service.dart';
181 import 'package:cake_wallet/store/app_store.dart';
182 import 'package:cw_core/wallet_type.dart';
@@ -320,25 +318,6 @@ Future setup({
318 getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
319 type: type));
320
323 - getIt.registerFactoryParam<WalletRestorationFromSeedVM, List, void>((args, _) {
324 - final type = args.first as WalletType;
325 - final language = args[1] as String;
326 - final mnemonic = args[2] as String;
327 -
328 - return WalletRestorationFromSeedVM(
329 - getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
330 - type: type, language: language, seed: mnemonic);
331 - });
332 -
333 - getIt.registerFactoryParam<WalletRestorationFromKeysVM, List, void>((args, _) {
334 - final type = args.first as WalletType;
335 - final language = args[1] as String;
336 -
337 - return WalletRestorationFromKeysVM(
338 - getIt.get<AppStore>(), getIt.get<WalletCreationService>(param1: type), _walletInfoSource,
339 - type: type, language: language);
340 - });
341 -
321 getIt.registerFactoryParam<WalletRestorationFromQRVM, WalletType, void>((WalletType type, _) {
322 return WalletRestorationFromQRVM(getIt.get<AppStore>(),
323 getIt.get<WalletCreationService>(param1: type), _walletInfoSource, type);
lib/ethereum/cw_ethereum.dart
+8
@@ -22,6 +22,14 @@ class CWEthereum extends Ethereum {
22 }) =>
23 EthereumRestoreWalletFromSeedCredentials(name: name, password: password, mnemonic: mnemonic);
24
25 + @override
26 + WalletCredentials createEthereumRestoreWalletFromPrivateKey({
27 + required String name,
28 + required String privateKey,
29 + required String password,
30 + }) =>
31 + EthereumRestoreWalletFromPrivateKey(name: name, password: password, privateKey: privateKey);
32 +
33 @override
34 String getAddress(WalletBase wallet) => (wallet as EthereumWallet).walletAddresses.address;
35
lib/router.dart
-12
@@ -57,7 +57,6 @@ import 'package:cake_wallet/routes.dart';
57 import 'package:cake_wallet/generated/i18n.dart';
58 import 'package:cake_wallet/di.dart';
59 import 'package:cake_wallet/view_model/wallet_new_vm.dart';
60 -import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
60 import 'package:cake_wallet/exchange/trade.dart';
61 import 'package:cw_core/transaction_info.dart';
62 import 'package:cw_core/wallet_type.dart';
@@ -80,7 +79,6 @@ import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_c
79 import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
80 import 'package:cake_wallet/src/screens/contact/contact_page.dart';
81 import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart';
83 -import 'package:cake_wallet/src/screens/restore/restore_wallet_from_seed_details.dart';
82 import 'package:cake_wallet/src/screens/exchange/exchange_page.dart';
83 import 'package:cake_wallet/src/screens/rescan/rescan_page.dart';
84 import 'package:cake_wallet/src/screens/faq/faq_page.dart';
@@ -398,16 +396,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
396 builder: (_) =>
397 getIt.get<BuyWebViewPage>(param1: args));
398
401 - case Routes.restoreWalletFromSeedDetails:
402 - final args = settings.arguments as List;
403 - final walletRestorationFromSeedVM =
404 - getIt.get<WalletRestorationFromSeedVM>(param1: args);
405 -
406 - return CupertinoPageRoute<void>(
407 - fullscreenDialog: true,
408 - builder: (_) => RestoreWalletFromSeedDetailsPage(
409 - walletRestorationFromSeedVM: walletRestorationFromSeedVM));
410 -
399 case Routes.exchange:
400 return CupertinoPageRoute<void>(
401 fullscreenDialog: true,
lib/routes.dart
-1
@@ -30,7 +30,6 @@ class Routes {
30 static const tradeDetails = '/trade_details';
31 static const exchangeFunds = '/exchange_funds';
32 static const exchangeTrade = '/exchange_trade';
33 - static const restoreWalletFromSeedDetails = '/restore_from_seed_details';
33 static const exchange = '/exchange';
34 static const settings = '/settings';
35 static const desktop_settings_page = '/desktop_settings_page';
lib/src/screens/restore/restore_wallet_from_seed_details.dart deleted
-145
@@ -1,145 +0,0 @@
1 -import 'package:cake_wallet/utils/show_pop_up.dart';
2 -import 'package:mobx/mobx.dart';
3 -import 'package:flutter/material.dart';
4 -import 'package:flutter_mobx/flutter_mobx.dart';
5 -import 'package:cake_wallet/generated/i18n.dart';
6 -import 'package:cake_wallet/core/wallet_name_validator.dart';
7 -import 'package:cake_wallet/core/execution_state.dart';
8 -import 'package:cake_wallet/src/screens/base_page.dart';
9 -import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
10 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
11 -import 'package:cake_wallet/src/widgets/primary_button.dart';
12 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
13 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
14 -import 'package:cake_wallet/view_model/wallet_restoration_from_seed_vm.dart';
15 -
16 -class RestoreWalletFromSeedDetailsPage extends BasePage {
17 - RestoreWalletFromSeedDetailsPage(
18 - {required this.walletRestorationFromSeedVM});
19 -
20 - final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
21 -
22 - @override
23 - String get title => S.current.restore_wallet_restore_description;
24 -
25 - @override
26 - Widget body(BuildContext context) => RestoreFromSeedDetailsForm(
27 - walletRestorationFromSeedVM: walletRestorationFromSeedVM);
28 -}
29 -
30 -class RestoreFromSeedDetailsForm extends StatefulWidget {
31 - RestoreFromSeedDetailsForm({required this.walletRestorationFromSeedVM});
32 -
33 - final WalletRestorationFromSeedVM walletRestorationFromSeedVM;
34 -
35 - @override
36 - _RestoreFromSeedDetailsFormState createState() =>
37 - _RestoreFromSeedDetailsFormState();
38 -}
39 -
40 -class _RestoreFromSeedDetailsFormState
41 - extends State<RestoreFromSeedDetailsForm> {
42 - final _formKey = GlobalKey<FormState>();
43 - final _blockchainHeightKey = GlobalKey<BlockchainHeightState>();
44 - final _nameController = TextEditingController();
45 - ReactionDisposer? _stateReaction;
46 -
47 - @override
48 - void initState() {
49 - _stateReaction = reaction((_) => widget.walletRestorationFromSeedVM.state,
50 - (ExecutionState state) {
51 - if (state is ExecutedSuccessfullyState) {
52 - Navigator.of(context).popUntil((route) => route.isFirst);
53 - }
54 -
55 - if (state is FailureState) {
56 - WidgetsBinding.instance.addPostFrameCallback((_) {
57 - showPopUp<void>(
58 - context: context,
59 - builder: (BuildContext context) {
60 - return AlertWithOneAction(
61 - alertTitle: S.current.restore_title_from_seed,
62 - alertContent: state.error,
63 - buttonText: S.of(context).ok,
64 - buttonAction: () => Navigator.of(context).pop());
65 - });
66 - });
67 - }
68 - });
69 -
70 - _nameController.addListener(
71 - () => widget.walletRestorationFromSeedVM.name = _nameController.text);
72 - super.initState();
73 - }
74 -
75 - @override
76 - void dispose() {
77 - _nameController.dispose();
78 - _stateReaction?.reaction.dispose();
79 - super.dispose();
80 - }
81 -
82 - @override
83 - Widget build(BuildContext context) {
84 - return Container(
85 - padding: EdgeInsets.only(left: 24, right: 24),
86 - child: ScrollableWithBottomSection(
87 - contentPadding: EdgeInsets.only(bottom: 24.0),
88 - content: Form(
89 - key: _formKey,
90 - child: Column(children: <Widget>[
91 - Row(
92 - children: <Widget>[
93 - Flexible(
94 - child: Container(
95 - padding: EdgeInsets.only(top: 20.0),
96 - child: BaseTextFormField(
97 - controller: _nameController,
98 - hintText: S.of(context).restore_wallet_name,
99 - validator: WalletNameValidator(),
100 - ),
101 - ))
102 - ],
103 - ),
104 - if (widget.walletRestorationFromSeedVM.hasRestorationHeight) ... [
105 - BlockchainHeightWidget(
106 - key: _blockchainHeightKey,
107 - onHeightChange: (height) {
108 - widget.walletRestorationFromSeedVM.height = height;
109 - print(height);
110 - }),
111 - Padding(
112 - padding: EdgeInsets.only(left: 40, right: 40, top: 24),
113 - child: Text(
114 - S.of(context).restore_from_date_or_blockheight,
115 - textAlign: TextAlign.center,
116 - style: TextStyle(
117 - fontSize: 12,
118 - fontWeight: FontWeight.normal,
119 - color: Theme.of(context).hintColor
120 - ),
121 - ),
122 - )
123 - ]
124 - ]),
125 - ),
126 - bottomSectionPadding: EdgeInsets.only(bottom: 24),
127 - bottomSection: Observer(builder: (_) {
128 - return LoadingPrimaryButton(
129 - onPressed: () {
130 - if (_formKey.currentState != null && _formKey.currentState!.validate()) {
131 - widget.walletRestorationFromSeedVM.create();
132 - }
133 - },
134 - isLoading:
135 - widget.walletRestorationFromSeedVM.state is IsExecutingState,
136 - text: S.of(context).restore_recover,
137 - color: Theme.of(context).primaryColor,
138 - textColor: Colors.white,
139 - isDisabled: _nameController.text.isNotEmpty,
140 - );
141 - }),
142 - ),
143 - );
144 - }
145 -}
lib/src/screens/restore/wallet_restore_from_keys_form.dart
+91 -41
@@ -1,30 +1,29 @@
1 +import 'package:cake_wallet/src/widgets/address_text_field.dart';
2 import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
3 import 'package:cw_core/wallet_type.dart';
3 -import 'package:flutter/services.dart';
4 -import 'package:flutter/cupertino.dart';
4 import 'package:flutter/material.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
7 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
9 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
10 -import 'package:cake_wallet/generated/i18n.dart';
8 import 'package:cake_wallet/core/wallet_name_validator.dart';
9 import 'package:cake_wallet/entities/generate_name.dart';
10 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
11 +import 'package:flutter/services.dart';
12
13 class WalletRestoreFromKeysFrom extends StatefulWidget {
14 WalletRestoreFromKeysFrom({
15 required this.walletRestoreViewModel,
16 + required this.displayPrivateKeyField,
17 + required this.onHeightOrDateEntered,
18 Key? key,
19 - this.onHeightOrDateEntered,})
20 - : super(key: key);
19 + }) : super(key: key);
20
22 - final Function(bool)? onHeightOrDateEntered;
21 + final Function(bool) onHeightOrDateEntered;
22 final WalletRestoreViewModel walletRestoreViewModel;
23 + final bool displayPrivateKeyField;
24
25 @override
26 - WalletRestoreFromKeysFromState createState() =>
27 - WalletRestoreFromKeysFromState();
26 + WalletRestoreFromKeysFromState createState() => WalletRestoreFromKeysFromState();
27 }
28
29 class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
@@ -35,6 +34,7 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
34 addressController = TextEditingController(),
35 viewKeyController = TextEditingController(),
36 spendKeyController = TextEditingController(),
37 + privateKeyController = TextEditingController(),
38 nameTextEditingController = TextEditingController();
39
40 final GlobalKey<FormState> formKey;
@@ -44,6 +44,18 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
44 final TextEditingController viewKeyController;
45 final TextEditingController spendKeyController;
46 final TextEditingController nameTextEditingController;
47 + final TextEditingController privateKeyController;
48 +
49 + @override
50 + void initState() {
51 + super.initState();
52 +
53 + privateKeyController.addListener(() {
54 + if (privateKeyController.text.isNotEmpty) {
55 + widget.onHeightOrDateEntered(true);
56 + }
57 + });
58 + }
59
60 @override
61 void dispose() {
@@ -51,16 +63,18 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
63 addressController.dispose();
64 viewKeyController.dispose();
65 spendKeyController.dispose();
66 + privateKeyController.dispose();
67 super.dispose();
68 }
69
70 @override
71 Widget build(BuildContext context) {
72 return Container(
60 - padding: EdgeInsets.only(left: 24, right: 24),
61 - child: Form(
62 - key: formKey,
63 - child: Column(children: <Widget>[
73 + padding: EdgeInsets.only(left: 24, right: 24),
74 + child: Form(
75 + key: formKey,
76 + child: Column(
77 + children: <Widget>[
78 Stack(
79 alignment: Alignment.centerRight,
80 children: [
@@ -75,9 +89,8 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
89
90 setState(() {
91 nameTextEditingController.text = rName;
78 - nameTextEditingController.selection =
79 - TextSelection.fromPosition(TextPosition(
80 - offset: nameTextEditingController.text.length));
92 + nameTextEditingController.selection = TextSelection.fromPosition(
93 + TextPosition(offset: nameTextEditingController.text.length));
94 });
95 },
96 icon: Container(
@@ -90,7 +103,8 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
103 height: 34,
104 child: Image.asset(
105 'assets/images/refresh_icon.png',
93 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
106 + color:
107 + Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
108 ),
109 ),
110 ),
@@ -98,29 +112,65 @@ class WalletRestoreFromKeysFromState extends State<WalletRestoreFromKeysFrom> {
112 ],
113 ),
114 Container(height: 20),
101 - BaseTextFormField(
102 - controller: addressController,
103 - keyboardType: TextInputType.multiline,
104 - maxLines: null,
105 - hintText: S.of(context).restore_address),
106 - Container(
107 - padding: EdgeInsets.only(top: 20.0),
108 - child: BaseTextFormField(
109 - controller: viewKeyController,
110 - hintText: S.of(context).restore_view_key_private,
111 - maxLines: null)),
112 - Container(
113 - padding: EdgeInsets.only(top: 20.0),
114 - child: BaseTextFormField(
115 - controller: spendKeyController,
116 - hintText: S.of(context).restore_spend_key_private,
117 - maxLines: null)),
118 - BlockchainHeightWidget(
119 - key: blockchainHeightKey,
120 - hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
121 - onHeightChange: (_) => null,
122 - onHeightOrDateEntered: widget.onHeightOrDateEntered)
123 - ]),
124 - ));
115 + _restoreFromKeysFormFields(),
116 + ],
117 + ),
118 + ),
119 + );
120 + }
121 +
122 + Widget _restoreFromKeysFormFields() {
123 + if (widget.displayPrivateKeyField) {
124 + return AddressTextField(
125 + controller: privateKeyController,
126 + placeholder: S.of(context).private_key,
127 + options: [AddressTextFieldOption.paste],
128 + buttonColor: Theme.of(context).hintColor,
129 + onPushPasteButton: (_) {
130 + _pasteText();
131 + },
132 + );
133 + }
134 +
135 + return Column(
136 + children: [
137 + BaseTextFormField(
138 + controller: addressController,
139 + keyboardType: TextInputType.multiline,
140 + maxLines: null,
141 + hintText: S.of(context).restore_address,
142 + ),
143 + Container(
144 + padding: EdgeInsets.only(top: 20.0),
145 + child: BaseTextFormField(
146 + controller: viewKeyController,
147 + hintText: S.of(context).restore_view_key_private,
148 + maxLines: null,
149 + ),
150 + ),
151 + Container(
152 + padding: EdgeInsets.only(top: 20.0),
153 + child: BaseTextFormField(
154 + controller: spendKeyController,
155 + hintText: S.of(context).restore_spend_key_private,
156 + maxLines: null,
157 + ),
158 + ),
159 + BlockchainHeightWidget(
160 + key: blockchainHeightKey,
161 + hasDatePicker: widget.walletRestoreViewModel.type != WalletType.haven,
162 + onHeightChange: (_) => null,
163 + onHeightOrDateEntered: widget.onHeightOrDateEntered,
164 + ),
165 + ],
166 + );
167 + }
168 +
169 + Future<void> _pasteText() async {
170 + final value = await Clipboard.getData('text/plain');
171 +
172 + if (value?.text?.isNotEmpty ?? false) {
173 + privateKeyController.text = value!.text!;
174 + }
175 }
176 }
lib/src/screens/restore/wallet_restore_page.dart
+19 -14
@@ -1,10 +1,7 @@
1 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
2 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
4 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
3 import 'package:cake_wallet/utils/responsive_layout_util.dart';
4 import 'package:cw_core/wallet_type.dart';
7 -import 'package:flutter/cupertino.dart';
5 import 'package:flutter/material.dart';
6 import 'package:keyboard_actions/keyboard_actions.dart';
7 import 'package:mobx/mobx.dart';
@@ -19,10 +16,6 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.da
16 import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
17 import 'package:cake_wallet/src/widgets/primary_button.dart';
18 import 'package:cake_wallet/utils/show_pop_up.dart';
22 -import 'package:cake_wallet/core/validator.dart';
23 -import 'package:cake_wallet/generated/i18n.dart';
24 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
25 -import 'package:cake_wallet/core/seed_validator.dart';
19 import 'package:cake_wallet/view_model/restore/restore_mode.dart';
20 import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
21
@@ -76,6 +69,7 @@ class WalletRestorePage extends BasePage {
69 _pages.add(WalletRestoreFromKeysFrom(
70 key: walletRestoreFromKeysFormKey,
71 walletRestoreViewModel: walletRestoreViewModel,
72 + displayPrivateKeyField: walletRestoreViewModel.hasRestoreFromPrivateKey,
73 onHeightOrDateEntered: (value) => walletRestoreViewModel.isButtonEnabled = value));
74 break;
75 default:
@@ -193,8 +187,12 @@ class WalletRestorePage extends BasePage {
187 return LoadingPrimaryButton(
188 onPressed: _confirmForm,
189 text: S.of(context).restore_recover,
196 - color: Theme.of(context).extension<WalletListTheme>()!.createNewWalletButtonBackgroundColor,
197 - textColor: Theme.of(context).extension<WalletListTheme>()!.restoreWalletButtonTextColor,
190 + color: Theme.of(context)
191 + .extension<WalletListTheme>()!
192 + .createNewWalletButtonBackgroundColor,
193 + textColor: Theme.of(context)
194 + .extension<WalletListTheme>()!
195 + .restoreWalletButtonTextColor,
196 isLoading: walletRestoreViewModel.state is IsExecutingState,
197 isDisabled: !walletRestoreViewModel.isButtonEnabled,
198 );
@@ -246,11 +244,18 @@ class WalletRestorePage extends BasePage {
244 credentials['name'] =
245 walletRestoreFromSeedFormKey.currentState!.nameTextEditingController.text;
246 } else {
249 - credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
250 - credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
251 - credentials['spendKey'] = walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
252 - credentials['height'] =
253 - walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
247 + if (walletRestoreViewModel.hasRestoreFromPrivateKey) {
248 + credentials['private_key'] =
249 + walletRestoreFromKeysFormKey.currentState!.privateKeyController.text;
250 + } else {
251 + credentials['address'] = walletRestoreFromKeysFormKey.currentState!.addressController.text;
252 + credentials['viewKey'] = walletRestoreFromKeysFormKey.currentState!.viewKeyController.text;
253 + credentials['spendKey'] =
254 + walletRestoreFromKeysFormKey.currentState!.spendKeyController.text;
255 + credentials['height'] =
256 + walletRestoreFromKeysFormKey.currentState!.blockchainHeightKey.currentState!.height;
257 + }
258 +
259 credentials['name'] =
260 walletRestoreFromKeysFormKey.currentState!.nameTextEditingController.text;
261 }
lib/src/widgets/seed_widget.dart
-7
@@ -1,16 +1,9 @@
1 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 import 'package:cw_core/wallet_type.dart';
3 import 'package:cake_wallet/src/widgets/validable_annotated_editable_text.dart';
4 -import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
5 -import 'package:flutter/cupertino.dart';
4 import 'package:flutter/material.dart';
5 import 'package:flutter/services.dart';
8 -import 'package:cake_wallet/palette.dart';
6 import 'package:cake_wallet/core/seed_validator.dart';
10 -import 'package:cake_wallet/src/widgets/primary_button.dart';
11 -import 'package:cake_wallet/entities/mnemonic_item.dart';
12 -import 'package:cake_wallet/generated/i18n.dart';
13 -import 'package:flutter/widgets.dart';
7 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
8
9 class SeedWidget extends StatefulWidget {
lib/utils/exception_handler.dart
+1
@@ -148,6 +148,7 @@ class ExceptionHandler {
148 "CERTIFICATE_VERIFY_FAILED",
149 "Handshake error in client",
150 "Error while launching http",
151 + "OS Error: Network is unreachable",
152 ];
153
154 static Future<void> _addDeviceInfo(File file) async {
lib/view_model/dashboard/dashboard_view_model.dart
+2 -2
@@ -107,7 +107,7 @@ abstract class DashboardViewModelBase with Store {
107 name = wallet.name;
108 type = wallet.type;
109 isOutdatedElectrumWallet =
110 - wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
110 + wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
111 isShowFirstYatIntroduction = false;
112 isShowSecondYatIntroduction = false;
113 isShowThirdYatIntroduction = false;
@@ -327,7 +327,7 @@ abstract class DashboardViewModelBase with Store {
327 type = wallet.type;
328 name = wallet.name;
329 isOutdatedElectrumWallet =
330 - wallet.type == WalletType.bitcoin && wallet.seed.split(' ').length < 24;
330 + wallet.type == WalletType.bitcoin && wallet.seed!.split(' ').length < 24;
331 updateActions();
332
333 if (wallet.type == WalletType.monero) {
lib/view_model/restore/restore_from_qr_vm.dart
+3
@@ -66,6 +66,9 @@ abstract class WalletRestorationFromQRVMBase extends WalletCreationVM with Store
66 case WalletType.litecoin:
67 return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
68 name: name, password: password, wif: wif);
69 + case WalletType.ethereum:
70 + return ethereum!.createEthereumRestoreWalletFromPrivateKey(
71 + name: name, password: password, privateKey: restoreWallet.privateKey!);
72 default:
73 throw Exception('Unexpected type: ${restoreWallet.type.toString()}');
74 }
lib/view_model/restore/restore_wallet.dart
+4 -1
@@ -13,7 +13,8 @@ class RestoredWallet {
13 this.txAmount,
14 this.txDescription,
15 this.recipientName,
16 - this.height});
16 + this.height,
17 + this.privateKey});
18
19 final WalletRestoreMode restoreMode;
20 final WalletType type;
@@ -26,6 +27,7 @@ class RestoredWallet {
27 final String? txDescription;
28 final String? recipientName;
29 final int? height;
30 + final String? privateKey;
31
32 factory RestoredWallet.fromKey(Map<String, dynamic> json) {
33 final height = json['height'] as String?;
@@ -36,6 +38,7 @@ class RestoredWallet {
38 spendKey: json['spend_key'] as String?,
39 viewKey: json['view_key'] as String?,
40 height: height != null ? int.parse(height) : 0,
41 + privateKey: json['private_key'] as String?,
42 );
43 }
44
lib/view_model/restore/wallet_restore_from_qr_code.dart
+14
@@ -33,7 +33,10 @@ class WalletRestoreFromQRCode {
33 getSeedPhraseFromUrl(queryParameters.toString(), credentials['type'] as WalletType);
34 if (seed != null) {
35 credentials['seed'] = seed;
36 + } else {
37 + credentials['private_key'] = queryParameters['private_key'];
38 }
39 +
40 credentials.addAll(queryParameters);
41 credentials['mode'] = getWalletRestoreMode(credentials);
42
@@ -69,6 +72,8 @@ class WalletRestoreFromQRCode {
72 case 'litecoin':
73 case 'litecoin-wallet':
74 return WalletType.litecoin;
75 + case 'ethereum-wallet':
76 + return WalletType.ethereum;
77 default:
78 throw Exception('Unexpected wallet type: ${scheme.toString()}');
79 }
@@ -101,6 +106,7 @@ class WalletRestoreFromQRCode {
106 }
107 case WalletType.bitcoin:
108 case WalletType.litecoin:
109 + case WalletType.ethereum:
110 RegExp regex24 = RegExp(r'\b(\S+\b\s+){23}\S+\b');
111 RegExp regex18 = RegExp(r'\b(\S+\b\s+){17}\S+\b');
112 RegExp regex12 = RegExp(r'\b(\S+\b\s+){11}\S+\b');
@@ -152,6 +158,14 @@ class WalletRestoreFromQRCode {
158 : throw Exception('Unexpected restore mode: spend_key or view_key is invalid');
159 }
160
161 + if (type == WalletType.ethereum && credentials.containsKey('private_key')) {
162 + final privateKey = credentials['private_key'] as String;
163 + if (privateKey.isEmpty) {
164 + throw Exception('Unexpected restore mode: private_key');
165 + }
166 + return WalletRestoreMode.keys;
167 + }
168 +
169 throw Exception('Unexpected restore mode: restore params are invalid');
170 }
171 }
lib/view_model/wallet_keys_view_model.dart
+15 -6
@@ -69,7 +69,7 @@ abstract class WalletKeysViewModelBase with Store {
69 StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!),
70 if (keys['privateViewKey'] != null)
71 StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!),
72 - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
72 + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
73 ]);
74 }
75
@@ -85,15 +85,23 @@ abstract class WalletKeysViewModelBase with Store {
85 StandartListItem(title: S.current.view_key_public, value: keys['publicViewKey']!),
86 if (keys['privateViewKey'] != null)
87 StandartListItem(title: S.current.view_key_private, value: keys['privateViewKey']!),
88 - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
88 + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
89 ]);
90 }
91
92 if (_appStore.wallet!.type == WalletType.bitcoin ||
93 - _appStore.wallet!.type == WalletType.litecoin ||
94 - _appStore.wallet!.type == WalletType.ethereum) {
93 + _appStore.wallet!.type == WalletType.litecoin) {
94 items.addAll([
96 - StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed),
95 + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
96 + ]);
97 + }
98 +
99 + if (_appStore.wallet!.type == WalletType.ethereum) {
100 + items.addAll([
101 + if (_appStore.wallet!.privateKey != null)
102 + StandartListItem(title: S.current.private_key, value: _appStore.wallet!.privateKey!),
103 + if (_appStore.wallet!.seed != null)
104 + StandartListItem(title: S.current.wallet_seed, value: _appStore.wallet!.seed!),
105 ]);
106 }
107 }
@@ -139,7 +147,8 @@ abstract class WalletKeysViewModelBase with Store {
147 Future<Map<String, String>> get _queryParams async {
148 final restoreHeightResult = await restoreHeight;
149 return {
142 - 'seed': _appStore.wallet!.seed,
150 + if (_appStore.wallet!.seed != null) 'seed': _appStore.wallet!.seed!,
151 + if (_appStore.wallet!.privateKey != null) 'private_key': _appStore.wallet!.privateKey!,
152 if (restoreHeightResult != null) ...{'height': restoreHeightResult}
153 };
154 }
lib/view_model/wallet_restoration_from_keys_vm.dart deleted
-77
@@ -1,77 +0,0 @@
1 -import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
2 -import 'package:flutter/foundation.dart';
3 -import 'package:hive/hive.dart';
4 -import 'package:mobx/mobx.dart';
5 -import 'package:cake_wallet/monero/monero.dart';
6 -import 'package:cake_wallet/store/app_store.dart';
7 -import 'package:cw_core/wallet_base.dart';
8 -import 'package:cake_wallet/core/generate_wallet_password.dart';
9 -import 'package:cake_wallet/core/wallet_creation_service.dart';
10 -import 'package:cw_core/wallet_credentials.dart';
11 -import 'package:cw_core/wallet_type.dart';
12 -import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
13 -import 'package:cw_core/wallet_info.dart';
14 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
15 -
16 -part 'wallet_restoration_from_keys_vm.g.dart';
17 -
18 -class WalletRestorationFromKeysVM = WalletRestorationFromKeysVMBase
19 - with _$WalletRestorationFromKeysVM;
20 -
21 -abstract class WalletRestorationFromKeysVMBase extends WalletCreationVM
22 - with Store {
23 - WalletRestorationFromKeysVMBase(AppStore appStore,
24 - WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
25 - {required WalletType type, required this.language})
26 - : height = 0,
27 - viewKey = '',
28 - spendKey = '',
29 - wif = '',
30 - address = '',
31 - super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
32 -
33 - @observable
34 - int height;
35 -
36 - @observable
37 - String viewKey;
38 -
39 - @observable
40 - String spendKey;
41 -
42 - @observable
43 - String wif;
44 -
45 - @observable
46 - String address;
47 -
48 - bool get hasRestorationHeight => type == WalletType.monero;
49 -
50 - final String language;
51 -
52 - @override
53 - WalletCredentials getCredentials(dynamic options) {
54 - final password = generateWalletPassword();
55 -
56 - switch (type) {
57 - case WalletType.monero:
58 - return monero!.createMoneroRestoreWalletFromKeysCredentials(
59 - name: name,
60 - password: password,
61 - language: language,
62 - address: address,
63 - viewKey: viewKey,
64 - spendKey: spendKey,
65 - height: height);
66 - case WalletType.bitcoin:
67 - return bitcoin!.createBitcoinRestoreWalletFromWIFCredentials(
68 - name: name, password: password, wif: wif);
69 - default:
70 - throw Exception('Unexpected type: ${type.toString()}');
71 - }
72 - }
73 -
74 - @override
75 - Future<WalletBase> process(WalletCredentials credentials) async =>
76 - walletCreationService.restoreFromKeys(credentials);
77 -}
lib/view_model/wallet_restoration_from_seed_vm.dart deleted
-58
@@ -1,58 +0,0 @@
1 -import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
2 -import 'package:flutter/foundation.dart';
3 -import 'package:hive/hive.dart';
4 -import 'package:mobx/mobx.dart';
5 -import 'package:cake_wallet/monero/monero.dart';
6 -import 'package:cake_wallet/store/app_store.dart';
7 -import 'package:cake_wallet/bitcoin/bitcoin.dart';
8 -import 'package:cw_core/wallet_base.dart';
9 -import 'package:cake_wallet/core/generate_wallet_password.dart';
10 -import 'package:cake_wallet/core/wallet_creation_service.dart';
11 -import 'package:cw_core/wallet_credentials.dart';
12 -import 'package:cw_core/wallet_type.dart';
13 -import 'package:cake_wallet/view_model/wallet_creation_vm.dart';
14 -import 'package:cw_core/wallet_info.dart';
15 -
16 -part 'wallet_restoration_from_seed_vm.g.dart';
17 -
18 -class WalletRestorationFromSeedVM = WalletRestorationFromSeedVMBase
19 - with _$WalletRestorationFromSeedVM;
20 -
21 -abstract class WalletRestorationFromSeedVMBase extends WalletCreationVM
22 - with Store {
23 - WalletRestorationFromSeedVMBase(AppStore appStore,
24 - WalletCreationService walletCreationService, Box<WalletInfo> walletInfoSource,
25 - {required WalletType type, required this.language, this.seed = ''})
26 - : height = 0,
27 - super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true);
28 -
29 - @observable
30 - String seed;
31 -
32 - @observable
33 - int height;
34 -
35 - bool get hasRestorationHeight => type == WalletType.monero;
36 -
37 - final String language;
38 -
39 - @override
40 - WalletCredentials getCredentials(dynamic options) {
41 - final password = generateWalletPassword();
42 -
43 - switch (type) {
44 - case WalletType.monero:
45 - return monero!.createMoneroRestoreWalletFromSeedCredentials(
46 - name: name, height: height, mnemonic: seed, password: password);
47 - case WalletType.bitcoin:
48 - return bitcoin!.createBitcoinRestoreWalletFromSeedCredentials(
49 - name: name, mnemonic: seed, password: password);
50 - default:
51 - throw Exception('Unexpected type: ${type.toString()}');
52 - }
53 - }
54 -
55 - @override
56 - Future<WalletBase> process(WalletCredentials credentials) async =>
57 - walletCreationService.restoreFromSeed(credentials);
58 -}
lib/view_model/wallet_restore_view_model.dart
+24 -17
@@ -1,7 +1,4 @@
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 -import 'package:cake_wallet/core/mnemonic_length.dart';
3 -import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
4 -import 'package:flutter/foundation.dart';
2 import 'package:cake_wallet/ethereum/ethereum.dart';
3 import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
@@ -19,7 +16,6 @@ import 'package:cake_wallet/view_model/restore/restore_mode.dart';
16
17 part 'wallet_restore_view_model.g.dart';
18
22 -
19 class WalletRestoreViewModel = WalletRestoreViewModelBase
20 with _$WalletRestoreViewModel;
21
@@ -27,11 +23,13 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
23 WalletRestoreViewModelBase(AppStore appStore, WalletCreationService walletCreationService,
24 Box<WalletInfo> walletInfoSource,
25 {required WalletType type})
30 - : availableModes = (type == WalletType.monero || type == WalletType.haven)
31 - ? WalletRestoreMode.values
32 - : [WalletRestoreMode.seed],
26 + : availableModes =
27 + (type == WalletType.monero || type == WalletType.haven || type == WalletType.ethereum)
28 + ? WalletRestoreMode.values
29 + : [WalletRestoreMode.seed],
30 hasSeedLanguageSelector = type == WalletType.monero || type == WalletType.haven,
31 hasBlockchainHeightLanguageSelector = type == WalletType.monero || type == WalletType.haven,
32 + hasRestoreFromPrivateKey = type == WalletType.ethereum,
33 isButtonEnabled = false,
34 mode = WalletRestoreMode.seed,
35 super(appStore, walletInfoSource, walletCreationService, type: type, isRecovery: true) {
@@ -47,13 +45,14 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
45 final List<WalletRestoreMode> availableModes;
46 final bool hasSeedLanguageSelector;
47 final bool hasBlockchainHeightLanguageSelector;
48 + final bool hasRestoreFromPrivateKey;
49
50 @observable
51 WalletRestoreMode mode;
52
53 @observable
54 bool isButtonEnabled;
56 -
55 +
56 @override
57 WalletCredentials getCredentials(dynamic options) {
58 final password = generateWalletPassword();
@@ -97,17 +96,17 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
96 }
97
98 if (mode == WalletRestoreMode.keys) {
100 - final viewKey = options['viewKey'] as String;
101 - final spendKey = options['spendKey'] as String;
102 - final address = options['address'] as String;
99 + final viewKey = options['viewKey'] as String?;
100 + final spendKey = options['spendKey'] as String?;
101 + final address = options['address'] as String?;
102
103 if (type == WalletType.monero) {
104 return monero!.createMoneroRestoreWalletFromKeysCredentials(
105 name: name,
106 height: height,
108 - spendKey: spendKey,
109 - viewKey: viewKey,
110 - address: address,
107 + spendKey: spendKey!,
108 + viewKey: viewKey!,
109 + address: address!,
110 password: password,
111 language: 'English');
112 }
@@ -116,12 +115,20 @@ abstract class WalletRestoreViewModelBase extends WalletCreationVM with Store {
115 return haven!.createHavenRestoreWalletFromKeysCredentials(
116 name: name,
117 height: height,
119 - spendKey: spendKey,
120 - viewKey: viewKey,
121 - address: address,
118 + spendKey: spendKey!,
119 + viewKey: viewKey!,
120 + address: address!,
121 password: password,
122 language: 'English');
123 }
124 +
125 + if (type == WalletType.ethereum) {
126 + return ethereum!.createEthereumRestoreWalletFromPrivateKey(
127 + name: name,
128 + privateKey: options['private_key'] as String,
129 + password: password,
130 + );
131 + }
132 }
133
134 throw Exception('Unexpected type: ${type.toString()}');
lib/view_model/wallet_seed_view_model.dart
+1 -1
@@ -8,7 +8,7 @@ class WalletSeedViewModel = WalletSeedViewModelBase with _$WalletSeedViewModel;
8 abstract class WalletSeedViewModelBase with Store {
9 WalletSeedViewModelBase(WalletBase wallet)
10 : name = wallet.name,
11 - seed = wallet.seed;
11 + seed = wallet.seed!;
12
13 @observable
14 String name;
tool/configure.dart
+1
@@ -507,6 +507,7 @@ abstract class Ethereum {
507 WalletService createEthereumWalletService(Box<WalletInfo> walletInfoSource);
508 WalletCredentials createEthereumNewWalletCredentials({required String name, WalletInfo? walletInfo});
509 WalletCredentials createEthereumRestoreWalletFromSeedCredentials({required String name, required String mnemonic, required String password});
510 + WalletCredentials createEthereumRestoreWalletFromPrivateKey({required String name, required String privateKey, required String password});
511 String getAddress(WalletBase wallet);
512 TransactionPriority getDefaultTransactionPriority();
513 List<TransactionPriority> getTransactionPriorities();