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();