Cw 462 monero polyseed restore support (#1109)

* CW-462 Mark Places to integrate Polyseed * CW-462 Add Restore from Polyseed * CW-462 Add Restore from Polyseed * CW-462 Add new Monero date-height pairs * CW-462 Little Cleanup * CW-462 Ups I missed that Debug line :/ * CW-462 Fix Polyseed not showing in Wallet-Seed/Keys Page * CW-462 Prepare for Wallet creation * CW-462 Fix merge conflict * CW-462 Fix generating monero.dart * CW-462 Add Polyseed generation * CW-462 Add Polyseed Languages to SeedLanguagePicker * CW-462 Apply requested changes * CW-462 Minor bug fixes in restore screen * Update wallet_restore_from_seed_form.dart * CW-462 Minor Bugfix * CW-462 Fix Restore from QR for Polyseeds * CW-462 Fix null-check-operator exception for Polyseeds and minor inconveniences * CW-462 Fix minor inconveniences * Fix conflicts and review comments and wrap unspent issue with try and catch with reporting failure --------- Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Konstantin Ullrich committed Nov 25, 2023 at 01:37 UTC 00c97c74b81d3d0af272379b9971aa8402773a69
57 files changed +905 -443
cw_core/lib/get_height_by_date.dart
+35 -1
@@ -84,7 +84,41 @@ final dates = {
84 "2020-8": 2153983,
85 "2020-9": 2176466,
86 "2020-10": 2198453,
87 - "2020-11": 2220000
87 + "2020-11": 2220000,
88 + "2020-12": 2242240,
89 + "2021-1": 2264584,
90 + "2021-2": 2286892,
91 + "2021-3": 2307079,
92 + "2021-4": 2329385,
93 + "2021-5": 2351004,
94 + "2021-6": 2373306,
95 + "2021-7": 2394882,
96 + "2021-8": 2417162,
97 + "2021-9": 2439490,
98 + "2021-10": 2461020,
99 + "2021-11": 2483377,
100 + "2021-12": 2504932,
101 + "2022-1": 2527316,
102 + "2022-2": 2549605,
103 + "2022-3": 2569711,
104 + "2022-4": 2591995,
105 + "2022-5": 2613603,
106 + "2022-6": 2635840,
107 + "2022-7": 2657395,
108 + "2022-8": 2679705,
109 + "2022-9": 2701991,
110 + "2022-10": 2723607,
111 + "2022-11": 2745899,
112 + "2022-12": 2767427,
113 + "2023-1": 2789763,
114 + "2023-2": 2811996,
115 + "2023-3": 2832118,
116 + "2023-4": 2854365,
117 + "2023-5": 2875972,
118 + "2023-6": 2898234,
119 + "2023-7": 2919771,
120 + "2023-8": 2942045,
121 + "2023-9": 2964280
122 };
123
124 int getMoneroHeigthByDate({required DateTime date}) {
cw_core/pubspec.lock
+2 -2
@@ -271,10 +271,10 @@ packages:
271 dependency: "direct dev"
272 description:
273 name: hive_generator
274 - sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
274 + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
275 url: "https://pub.dev"
276 source: hosted
277 - version: "1.1.3"
277 + version: "2.0.1"
278 http:
279 dependency: "direct main"
280 description:
cw_core/pubspec.yaml
+1 -1
@@ -31,7 +31,7 @@ dev_dependencies:
31 build_runner: ^2.1.11
32 build_resolvers: ^2.0.9
33 mobx_codegen: ^2.0.7
34 - hive_generator: ^1.1.3
34 + hive_generator: ^2.0.1
35
36 # For information on the generic Dart part of this file, see the
37 # following page: https://dart.dev/tools/pub/pubspec
cw_monero/example/pubspec.lock
+12 -3
@@ -301,10 +301,19 @@ packages:
301 dependency: transitive
302 description:
303 name: pointycastle
304 - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
304 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
305 url: "https://pub.dev"
306 source: hosted
307 - version: "3.6.2"
307 + version: "3.7.3"
308 + polyseed:
309 + dependency: transitive
310 + description:
311 + path: "."
312 + ref: HEAD
313 + resolved-ref: "504d58a5b147fccd3bc85a25f2e72fb32771ddd7"
314 + url: "https://github.com/cake-tech/polyseed_dart.git"
315 + source: git
316 + version: "0.0.1"
317 process:
318 dependency: transitive
319 description:
@@ -416,5 +425,5 @@ packages:
425 source: hosted
426 version: "0.2.0+3"
427 sdks:
419 - dart: ">=3.0.0 <4.0.0"
428 + dart: ">=3.0.6 <4.0.0"
429 flutter: ">=3.7.0"
cw_monero/ios/Classes/monero_api.cpp
+34
@@ -374,6 +374,35 @@ extern "C"
374 return true;
375 }
376
377 + bool restore_wallet_from_spend_key(char *path, char *password, char *seed, char *language, char *spendKey, int32_t networkType, uint64_t restoreHeight, char *error)
378 + {
379 + Monero::NetworkType _networkType = static_cast<Monero::NetworkType>(networkType);
380 + Monero::Wallet *wallet = Monero::WalletManagerFactory::getWalletManager()->createDeterministicWalletFromSpendKey(
381 + std::string(path),
382 + std::string(password),
383 + std::string(language),
384 + _networkType,
385 + (uint64_t)restoreHeight,
386 + std::string(spendKey));
387 +
388 + int status;
389 + std::string errorString;
390 +
391 + wallet->statusWithErrorString(status, errorString);
392 +
393 + if (status != Monero::Wallet::Status_Ok || !errorString.empty())
394 + {
395 + error = strdup(errorString.c_str());
396 + return false;
397 + }
398 +
399 + // Cache Raw to support Polyseed
400 + wallet->setCacheAttribute("cakewallet.seed", std::string(seed));
401 +
402 + change_current_wallet(wallet);
403 + return true;
404 + }
405 +
406 bool load_wallet(char *path, char *password, int32_t nettype)
407 {
408 nice(19);
@@ -438,6 +467,11 @@ extern "C"
467
468 const char *seed()
469 {
470 + std::string _rawSeed = get_current_wallet()->getCacheAttribute("cakewallet.seed");
471 + if (!_rawSeed.empty())
472 + {
473 + return strdup(_rawSeed.c_str());
474 + }
475 return strdup(get_current_wallet()->seed().c_str());
476 }
477
cw_monero/lib/api/signatures.dart
+3
@@ -14,6 +14,9 @@ typedef restore_wallet_from_seed = Int8 Function(
14 typedef restore_wallet_from_keys = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
15 Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
16
17 +typedef restore_wallet_from_spend_key = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
18 + Pointer<Utf8>, Pointer<Utf8>, Int32, Int64, Pointer<Utf8>);
19 +
20 typedef is_wallet_exist = Int8 Function(Pointer<Utf8>);
21
22 typedef load_wallet = Int8 Function(Pointer<Utf8>, Pointer<Utf8>, Int8);
cw_monero/lib/api/types.dart
+3
@@ -14,6 +14,9 @@ typedef RestoreWalletFromSeed = int Function(
14 typedef RestoreWalletFromKeys = int Function(Pointer<Utf8>, Pointer<Utf8>,
15 Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
16
17 +typedef RestoreWalletFromSpendKey = int Function(Pointer<Utf8>, Pointer<Utf8>, Pointer<Utf8>,
18 + Pointer<Utf8>, Pointer<Utf8>, int, int, Pointer<Utf8>);
19 +
20 typedef IsWalletExist = int Function(Pointer<Utf8>);
21
22 typedef LoadWallet = int Function(Pointer<Utf8>, Pointer<Utf8>, int);
cw_monero/lib/api/wallet_manager.dart
+76 -1
@@ -5,7 +5,6 @@ import 'package:cw_monero/api/convert_utf8_to_string.dart';
5 import 'package:cw_monero/api/signatures.dart';
6 import 'package:cw_monero/api/types.dart';
7 import 'package:cw_monero/api/monero_api.dart';
8 -import 'package:cw_monero/api/wallet.dart';
8 import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
9 import 'package:cw_monero/api/exceptions/wallet_creation_exception.dart';
10 import 'package:cw_monero/api/exceptions/wallet_restore_from_keys_exception.dart';
@@ -25,6 +24,11 @@ final restoreWalletFromKeysNative = moneroApi
24 'restore_wallet_from_keys')
25 .asFunction<RestoreWalletFromKeys>();
26
27 +final restoreWalletFromSpendKeyNative = moneroApi
28 + .lookup<NativeFunction<restore_wallet_from_spend_key>>(
29 + 'restore_wallet_from_spend_key')
30 + .asFunction<RestoreWalletFromSpendKey>();
31 +
32 final isWalletExistNative = moneroApi
33 .lookup<NativeFunction<is_wallet_exist>>('is_wallet_exist')
34 .asFunction<IsWalletExist>();
@@ -141,6 +145,42 @@ void restoreWalletFromKeysSync(
145 }
146 }
147
148 +void restoreWalletFromSpendKeySync(
149 + {required String path,
150 + required String password,
151 + required String seed,
152 + required String language,
153 + required String spendKey,
154 + int nettype = 0,
155 + int restoreHeight = 0}) {
156 + final pathPointer = path.toNativeUtf8();
157 + final passwordPointer = password.toNativeUtf8();
158 + final seedPointer = seed.toNativeUtf8();
159 + final languagePointer = language.toNativeUtf8();
160 + final spendKeyPointer = spendKey.toNativeUtf8();
161 + final errorMessagePointer = ''.toNativeUtf8();
162 + final isWalletRestored = restoreWalletFromSpendKeyNative(
163 + pathPointer,
164 + passwordPointer,
165 + seedPointer,
166 + languagePointer,
167 + spendKeyPointer,
168 + nettype,
169 + restoreHeight,
170 + errorMessagePointer) !=
171 + 0;
172 +
173 + calloc.free(pathPointer);
174 + calloc.free(passwordPointer);
175 + calloc.free(languagePointer);
176 + calloc.free(spendKeyPointer);
177 +
178 + if (!isWalletRestored) {
179 + throw WalletRestoreFromKeysException(
180 + message: convertUTF8ToString(pointer: errorMessagePointer));
181 + }
182 +}
183 +
184 void loadWallet({
185 required String path,
186 required String password,
@@ -194,6 +234,23 @@ void _restoreFromKeys(Map<String, dynamic> args) {
234 spendKey: spendKey);
235 }
236
237 +void _restoreFromSpendKey(Map<String, dynamic> args) {
238 + final path = args['path'] as String;
239 + final password = args['password'] as String;
240 + final seed = args['seed'] as String;
241 + final language = args['language'] as String;
242 + final spendKey = args['spendKey'] as String;
243 + final restoreHeight = args['restoreHeight'] as int;
244 +
245 + restoreWalletFromSpendKeySync(
246 + path: path,
247 + password: password,
248 + seed: seed,
249 + language: language,
250 + restoreHeight: restoreHeight,
251 + spendKey: spendKey);
252 +}
253 +
254 Future<void> _openWallet(Map<String, String> args) async =>
255 loadWallet(path: args['path'] as String, password: args['password'] as String);
256
@@ -251,4 +308,22 @@ Future<void> restoreFromKeys(
308 'restoreHeight': restoreHeight
309 });
310
311 +Future<void> restoreFromSpendKey(
312 + {required String path,
313 + required String password,
314 + required String seed,
315 + required String language,
316 + required String spendKey,
317 + int nettype = 0,
318 + int restoreHeight = 0}) async =>
319 + compute<Map<String, Object>, void>(_restoreFromSpendKey, {
320 + 'path': path,
321 + 'password': password,
322 + 'seed': seed,
323 + 'language': language,
324 + 'spendKey': spendKey,
325 + 'nettype': nettype,
326 + 'restoreHeight': restoreHeight
327 + });
328 +
329 Future<bool> isWalletExist({required String path}) => compute(_isWalletExist, path);
cw_monero/lib/monero_wallet.dart
+54 -35
@@ -29,6 +29,7 @@ import 'package:cw_monero/monero_transaction_info.dart';
29 import 'package:cw_monero/monero_unspent.dart';
30 import 'package:cw_monero/monero_wallet_addresses.dart';
31 import 'package:cw_monero/pending_monero_transaction.dart';
32 +import 'package:flutter/foundation.dart';
33 import 'package:hive/hive.dart';
34 import 'package:mobx/mobx.dart';
35
@@ -78,6 +79,8 @@ abstract class MoneroWalletBase
79
80 Box<UnspentCoinsInfo> unspentCoinsInfo;
81
82 + void Function(FlutterErrorDetails)? _onError;
83 +
84 @override
85 late MoneroWalletAddresses walletAddresses;
86
@@ -388,46 +391,59 @@ abstract class MoneroWalletBase
391 }
392
393 Future<void> updateUnspent() async {
391 - refreshCoins(walletAddresses.account!.id);
392 -
393 - unspentCoins.clear();
394 + try {
395 + refreshCoins(walletAddresses.account!.id);
396 +
397 + unspentCoins.clear();
398 +
399 + final coinCount = countOfCoins();
400 + for (var i = 0; i < coinCount; i++) {
401 + final coin = getCoin(i);
402 + if (coin.spent == 0) {
403 + final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
404 + if (unspent.hash.isNotEmpty) {
405 + unspent.isChange = transaction_history
406 + .getTransaction(unspent.hash)
407 + .direction == 1;
408 + }
409 + unspentCoins.add(unspent);
410 + }
411 + }
412
395 - final coinCount = countOfCoins();
396 - for (var i = 0; i < coinCount; i++) {
397 - final coin = getCoin(i);
398 - if (coin.spent == 0) {
399 - final unspent = MoneroUnspent.fromCoinsInfoRow(coin);
400 - unspent.isChange = transaction_history.getTransaction(unspent.hash).direction == 1;
401 - unspentCoins.add(unspent);
413 + if (unspentCoinsInfo.isEmpty) {
414 + unspentCoins.forEach((coin) => _addCoinInfo(coin));
415 + return;
416 }
403 - }
417
405 - if (unspentCoinsInfo.isEmpty) {
406 - unspentCoins.forEach((coin) => _addCoinInfo(coin));
407 - return;
408 - }
418 + if (unspentCoins.isNotEmpty) {
419 + unspentCoins.forEach((coin) {
420 + final coinInfoList = unspentCoinsInfo.values.where((element) =>
421 + element.walletId.contains(id) &&
422 + element.accountIndex == walletAddresses.account!.id &&
423 + element.keyImage!.contains(coin.keyImage!));
424 +
425 + if (coinInfoList.isNotEmpty) {
426 + final coinInfo = coinInfoList.first;
427 +
428 + coin.isFrozen = coinInfo.isFrozen;
429 + coin.isSending = coinInfo.isSending;
430 + coin.note = coinInfo.note;
431 + } else {
432 + _addCoinInfo(coin);
433 + }
434 + });
435 + }
436
410 - if (unspentCoins.isNotEmpty) {
411 - unspentCoins.forEach((coin) {
412 - final coinInfoList = unspentCoinsInfo.values.where((element) =>
413 - element.walletId.contains(id) &&
414 - element.accountIndex == walletAddresses.account!.id &&
415 - element.keyImage!.contains(coin.keyImage!));
416 -
417 - if (coinInfoList.isNotEmpty) {
418 - final coinInfo = coinInfoList.first;
419 -
420 - coin.isFrozen = coinInfo.isFrozen;
421 - coin.isSending = coinInfo.isSending;
422 - coin.note = coinInfo.note;
423 - } else {
424 - _addCoinInfo(coin);
425 - }
426 - });
437 + await _refreshUnspentCoinsInfo();
438 + _askForUpdateBalance();
439 + } catch (e, s) {
440 + print(e.toString());
441 + _onError?.call(FlutterErrorDetails(
442 + exception: e,
443 + stack: s,
444 + library: this.runtimeType.toString(),
445 + ));
446 }
428 -
429 - await _refreshUnspentCoinsInfo();
430 - _askForUpdateBalance();
447 }
448
449 Future<void> _addCoinInfo(MoneroUnspent coin) async {
@@ -632,4 +648,7 @@ abstract class MoneroWalletBase
648 walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
649 }
650 }
651 +
652 + @override
653 + void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
654 }
cw_monero/lib/monero_wallet_service.dart
+57 -4
@@ -7,16 +7,20 @@ import 'package:cw_core/wallet_credentials.dart';
7 import 'package:cw_core/wallet_info.dart';
8 import 'package:cw_core/wallet_service.dart';
9 import 'package:cw_core/wallet_type.dart';
10 +import 'package:cw_core/get_height_by_date.dart';
11 import 'package:cw_monero/api/exceptions/wallet_opening_exception.dart';
12 import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
13 import 'package:cw_monero/monero_wallet.dart';
14 import 'package:hive/hive.dart';
15 +import 'package:polyseed/polyseed.dart';
16 +import 'package:polyseed/src/utils/key_utils.dart';
17
18 class MoneroNewWalletCredentials extends WalletCredentials {
16 - MoneroNewWalletCredentials({required String name, required this.language, String? password})
19 + MoneroNewWalletCredentials({required String name, required this.language, required this.isPolyseed, String? password})
20 : super(name: name, password: password);
21
22 final String language;
23 + final bool isPolyseed;
24 }
25
26 class MoneroRestoreWalletFromSeedCredentials extends WalletCredentials {
@@ -68,10 +72,17 @@ class MoneroWalletService extends WalletService<
72 Future<MoneroWallet> create(MoneroNewWalletCredentials credentials) async {
73 try {
74 final path = await pathForWallet(name: credentials.name, type: getType());
75 +
76 + if (credentials.isPolyseed) {
77 + final polyseed = Polyseed.create();
78 + final lang = PolyseedLang.getByEnglishName(credentials.language);
79 +
80 + return _restoreFromPolyseed(
81 + path, credentials.password!, polyseed, credentials.walletInfo!, lang);
82 + }
83 +
84 await monero_wallet_manager.createWallet(
72 - path: path,
73 - password: credentials.password!,
74 - language: credentials.language);
85 + path: path, password: credentials.password!, language: credentials.language);
86 final wallet = MoneroWallet(
87 walletInfo: credentials.walletInfo!, unspentCoinsInfo: unspentCoinsInfoSource);
88 await wallet.init();
@@ -215,6 +226,12 @@ class MoneroWalletService extends WalletService<
226 @override
227 Future<MoneroWallet> restoreFromSeed(
228 MoneroRestoreWalletFromSeedCredentials credentials) async {
229 +
230 + // Restore from Polyseed
231 + if (Polyseed.isValidSeed(credentials.mnemonic)) {
232 + return restoreFromPolyseed(credentials);
233 + }
234 +
235 try {
236 final path = await pathForWallet(name: credentials.name, type: getType());
237 await monero_wallet_manager.restoreFromSeed(
@@ -234,6 +251,42 @@ class MoneroWalletService extends WalletService<
251 }
252 }
253
254 + Future<MoneroWallet> restoreFromPolyseed(MoneroRestoreWalletFromSeedCredentials credentials) async {
255 + try {
256 + final path = await pathForWallet(name: credentials.name, type: getType());
257 + final polyseedCoin = PolyseedCoin.POLYSEED_MONERO;
258 + final lang = PolyseedLang.getByPhrase(credentials.mnemonic);
259 + final polyseed = Polyseed.decode(credentials.mnemonic, lang, polyseedCoin);
260 +
261 + return _restoreFromPolyseed(path, credentials.password!, polyseed, credentials.walletInfo!, lang);
262 + } catch (e) {
263 + // TODO: Implement Exception for wallet list service.
264 + print('MoneroWalletsManager Error: $e');
265 + rethrow;
266 + }
267 + }
268 +
269 + Future<MoneroWallet> _restoreFromPolyseed(String path, String password, Polyseed polyseed,
270 + WalletInfo walletInfo, PolyseedLang lang,
271 + {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO}) async {
272 + final height = getMoneroHeigthByDate(
273 + date: DateTime.fromMillisecondsSinceEpoch(polyseed.birthday * 1000));
274 + final spendKey = keyToHexString(polyseed.generateKey(coin, 32));
275 +
276 + await monero_wallet_manager.restoreFromSpendKey(
277 + path: path,
278 + password: password,
279 + seed: polyseed.encode(lang, coin),
280 + language: lang.nameEnglish,
281 + restoreHeight: height,
282 + spendKey: spendKey);
283 + final wallet = MoneroWallet(
284 + walletInfo: walletInfo, unspentCoinsInfo: unspentCoinsInfoSource);
285 + await wallet.init();
286 +
287 + return wallet;
288 + }
289 +
290 Future<void> repairOldAndroidWallet(String name) async {
291 try {
292 if (!Platform.isAndroid) {
cw_monero/pubspec.lock
+12 -3
@@ -478,10 +478,19 @@ packages:
478 dependency: transitive
479 description:
480 name: pointycastle
481 - sha256: db7306cf0249f838d1a24af52b5a5887c5bf7f31d8bb4e827d071dc0939ad346
481 + sha256: "7c1e5f0d23c9016c5bbd8b1473d0d3fb3fc851b876046039509e18e0c7485f2c"
482 url: "https://pub.dev"
483 source: hosted
484 - version: "3.6.2"
484 + version: "3.7.3"
485 + polyseed:
486 + dependency: "direct main"
487 + description:
488 + path: "."
489 + ref: HEAD
490 + resolved-ref: "504d58a5b147fccd3bc85a25f2e72fb32771ddd7"
491 + url: "https://github.com/cake-tech/polyseed_dart.git"
492 + source: git
493 + version: "0.0.1"
494 pool:
495 dependency: transitive
496 description:
@@ -689,5 +698,5 @@ packages:
698 source: hosted
699 version: "3.1.1"
700 sdks:
692 - dart: ">=3.0.0 <4.0.0"
701 + dart: ">=3.0.6 <4.0.0"
702 flutter: ">=3.7.0"
cw_monero/pubspec.yaml
+3
@@ -19,6 +19,9 @@ dependencies:
19 flutter_mobx: ^2.0.6+1
20 intl: ^0.18.0
21 encrypt: ^5.0.1
22 + polyseed:
23 + git:
24 + url: https://github.com/cake-tech/polyseed_dart.git
25 cw_core:
26 path: ../cw_core
27
lib/entities/preferences_key.dart
+1
@@ -64,6 +64,7 @@ class PreferencesKey {
64
65 static const exchangeProvidersSelection = 'exchange-providers-selection';
66 static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status';
67 + static const moneroSeedType = 'monero_seed_type';
68 static const clearnetDonationLink = 'clearnet_donation_link';
69 static const onionDonationLink = 'onion_donation_link';
70 static const lastSeenAppVersion = 'last_seen_app_version';
lib/entities/seed_type.dart new
+36
@@ -0,0 +1,36 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +import 'package:cw_core/enumerable_item.dart';
3 +
4 +class SeedType extends EnumerableItem<int> with Serializable<int> {
5 + const SeedType({required String title, required int raw}) : super(title: title, raw: raw);
6 +
7 + static const all = [SeedType.legacy, SeedType.polyseed];
8 +
9 + static const defaultSeedType = legacy;
10 +
11 + static const legacy = SeedType(raw: 0, title: 'Legacy (25 words)');
12 + static const polyseed = SeedType(raw: 1, title: 'Polyseed (16 words)');
13 +
14 + static SeedType deserialize({required int raw}) {
15 + switch (raw) {
16 + case 0:
17 + return legacy;
18 + case 1:
19 + return polyseed;
20 + default:
21 + throw Exception('Unexpected token: $raw for SeedType deserialize');
22 + }
23 + }
24 +
25 + @override
26 + String toString() {
27 + switch (this) {
28 + case SeedType.legacy:
29 + return S.current.seedtype_legacy;
30 + case SeedType.polyseed:
31 + return S.current.seedtype_polyseed;
32 + default:
33 + return '';
34 + }
35 + }
36 +}
lib/monero/cw_monero.dart
+7 -1
@@ -166,6 +166,10 @@ class CWMonero extends Monero {
166
167 @override
168 List<String> getMoneroWordList(String language) {
169 + if (language.startsWith("POLYSEED_")) {
170 + final lang = language.replaceAll("POLYSEED_", "");
171 + return PolyseedLang.getByEnglishName(lang).words;
172 + }
173 switch (language.toLowerCase()) {
174 case 'english':
175 return EnglishMnemonics.words;
@@ -223,8 +227,10 @@ class CWMonero extends Monero {
227 WalletCredentials createMoneroNewWalletCredentials({
228 required String name,
229 required String language,
230 + required bool isPolyseed,
231 String? password}) =>
227 - MoneroNewWalletCredentials(name: name, password: password, language: language);
232 + MoneroNewWalletCredentials(
233 + name: name, password: password, language: language, isPolyseed: isPolyseed);
234
235 @override
236 Map<String, String> getKeys(Object wallet) {
lib/router.dart
+3 -13
@@ -56,6 +56,7 @@ import 'package:cake_wallet/src/screens/support_other_links/support_other_links_
56 import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_details_page.dart';
57 import 'package:cake_wallet/src/screens/unspent_coins/unspent_coins_list_page.dart';
58 import 'package:cake_wallet/src/screens/wallet_connect/wc_connections_listing_view.dart';
59 +import 'package:cake_wallet/store/settings_store.dart';
60 import 'package:cake_wallet/utils/payment_request.dart';
61 import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
62 import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
@@ -87,7 +88,6 @@ import 'package:cake_wallet/src/screens/setup_pin_code/setup_pin_code.dart';
88 import 'package:cake_wallet/src/screens/restore/restore_options_page.dart';
89 import 'package:cake_wallet/src/screens/send/send_page.dart';
90 import 'package:cake_wallet/src/screens/disclaimer/disclaimer_page.dart';
90 -import 'package:cake_wallet/src/screens/seed_language/seed_language_page.dart';
91 import 'package:cake_wallet/src/screens/transaction_details/transaction_details_page.dart';
92 import 'package:cake_wallet/src/screens/monero_accounts/monero_account_edit_or_create_page.dart';
93 import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
@@ -147,8 +147,9 @@ Route<dynamic> createRoute(RouteSettings settings) {
147 case Routes.newWallet:
148 final type = settings.arguments as WalletType;
149 final walletNewVM = getIt.get<WalletNewVM>(param1: type);
150 + final settingsStore = getIt.get<SettingsStore>();
151
151 - return CupertinoPageRoute<void>(builder: (_) => NewWalletPage(walletNewVM));
152 + return CupertinoPageRoute<void>(builder: (_) => NewWalletPage(walletNewVM, settingsStore));
153
154 case Routes.setupPin:
155 Function(PinCodeState<PinCodeWidget>, String)? callback;
@@ -264,17 +265,6 @@ Route<dynamic> createRoute(RouteSettings settings) {
265 case Routes.changeRep:
266 return CupertinoPageRoute<void>(builder: (_) => getIt.get<NanoChangeRepPage>());
267
267 - case Routes.seedLanguage:
268 - final args = settings.arguments as List<dynamic>;
269 - final type = args.first as WalletType;
270 - final redirectRoute = args[1] as String;
271 -
272 - return CupertinoPageRoute<void>(builder: (_) {
273 - return SeedLanguage(
274 - onConfirm: (context, lang) =>
275 - Navigator.of(context).popAndPushNamed(redirectRoute, arguments: [type, lang]));
276 - });
277 -
268 case Routes.walletList:
269 return MaterialPageRoute<void>(
270 fullscreenDialog: true, builder: (_) => getIt.get<WalletListPage>());
lib/routes.dart
-1
@@ -18,7 +18,6 @@ class Routes {
18 static const disclaimer = '/disclaimer';
19 static const readDisclaimer = '/read_disclaimer';
20 static const changeRep = '/change_representative';
21 - static const seedLanguage = '/seed_language';
21 static const walletList = '/view_model.wallet_list';
22 static const auth = '/auth';
23 static const newNode = '/new_node_list';
lib/src/screens/new_wallet/advanced_privacy_settings_page.dart
+22 -10
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/entities/exchange_api_mode.dart';
2 import 'package:cake_wallet/entities/fiat_api_mode.dart';
3 import 'package:cake_wallet/entities/seed_phrase_length.dart';
4 +import 'package:cake_wallet/entities/seed_type.dart';
5 import 'package:cake_wallet/src/screens/nodes/widgets/node_form.dart';
6 import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart';
7 import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart';
@@ -97,16 +98,27 @@ class _AdvancedPrivacySettingsBodyState extends State<AdvancedPrivacySettingsBod
98 );
99 }),
100 if (widget.privacySettingsViewModel.hasSeedPhraseLengthOption)
100 - Observer(builder: (_) {
101 - return SettingsPickerCell<SeedPhraseLength>(
102 - title: S.current.seed_phrase_length,
103 - items: SeedPhraseLength.values,
104 - selectedItem: widget.privacySettingsViewModel.seedPhraseLength,
105 - onItemSelected: (SeedPhraseLength length) {
106 - widget.privacySettingsViewModel.setSeedPhraseLength(length);
107 - },
108 - );
109 - }),
101 + Observer(builder: (_) {
102 + return SettingsPickerCell<SeedPhraseLength>(
103 + title: S.current.seed_phrase_length,
104 + items: SeedPhraseLength.values,
105 + selectedItem: widget.privacySettingsViewModel.seedPhraseLength,
106 + onItemSelected: (SeedPhraseLength length) {
107 + widget.privacySettingsViewModel.setSeedPhraseLength(length);
108 + },
109 + );
110 + }),
111 + if (widget.privacySettingsViewModel.hasSeedTypeOption)
112 + Observer(builder: (_) {
113 + return SettingsChoicesCell(
114 + ChoicesListItem<SeedType>(
115 + title: S.current.seedtype,
116 + items: SeedType.all,
117 + selectedItem: widget.privacySettingsViewModel.seedType,
118 + onItemSelected: widget.privacySettingsViewModel.setSeedType,
119 + ),
120 + );
121 + }),
122 ],
123 ),
124 bottomSectionPadding: EdgeInsets.all(24),
lib/src/screens/new_wallet/new_wallet_page.dart
+30 -13
@@ -14,17 +14,20 @@ import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
14 import 'package:cake_wallet/src/screens/base_page.dart';
15 import 'package:cake_wallet/src/widgets/primary_button.dart';
16 import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
17 -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
17 +import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
18 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
19 import 'package:cake_wallet/core/execution_state.dart';
20 import 'package:cake_wallet/view_model/wallet_new_vm.dart';
21 import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
22 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
23 +import 'package:cake_wallet/entities/seed_type.dart';
24 +import 'package:cake_wallet/store/settings_store.dart';
25
26 class NewWalletPage extends BasePage {
25 - NewWalletPage(this._walletNewVM);
27 + NewWalletPage(this._walletNewVM, this._settingsStore);
28
29 final WalletNewVM _walletNewVM;
30 + final SettingsStore _settingsStore;
31
32 final walletNameImage = Image.asset('assets/images/wallet_name.png');
33
@@ -35,14 +38,15 @@ class NewWalletPage extends BasePage {
38
39 @override
40 Widget body(BuildContext context) => WalletNameForm(
38 - _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage);
41 + _walletNewVM, currentTheme.type == ThemeType.dark ? walletNameImage : walletNameLightImage, _settingsStore);
42 }
43
44 class WalletNameForm extends StatefulWidget {
42 - WalletNameForm(this._walletNewVM, this.walletImage);
45 + WalletNameForm(this._walletNewVM, this.walletImage, this._settingsStore);
46
47 final WalletNewVM _walletNewVM;
48 final Image walletImage;
49 + final SettingsStore _settingsStore;
50
51 @override
52 _WalletNameFormState createState() => _WalletNameFormState(_walletNewVM);
@@ -125,15 +129,20 @@ class _WalletNameFormState extends State<WalletNameForm> {
129 hintStyle: TextStyle(
130 fontSize: 18.0,
131 fontWeight: FontWeight.w500,
128 - color: Theme.of(context).extension<NewWalletTheme>()!.hintTextColor),
132 + color:
133 + Theme.of(context).extension<NewWalletTheme>()!.hintTextColor),
134 hintText: S.of(context).wallet_name,
135 focusedBorder: UnderlineInputBorder(
136 borderSide: BorderSide(
132 - color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
137 + color: Theme.of(context)
138 + .extension<NewWalletTheme>()!
139 + .underlineColor,
140 width: 1.0)),
141 enabledBorder: UnderlineInputBorder(
142 borderSide: BorderSide(
136 - color: Theme.of(context).extension<NewWalletTheme>()!.underlineColor,
143 + color: Theme.of(context)
144 + .extension<NewWalletTheme>()!
145 + .underlineColor,
146 width: 1.0),
147 ),
148 suffixIcon: Semantics(
@@ -160,7 +169,9 @@ class _WalletNameFormState extends State<WalletNameForm> {
169 height: 34,
170 child: Image.asset(
171 'assets/images/refresh_icon.png',
163 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
172 + color: Theme.of(context)
173 + .extension<SendPageTheme>()!
174 + .textFieldButtonIconColor,
175 ),
176 ),
177 ),
@@ -184,10 +195,15 @@ class _WalletNameFormState extends State<WalletNameForm> {
195 color: Theme.of(context).extension<CakeTextTheme>()!.titleColor),
196 ),
197 ),
187 - Padding(
188 - padding: EdgeInsets.only(top: 24),
189 - child: SeedLanguageSelector(
190 - key: _languageSelectorKey, initialSelected: defaultSeedLanguage),
198 + Observer(
199 + builder: (BuildContext build) => Padding(
200 + padding: EdgeInsets.only(top: 24),
201 + child: SeedLanguageSelector(
202 + key: _languageSelectorKey,
203 + initialSelected: defaultSeedLanguage,
204 + seedType: widget._settingsStore.moneroSeedType,
205 + ),
206 + ),
207 )
208 ]
209 ],
@@ -237,9 +253,10 @@ class _WalletNameFormState extends State<WalletNameForm> {
253 buttonAction: () => Navigator.of(context).pop());
254 });
255 } else {
256 + final isPolyseed = widget._settingsStore.moneroSeedType == SeedType.polyseed;
257 _walletNewVM.create(
258 options: _walletNewVM.hasLanguageSelector
242 - ? _languageSelectorKey.currentState!.selected
259 + ? [_languageSelectorKey.currentState!.selected, isPolyseed]
260 : null);
261 }
262 }
lib/src/screens/restore/wallet_restore_from_seed_form.dart
+105 -56
@@ -1,17 +1,17 @@
1 +import 'package:cake_wallet/core/wallet_name_validator.dart';
2 import 'package:cake_wallet/entities/generate_name.dart';
2 -import 'package:cw_core/wallet_type.dart';
3 -import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
4 -import 'package:flutter/cupertino.dart';
5 -import 'package:flutter/material.dart';
6 -import 'package:cake_wallet/utils/show_pop_up.dart';
7 -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
8 -import 'package:cake_wallet/src/widgets/seed_widget.dart';
3 +import 'package:cake_wallet/entities/seed_type.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
6 import 'package:cake_wallet/src/widgets/blockchain_height_widget.dart';
11 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
12 -import 'package:cake_wallet/generated/i18n.dart';
13 -import 'package:cake_wallet/core/wallet_name_validator.dart';
7 +import 'package:cake_wallet/src/widgets/picker.dart';
8 +import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
9 +import 'package:cake_wallet/src/widgets/seed_widget.dart';
10 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
11 +import 'package:cake_wallet/utils/show_pop_up.dart';
12 +import 'package:cw_core/wallet_type.dart';
13 +import 'package:flutter/material.dart';
14 +import 'package:polyseed/polyseed.dart';
15
16 class WalletRestoreFromSeedForm extends StatefulWidget {
17 WalletRestoreFromSeedForm(
@@ -34,8 +34,7 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
34 final void Function(String)? onLanguageChange;
35
36 @override
37 - WalletRestoreFromSeedFormState createState() =>
38 - WalletRestoreFromSeedFormState('English');
37 + WalletRestoreFromSeedFormState createState() => WalletRestoreFromSeedFormState('English');
38 }
39
40 class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
@@ -44,77 +43,116 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
43 blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
44 formKey = GlobalKey<FormState>(),
45 languageController = TextEditingController(),
47 - nameTextEditingController = TextEditingController();
46 + nameTextEditingController = TextEditingController(),
47 + seedTypeController = TextEditingController();
48
49 final GlobalKey<SeedWidgetState> seedWidgetStateKey;
50 final GlobalKey<BlockchainHeightState> blockchainHeightKey;
51 final TextEditingController languageController;
52 final TextEditingController nameTextEditingController;
53 + final TextEditingController seedTypeController;
54 final GlobalKey<FormState> formKey;
55 String language;
56 + bool isPolyseed = false;
57
58 @override
59 void initState() {
60 _setLanguageLabel(language);
61 + _setSeedType(SeedType.defaultSeedType);
62 super.initState();
63 }
64
65 + void onSeedChange(String seed) {
66 + if (widget.type == WalletType.monero && Polyseed.isValidSeed(seed)) {
67 + final lang = PolyseedLang.getByPhrase(seed);
68 +
69 + _changeSeedType(SeedType.polyseed);
70 + _changeLanguage(lang.nameEnglish);
71 + }
72 + widget.onSeedChange?.call(seed);
73 + }
74 +
75 @override
76 Widget build(BuildContext context) {
77 return Container(
78 padding: EdgeInsets.only(left: 24, right: 24),
79 child: Column(children: [
80 Form(
68 - key: formKey,
69 - child: Stack(
70 - alignment: Alignment.centerRight,
71 - children: [
72 - BaseTextFormField(
73 - controller: nameTextEditingController,
74 - hintText: S.of(context).wallet_name,
75 - suffixIcon: IconButton(
76 - onPressed: () async {
77 - final rName = await generateName();
78 - FocusManager.instance.primaryFocus?.unfocus();
81 + key: formKey,
82 + child: Stack(
83 + alignment: Alignment.centerRight,
84 + children: [
85 + BaseTextFormField(
86 + controller: nameTextEditingController,
87 + hintText: S.of(context).wallet_name,
88 + suffixIcon: IconButton(
89 + onPressed: () async {
90 + final rName = await generateName();
91 + FocusManager.instance.primaryFocus?.unfocus();
92
80 - setState(() {
81 - nameTextEditingController.text = rName;
82 - nameTextEditingController.selection =
83 - TextSelection.fromPosition(TextPosition(
84 - offset: nameTextEditingController.text.length));
85 - });
86 - },
87 - icon: Container(
88 - padding: const EdgeInsets.all(8),
89 - decoration: BoxDecoration(
90 - borderRadius: BorderRadius.circular(6.0),
91 - color: Theme.of(context).hintColor,
92 - ),
93 - width: 34,
94 - height: 34,
95 - child: Image.asset(
96 - 'assets/images/refresh_icon.png',
97 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldButtonIconColor,
93 + setState(() {
94 + nameTextEditingController.text = rName;
95 + nameTextEditingController.selection = TextSelection.fromPosition(
96 + TextPosition(offset: nameTextEditingController.text.length));
97 + });
98 + },
99 + icon: Container(
100 + padding: const EdgeInsets.all(8),
101 + decoration: BoxDecoration(
102 + borderRadius: BorderRadius.circular(6.0),
103 + color: Theme.of(context).hintColor,
104 + ),
105 + width: 34,
106 + height: 34,
107 + child: Image.asset(
108 + 'assets/images/refresh_icon.png',
109 + color: Theme.of(context)
110 + .extension<SendPageTheme>()!
111 + .textFieldButtonIconColor,
112 + ),
113 + ),
114 ),
115 + validator: WalletNameValidator(),
116 ),
100 - ),
101 - validator: WalletNameValidator(),
102 - ),
103 - ],
104 - )),
117 + ],
118 + )),
119 Container(height: 20),
120 SeedWidget(
121 key: seedWidgetStateKey,
122 language: language,
123 type: widget.type,
110 - onSeedChange: widget.onSeedChange),
124 + onSeedChange: onSeedChange),
125 + if (widget.type == WalletType.monero)
126 + GestureDetector(
127 + onTap: () async {
128 + await showPopUp<void>(
129 + context: context,
130 + builder: (_) => Picker(
131 + items: SeedType.all,
132 + selectedAtIndex: isPolyseed ? 1 : 0,
133 + mainAxisAlignment: MainAxisAlignment.start,
134 + onItemSelected: _changeSeedType,
135 + isSeparated: false,
136 + ));
137 + },
138 + child: Container(
139 + color: Colors.transparent,
140 + padding: EdgeInsets.only(top: 20.0),
141 + child: IgnorePointer(
142 + child: BaseTextFormField(
143 + controller: seedTypeController,
144 + enableInteractiveSelection: false,
145 + readOnly: true)))),
146 if (widget.displayLanguageSelector)
147 GestureDetector(
148 onTap: () async {
149 await showPopUp<void>(
150 context: context,
151 builder: (_) => SeedLanguagePicker(
117 - selected: language, onItemSelected: _changeLanguage));
152 + selected: language,
153 + onItemSelected: _changeLanguage,
154 + seedType: isPolyseed ? SeedType.polyseed : SeedType.legacy,
155 + ));
156 },
157 child: Container(
158 color: Colors.transparent,
@@ -124,24 +162,35 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
162 controller: languageController,
163 enableInteractiveSelection: false,
164 readOnly: true)))),
127 - if (widget.displayBlockHeightSelector)
165 + if (!isPolyseed && widget.displayBlockHeightSelector)
166 BlockchainHeightWidget(
167 focusNode: widget.blockHeightFocusNode,
168 key: blockchainHeightKey,
169 onHeightOrDateEntered: widget.onHeightOrDateEntered,
132 - hasDatePicker: widget.type == WalletType.monero)
170 + hasDatePicker: widget.type == WalletType.monero),
171 ]));
172 }
173
174 void _changeLanguage(String language) {
175 + final setLang = isPolyseed ? "POLYSEED_$language" : language;
176 setState(() {
138 - this.language = language;
139 - seedWidgetStateKey.currentState!.changeSeedLanguage(language);
140 - _setLanguageLabel(language);
141 - widget.onLanguageChange?.call(language);
177 + this.language = setLang;
178 + seedWidgetStateKey.currentState!.changeSeedLanguage(setLang);
179 + _setLanguageLabel(setLang);
180 + widget.onLanguageChange?.call(setLang);
181 });
182 }
183
184 void _setLanguageLabel(String language) =>
146 - languageController.text = '$language (Seed language)';
185 + languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)';
186 +
187 + void _changeSeedType(SeedType item) {
188 + _setSeedType(item);
189 + _changeLanguage('English');
190 + }
191 +
192 + void _setSeedType(SeedType item) {
193 + setState(() => isPolyseed = item == SeedType.polyseed);
194 + seedTypeController.text = item.toString();
195 + }
196 }
lib/src/screens/restore/wallet_restore_page.dart
+45 -39
@@ -1,29 +1,30 @@
1 +import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/di.dart';
3 +import 'package:cake_wallet/generated/i18n.dart';
4 import 'package:cake_wallet/nano/nano.dart';
5 import 'package:cake_wallet/routes.dart';
6 +import 'package:cake_wallet/src/screens/base_page.dart';
7 +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
8 +import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
9 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
10 +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
11 +import 'package:cake_wallet/src/widgets/primary_button.dart';
12 import 'package:cake_wallet/store/app_store.dart';
13 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
6 -import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
14 +import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
15 import 'package:cake_wallet/utils/responsive_layout_util.dart';
16 +import 'package:cake_wallet/utils/show_pop_up.dart';
17 +import 'package:cake_wallet/view_model/restore/restore_mode.dart';
18 +import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
19 import 'package:cw_core/nano_account_info_response.dart';
20 import 'package:cw_core/wallet_info.dart';
21 import 'package:cw_core/wallet_type.dart';
22 import 'package:flutter/material.dart';
23 +import 'package:flutter_mobx/flutter_mobx.dart';
24 import 'package:keyboard_actions/keyboard_actions.dart';
25 import 'package:mobx/mobx.dart';
14 -import 'package:flutter_mobx/flutter_mobx.dart';
26 +import 'package:polyseed/polyseed.dart';
27 import 'package:smooth_page_indicator/smooth_page_indicator.dart';
16 -import 'package:cake_wallet/generated/i18n.dart';
17 -import 'package:cake_wallet/core/execution_state.dart';
18 -import 'package:cake_wallet/src/screens/base_page.dart';
19 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
20 -import 'package:cake_wallet/view_model/wallet_restore_view_model.dart';
21 -import 'package:cake_wallet/src/screens/restore/wallet_restore_from_keys_form.dart';
22 -import 'package:cake_wallet/src/screens/restore/wallet_restore_from_seed_form.dart';
23 -import 'package:cake_wallet/src/widgets/primary_button.dart';
24 -import 'package:cake_wallet/utils/show_pop_up.dart';
25 -import 'package:cake_wallet/view_model/restore/restore_mode.dart';
26 -import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
28
29 class WalletRestorePage extends BasePage {
30 WalletRestorePage(this.walletRestoreViewModel)
@@ -48,27 +49,13 @@ class WalletRestorePage extends BasePage {
49 }
50 },
51 onSeedChange: (String seed) {
51 - if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
52 - final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey
53 - .currentState!.restoreHeightController.text.isNotEmpty;
54 - if (hasHeight) {
55 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
56 - }
57 - } else {
58 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
59 - }
52 + final isPolyseed =
53 + walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seed);
54 + _validateOnChange(isPolyseed: isPolyseed);
55 },
61 - onLanguageChange: (_) {
62 - if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
63 - final hasHeight = walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey
64 - .currentState!.restoreHeightController.text.isNotEmpty;
65 -
66 - if (hasHeight) {
67 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
68 - }
69 - } else {
70 - walletRestoreViewModel.isButtonEnabled = _isValidSeed();
71 - }
56 + onLanguageChange: (String language) {
57 + final isPolyseed = language.startsWith("POLYSEED_");
58 + _validateOnChange(isPolyseed: isPolyseed);
59 }));
60 break;
61 case WalletRestoreMode.keys:
@@ -163,7 +150,8 @@ class WalletRestorePage extends BasePage {
150 color: Theme.of(context).colorScheme.background,
151 child: Center(
152 child: ConstrainedBox(
166 - constraints: BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
153 + constraints:
154 + BoxConstraints(maxWidth: ResponsiveLayoutUtilBase.kDesktopMaxWidthConstraint),
155 child: Column(
156 mainAxisAlignment: MainAxisAlignment.center,
157 children: [
@@ -219,8 +207,8 @@ class WalletRestorePage extends BasePage {
207 const SizedBox(height: 25),
208 GestureDetector(
209 onTap: () {
222 - Navigator.of(context)
223 - .pushNamed(Routes.advancedPrivacySettings, arguments: walletRestoreViewModel.type);
210 + Navigator.of(context).pushNamed(Routes.advancedPrivacySettings,
211 + arguments: walletRestoreViewModel.type);
212 },
213 child: Text(S.of(context).advanced_privacy_settings),
214 ),
@@ -235,9 +223,26 @@ class WalletRestorePage extends BasePage {
223 );
224 }
225
226 + void _validateOnChange({bool isPolyseed = false}) {
227 + if (!isPolyseed && walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
228 + final hasHeight = walletRestoreFromSeedFormKey
229 + .currentState?.blockchainHeightKey.currentState?.restoreHeightController.text.isNotEmpty;
230 +
231 + if (hasHeight == true) {
232 + walletRestoreViewModel.isButtonEnabled = _isValidSeed();
233 + }
234 + } else {
235 + walletRestoreViewModel.isButtonEnabled = _isValidSeed();
236 + }
237 + }
238 +
239 bool _isValidSeed() {
239 - final seedWords =
240 - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text.split(' ');
240 + final seedPhrase =
241 + walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
242 + if (walletRestoreViewModel.type == WalletType.monero && Polyseed.isValidSeed(seedPhrase))
243 + return true;
244 +
245 + final seedWords = seedPhrase.split(' ');
246
247 if ((walletRestoreViewModel.type == WalletType.monero ||
248 walletRestoreViewModel.type == WalletType.haven) &&
@@ -282,7 +287,8 @@ class WalletRestorePage extends BasePage {
287
288 if (walletRestoreViewModel.hasBlockchainHeightLanguageSelector) {
289 credentials['height'] =
285 - walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState!.height;
290 + walletRestoreFromSeedFormKey.currentState!.blockchainHeightKey.currentState?.height ??
291 + -1;
292 }
293
294 credentials['name'] =
lib/src/screens/seed_language/seed_language_page.dart deleted
-95
@@ -1,95 +0,0 @@
1 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 -import 'package:cake_wallet/themes/theme_base.dart';
3 -import 'package:cake_wallet/src/widgets/seed_language_selector.dart';
4 -import 'package:flutter_mobx/flutter_mobx.dart';
5 -import 'package:flutter/material.dart';
6 -import 'package:flutter/cupertino.dart';
7 -import 'package:cake_wallet/generated/i18n.dart';
8 -import 'package:cake_wallet/src/screens/base_page.dart';
9 -import 'package:cake_wallet/src/widgets/primary_button.dart';
10 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
11 -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
12 -
13 -class SeedLanguage extends BasePage {
14 - SeedLanguage({required this.onConfirm});
15 -
16 - final Function(BuildContext, String) onConfirm;
17 -
18 - final walletNameImage = Image.asset('assets/images/wallet_name.png');
19 - final walletNameLightImage =
20 - Image.asset('assets/images/wallet_name_light.png');
21 -
22 - @override
23 - String get title => S.current.wallet_list_restore_wallet;
24 -
25 - @override
26 - Widget body(BuildContext context) =>
27 - SeedLanguageForm(
28 - onConfirm: onConfirm,
29 - walletImage: currentTheme.type == ThemeType.dark
30 - ? walletNameImage : walletNameLightImage);
31 -}
32 -
33 -class SeedLanguageForm extends StatefulWidget {
34 - SeedLanguageForm({required this.onConfirm, required this.walletImage});
35 -
36 - final Function(BuildContext, String) onConfirm;
37 - final Image walletImage;
38 -
39 - @override
40 - SeedLanguageFormState createState() => SeedLanguageFormState();
41 -}
42 -
43 -class SeedLanguageFormState extends State<SeedLanguageForm> {
44 - static const aspectRatioImage = 1.22;
45 - final _languageSelectorKey = GlobalKey<SeedLanguageSelectorState>();
46 -
47 - @override
48 - Widget build(BuildContext context) {
49 - return Container(
50 - padding: EdgeInsets.only(top: 24),
51 - child: ScrollableWithBottomSection(
52 - contentPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
53 - content:
54 - Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
55 - Padding(
56 - padding: EdgeInsets.only(left: 12, right: 12),
57 - child: AspectRatio(
58 - aspectRatio: aspectRatioImage,
59 - child: FittedBox(child: widget.walletImage,
60 - fit: BoxFit.fill)),
61 - ),
62 - Padding(
63 - padding: EdgeInsets.only(top: 40),
64 - child: Text(
65 - S.of(context).seed_language_choose,
66 - textAlign: TextAlign.center,
67 - style: TextStyle(
68 - fontSize: 16.0,
69 - fontWeight: FontWeight.w500,
70 - color:
71 - Theme.of(context).extension<CakeTextTheme>()!.titleColor),
72 - ),
73 - ),
74 - Padding(
75 - padding: EdgeInsets.only(top: 24),
76 - child: SeedLanguageSelector(
77 - key: _languageSelectorKey,
78 - initialSelected: defaultSeedLanguage),
79 - )
80 - ]),
81 - bottomSectionPadding:
82 - EdgeInsets.only(left: 24, right: 24, bottom: 24),
83 - bottomSection: Observer(
84 - builder: (context) {
85 - return PrimaryButton(
86 - onPressed: () => widget.onConfirm(
87 - context, _languageSelectorKey.currentState!.selected),
88 - text: S.of(context).seed_language_next,
89 - color: Colors.green,
90 - textColor: Colors.white);
91 - },
92 - )),
93 - );
94 - }
95 -}
lib/src/screens/seed_language/widgets/seed_language_picker.dart deleted
-82
@@ -1,82 +0,0 @@
1 -import 'package:flutter/material.dart';
2 -import 'package:cake_wallet/src/widgets/picker.dart';
3 -import 'package:flutter/cupertino.dart';
4 -import 'package:cake_wallet/generated/i18n.dart';
5 -
6 -List<Image> flagImages = [
7 - Image.asset('assets/images/flags/usa.png'),
8 - Image.asset('assets/images/flags/chn.png'),
9 - Image.asset('assets/images/flags/nld.png'),
10 - Image.asset('assets/images/flags/deu.png'),
11 - Image.asset('assets/images/flags/jpn.png'),
12 - Image.asset('assets/images/flags/prt.png'),
13 - Image.asset('assets/images/flags/rus.png'),
14 - Image.asset('assets/images/flags/esp.png'),
15 - Image.asset('assets/images/flags/fra.png'),
16 - Image.asset('assets/images/flags/ita.png'),
17 -];
18 -
19 -const List<String> languageCodes = [
20 - 'Eng',
21 - 'Chi',
22 - 'Ned',
23 - 'Ger',
24 - 'Jap',
25 - 'Por',
26 - 'Rus',
27 - 'Esp',
28 - 'Fre',
29 - 'Ita',
30 -];
31 -
32 -const defaultSeedLanguage = 'English';
33 -
34 -const List<String> seedLanguages = [
35 - defaultSeedLanguage,
36 - 'Chinese (simplified)',
37 - 'Dutch',
38 - 'German',
39 - 'Japanese',
40 - 'Portuguese',
41 - 'Russian',
42 - 'Spanish',
43 - 'French',
44 - 'Italian',
45 -];
46 -
47 -enum Places { topLeft, topRight, bottomLeft, bottomRight, inside }
48 -
49 -class SeedLanguagePicker extends StatefulWidget {
50 - SeedLanguagePicker({Key? key, this.selected = defaultSeedLanguage, required this.onItemSelected})
51 - : super(key: key);
52 -
53 - final String selected;
54 - final Function(String) onItemSelected;
55 -
56 - @override
57 - SeedLanguagePickerState createState() =>
58 - SeedLanguagePickerState(selected: selected, onItemSelected: onItemSelected);
59 -}
60 -
61 -class SeedLanguagePickerState extends State<SeedLanguagePicker> {
62 - SeedLanguagePickerState({required this.selected, required this.onItemSelected});
63 -
64 - final String selected;
65 - final Function(String) onItemSelected;
66 -
67 - @override
68 - Widget build(BuildContext context) {
69 - return Picker(
70 - selectedAtIndex: seedLanguages.indexOf(selected),
71 - items: seedLanguages,
72 - images: flagImages,
73 - isGridView: true,
74 - title: S.of(context).seed_choose,
75 - hintText: S.of(context).seed_choose,
76 - matchingCriteria: (String language, String searchText) {
77 - return language.toLowerCase().contains(searchText);
78 - },
79 - onItemSelected: onItemSelected,
80 - );
81 - }
82 -}
lib/src/widgets/seed_language_picker.dart new
+93
@@ -0,0 +1,93 @@
1 +import 'package:flutter/material.dart';
2 +import 'package:cake_wallet/src/widgets/picker.dart';
3 +import 'package:flutter/cupertino.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +
6 +import 'package:cake_wallet/entities/seed_type.dart';
7 +
8 +class SeedLanguagePickerOption {
9 + SeedLanguagePickerOption(this.name, this.nameLocalized, this.image, this.supportedSeedTypes);
10 +
11 + final String name;
12 + final String nameLocalized;
13 + final Image image;
14 + final List<SeedType> supportedSeedTypes;
15 +}
16 +
17 +final List<SeedLanguagePickerOption> seedLanguages = [
18 + SeedLanguagePickerOption('English', S.current.seed_language_english,
19 + Image.asset('assets/images/flags/usa.png'), [SeedType.legacy, SeedType.polyseed]),
20 + SeedLanguagePickerOption('Chinese (simplified)', S.current.seed_language_chinese,
21 + Image.asset('assets/images/flags/chn.png'), [SeedType.legacy, SeedType.polyseed]),
22 + SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional,
23 + Image.asset('assets/images/flags/chn.png'), [SeedType.polyseed]),
24 + SeedLanguagePickerOption('Dutch', S.current.seed_language_dutch,
25 + Image.asset('assets/images/flags/nld.png'), [SeedType.legacy]),
26 + SeedLanguagePickerOption('German', S.current.seed_language_german,
27 + Image.asset('assets/images/flags/deu.png'), [SeedType.legacy]),
28 + SeedLanguagePickerOption('Japanese', S.current.seed_language_japanese,
29 + Image.asset('assets/images/flags/jpn.png'), [SeedType.legacy, SeedType.polyseed]),
30 + SeedLanguagePickerOption('Korean', S.current.seed_language_korean,
31 + Image.asset('assets/images/flags/kor.png'), [SeedType.polyseed]),
32 + SeedLanguagePickerOption('Portuguese', S.current.seed_language_portuguese,
33 + Image.asset('assets/images/flags/prt.png'), [SeedType.legacy, SeedType.polyseed]),
34 + SeedLanguagePickerOption('Russian', S.current.seed_language_russian,
35 + Image.asset('assets/images/flags/rus.png'), [SeedType.legacy]),
36 + SeedLanguagePickerOption('Czech', S.current.seed_language_czech,
37 + Image.asset('assets/images/flags/czk.png'), [SeedType.polyseed]),
38 + SeedLanguagePickerOption('Spanish', S.current.seed_language_spanish,
39 + Image.asset('assets/images/flags/esp.png'), [SeedType.legacy, SeedType.polyseed]),
40 + SeedLanguagePickerOption('French', S.current.seed_language_french,
41 + Image.asset('assets/images/flags/fra.png'), [SeedType.legacy, SeedType.polyseed]),
42 + SeedLanguagePickerOption('Italian', S.current.seed_language_italian,
43 + Image.asset('assets/images/flags/ita.png'), [SeedType.legacy, SeedType.polyseed]),
44 +];
45 +
46 +const defaultSeedLanguage = 'English';
47 +
48 +enum Places { topLeft, topRight, bottomLeft, bottomRight, inside }
49 +
50 +class SeedLanguagePicker extends StatefulWidget {
51 + SeedLanguagePicker(
52 + {Key? key,
53 + this.selected = defaultSeedLanguage,
54 + this.seedType = SeedType.defaultSeedType,
55 + required this.onItemSelected})
56 + : super(key: key);
57 +
58 + final SeedType seedType;
59 + final String selected;
60 + final Function(String) onItemSelected;
61 +
62 + @override
63 + SeedLanguagePickerState createState() => SeedLanguagePickerState(
64 + selected: selected, onItemSelected: onItemSelected, seedType: seedType);
65 +}
66 +
67 +class SeedLanguagePickerState extends State<SeedLanguagePicker> {
68 + SeedLanguagePickerState(
69 + {required this.selected, required this.onItemSelected, required this.seedType});
70 +
71 + final SeedType seedType;
72 + final String selected;
73 + final Function(String) onItemSelected;
74 +
75 + @override
76 + Widget build(BuildContext context) {
77 + final availableSeedLanguages = seedLanguages
78 + .where((SeedLanguagePickerOption e) => e.supportedSeedTypes.contains(seedType));
79 +
80 + return Picker(
81 + selectedAtIndex: availableSeedLanguages.map((e) => e.name).toList().indexOf(selected),
82 + items: availableSeedLanguages.map((e) => e.name).toList(),
83 + images: availableSeedLanguages.map((e) => e.image).toList(),
84 + isGridView: true,
85 + title: S.of(context).seed_choose,
86 + hintText: S.of(context).seed_choose,
87 + matchingCriteria: (String language, String searchText) {
88 + return language.toLowerCase().contains(searchText);
89 + },
90 + onItemSelected: onItemSelected,
91 + );
92 + }
93 +}
lib/src/widgets/seed_language_selector.dart
+7 -17
@@ -1,14 +1,15 @@
1 +import 'package:cake_wallet/entities/seed_type.dart';
2 +import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
3 +import 'package:cake_wallet/src/widgets/seed_language_picker.dart';
4 import 'package:cake_wallet/utils/show_pop_up.dart';
5 import 'package:flutter/material.dart';
3 -import 'package:cake_wallet/generated/i18n.dart';
4 -import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
5 -import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
6
7 class SeedLanguageSelector extends StatefulWidget {
8 - SeedLanguageSelector({Key? key, required this.initialSelected})
8 + SeedLanguageSelector({Key? key, required this.initialSelected, this.seedType = SeedType.defaultSeedType})
9 : super(key: key);
10
11 final String initialSelected;
12 + final SeedType seedType;
13
14 @override
15 SeedLanguageSelectorState createState() =>
@@ -18,30 +19,19 @@ class SeedLanguageSelector extends StatefulWidget {
19 class SeedLanguageSelectorState extends State<SeedLanguageSelector> {
20 SeedLanguageSelectorState({required this.selected});
21
21 - final seedLocales = [
22 - S.current.seed_language_english,
23 - S.current.seed_language_chinese,
24 - S.current.seed_language_dutch,
25 - S.current.seed_language_german,
26 - S.current.seed_language_japanese,
27 - S.current.seed_language_portuguese,
28 - S.current.seed_language_russian,
29 - S.current.seed_language_spanish,
30 - S.current.seed_language_french,
31 - S.current.seed_language_italian,
32 - ];
22 String selected;
23
24 @override
25 Widget build(BuildContext context) {
26 return SelectButton(
27 image: null,
39 - text: seedLocales[seedLanguages.indexOf(selected)],
28 + text: seedLanguages.firstWhere((e) => e.name == selected).nameLocalized,
29 onTap: () async {
30 await showPopUp<String>(
31 context: context,
32 builder: (_) => SeedLanguagePicker(
33 selected: this.selected,
34 + seedType: widget.seedType,
35 onItemSelected: (String selected) =>
36 setState(() => this.selected = selected)));
37 },
lib/store/settings_store.dart
+51 -25
@@ -10,6 +10,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
10 import 'package:cake_wallet/entities/pin_code_required_duration.dart';
11 import 'package:cake_wallet/entities/preferences_key.dart';
12 import 'package:cake_wallet/entities/seed_phrase_length.dart';
13 +import 'package:cake_wallet/entities/seed_type.dart';
14 import 'package:cake_wallet/entities/sort_balance_types.dart';
15 import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
16 import 'package:cake_wallet/view_model/settings/sync_mode.dart';
@@ -48,6 +49,7 @@ abstract class SettingsStoreBase with Store {
49 required BalanceDisplayMode initialBalanceDisplayMode,
50 required bool initialSaveRecipientAddress,
51 required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus,
52 + required SeedType initialMoneroSeedType,
53 required bool initialAppSecure,
54 required bool initialDisableBuy,
55 required bool initialDisableSell,
@@ -108,6 +110,7 @@ abstract class SettingsStoreBase with Store {
110 balanceDisplayMode = initialBalanceDisplayMode,
111 shouldSaveRecipientAddress = initialSaveRecipientAddress,
112 autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus,
113 + moneroSeedType = initialMoneroSeedType,
114 fiatApiMode = initialFiatMode,
115 allowBiometricalAuthentication = initialAllowBiometricalAuthentication,
116 selectedCake2FAPreset = initialCake2FAPresetOptions,
@@ -240,6 +243,11 @@ abstract class SettingsStoreBase with Store {
243 (AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt(
244 PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus.value));
245
246 + reaction(
247 + (_) => moneroSeedType,
248 + (SeedType moneroSeedType) => sharedPreferences.setInt(
249 + PreferencesKey.moneroSeedType, moneroSeedType.raw));
250 +
251 reaction(
252 (_) => fiatApiMode,
253 (FiatApiMode mode) =>
@@ -435,6 +443,7 @@ abstract class SettingsStoreBase with Store {
443 static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes;
444 static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized;
445 static const defaultSeedPhraseLength = SeedPhraseLength.twelveWords;
446 + static const defaultMoneroSeedType = SeedType.defaultSeedType;
447
448 @observable
449 FiatCurrency fiatCurrency;
@@ -460,6 +469,9 @@ abstract class SettingsStoreBase with Store {
469 @observable
470 AutoGenerateSubaddressStatus autoGenerateSubaddressStatus;
471
472 + @observable
473 + SeedType moneroSeedType;
474 +
475 @observable
476 bool isAppSecure;
477
@@ -775,12 +787,20 @@ abstract class SettingsStoreBase with Store {
787 final packageInfo = await PackageInfo.fromPlatform();
788 final deviceName = await _getDeviceName() ?? '';
789 final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
790 +
791 final generateSubaddresses =
792 sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey);
793
794 final autoGenerateSubaddressStatus = generateSubaddresses != null
795 ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
796 : defaultAutoGenerateSubaddressStatus;
797 +
798 + final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType);
799 +
800 + final moneroSeedType = _moneroSeedType != null
801 + ? SeedType.deserialize(raw: _moneroSeedType)
802 + : defaultMoneroSeedType;
803 +
804 final nodes = <WalletType, Node>{};
805 final powNodes = <WalletType, Node>{};
806
@@ -833,27 +853,27 @@ abstract class SettingsStoreBase with Store {
853 initialBalanceDisplayMode: currentBalanceDisplayMode,
854 initialSaveRecipientAddress: shouldSaveRecipientAddress,
855 initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus,
836 - initialAppSecure: isAppSecure,
837 - initialDisableBuy: disableBuy,
838 - initialDisableSell: disableSell,
839 - initialDefaultBuyProvider: defaultBuyProvider,
840 - initialFiatMode: currentFiatApiMode,
841 - initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
842 - initialCake2FAPresetOptions: selectedCake2FAPreset,
843 - initialUseTOTP2FA: useTOTP2FA,
844 - initialTotpSecretKey: totpSecretKey,
845 - initialFailedTokenTrial: tokenTrialNumber,
846 - initialExchangeStatus: exchangeStatus,
847 - initialTheme: savedTheme,
848 - actionlistDisplayMode: actionListDisplayMode,
849 - initialPinLength: pinLength,
850 - pinTimeOutDuration: pinCodeTimeOutDuration,
851 - seedPhraseLength: seedPhraseWordCount,
852 - initialLanguageCode: savedLanguageCode,
853 - sortBalanceBy: sortBalanceBy,
854 - pinNativeTokenAtTop: pinNativeTokenAtTop,
855 - useEtherscan: useEtherscan,
856 - defaultNanoRep: defaultNanoRep,
856 + initialMoneroSeedType: moneroSeedType,
857 + initialAppSecure: isAppSecure,
858 + initialDisableBuy: disableBuy,
859 + initialDisableSell: disableSell,
860 + initialDefaultBuyProvider: defaultBuyProvider,
861 + initialFiatMode: currentFiatApiMode,
862 + initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
863 + initialCake2FAPresetOptions: selectedCake2FAPreset,
864 + initialUseTOTP2FA: useTOTP2FA,
865 + initialTotpSecretKey: totpSecretKey,
866 + initialFailedTokenTrial: tokenTrialNumber,
867 + initialExchangeStatus: exchangeStatus,
868 + initialTheme: savedTheme,
869 + actionlistDisplayMode: actionListDisplayMode,
870 + initialPinLength: pinLength,
871 + pinTimeOutDuration: pinCodeTimeOutDuration,
872 + seedPhraseLength: seedPhraseWordCount,initialLanguageCode: savedLanguageCode,
873 + sortBalanceBy: sortBalanceBy,
874 + pinNativeTokenAtTop: pinNativeTokenAtTop,
875 + useEtherscan: useEtherscan,
876 + defaultNanoRep: defaultNanoRep,
877 defaultBananoRep: defaultBananoRep,
878 lookupsTwitter: lookupsTwitter,
879 lookupsMastodon: lookupsMastodon,
@@ -862,10 +882,10 @@ abstract class SettingsStoreBase with Store {
882 lookupsOpenAlias: lookupsOpenAlias,
883 lookupsENS: lookupsENS,
884 initialMoneroTransactionPriority: moneroTransactionPriority,
865 - initialBitcoinTransactionPriority: bitcoinTransactionPriority,
866 - initialHavenTransactionPriority: havenTransactionPriority,
867 - initialLitecoinTransactionPriority: litecoinTransactionPriority,
868 - initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority,
885 + initialBitcoinTransactionPriority: bitcoinTransactionPriority,
886 + initialHavenTransactionPriority: havenTransactionPriority,
887 + initialLitecoinTransactionPriority: litecoinTransactionPriority,
888 + initialBitcoinCashTransactionPriority: bitcoinCashTransactionPriority,
889 initialShouldRequireTOTP2FAForAccessingWallet: shouldRequireTOTP2FAForAccessingWallet,
890 initialShouldRequireTOTP2FAForSendsToContact: shouldRequireTOTP2FAForSendsToContact,
891 initialShouldRequireTOTP2FAForSendsToNonContact: shouldRequireTOTP2FAForSendsToNonContact,
@@ -927,6 +947,12 @@ abstract class SettingsStoreBase with Store {
947 ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses)
948 : defaultAutoGenerateSubaddressStatus;
949
950 + final _moneroSeedType = sharedPreferences.getInt(PreferencesKey.moneroSeedType);
951 +
952 + moneroSeedType = _moneroSeedType != null
953 + ? SeedType.deserialize(raw: _moneroSeedType)
954 + : defaultMoneroSeedType;
955 +
956 balanceDisplayMode = BalanceDisplayMode.deserialize(
957 raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!);
958 shouldSaveRecipientAddress =
lib/view_model/advanced_privacy_settings_view_model.dart
+9
@@ -1,6 +1,7 @@
1 import 'package:cake_wallet/entities/exchange_api_mode.dart';
2 import 'package:cake_wallet/entities/fiat_api_mode.dart';
3 import 'package:cake_wallet/entities/seed_phrase_length.dart';
4 +import 'package:cake_wallet/entities/seed_type.dart';
5 import 'package:cake_wallet/store/settings_store.dart';
6 import 'package:cw_core/wallet_type.dart';
7 import 'package:mobx/mobx.dart';
@@ -19,6 +20,9 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
20 @computed
21 FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode;
22
23 + @computed
24 + SeedType get seedType => _settingsStore.moneroSeedType;
25 +
26 @observable
27 bool _addCustomNode = false;
28
@@ -29,6 +33,8 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
33 bool get hasSeedPhraseLengthOption =>
34 type == WalletType.bitcoinCash || type == WalletType.ethereum;
35
36 + bool get hasSeedTypeOption => type == WalletType.monero;
37 +
38 @computed
39 bool get addCustomNode => _addCustomNode;
40
@@ -38,6 +44,9 @@ abstract class AdvancedPrivacySettingsViewModelBase with Store {
44 @action
45 void setFiatApiMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode;
46
47 + @action
48 + void setSeedType(SeedType seedType) => _settingsStore.moneroSeedType = seedType;
49 +
50 @action
51 void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value;
52
lib/view_model/restore/wallet_restore_from_qr_code.dart
+9 -3
@@ -10,6 +10,7 @@ import 'package:cw_core/wallet_type.dart';
10 import 'package:flutter/cupertino.dart';
11 import 'package:cake_wallet/generated/i18n.dart';
12 import 'package:collection/collection.dart';
13 +import 'package:polyseed/polyseed.dart';
14
15 class WalletRestoreFromQRCode {
16 WalletRestoreFromQRCode();
@@ -51,7 +52,7 @@ class WalletRestoreFromQRCode {
52 RegExp _getPattern(int wordCount) =>
53 RegExp(r'(?<=\W|^)((?:\w+\s+){' + (wordCount - 1).toString() + r'}\w+)(?=\W|$)');
54
54 - List<int> patternCounts = walletType == WalletType.monero ? [25, 14, 13] : [24, 18, 12];
55 + List<int> patternCounts = walletType == WalletType.monero ? [25, 16, 14, 13] : [24, 18, 12];
56
57 for (final count in patternCounts) {
58 final pattern = _getPattern(count);
@@ -123,12 +124,17 @@ class WalletRestoreFromQRCode {
124 }
125
126 if (credentials['seed'] != null) {
126 - final seedValue = credentials['seed'];
127 + final seedValue = credentials['seed'] as String;
128 final words = SeedValidator.getWordList(type: type, language: 'english');
129 +
130 + if (type == WalletType.monero && Polyseed.isValidSeed(seedValue)) {
131 + return WalletRestoreMode.seed;
132 + }
133 +
134 seedValue.split(' ').forEach((element) {
135 if (!words.contains(element)) {
136 throw Exception(
131 - 'Unexpected restore mode: mnemonic_seed is invalid or does\'t match wallet type');
137 + "Unexpected restore mode: mnemonic_seed is invalid or doesn't match wallet type");
138 }
139 });
140 return WalletRestoreMode.seed;
lib/view_model/wallet_new_vm.dart
+4 -5
@@ -1,6 +1,4 @@
1 -import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
1 import 'package:cake_wallet/ethereum/ethereum.dart';
3 -import 'package:flutter/foundation.dart';
2 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
3 import 'package:hive/hive.dart';
4 import 'package:mobx/mobx.dart';
@@ -33,18 +31,19 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
31 bool get hasLanguageSelector => type == WalletType.monero || type == WalletType.haven;
32
33 @override
36 - WalletCredentials getCredentials(dynamic options) {
34 + WalletCredentials getCredentials(dynamic _options) {
35 + final options = _options as List<dynamic>;
36 switch (type) {
37 case WalletType.monero:
38 return monero!.createMoneroNewWalletCredentials(
40 - name: name, language: options as String);
39 + name: name, language: options.first as String, isPolyseed: options.last as bool);
40 case WalletType.bitcoin:
41 return bitcoin!.createBitcoinNewWalletCredentials(name: name);
42 case WalletType.litecoin:
43 return bitcoin!.createBitcoinNewWalletCredentials(name: name);
44 case WalletType.haven:
45 return haven!.createHavenNewWalletCredentials(
47 - name: name, language: options as String);
46 + name: name, language: options.first as String);
47 case WalletType.ethereum:
48 return ethereum!.createEthereumNewWalletCredentials(name: name);
49 case WalletType.bitcoinCash:
pubspec_base.yaml
+3
@@ -104,6 +104,9 @@ dependencies:
104 ref: main
105 socks5_proxy: ^1.0.4
106 flutter_svg: ^2.0.9
107 + polyseed:
108 + git:
109 + url: https://github.com/cake-tech/polyseed_dart.git
110
111 dev_dependencies:
112 flutter_test:
res/values/strings_ar.arb
+7 -1
@@ -739,5 +739,11 @@
739 "unavailable_balance_description": ".ﺎﻫﺪﻴﻤﺠﺗ ءﺎﻐﻟﺇ ﺭﺮﻘﺗ ﻰﺘﺣ ﺕﻼﻣﺎﻌﻤﻠﻟ ﻝﻮﺻﻮﻠﻟ ﺔﻠﺑﺎﻗ ﺮﻴﻏ ﺓﺪﻤﺠﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﻞﻈﺗ ﺎﻤﻨﻴﺑ ،ﺎﻬﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻣﺎﻌﻤﻟﺍ ﻝﺎﻤﺘﻛﺍ ﺩﺮﺠﻤﺑ ﺔﺣﺎﺘﻣ ﺔﻠﻔﻘﻤﻟﺍ ﺓﺪﺻﺭﻷﺍ ﺢﺒﺼﺘﺳ .ﻚﺑ ﺔﺻﺎﺨﻟﺍ ﺕﻼﻤﻌﻟﺍ ﻲﻓ ﻢﻜﺤﺘﻟﺍ ﺕﺍﺩﺍﺪﻋﺇ ﻲﻓ ﻂﺸﻧ ﻞﻜﺸﺑ ﺎﻫﺪﻴﻤﺠﺘﺑ ﺖﻤﻗ",
740 "unspent_change": "يتغير",
741 "seed_hex_form": "بذور المحفظة (شكل عرافة)",
742 - "tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ"
742 + "tor_connection": "ﺭﻮﺗ ﻝﺎﺼﺗﺍ",
743 + "seedtype": "البذور",
744 + "seedtype_legacy": "إرث (25 كلمة)",
745 + "seedtype_polyseed": "بوليسيد (16 كلمة)",
746 + "seed_language_czech": "التشيكية",
747 + "seed_language_korean": "الكورية",
748 + "seed_language_chinese_traditional": "تقاليد صينية)"
749 }
res/values/strings_bg.arb
+7 -1
@@ -735,5 +735,11 @@
735 "unavailable_balance_description": "Неналично салдо: Тази обща сума включва средства, които са заключени в чакащи транзакции и тези, които сте замразили активно в настройките за контрол на монетите. Заключените баланси ще станат достъпни, след като съответните им транзакции бъдат завършени, докато замразените баланси остават недостъпни за транзакции, докато не решите да ги размразите.",
736 "unspent_change": "Промяна",
737 "tor_connection": "Tor връзка",
738 - "seed_hex_form": "Семена от портфейл (шестнадесетична форма)"
738 + "seed_hex_form": "Семена от портфейл (шестнадесетична форма)",
739 + "seedtype": "Семенна тип",
740 + "seedtype_legacy": "Наследство (25 думи)",
741 + "seedtype_polyseed": "Поли семе (16 думи)",
742 + "seed_language_czech": "Чех",
743 + "seed_language_korean": "Корейски",
744 + "seed_language_chinese_traditional": "Традиционен китайски)"
745 }
\ No newline at end of file
res/values/strings_cs.arb
+7 -1
@@ -735,5 +735,11 @@
735 "unavailable_balance_description": "Nedostupný zůstatek: Tento součet zahrnuje prostředky, které jsou uzamčeny v nevyřízených transakcích a ty, které jste aktivně zmrazili v nastavení kontroly mincí. Uzamčené zůstatky budou k dispozici po dokončení příslušných transakcí, zatímco zmrazené zůstatky zůstanou pro transakce nepřístupné, dokud se nerozhodnete je uvolnit.",
736 "unspent_change": "Změna",
737 "tor_connection": "Připojení Tor",
738 - "seed_hex_form": "Semeno peněženky (hex formulář)"
738 + "seed_hex_form": "Semeno peněženky (hex formulář)",
739 + "seedtype": "SeedType",
740 + "seedtype_legacy": "Legacy (25 slov)",
741 + "seedtype_polyseed": "Polyseed (16 slov)",
742 + "seed_language_czech": "čeština",
743 + "seed_language_korean": "korejština",
744 + "seed_language_chinese_traditional": "Číňan (tradiční)"
745 }
\ No newline at end of file
res/values/strings_de.arb
+10 -4
@@ -722,9 +722,9 @@
722 "awaitDAppProcessing": "Bitte warten Sie, bis die dApp die Verarbeitung abgeschlossen hat.",
723 "copyWalletConnectLink": "Kopieren Sie den WalletConnect-Link von dApp und fügen Sie ihn hier ein",
724 "enterWalletConnectURI": "Geben Sie den WalletConnect-URI ein",
725 - "seed_key": "Samenschlüssel",
726 - "enter_seed_phrase": "Geben Sie Ihre Samenphrase ein",
727 - "change_rep_successful": "Erfolgreich veränderte Vertreter",
725 + "seed_key": "Seed-Schlüssel",
726 + "enter_seed_phrase": "Geben Sie Ihre Seed-Phrase ein",
727 + "change_rep_successful": "Vertreter erfolgreich gerändert",
728 "add_contact": "Kontakt hinzufügen",
729 "exchange_provider_unsupported": "${providerName} wird nicht mehr unterstützt!",
730 "domain_looks_up": "Domain-Suchen",
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Nicht verfügbares Guthaben: Diese Summe umfasst Gelder, die in ausstehenden Transaktionen gesperrt sind, und solche, die Sie in Ihren Münzkontrolleinstellungen aktiv eingefroren haben. Gesperrte Guthaben werden verfügbar, sobald die entsprechenden Transaktionen abgeschlossen sind, während eingefrorene Guthaben für Transaktionen nicht zugänglich bleiben, bis Sie sich dazu entschließen, sie wieder freizugeben.",
744 "unspent_change": "Wechselgeld",
745 "tor_connection": "Tor-Verbindung",
746 - "seed_hex_form": "Brieftaschensamen (Sechskantform)"
746 + "seed_hex_form": "Brieftaschensamen (Sechskantform)",
747 + "seedtype": "Seedtyp",
748 + "seedtype_legacy": "Veraltet (25 Wörter)",
749 + "seedtype_polyseed": "Polyseed (16 Wörter)",
750 + "seed_language_czech": "Tschechisch",
751 + "seed_language_korean": "Koreanisch",
752 + "seed_language_chinese_traditional": "Chinesisch (Traditionell)"
753 }
\ No newline at end of file
res/values/strings_en.arb
+7 -1
@@ -744,5 +744,11 @@
744 "unavailable_balance_description": "Unavailable Balance: This total includes funds that are locked in pending transactions and those you have actively frozen in your coin control settings. Locked balances will become available once their respective transactions are completed, while frozen balances remain inaccessible for transactions until you decide to unfreeze them.",
745 "unspent_change": "Change",
746 "tor_connection": "Tor connection",
747 - "seed_hex_form": "Wallet seed (hex form)"
747 + "seed_hex_form": "Wallet seed (hex form)",
748 + "seedtype": "Seedtype",
749 + "seedtype_legacy": "Legacy (25 words)",
750 + "seedtype_polyseed": "Polyseed (16 words)",
751 + "seed_language_czech": "Czech",
752 + "seed_language_korean": "Korean",
753 + "seed_language_chinese_traditional": "Chinese (Traditional)"
754 }
\ No newline at end of file
res/values/strings_es.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Saldo no disponible: este total incluye fondos que están bloqueados en transacciones pendientes y aquellos que usted ha congelado activamente en su configuración de control de monedas. Los saldos bloqueados estarán disponibles una vez que se completen sus respectivas transacciones, mientras que los saldos congelados permanecerán inaccesibles para las transacciones hasta que usted decida descongelarlos.",
744 "unspent_change": "Cambiar",
745 "tor_connection": "conexión tor",
746 - "seed_hex_form": "Semilla de billetera (forma hexadecimal)"
746 + "seed_hex_form": "Semilla de billetera (forma hexadecimal)",
747 + "seedtype": "Type de semillas",
748 + "seedtype_legacy": "Legado (25 palabras)",
749 + "seedtype_polyseed": "Polieta (16 palabras)",
750 + "seed_language_czech": "checo",
751 + "seed_language_korean": "coreano",
752 + "seed_language_chinese_traditional": "Chino tradicional)"
753 }
\ No newline at end of file
res/values/strings_fr.arb
+8 -1
@@ -745,5 +745,12 @@
745 "switchToETHWallet": "Veuillez passer à un portefeuille (wallet) Ethereum et réessayer",
746 "unspent_change": "Changement",
747 "tor_connection": "Connexion Tor",
748 - "seed_hex_form": "Graine du portefeuille (forme hexagonale)"
748 + "seed_hex_form": "Graine du portefeuille (forme hexagonale)",
749 + "camera_permission_is_required": "L'autorisation de la caméra est requise.\nVeuillez l'activer à partir des paramètres de l'application.",
750 + "seedtype": "Type de type graine",
751 + "seedtype_legacy": "Héritage (25 mots)",
752 + "seedtype_polyseed": "Polyseed (16 mots)",
753 + "seed_language_czech": "tchèque",
754 + "seed_language_korean": "coréen",
755 + "seed_language_chinese_traditional": "Chinois (Traditionnel)"
756 }
\ No newline at end of file
res/values/strings_ha.arb
+7 -1
@@ -721,5 +721,11 @@
721 "unavailable_balance_description": "Ma'auni Babu: Wannan jimlar ya haɗa da kuɗi waɗanda ke kulle a cikin ma'amaloli da ke jiran aiki da waɗanda kuka daskare sosai a cikin saitunan sarrafa kuɗin ku. Ma'auni da aka kulle za su kasance da zarar an kammala ma'amalolinsu, yayin da daskararrun ma'auni ba za su iya samun damar yin ciniki ba har sai kun yanke shawarar cire su.",
722 "unspent_change": "Canza",
723 "tor_connection": "Tor haɗin gwiwa",
724 - "seed_hex_form": "Gany Sero (form form)"
724 + "seed_hex_form": "Gany Sero (form form)",
725 + "seedtype": "Seedtype",
726 + "seedtype_legacy": "Legacy (25 kalmomi)",
727 + "seedtype_polyseed": "Polyseed (16 kalmomi)",
728 + "seed_language_czech": "Czech",
729 + "seed_language_korean": "Yaren Koriya",
730 + "seed_language_chinese_traditional": "Sinanci (na gargajiya)"
731 }
\ No newline at end of file
res/values/strings_hi.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "अनुपलब्ध शेष राशि: इस कुल में वे धनराशि शामिल हैं जो लंबित लेनदेन में बंद हैं और जिन्हें आपने अपनी सिक्का नियंत्रण सेटिंग्स में सक्रिय रूप से जमा कर रखा है। लॉक किए गए शेष उनके संबंधित लेन-देन पूरे होने के बाद उपलब्ध हो जाएंगे, जबकि जमे हुए शेष लेन-देन के लिए अप्राप्य रहेंगे जब तक कि आप उन्हें अनफ्रीज करने का निर्णय नहीं लेते।",
744 "unspent_change": "परिवर्तन",
745 "tor_connection": "टोर कनेक्शन",
746 - "seed_hex_form": "वॉलेट सीड (हेक्स फॉर्म)"
746 + "seed_hex_form": "वॉलेट सीड (हेक्स फॉर्म)",
747 + "seedtype": "बीज",
748 + "seedtype_legacy": "विरासत (25 शब्द)",
749 + "seedtype_polyseed": "पॉलीसीड (16 शब्द)",
750 + "seed_language_czech": "चेक",
751 + "seed_language_korean": "कोरियाई",
752 + "seed_language_chinese_traditional": "चीनी पारंपरिक)"
753 }
\ No newline at end of file
res/values/strings_hr.arb
+7 -1
@@ -741,5 +741,11 @@
741 "unavailable_balance_description": "Nedostupno stanje: Ovaj ukupni iznos uključuje sredstva koja su zaključana u transakcijama na čekanju i ona koja ste aktivno zamrznuli u postavkama kontrole novčića. Zaključani saldi postat će dostupni kada se dovrše njihove transakcije, dok zamrznuti saldi ostaju nedostupni za transakcije sve dok ih ne odlučite odmrznuti.",
742 "unspent_change": "Promijeniti",
743 "tor_connection": "Tor veza",
744 - "seed_hex_form": "Sjeme novčanika (šesterokutni oblik)"
744 + "seed_hex_form": "Sjeme novčanika (šesterokutni oblik)",
745 + "seedtype": "Sjemenska vrsta",
746 + "seedtype_legacy": "Nasljeđe (25 riječi)",
747 + "seedtype_polyseed": "Poliseed (16 riječi)",
748 + "seed_language_czech": "češki",
749 + "seed_language_korean": "korejski",
750 + "seed_language_chinese_traditional": "Kinesko tradicionalno)"
751 }
\ No newline at end of file
res/values/strings_id.arb
+7 -1
@@ -731,5 +731,11 @@
731 "unavailable_balance_description": "Saldo Tidak Tersedia: Total ini termasuk dana yang terkunci dalam transaksi yang tertunda dan dana yang telah Anda bekukan secara aktif di pengaturan kontrol koin Anda. Saldo yang terkunci akan tersedia setelah transaksi masing-masing selesai, sedangkan saldo yang dibekukan tetap tidak dapat diakses untuk transaksi sampai Anda memutuskan untuk mencairkannya.",
732 "unspent_change": "Mengubah",
733 "tor_connection": "koneksi Tor",
734 - "seed_hex_form": "Biji dompet (bentuk hex)"
734 + "seed_hex_form": "Biji dompet (bentuk hex)",
735 + "seedtype": "Seedtype",
736 + "seedtype_legacy": "Legacy (25 kata)",
737 + "seedtype_polyseed": "Polyseed (16 kata)",
738 + "seed_language_czech": "Ceko",
739 + "seed_language_korean": "Korea",
740 + "seed_language_chinese_traditional": "Cina tradisional)"
741 }
\ No newline at end of file
res/values/strings_it.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Saldo non disponibile: questo totale include i fondi bloccati nelle transazioni in sospeso e quelli che hai congelato attivamente nelle impostazioni di controllo delle monete. I saldi bloccati diventeranno disponibili una volta completate le rispettive transazioni, mentre i saldi congelati rimarranno inaccessibili per le transazioni finché non deciderai di sbloccarli.",
744 "unspent_change": "Modifica",
745 "tor_connection": "Connessione Tor",
746 - "seed_hex_form": "Seme di portafoglio (forma esadecimale)"
746 + "seed_hex_form": "Seme di portafoglio (forma esadecimale)",
747 + "seedtype": "Seedtype",
748 + "seedtype_legacy": "Legacy (25 parole)",
749 + "seedtype_polyseed": "Polyseed (16 parole)",
750 + "seed_language_czech": "ceco",
751 + "seed_language_korean": "coreano",
752 + "seed_language_chinese_traditional": "Cinese tradizionale)"
753 }
\ No newline at end of file
res/values/strings_ja.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "利用不可能な残高: この合計には、保留中のトランザクションにロックされている資金と、コイン管理設定でアクティブに凍結した資金が含まれます。ロックされた残高は、それぞれの取引が完了すると利用可能になりますが、凍結された残高は、凍結を解除するまで取引にアクセスできません。",
744 "unspent_change": "変化",
745 "tor_connection": "Tor接続",
746 - "seed_hex_form": "ウォレットシード(ヘックスフォーム)"
746 + "seed_hex_form": "ウォレットシード(ヘックスフォーム)",
747 + "seedtype": "SeedType",
748 + "seedtype_legacy": "レガシー(25語)",
749 + "seedtype_polyseed": "ポリシード(16語)",
750 + "seed_language_czech": "チェコ",
751 + "seed_language_korean": "韓国語",
752 + "seed_language_chinese_traditional": "中国の伝統的な)"
753 }
\ No newline at end of file
res/values/strings_ko.arb
+7 -1
@@ -741,5 +741,11 @@
741 "unavailable_balance_description": "사용할 수 없는 잔액: 이 총계에는 보류 중인 거래에 잠겨 있는 자금과 코인 관리 설정에서 적극적으로 동결된 자금이 포함됩니다. 잠긴 잔액은 해당 거래가 완료되면 사용할 수 있게 되며, 동결된 잔액은 동결을 해제하기 전까지 거래에 액세스할 수 없습니다.",
742 "unspent_change": "변화",
743 "tor_connection": "토르 연결",
744 - "seed_hex_form": "지갑 씨앗 (16 진 양식)"
744 + "seed_hex_form": "지갑 씨앗 (16 진 양식)",
745 + "seedtype": "시드 타입",
746 + "seedtype_legacy": "레거시 (25 단어)",
747 + "seedtype_polyseed": "다문 (16 단어)",
748 + "seed_language_czech": "체코 사람",
749 + "seed_language_korean": "한국인",
750 + "seed_language_chinese_traditional": "중국 전통)"
751 }
\ No newline at end of file
res/values/strings_my.arb
+7 -1
@@ -741,5 +741,11 @@
741 "unavailable_balance_description": "မရရှိနိုင်သော လက်ကျန်ငွေ- ဤစုစုပေါင်းတွင် ဆိုင်းငံ့ထားသော ငွေပေးငွေယူများတွင် သော့ခတ်ထားသော ငွေကြေးများနှင့် သင်၏ coin ထိန်းချုပ်မှုဆက်တင်များတွင် သင် တက်ကြွစွာ အေးခဲထားသော ငွေများ ပါဝင်သည်။ သော့ခတ်ထားသော လက်ကျန်ငွေများကို ၎င်းတို့၏ သက်ဆိုင်ရာ ငွေပေးငွေယူများ ပြီးမြောက်သည်နှင့် တပြိုင်နက် ရရှိနိုင်မည်ဖြစ်ပြီး၊ အေးခဲထားသော လက်ကျန်များကို ၎င်းတို့အား ပြန်ဖြုတ်ရန် သင်ဆုံးဖြတ်သည်အထိ ငွေပေးငွေယူများအတွက် ဆက်လက်၍မရနိုင်ပါ။",
742 "unspent_change": "ပေြာင်းလဲခြင်း",
743 "tor_connection": "Tor ချိတ်ဆက်မှု",
744 - "seed_hex_form": "ပိုက်ဆံအိတ်မျိုးစေ့ (Hex Form)"
744 + "seed_hex_form": "ပိုက်ဆံအိတ်မျိုးစေ့ (Hex Form)",
745 + "seedtype": "မျိုးပွားခြင်း",
746 + "seedtype_legacy": "အမွေအနှစ် (စကားလုံး 25 လုံး)",
747 + "seedtype_polyseed": "polyseed (စကားလုံး 16 လုံး)",
748 + "seed_language_czech": "ချက်",
749 + "seed_language_korean": "ကိုးရီးယား",
750 + "seed_language_chinese_traditional": "တရုတ်ရိုးရာ)"
751 }
\ No newline at end of file
res/values/strings_nl.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Niet-beschikbaar saldo: Dit totaal omvat het geld dat is vergrendeld in lopende transacties en het geld dat u actief hebt bevroren in uw muntcontrole-instellingen. Vergrendelde saldi komen beschikbaar zodra de betreffende transacties zijn voltooid, terwijl bevroren saldi ontoegankelijk blijven voor transacties totdat u besluit ze weer vrij te geven.",
744 "unspent_change": "Wijziging",
745 "tor_connection": "Tor-verbinding",
746 - "seed_hex_form": "Portemonnee zaad (hexvorm)"
746 + "seed_hex_form": "Portemonnee zaad (hexvorm)",
747 + "seedtype": "Zaadtype",
748 + "seedtype_legacy": "Legacy (25 woorden)",
749 + "seedtype_polyseed": "Polyseed (16 woorden)",
750 + "seed_language_czech": "Tsjechisch",
751 + "seed_language_korean": "Koreaans",
752 + "seed_language_chinese_traditional": "Chinese traditionele)"
753 }
\ No newline at end of file
res/values/strings_pl.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Niedostępne saldo: Suma ta obejmuje środki zablokowane w transakcjach oczekujących oraz te, które aktywnie zamroziłeś w ustawieniach kontroli monet. Zablokowane salda staną się dostępne po zakończeniu odpowiednich transakcji, natomiast zamrożone salda pozostaną niedostępne dla transakcji, dopóki nie zdecydujesz się ich odblokować.",
744 "unspent_change": "Zmiana",
745 "tor_connection": "Połączenie Torem",
746 - "seed_hex_form": "Nasiona portfela (forma sześciokątna)"
746 + "seed_hex_form": "Nasiona portfela (forma sześciokątna)",
747 + "seedtype": "Sedtype",
748 + "seedtype_legacy": "Dziedzictwo (25 słów)",
749 + "seedtype_polyseed": "Poliqueed (16 słów)",
750 + "seed_language_czech": "Czech",
751 + "seed_language_korean": "koreański",
752 + "seed_language_chinese_traditional": "Chiński tradycyjny)"
753 }
\ No newline at end of file
res/values/strings_pt.arb
+7 -1
@@ -742,5 +742,11 @@
742 "unavailable_balance_description": "Saldo Indisponível: Este total inclui fundos bloqueados em transações pendentes e aqueles que você congelou ativamente nas configurações de controle de moedas. Os saldos bloqueados ficarão disponíveis assim que suas respectivas transações forem concluídas, enquanto os saldos congelados permanecerão inacessíveis para transações até que você decida descongelá-los.",
743 "unspent_change": "Mudar",
744 "tor_connection": "Conexão Tor",
745 - "seed_hex_form": "Semente de carteira (forma hexadecimal)"
745 + "seed_hex_form": "Semente de carteira (forma hexadecimal)",
746 + "seedtype": "SeedType",
747 + "seedtype_legacy": "Legado (25 palavras)",
748 + "seedtype_polyseed": "Polyseed (16 palavras)",
749 + "seed_language_czech": "Tcheco",
750 + "seed_language_korean": "coreano",
751 + "seed_language_chinese_traditional": "Chinês tradicional)"
752 }
\ No newline at end of file
res/values/strings_ru.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Недоступный баланс: в эту сумму входят средства, заблокированные в ожидающих транзакциях, и средства, которые вы активно заморозили в настройках управления монетами. Заблокированные балансы станут доступны после завершения соответствующих транзакций, а замороженные балансы останутся недоступными для транзакций, пока вы не решите их разморозить.",
744 "unspent_change": "Изменять",
745 "tor_connection": "Тор соединение",
746 - "seed_hex_form": "Семя кошелька (шестнадцатеричная форма)"
746 + "seed_hex_form": "Семя кошелька (шестнадцатеричная форма)",
747 + "seedtype": "SEEDTYPE",
748 + "seedtype_legacy": "Наследие (25 слов)",
749 + "seedtype_polyseed": "Полиса (16 слов)",
750 + "seed_language_czech": "Чешский",
751 + "seed_language_korean": "Корейский",
752 + "seed_language_chinese_traditional": "Китайский традиционный)"
753 }
\ No newline at end of file
res/values/strings_th.arb
+7 -1
@@ -741,5 +741,11 @@
741 "unavailable_balance_description": "ยอดคงเหลือที่ไม่พร้อมใช้งาน: ยอดรวมนี้รวมถึงเงินทุนที่ถูกล็อคในการทำธุรกรรมที่รอดำเนินการและที่คุณได้แช่แข็งไว้ในการตั้งค่าการควบคุมเหรียญของคุณ ยอดคงเหลือที่ถูกล็อคจะพร้อมใช้งานเมื่อธุรกรรมที่เกี่ยวข้องเสร็จสมบูรณ์ ในขณะที่ยอดคงเหลือที่แช่แข็งจะไม่สามารถเข้าถึงได้สำหรับธุรกรรมจนกว่าคุณจะตัดสินใจยกเลิกการแช่แข็ง",
742 "unspent_change": "เปลี่ยน",
743 "tor_connection": "การเชื่อมต่อทอร์",
744 - "seed_hex_form": "เมล็ดกระเป๋าเงิน (รูปแบบฐานสิบหก)"
744 + "seed_hex_form": "เมล็ดกระเป๋าเงิน (รูปแบบฐานสิบหก)",
745 + "seedtype": "เมล็ดพันธุ์",
746 + "seedtype_legacy": "มรดก (25 คำ)",
747 + "seedtype_polyseed": "โพลีส (16 คำ)",
748 + "seed_language_czech": "ภาษาเช็ก",
749 + "seed_language_korean": "เกาหลี",
750 + "seed_language_chinese_traditional": "จีน (ดั้งเดิม)"
751 }
\ No newline at end of file
res/values/strings_tl.arb
+6 -1
@@ -738,5 +738,10 @@
738 "unavailable_balance_description": "Hindi Available na Balanse: Kasama sa kabuuang ito ang mga pondong naka-lock sa mga nakabinbing transaksyon at ang mga aktibong na-freeze mo sa iyong mga setting ng kontrol ng coin. Magiging available ang mga naka-lock na balanse kapag nakumpleto na ang kani-kanilang mga transaksyon, habang ang mga nakapirming balanse ay nananatiling hindi naa-access para sa mga transaksyon hanggang sa magpasya kang i-unfreeze ang mga ito.",
739 "unspent_change": "Baguhin",
740 "tor_connection": "Koneksyon ng Tor",
741 - "seed_hex_form": "Wallet seed (hex form)"
741 + "seed_hex_form": "Wallet seed (hex form)",
742 + "seedtype_legacy": "Pamana (25 salita)",
743 + "seedtype_polyseed": "Polyseed (16 na salita)",
744 + "seed_language_czech": "Czech",
745 + "seed_language_korean": "Korean",
746 + "seed_language_chinese_traditional": "Intsik (tradisyonal)"
747 }
\ No newline at end of file
res/values/strings_tr.arb
+7 -1
@@ -741,5 +741,11 @@
741 "unavailable_balance_description": "Kullanılamayan Bakiye: Bu toplam, bekleyen işlemlerde kilitlenen fonları ve jeton kontrol ayarlarınızda aktif olarak dondurduğunuz fonları içerir. Kilitli bakiyeler, ilgili işlemleri tamamlandıktan sonra kullanılabilir hale gelir; dondurulmuş bakiyeler ise siz onları dondurmaya karar verene kadar işlemler için erişilemez durumda kalır.",
742 "unspent_change": "Değiştirmek",
743 "tor_connection": "Tor bağlantısı",
744 - "seed_hex_form": "Cüzdan tohumu (onaltılık form)"
744 + "seed_hex_form": "Cüzdan tohumu (onaltılık form)",
745 + "seedtype": "Tohum",
746 + "seedtype_legacy": "Miras (25 kelime)",
747 + "seedtype_polyseed": "Polyseed (16 kelime)",
748 + "seed_language_czech": "Çek",
749 + "seed_language_korean": "Koreli",
750 + "seed_language_chinese_traditional": "Çin geleneği)"
751 }
\ No newline at end of file
res/values/strings_uk.arb
+7 -1
@@ -743,5 +743,11 @@
743 "unavailable_balance_description": "Недоступний баланс: ця сума включає кошти, заблоковані в незавершених транзакціях, і ті, які ви активно заморозили в налаштуваннях контролю монет. Заблоковані баланси стануть доступними після завершення відповідних транзакцій, тоді як заморожені баланси залишаються недоступними для транзакцій, доки ви не вирішите їх розморозити.",
744 "unspent_change": "Зміна",
745 "tor_connection": "Підключення Tor",
746 - "seed_hex_form": "Насіння гаманця (шістнадцяткова форма)"
746 + "seed_hex_form": "Насіння гаманця (шістнадцяткова форма)",
747 + "seedtype": "Насіннєвий тип",
748 + "seedtype_legacy": "Спадщина (25 слів)",
749 + "seedtype_polyseed": "Полісей (16 слів)",
750 + "seed_language_czech": "Чеський",
751 + "seed_language_korean": "Корейський",
752 + "seed_language_chinese_traditional": "Китайський традиційний)"
753 }
\ No newline at end of file
res/values/strings_ur.arb
+7 -1
@@ -735,5 +735,11 @@
735 "unavailable_balance_description": "۔ﮯﺗﺮﮐ ﮟﯿﮩﻧ ﮧﻠﺼﯿﻓ ﺎﮐ ﮯﻧﺮﮐ ﺪﻤﺠﻨﻣ ﻥﺍ ﮟﯿﮩﻧﺍ ﭖﺁ ﮧﮐ ﮏﺗ ﺐﺟ ﮟﯿﮨ ﮯﺘﮨﺭ ﯽﺋﺎﺳﺭ ﻞﺑﺎﻗﺎﻧ ﮏﺗ ﺖﻗﻭ ﺱﺍ ﮯﯿﻟ ﮯﮐ ﻦﯾﺩ ﻦﯿﻟ ﺲﻨﻠﯿﺑ ﺪﻤﺠﻨﻣ ﮧﮐ ﺐﺟ ،ﮯﮔ ﮟﯿﺋﺎﺟ ﻮﮨ ﺏﺎﯿﺘﺳﺩ ﺲﻨﻠﯿﺑ ﻞﻔﻘﻣ ﺪﻌﺑ ﮯﮐ ﮯﻧﻮﮨ ﻞﻤﮑﻣ ﻦﯾﺩ ﻦﯿﻟ ﮧﻘﻠﻌﺘﻣ ﮯﮐ ﻥﺍ ۔ﮯﮨ ﺎﮭﮐﺭ ﺮ",
736 "unspent_change": "تبدیل کریں",
737 "seed_hex_form": "پرس بیج (ہیکس فارم)",
738 - "tor_connection": "ﻦﺸﮑﻨﮐ ﺭﻮﭨ"
738 + "tor_connection": "ﻦﺸﮑﻨﮐ ﺭﻮﭨ",
739 + "seedtype": "سیڈ ٹائپ",
740 + "seedtype_legacy": "میراث (25 الفاظ)",
741 + "seedtype_polyseed": "پالیسیڈ (16 الفاظ)",
742 + "seed_language_czech": "چیک",
743 + "seed_language_korean": "کورین",
744 + "seed_language_chinese_traditional": "چینی (روایتی)"
745 }
\ No newline at end of file
res/values/strings_yo.arb
+7 -1
@@ -737,5 +737,11 @@
737 "unavailable_balance_description": "Iwontunws.funfun ti ko si: Lapapọ yii pẹlu awọn owo ti o wa ni titiipa ni awọn iṣowo isunmọ ati awọn ti o ti didi ni itara ninu awọn eto iṣakoso owo rẹ. Awọn iwọntunwọnsi titiipa yoo wa ni kete ti awọn iṣowo oniwun wọn ba ti pari, lakoko ti awọn iwọntunwọnsi tio tutunini ko ni iraye si fun awọn iṣowo titi iwọ o fi pinnu lati mu wọn kuro.",
738 "unspent_change": "Yipada",
739 "tor_connection": "Tor asopọ",
740 - "seed_hex_form": "Irú Opamọwọ apamọwọ (HOX)"
740 + "seed_hex_form": "Irú Opamọwọ apamọwọ (HOX)",
741 + "seedtype": "Irugbin-seetypu",
742 + "seedtype_legacy": "Legacy (awọn ọrọ 25)",
743 + "seedtype_polyseed": "Polyseed (awọn ọrọ 16)",
744 + "seed_language_czech": "Czech",
745 + "seed_language_korean": "Ara ẹni",
746 + "seed_language_chinese_traditional": "Kannada (ibile)"
747 }
\ No newline at end of file
res/values/strings_zh.arb
+7 -1
@@ -742,5 +742,11 @@
742 "unavailable_balance_description": "不可用余额:此总额包括锁定在待处理交易中的资金以及您在硬币控制设置中主动冻结的资金。一旦各自的交易完成,锁定的余额将变得可用,而冻结的余额在您决定解冻之前仍然无法进行交易。",
743 "unspent_change": "改变",
744 "tor_connection": "Tor连接",
745 - "seed_hex_form": "钱包种子(十六进制形式)"
745 + "seed_hex_form": "钱包种子(十六进制形式)",
746 + "seedtype": "籽粒",
747 + "seedtype_legacy": "遗产(25个单词)",
748 + "seedtype_polyseed": "多种物品(16个单词)",
749 + "seed_language_czech": "捷克",
750 + "seed_language_korean": "韩国人",
751 + "seed_language_chinese_traditional": "中国传统的)"
752 }
\ No newline at end of file
tool/configure.dart
+3 -4
@@ -134,7 +134,6 @@ import 'package:cw_core/unspent_transaction_output.dart';
134 import 'package:cw_core/unspent_coins_info.dart';
135 import 'package:cw_monero/monero_unspent.dart';
136 import 'package:mobx/mobx.dart';
137 -import 'package:flutter/foundation.dart';
137 import 'package:cw_core/wallet_credentials.dart';
138 import 'package:cw_core/wallet_info.dart';
139 import 'package:cw_core/transaction_priority.dart';
@@ -144,7 +143,8 @@ import 'package:cw_core/balance.dart';
143 import 'package:cw_core/output_info.dart';
144 import 'package:cake_wallet/view_model/send/output.dart';
145 import 'package:cw_core/wallet_service.dart';
147 -import 'package:hive/hive.dart';""";
146 +import 'package:hive/hive.dart';
147 +import 'package:polyseed/polyseed.dart';""";
148 const moneroCWHeaders = """
149 import 'package:cw_core/get_height_by_date.dart';
150 import 'package:cw_core/monero_amount_format.dart';
@@ -152,7 +152,6 @@ import 'package:cw_core/monero_transaction_priority.dart';
152 import 'package:cw_monero/monero_wallet_service.dart';
153 import 'package:cw_monero/monero_wallet.dart';
154 import 'package:cw_monero/monero_transaction_info.dart';
155 -import 'package:cw_monero/monero_transaction_history.dart';
155 import 'package:cw_monero/monero_transaction_creation_credentials.dart';
156 import 'package:cw_core/account.dart' as monero_account;
157 import 'package:cw_monero/api/wallet.dart' as monero_wallet_api;
@@ -255,7 +254,7 @@ abstract class Monero {
254 required String language,
255 required int height});
256 WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required int height, required String mnemonic});
258 - WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, String password,});
257 + WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, String password});
258 Map<String, String> getKeys(Object wallet);
259 Object createMoneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority});
260 Object createMoneroTransactionCreationCredentialsRaw({required List<OutputInfo> outputs, required TransactionPriority priority});