CW-723-Add-Monero-support-to-the-Shared-Seed-feature-in-Cake (#2131)

* feat: add exodus style bip39 to monero legacy seed * feat: restore monero wallet from bip39 and add test * bug: fix wrong naming in CI * feat: add monero bip39 UI flow * fix: monero.dart generation * fix: skip monero_wallet_service tests till CI is fixed * ci: copy monero_libwallet2_api_c.so to /usr/lib for testing ci: reduce timeout for cw_monero tests * fix: monero wallet creation credentials default to bip39 if mnemonic are set * fix: do not skip monero wallets services test * fix: Include non bip39 monero wallets on Wallet Group * fix: null pointer stemming from missing language selector if seed is selected * fix: Fixes to Bip39 Creation and restore - Do not restore from 0 for fresh bip39 wallet - disallow restoring bip39 wallet without date or height * fix: Fixes to Bip39 restore - Refresh height is now getting set correctly - Add new create monero wallet tests - Add seed-language English for Bip39 Monero wallets - Fix seed-type naming * feat (cw_monero): Store monero wallet after bip39 creation * feat (cw_monero): remove prints from monero_wallet_service_test.dart * fix: exception during seed language autodetect * feat (cw_monero): Add support for passphrases on bip39 seeds * feat (cw_monero): Add support for passphrases on bip39 seeds * fix: seed language selection for recovering bip39 wallets * style: improve readability of isLegacySeedOnly in wallet_keys_view_model.dart * feat: hide monero seed type selector from advanced settings when creating a child wallet * fix(cw_monero): use named arguments for bip39_seed tests --------- Co-authored-by: cyan <cyjan@mrcyjanek.net>

Konstantin Ullrich committed Apr 10, 2025 at 03:31 UTC f58a5fb8fd7df0de04709e568e4930bcbd1e9d2e
51 files changed +702 -283
.github/workflows/pr_test_build_linux.yml
+3
@@ -283,6 +283,9 @@ jobs:
283 xmessage -timeout 30 "restore_wallet_through_seeds_flow_test" &
284 rm -rf ~/.local/share/com.example.cake_wallet/ ~/Documents/cake_wallet/ ~/cake_wallet
285 exec timeout --signal=SIGKILL 900 flutter drive --driver=test_driver/integration_test.dart --target=integration_test/test_suites/restore_wallet_through_seeds_flow_test.dart
286 + - name: Test [cw_monero]
287 + timeout-minutes: 2
288 + run: cd cw_monero && flutter test
289 - name: Stop screen recording, encrypt and upload
290 if: always()
291 run: |
cw_monero/.gitignore
+2 -1
@@ -11,4 +11,5 @@ android/.externalNativeBuild/
11 android/.cxx/
12
13 macos/cw_monero.podspec
14 -macos/External/
\ No newline at end of file
14 +macos/External/
15 +*monero_libwallet2_api_c.*
\ No newline at end of file
cw_monero/lib/api/wallet.dart
+5
@@ -62,6 +62,11 @@ String getSeed() {
62 }
63 return cakepolyseed;
64 }
65 +
66 + final bip39 = monero.Wallet_getCacheAttribute(wptr!, key: "cakewallet.seed.bip39");
67 +
68 + if(bip39.isNotEmpty) return bip39;
69 +
70 final legacy = getSeedLegacy(null);
71 return legacy;
72 }
cw_monero/lib/bip39_seed.dart new
+59
@@ -0,0 +1,59 @@
1 +import 'dart:typed_data';
2 +
3 +import 'package:bip32/bip32.dart' as bip32;
4 +import 'package:bip39/bip39.dart' as bip39;
5 +import 'package:polyseed/polyseed.dart';
6 +
7 +bool isBip39Seed(String mnemonic) => bip39.validateMnemonic(mnemonic);
8 +
9 +String getBip39Seed() => bip39.generateMnemonic();
10 +
11 +String getLegacySeedFromBip39(String mnemonic,
12 + {int accountIndex = 0, String passphrase = ""}) {
13 + final seed = bip39.mnemonicToSeed(mnemonic, passphrase: passphrase);
14 +
15 + final bip32KeyPair =
16 + bip32.BIP32.fromSeed(seed).derivePath("m/44'/128'/$accountIndex'/0/0");
17 +
18 + final spendKey = _reduceECKey(bip32KeyPair.privateKey!);
19 +
20 + return LegacySeedLang.getByEnglishName("English")
21 + .encodePhrase(spendKey.toHexString());
22 +}
23 +
24 +const _ed25519CurveOrder =
25 + "1000000000000000000000000000000014DEF9DEA2F79CD65812631A5CF5D3ED";
26 +
27 +Uint8List _reduceECKey(Uint8List buffer) {
28 + final curveOrder = BigInt.parse(_ed25519CurveOrder, radix: 16);
29 + final bigNumber = _readBytes(buffer);
30 +
31 + var result = bigNumber % curveOrder;
32 +
33 + final resultBuffer = Uint8List(32);
34 + for (var i = 0; i < 32; i++) {
35 + resultBuffer[i] = (result & BigInt.from(0xff)).toInt();
36 + result = result >> 8;
37 + }
38 +
39 + return resultBuffer;
40 +}
41 +
42 +/// Read BigInt from a little-endian Uint8List
43 +/// From https://github.com/dart-lang/sdk/issues/32803#issuecomment-387405784
44 +BigInt _readBytes(Uint8List bytes) {
45 + BigInt read(int start, int end) {
46 + if (end - start <= 4) {
47 + var result = 0;
48 + for (int i = end - 1; i >= start; i--) {
49 + result = result * 256 + bytes[i];
50 + }
51 + return BigInt.from(result);
52 + }
53 + final mid = start + ((end - start) >> 1);
54 + return read(start, mid) +
55 + read(mid, end) * (BigInt.one << ((mid - start) * 8));
56 + }
57 +
58 + return read(0, bytes.length);
59 +}
cw_monero/lib/monero_wallet_service.dart
+126 -36
@@ -1,6 +1,7 @@
1 import 'dart:ffi';
2 import 'dart:io';
3
4 +import 'package:collection/collection.dart';
5 import 'package:cw_core/get_height_by_date.dart';
6 import 'package:cw_core/monero_wallet_utils.dart';
7 import 'package:cw_core/pathForWallet.dart';
@@ -14,29 +15,38 @@ import 'package:cw_core/wallet_type.dart';
15 import 'package:cw_monero/api/account_list.dart';
16 import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
17 import 'package:cw_monero/api/wallet_manager.dart';
18 +import 'package:cw_monero/bip39_seed.dart';
19 import 'package:cw_monero/ledger.dart';
20 import 'package:cw_monero/monero_wallet.dart';
19 -import 'package:collection/collection.dart';
21 import 'package:hive/hive.dart';
22 import 'package:ledger_flutter_plus/ledger_flutter_plus.dart';
23 import 'package:monero/monero.dart' as monero;
24 import 'package:polyseed/polyseed.dart';
25
26 +enum MoneroSeedType { polyseed, legacy, bip39 }
27 +
28 class MoneroNewWalletCredentials extends WalletCredentials {
29 MoneroNewWalletCredentials(
27 - {required String name, required this.language, required this.isPolyseed, String? password, this.passphrase})
30 + {required String name,
31 + required this.language,
32 + required this.seedType,
33 + String? password,
34 + this.passphrase,
35 + this.mnemonic})
36 : super(name: name, password: password);
37
38 final String language;
31 - final bool isPolyseed;
39 + final MoneroSeedType seedType;
40 final String? passphrase;
41 + final String? mnemonic;
42 }
43
44 class MoneroRestoreWalletFromHardwareCredentials extends WalletCredentials {
36 - MoneroRestoreWalletFromHardwareCredentials({required String name,
37 - required this.ledgerConnection,
38 - int height = 0,
39 - String? password})
45 + MoneroRestoreWalletFromHardwareCredentials(
46 + {required String name,
47 + required this.ledgerConnection,
48 + int height = 0,
49 + String? password})
50 : super(name: name, password: password, height: height);
51 LedgerConnection ledgerConnection;
52 }
@@ -60,13 +70,14 @@ class MoneroWalletLoadingException implements Exception {
70 }
71
72 class MoneroRestoreWalletFromKeysCredentials extends WalletCredentials {
63 - MoneroRestoreWalletFromKeysCredentials({required String name,
64 - required String password,
65 - required this.language,
66 - required this.address,
67 - required this.viewKey,
68 - required this.spendKey,
69 - int height = 0})
73 + MoneroRestoreWalletFromKeysCredentials(
74 + {required String name,
75 + required String password,
76 + required this.language,
77 + required this.address,
78 + required this.viewKey,
79 + required this.spendKey,
80 + int height = 0})
81 : super(name: name, password: password, height: height);
82
83 final String language;
@@ -97,27 +108,42 @@ class MoneroWalletService extends WalletService<
108 @override
109 WalletType getType() => WalletType.monero;
110
100 - @override
101 - Future<MoneroWallet> create(MoneroNewWalletCredentials credentials, {bool? isTestnet}) async {
111 + @override
112 + Future<MoneroWallet> create(MoneroNewWalletCredentials credentials,
113 + {bool? isTestnet}) async {
114 try {
115 final path = await pathForWallet(name: credentials.name, type: getType());
116
105 - if (credentials.isPolyseed) {
117 + if (credentials.seedType == MoneroSeedType.bip39) {
118 + return _restoreFromBip39(
119 + path: path,
120 + password: credentials.password!,
121 + mnemonic: credentials.mnemonic ?? getBip39Seed(),
122 + passphrase: credentials.passphrase,
123 + walletInfo: credentials.walletInfo!,
124 + );
125 + }
126 +
127 + if (credentials.seedType == MoneroSeedType.polyseed) {
128 final polyseed = Polyseed.create();
129 final lang = PolyseedLang.getByEnglishName(credentials.language);
130
109 - if (credentials.passphrase != null) polyseed.crypt(credentials.passphrase!);
131 + if (credentials.passphrase != null)
132 + polyseed.crypt(credentials.passphrase!);
133
111 - final heightOverride =
112 - getMoneroHeigthByDate(date: DateTime.now().subtract(Duration(days: 2)));
134 + final heightOverride = getMoneroHeigthByDate(
135 + date: DateTime.now().subtract(Duration(days: 2)));
136
114 - return _restoreFromPolyseed(
115 - path, credentials.password!, polyseed, credentials.walletInfo!, lang,
137 + return _restoreFromPolyseed(path, credentials.password!, polyseed,
138 + credentials.walletInfo!, lang,
139 overrideHeight: heightOverride, passphrase: credentials.passphrase);
140 }
141
142 await monero_wallet_manager.createWallet(
120 - path: path, password: credentials.password!, language: credentials.language, passphrase: credentials.passphrase??"");
143 + path: path,
144 + password: credentials.password!,
145 + language: credentials.language,
146 + passphrase: credentials.passphrase ?? "");
147 final wallet = MoneroWallet(
148 walletInfo: credentials.walletInfo!,
149 unspentCoinsInfo: unspentCoinsInfoSource,
@@ -145,7 +171,8 @@ class MoneroWalletService extends WalletService<
171 }
172
173 @override
148 - Future<MoneroWallet> openWallet(String name, String password, {OpenWalletTry openWalletTry = OpenWalletTry.initial}) async {
174 + Future<MoneroWallet> openWallet(String name, String password,
175 + {OpenWalletTry openWalletTry = OpenWalletTry.initial}) async {
176 try {
177 final path = await pathForWallet(name: name, type: getType());
178
@@ -303,8 +330,28 @@ class MoneroWalletService extends WalletService<
330 rethrow;
331 }
332
333 + try {
334 + if (isBip39Seed(credentials.mnemonic)) {
335 + final path =
336 + await pathForWallet(name: credentials.name, type: getType());
337 +
338 + return _restoreFromBip39(
339 + path: path,
340 + password: credentials.password!,
341 + mnemonic: credentials.mnemonic,
342 + walletInfo: credentials.walletInfo!,
343 + overrideHeight: credentials.height!,
344 + passphrase: credentials.passphrase,
345 + );
346 + }
347 + } catch (e) {
348 + printV("Bip39 restore failed: $e");
349 + rethrow;
350 + }
351 +
352 try {
353 final path = await pathForWallet(name: credentials.name, type: getType());
354 +
355 monero_wallet_manager.restoreFromSeed(
356 path: path,
357 password: credentials.password!,
@@ -325,6 +372,50 @@ class MoneroWalletService extends WalletService<
372 }
373 }
374
375 + Future<MoneroWallet> _restoreFromBip39({
376 + required String path,
377 + required String password,
378 + required String mnemonic,
379 + required WalletInfo walletInfo,
380 + String? passphrase,
381 + int? overrideHeight,
382 + }) async {
383 + walletInfo.derivationInfo = DerivationInfo(
384 + derivationType: DerivationType.bip39,
385 + derivationPath: "m/44'/128'/0'/0/0",
386 + );
387 +
388 + final legacyMnemonic =
389 + getLegacySeedFromBip39(mnemonic, passphrase: passphrase ?? "");
390 + final height =
391 + overrideHeight ?? getMoneroHeigthByDate(date: DateTime.now());
392 +
393 + walletInfo.isRecovery = true;
394 + walletInfo.restoreHeight = height;
395 +
396 + monero_wallet_manager.restoreFromSeed(
397 + path: path,
398 + password: password,
399 + passphrase: '',
400 + seed: legacyMnemonic,
401 + restoreHeight: height,
402 + );
403 +
404 + monero.Wallet_setCacheAttribute(wptr!,
405 + key: "cakewallet.seed.bip39", value: mnemonic);
406 +
407 + monero.Wallet_store(wptr!);
408 +
409 + final wallet = MoneroWallet(
410 + walletInfo: walletInfo,
411 + unspentCoinsInfo: unspentCoinsInfoSource,
412 + password: password,
413 + );
414 + await wallet.init();
415 +
416 + return wallet;
417 + }
418 +
419 Future<MoneroWallet> restoreFromPolyseed(
420 MoneroRestoreWalletFromSeedCredentials credentials) async {
421 try {
@@ -344,23 +435,21 @@ class MoneroWalletService extends WalletService<
435 }
436 }
437
347 - Future<MoneroWallet> _restoreFromPolyseed(
348 - String path, String password, Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
438 + Future<MoneroWallet> _restoreFromPolyseed(String path, String password,
439 + Polyseed polyseed, WalletInfo walletInfo, PolyseedLang lang,
440 {PolyseedCoin coin = PolyseedCoin.POLYSEED_MONERO,
441 int? overrideHeight,
442 String? passphrase}) async {
352 -
353 - if (polyseed.isEncrypted == false &&
354 - (passphrase??'') != "") {
443 + if (polyseed.isEncrypted == false && (passphrase ?? '') != "") {
444 // Fallback to the different passphrase offset method, when a passphrase
445 // was provided but the polyseed is not encrypted.
446 monero_wallet_manager.restoreWalletFromPolyseedWithOffset(
358 - path: path,
359 - password: password,
360 - seed: polyseed.encode(lang, coin),
361 - seedOffset: passphrase??'',
362 - language: "English");
363 -
447 + path: path,
448 + password: password,
449 + seed: polyseed.encode(lang, coin),
450 + seedOffset: passphrase ?? '',
451 + language: "English");
452 +
453 final wallet = MoneroWallet(
454 walletInfo: walletInfo,
455 unspentCoinsInfo: unspentCoinsInfoSource,
@@ -437,7 +526,8 @@ class MoneroWalletService extends WalletService<
526
527 if (walletFilesExist(path)) await repairOldAndroidWallet(name);
528
440 - await monero_wallet_manager.openWalletAsync({'path': path, 'password': password});
529 + await monero_wallet_manager
530 + .openWalletAsync({'path': path, 'password': password});
531 final walletInfo = walletInfoSource.values
532 .firstWhere((info) => info.id == WalletBase.idFor(name, getType()));
533 final wallet = MoneroWallet(
cw_monero/pubspec.lock
+70 -17
@@ -5,18 +5,23 @@ packages:
5 dependency: transitive
6 description:
7 name: _fe_analyzer_shared
8 - sha256: "4897882604d919befd350648c7f91926a9d5de99e67b455bf0917cc2362f4bb8"
8 + sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
9 url: "https://pub.dev"
10 source: hosted
11 - version: "47.0.0"
11 + version: "76.0.0"
12 + _macros:
13 + dependency: transitive
14 + description: dart
15 + source: sdk
16 + version: "0.3.3"
17 analyzer:
18 dependency: transitive
19 description:
20 name: analyzer
16 - sha256: "690e335554a8385bc9d787117d9eb52c0c03ee207a607e593de3c9d71b1cfe80"
21 + sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
22 url: "https://pub.dev"
23 source: hosted
19 - version: "4.7.0"
24 + version: "6.11.0"
25 args:
26 dependency: transitive
27 description:
@@ -41,6 +46,22 @@ packages:
46 url: "https://pub.dev"
47 source: hosted
48 version: "2.11.0"
49 + bip32:
50 + dependency: "direct main"
51 + description:
52 + name: bip32
53 + sha256: "54787cd7a111e9d37394aabbf53d1fc5e2e0e0af2cd01c459147a97c0e3f8a97"
54 + url: "https://pub.dev"
55 + source: hosted
56 + version: "2.0.0"
57 + bip39:
58 + dependency: "direct main"
59 + description:
60 + name: bip39
61 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc
62 + url: "https://pub.dev"
63 + source: hosted
64 + version: "1.0.6"
65 blockchain_utils:
66 dependency: transitive
67 description:
@@ -66,6 +87,14 @@ packages:
87 url: "https://pub.dev"
88 source: hosted
89 version: "2.1.1"
90 + bs58check:
91 + dependency: transitive
92 + description:
93 + name: bs58check
94 + sha256: c4a164d42b25c2f6bc88a8beccb9fc7d01440f3c60ba23663a20a70faf484ea9
95 + url: "https://pub.dev"
96 + source: hosted
97 + version: "1.0.2"
98 build:
99 dependency: transitive
100 description:
@@ -94,10 +123,10 @@ packages:
123 dependency: "direct dev"
124 description:
125 name: build_resolvers
97 - sha256: "687cf90a3951affac1bd5f9ecb5e3e90b60487f3d9cdc359bb310f8876bb02a6"
126 + sha256: b9e4fda21d846e192628e7a4f6deda6888c36b5b69ba02ff291a01fd529140f0
127 url: "https://pub.dev"
128 source: hosted
100 - version: "2.0.10"
129 + version: "2.4.4"
130 build_runner:
131 dependency: "direct dev"
132 description:
@@ -222,10 +251,10 @@ packages:
251 dependency: transitive
252 description:
253 name: dart_style
225 - sha256: "7a03456c3490394c8e7665890333e91ae8a49be43542b616e414449ac358acd4"
254 + sha256: "7306ab8a2359a48d22310ad823521d723acfed60ee1f7e37388e8986853b6820"
255 url: "https://pub.dev"
256 source: hosted
228 - version: "2.2.4"
257 + version: "2.3.8"
258 dbus:
259 dependency: transitive
260 description:
@@ -348,6 +377,14 @@ packages:
377 url: "https://pub.dev"
378 source: hosted
379 version: "2.6.0"
380 + hex:
381 + dependency: transitive
382 + description:
383 + name: hex
384 + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a"
385 + url: "https://pub.dev"
386 + source: hosted
387 + version: "0.2.0"
388 hive:
389 dependency: transitive
390 description:
@@ -360,10 +397,10 @@ packages:
397 dependency: "direct dev"
398 description:
399 name: hive_generator
363 - sha256: "81fd20125cb2ce8fd23623d7744ffbaf653aae93706c9bd3bf7019ea0ace3938"
400 + sha256: "06cb8f58ace74de61f63500564931f9505368f45f98958bd7a6c35ba24159db4"
401 url: "https://pub.dev"
402 source: hosted
366 - version: "1.1.3"
403 + version: "2.0.1"
404 http:
405 dependency: "direct main"
406 description:
@@ -468,6 +505,14 @@ packages:
505 url: "https://pub.dev"
506 source: hosted
507 version: "1.3.0"
508 + macros:
509 + dependency: transitive
510 + description:
511 + name: macros
512 + sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
513 + url: "https://pub.dev"
514 + source: hosted
515 + version: "0.1.3-main.0"
516 matcher:
517 dependency: transitive
518 description:
@@ -512,10 +557,18 @@ packages:
557 dependency: "direct dev"
558 description:
559 name: mobx_codegen
515 - sha256: d4beb9cea4b7b014321235f8fdc7c2193ee0fe1d1198e9da7403f8bc85c4407c
560 + sha256: "990da80722f7d7c0017dec92040b31545d625b15d40204c36a1e63d167c73cdc"
561 url: "https://pub.dev"
562 source: hosted
518 - version: "2.3.0"
563 + version: "2.7.0"
564 + mockito:
565 + dependency: "direct dev"
566 + description:
567 + name: mockito
568 + sha256: f99d8d072e249f719a5531735d146d8cf04c580d93920b04de75bef6dfb2daf6
569 + url: "https://pub.dev"
570 + source: hosted
571 + version: "5.4.5"
572 monero:
573 dependency: "direct main"
574 description:
@@ -735,18 +788,18 @@ packages:
788 dependency: transitive
789 description:
790 name: source_gen
738 - sha256: "2d79738b6bbf38a43920e2b8d189e9a3ce6cc201f4b8fc76be5e4fe377b1c38d"
791 + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
792 url: "https://pub.dev"
793 source: hosted
741 - version: "1.2.6"
794 + version: "1.5.0"
795 source_helper:
796 dependency: transitive
797 description:
798 name: source_helper
746 - sha256: "3b67aade1d52416149c633ba1bb36df44d97c6b51830c2198e934e3fca87ca1f"
799 + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
800 url: "https://pub.dev"
801 source: hosted
749 - version: "1.3.3"
802 + version: "1.3.5"
803 source_span:
804 dependency: transitive
805 description:
@@ -924,5 +977,5 @@ packages:
977 source: hosted
978 version: "3.1.3"
979 sdks:
927 - dart: ">=3.5.0 <4.0.0"
980 + dart: ">=3.6.0 <4.0.0"
981 flutter: ">=3.24.0"
cw_monero/pubspec.yaml
+4 -1
@@ -12,6 +12,8 @@ environment:
12 dependencies:
13 flutter:
14 sdk: flutter
15 + bip39: ^1.0.6
16 + bip32: ^2.0.0
17 ffi: ^2.0.1
18 http: ^1.1.0
19 path_provider: ^2.0.11
@@ -36,7 +38,8 @@ dev_dependencies:
38 build_runner: ^2.4.7
39 build_resolvers: ^2.0.9
40 mobx_codegen: ^2.0.7
39 - hive_generator: ^1.1.3
41 + mockito: ^5.4.5
42 + hive_generator: ^2.0.1
43
44 dependency_overrides:
45 watcher: ^1.1.0
cw_monero/test/bip39_seed_test.dart new
+39
@@ -0,0 +1,39 @@
1 +import 'package:cw_monero/bip39_seed.dart';
2 +import 'package:flutter_test/flutter_test.dart';
3 +
4 +void main() {
5 + group("Exodus Style bip39", () {
6 + group("Test Wallet 1", () {
7 + final bip39Seed = 'meadow tip best belt boss eyebrow control affair eternal piece very shiver';
8 + final expectedLegacySeed0 = "tasked eight afraid laboratory tail feline rift reinvest vane cafe bailed foggy dormant paper jigsaw king hazard suture king dapper dummy jolted dating dwindling king";
9 + final expectedLegacySeed1 = "palace pairing axes mohawk rekindle excess awful juvenile shipped talent nibs efficient dapper biggest swung fight pact innocent emerge issued titans affair nearby noises emerge";
10 +
11 + test("Get legacy Seed from bip39", () {
12 + final legacySeed = getLegacySeedFromBip39(bip39Seed);
13 + expect(legacySeed, expectedLegacySeed0);
14 + });
15 +
16 + test("Get legacy Seed from bip39 with account index", () {
17 + final legacySeed = getLegacySeedFromBip39(bip39Seed, accountIndex: 1);
18 + expect(legacySeed, expectedLegacySeed1);
19 + });
20 + });
21 +
22 + group("Test Wallet 2", () {
23 + final bip39Seed = "color ranch color remove subway public water embrace before begin liberty fault";
24 + final expectedLegacySeed0 = "somewhere problems gauze gigantic intended foxes upcoming saved waffle pipeline lurk bogeys empty wipeout abbey italics novelty tucks rafts elite lunar obnoxious awful bugs elite";
25 + final expectedLegacySeed1 = "playful toxic wildly eluded mesh fainted february mugged maps repent vigilant hitched seventh threaten clue fetches sample diet number alkaline future cottage tuition vegan alkaline";
26 +
27 + test("Get legacy Seed from bip39", () {
28 + final legacySeed = getLegacySeedFromBip39(bip39Seed);
29 + expect(legacySeed, expectedLegacySeed0);
30 + });
31 +
32 + test("Get legacy Seed from bip39 with account index", () {
33 + final legacySeed = getLegacySeedFromBip39(bip39Seed, accountIndex: 1);
34 + expect(legacySeed, expectedLegacySeed1);
35 + });
36 + });
37 +
38 + });
39 +}
cw_monero/test/mock/path_provider.dart new
+29
@@ -0,0 +1,29 @@
1 +import 'package:mockito/mockito.dart';
2 +import 'package:flutter_test/flutter_test.dart';
3 +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
4 +import 'package:plugin_platform_interface/plugin_platform_interface.dart';
5 +
6 +class MockPathProviderPlatform extends Mock
7 + with MockPlatformInterfaceMixin
8 + implements PathProviderPlatform {
9 + Future<String> getTemporaryPath() => throw UnimplementedError();
10 +
11 + Future<String> getApplicationSupportPath() => throw UnimplementedError();
12 +
13 + Future<String> getLibraryPath() => throw UnimplementedError();
14 +
15 + Future<String> getApplicationDocumentsPath() async => "./test/data";
16 +
17 + Future<String> getExternalStoragePath() => throw UnimplementedError();
18 +
19 + Future<List<String>> getExternalCachePaths() => throw UnimplementedError();
20 +
21 + Future<String> getDownloadsPath() => throw UnimplementedError();
22 +
23 + @override
24 + Future<String?> getApplicationCachePath() => throw UnimplementedError();
25 +
26 + @override
27 + Future<List<String>?> getExternalStoragePaths({StorageDirectory? type}) =>
28 + throw UnimplementedError();
29 +}
cw_monero/test/monero_wallet_service_test.dart new
+148
@@ -0,0 +1,148 @@
1 +import 'dart:io';
2 +
3 +import 'package:cw_core/unspent_coins_info.dart';
4 +import 'package:cw_core/wallet_base.dart';
5 +import 'package:cw_core/wallet_info.dart';
6 +import 'package:cw_core/wallet_type.dart';
7 +import 'package:cw_monero/monero_wallet_service.dart';
8 +import 'package:flutter_test/flutter_test.dart';
9 +import 'package:hive/hive.dart';
10 +import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
11 +
12 +import 'mock/path_provider.dart';
13 +import 'utils/setup_monero_c.dart';
14 +
15 +Future<void> main() async {
16 + group("MoneroWalletService Tests", () {
17 + Hive.init('./test/data/db');
18 + late MoneroWalletService walletService;
19 + late File moneroCBinary;
20 +
21 + setUpAll(() async {
22 + PathProviderPlatform.instance = MockPathProviderPlatform();
23 +
24 + final Box<WalletInfo> walletInfoSource =
25 + await Hive.openBox('testWalletInfo');
26 + final Box<UnspentCoinsInfo> unspentCoinsInfoSource =
27 + await Hive.openBox('testUnspentCoinsInfo');
28 +
29 + walletService = MoneroWalletService(walletInfoSource, unspentCoinsInfoSource);
30 + moneroCBinary = getMoneroCBinary().copySync(moneroCBinaryName);
31 + });
32 +
33 + tearDownAll(() {
34 + Directory('./test/data').deleteSync(recursive: true);
35 + moneroCBinary.deleteSync();
36 + });
37 +
38 + group("Create wallet", () {
39 + test("Create Legacy Wallet", () async {
40 + final credentials = _getTestCreateCredentials(
41 + name: 'Create Wallet LS',
42 + language: 'English',
43 + seedType: MoneroSeedType.legacy);
44 + final wallet = await walletService.create(credentials);
45 +
46 + expect(wallet.seed.split(" ").length, 25);
47 + expect(wallet.restoreHeight, greaterThan(3000000));
48 + });
49 +
50 + test("Create Polyseed Wallet", () async {
51 + final credentials = _getTestCreateCredentials(
52 + name: 'Create Wallet PS',
53 + language: 'English',
54 + seedType: MoneroSeedType.polyseed);
55 + final wallet = await walletService.create(credentials);
56 +
57 + expect(wallet.seed.split(" ").length, 16);
58 + expect(wallet.restoreHeight, greaterThan(3000000));
59 + });
60 +
61 + test("Create Bip39 Wallet", () async {
62 + final credentials = _getTestCreateCredentials(
63 + name: 'Create Wallet BS',
64 + language: 'English',
65 + seedType: MoneroSeedType.bip39);
66 + final wallet = await walletService.create(credentials);
67 +
68 + expect(wallet.seed.split(" ").length, 12);
69 + expect(wallet.restoreHeight, greaterThan(3000000));
70 + });
71 + });
72 +
73 + group("Restore wallet", () {
74 + test('Legacy Seed', () async {
75 + final credentials = _getTestRestoreCredentials(
76 + name: 'Test Wallet LS',
77 + mnemonic:
78 + 'ability pockets lordship tomorrow gypsy match neutral uncle avatar betting bicycle junk unzip pyramid lynx mammal edgy empty uneven knowledge juvenile wiring paradise psychic betting',
79 + );
80 +
81 + final wallet = await walletService.restoreFromSeed(credentials);
82 + expect(wallet.walletAddresses.primaryAddress,
83 + '48tLyQXpcwt8w6uKHyb5Zs3vdnoDWAEKFQr1c198o7aX9dBzXP3BTSMVsDiuH3ozDCNqwojb4vNeQZf7xg6URimDLaNtGSN');
84 + });
85 +
86 + test('Bip39 Seed', () async {
87 + final credentials = _getTestRestoreCredentials(
88 + name: 'Test Wallet BS',
89 + mnemonic:
90 + 'color ranch color remove subway public water embrace before begin liberty fault');
91 +
92 + final wallet = await walletService.restoreFromSeed(credentials);
93 + expect(wallet.walletAddresses.primaryAddress,
94 + '49MggvPosJugF8Zq7WAKbsSchz6vbyL6YiUxM4ryfGQDXphs6wiWiXLFWCSshnLPcceGTWUaKfWWMHQAAKESV3TQJVQsL9a');
95 + });
96 + });
97 + });
98 +}
99 +
100 +MoneroRestoreWalletFromSeedCredentials _getTestRestoreCredentials({
101 + required String name,
102 + required String mnemonic,
103 +}) {
104 + final credentials = MoneroRestoreWalletFromSeedCredentials(
105 + name: name, mnemonic: mnemonic, passphrase: '', password: "test");
106 +
107 + credentials.walletInfo = WalletInfo.external(
108 + id: WalletBase.idFor(name, WalletType.monero),
109 + name: name,
110 + type: WalletType.monero,
111 + isRecovery: true,
112 + restoreHeight: credentials.height ?? 0,
113 + date: DateTime.now(),
114 + path: '',
115 + dirPath: '',
116 + address: '',
117 + );
118 + return credentials;
119 +}
120 +
121 +MoneroNewWalletCredentials _getTestCreateCredentials({
122 + required String name,
123 + required String language,
124 + required MoneroSeedType seedType,
125 + String? mnemonic,
126 +}) {
127 + final credentials = MoneroNewWalletCredentials(
128 + name: name,
129 + language: language,
130 + seedType: seedType,
131 + password: "test",
132 + mnemonic: mnemonic,
133 + passphrase: '',
134 + );
135 +
136 + credentials.walletInfo = WalletInfo.external(
137 + id: WalletBase.idFor(name, WalletType.monero),
138 + name: name,
139 + type: WalletType.monero,
140 + isRecovery: false,
141 + restoreHeight: credentials.height ?? 0,
142 + date: DateTime.now(),
143 + path: '',
144 + dirPath: '',
145 + address: '',
146 + );
147 + return credentials;
148 +}
cw_monero/test/utils/setup_monero_c.dart new
+16
@@ -0,0 +1,16 @@
1 +import 'dart:io';
2 +
3 +File getMoneroCBinary() {
4 + if (Platform.isWindows)
5 + return File(
6 + '../scripts/monero_c/release/monero/x86_64-w64-mingw32_libwallet2_api_c.dll');
7 + if (Platform.isMacOS) return File('../macos/monero_libwallet2_api_c.dylib');
8 + return File('../scripts/monero_c/release/monero/x86_64-linux-gnu_libwallet2_api_c.so');
9 +}
10 +
11 +String get moneroCBinaryName {
12 + if (Platform.isWindows)
13 + return "monero_libwallet2_api_c.dll";
14 + if (Platform.isMacOS) return "monero_libwallet2_api_c.dylib";
15 + return "/usr/lib/monero_libwallet2_api_c.so";
16 +}
lib/entities/seed_type.dart
+8 -17
@@ -1,17 +1,17 @@
1 -import 'package:cake_wallet/generated/i18n.dart';
1 import 'package:cw_core/enumerable_item.dart';
2 import 'package:cw_core/wallet_info.dart';
3
4 class MoneroSeedType extends EnumerableItem<int> with Serializable<int> {
5 const MoneroSeedType({required String title, required int raw}) : super(title: title, raw: raw);
6
8 - static const all = [MoneroSeedType.legacy, MoneroSeedType.polyseed];
7 + static const all = [legacy, polyseed, bip39];
8
9 static const defaultSeedType = polyseed;
10
12 - static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)');
13 - static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)');
14 - static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero (14 words)');
11 + static const legacy = MoneroSeedType(raw: 0, title: 'Legacy');
12 + static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed');
13 + static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero');
14 + static const bip39 = MoneroSeedType(raw: 3, title: 'BIP39');
15
16 static MoneroSeedType deserialize({required int raw}) {
17 switch (raw) {
@@ -21,24 +21,15 @@ class MoneroSeedType extends EnumerableItem<int> with Serializable<int> {
21 return polyseed;
22 case 2:
23 return wowneroSeed;
24 + case 3:
25 + return bip39;
26 default:
27 throw Exception('Unexpected token: $raw for SeedType deserialize');
28 }
29 }
30
31 @override
30 - String toString() {
31 - switch (this) {
32 - case MoneroSeedType.legacy:
33 - return S.current.seedtype_legacy;
34 - case MoneroSeedType.polyseed:
35 - return S.current.seedtype_polyseed;
36 - case MoneroSeedType.wowneroSeed:
37 - return S.current.seedtype_wownero;
38 - default:
39 - return '';
40 - }
41 - }
32 + String toString() => title;
33 }
34
35 class BitcoinSeedType extends EnumerableItem<int> with Serializable<int> {
lib/monero/cw_monero.dart
+13 -3
@@ -252,11 +252,21 @@ class CWMonero extends Monero {
252 WalletCredentials createMoneroNewWalletCredentials({
253 required String name,
254 required String language,
255 - required bool isPolyseed,
255 + required int seedType,
256 required String? passphrase,
257 - String? password}) =>
257 + String? password,
258 + String? mnemonic,
259 + }) =>
260 MoneroNewWalletCredentials(
259 - name: name, password: password, language: language, isPolyseed: isPolyseed, passphrase: passphrase);
261 + name: name,
262 + password: password,
263 + language: language,
264 + seedType: seedType == 1
265 + ? MoneroSeedType.polyseed
266 + : (seedType == 3 ? MoneroSeedType.bip39 : MoneroSeedType.legacy),
267 + passphrase: passphrase,
268 + mnemonic: mnemonic,
269 + );
270
271 @override
272 Map<String, String> getKeys(Object wallet) {
lib/reactions/bip39_wallet_utils.dart
+1 -1
@@ -11,8 +11,8 @@ bool isBIP39Wallet(WalletType walletType) {
11 case WalletType.bitcoinCash:
12 case WalletType.nano:
13 case WalletType.banano:
14 - return true;
14 case WalletType.monero:
15 + return true;
16 case WalletType.wownero:
17 case WalletType.haven:
18 case WalletType.zano:
lib/src/screens/new_wallet/advanced_privacy_settings_page.dart
+3 -1
@@ -144,7 +144,9 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
144 ),
145 );
146 }),
147 - if (widget.privacySettingsViewModel.isMoneroSeedTypeOptionsEnabled)
147 + if (widget
148 + .privacySettingsViewModel.isMoneroSeedTypeOptionsEnabled &&
149 + !widget.isChildWallet)
150 Observer(builder: (_) {
151 return SettingsChoicesCell(
152 ChoicesListItem<MoneroSeedType>(
lib/src/screens/new_wallet/new_wallet_page.dart
+6 -2
@@ -305,7 +305,7 @@ class _WalletNameFormState extends State<WalletNameForm> {
305 ),
306 ),
307 ),
308 - if (_walletNewVM.hasLanguageSelector) ...[
308 + if (_walletNewVM.showLanguageSelector) ...[
309 if (_walletNewVM.hasSeedType) ...[
310 Observer(
311 builder: (BuildContext build) => Padding(
@@ -401,7 +401,11 @@ class _WalletNameFormState extends State<WalletNameForm> {
401 } else {
402 await _walletNewVM.create(
403 options: _walletNewVM.hasLanguageSelector
404 - ? [_languageSelectorKey.currentState!.selected, isPolyseed]
404 + ? [
405 + _languageSelectorKey.currentState?.selected ??
406 + defaultSeedLanguage,
407 + widget._seedSettingsViewModel.moneroSeedType
408 + ]
409 : null);
410 }
411 } catch (e) {
lib/src/screens/restore/wallet_restore_from_seed_form.dart
+85 -68
@@ -11,13 +11,15 @@ import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
11 import 'package:cake_wallet/utils/show_pop_up.dart';
12 import 'package:cake_wallet/view_model/restore/restore_wallet.dart';
13 import 'package:cake_wallet/view_model/seed_settings_view_model.dart';
14 +import 'package:cw_core/utils/print_verbose.dart';
15 import 'package:cw_core/wallet_type.dart';
16 import 'package:flutter/material.dart';
17 import 'package:mobx/mobx.dart';
18 import 'package:polyseed/polyseed.dart';
19
20 class WalletRestoreFromSeedForm extends StatefulWidget {
20 - WalletRestoreFromSeedForm({Key? key,
21 + WalletRestoreFromSeedForm({
22 + Key? key,
23 required this.displayLanguageSelector,
24 required this.displayBlockHeightSelector,
25 required this.type,
@@ -47,21 +49,23 @@ class WalletRestoreFromSeedForm extends StatefulWidget {
49
50 @override
51 WalletRestoreFromSeedFormState createState() =>
50 - WalletRestoreFromSeedFormState('English', displayWalletPassword: displayWalletPassword);
52 + WalletRestoreFromSeedFormState('English',
53 + displayWalletPassword: displayWalletPassword);
54 }
55
56 class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
54 - WalletRestoreFromSeedFormState(this.language, {required bool displayWalletPassword})
57 + WalletRestoreFromSeedFormState(this.language,
58 + {required bool displayWalletPassword})
59 : seedWidgetStateKey = GlobalKey<SeedWidgetState>(),
60 blockchainHeightKey = GlobalKey<BlockchainHeightState>(),
61 formKey = GlobalKey<FormState>(),
62 languageController = TextEditingController(),
63 nameTextEditingController = TextEditingController(),
60 - passwordTextEditingController = displayWalletPassword ? TextEditingController() : null,
61 - repeatedPasswordTextEditingController = displayWalletPassword
62 - ? TextEditingController()
63 - : null,
64 - seedTypeController = TextEditingController();
64 + passwordTextEditingController =
65 + displayWalletPassword ? TextEditingController() : null,
66 + repeatedPasswordTextEditingController =
67 + displayWalletPassword ? TextEditingController() : null,
68 + seedTypeController = TextEditingController();
69
70 final GlobalKey<SeedWidgetState> seedWidgetStateKey;
71 final GlobalKey<BlockchainHeightState> blockchainHeightKey;
@@ -83,27 +87,30 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
87 _setLanguageLabel(language);
88
89 if (passwordTextEditingController != null) {
86 - passwordListener = () => widget.onPasswordChange?.call(passwordTextEditingController!.text);
90 + passwordListener = () =>
91 + widget.onPasswordChange?.call(passwordTextEditingController!.text);
92 passwordTextEditingController?.addListener(passwordListener!);
93 }
94
95 if (repeatedPasswordTextEditingController != null) {
91 - repeatedPasswordListener =
92 - () => widget.onRepeatedPasswordChange?.call(repeatedPasswordTextEditingController!.text);
93 - repeatedPasswordTextEditingController?.addListener(repeatedPasswordListener!);
96 + repeatedPasswordListener = () => widget.onRepeatedPasswordChange
97 + ?.call(repeatedPasswordTextEditingController!.text);
98 + repeatedPasswordTextEditingController
99 + ?.addListener(repeatedPasswordListener!);
100 }
101
102 moneroSeedTypeReaction =
97 - reaction((_) => widget.seedSettingsViewModel.moneroSeedType, (MoneroSeedType item) {
98 - _setSeedType(item);
99 - _changeLanguage('English');
100 - });
103 + reaction((_) => widget.seedSettingsViewModel.moneroSeedType,
104 + (MoneroSeedType item) {
105 + _setSeedType(item);
106 + _changeLanguage('English');
107 + });
108
109 super.initState();
110 }
111
112 @override
106 - void dispose() {
113 + void dispose() {
114 moneroSeedTypeReaction();
115
116 if (passwordListener != null) {
@@ -118,16 +125,22 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
125 }
126
127 void onSeedChange(String seed) {
121 - if ((widget.type == WalletType.monero || widget.type == WalletType.wownero) &&
122 - Polyseed.isValidSeed(seed)) {
123 - final lang = PolyseedLang.getByPhrase(seed);
128 + if ([WalletType.monero, WalletType.wownero].contains(widget.type) &&
129 + (seed.split(" ").length == 12 || Polyseed.isValidSeed(seed))) {
130 + try {
131 + final lang = PolyseedLang.getByPhrase(seed);
132
125 - _changeSeedType(MoneroSeedType.polyseed);
126 - _changeLanguage(lang.nameEnglish);
133 + if (widget.type == WalletType.monero && seed.split(" ").length == 12) {
134 + _changeSeedType(MoneroSeedType.bip39);
135 + } else {
136 + _changeSeedType(MoneroSeedType.polyseed);
137 + }
138 + _changeLanguage(lang.nameEnglish, true);
139 + } catch (e) {
140 + printV(e);
141 + }
142 }
128 - if (widget.type == WalletType.wownero && seed
129 - .split(" ")
130 - .length == 14) {
143 + if (widget.type == WalletType.wownero && seed.split(" ").length == 14) {
144 _changeSeedType(MoneroSeedType.wowneroSeed);
145 _changeLanguage("English");
146 }
@@ -147,9 +160,7 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
160 BaseTextFormField(
161 key: ValueKey('wallet_restore_from_seed_wallet_name_textfield_key'),
162 controller: nameTextEditingController,
150 - hintText: S
151 - .of(context)
152 - .wallet_name,
163 + hintText: S.of(context).wallet_name,
164 suffixIcon: IconButton(
165 key: ValueKey('wallet_restore_from_seed_wallet_name_refresh_button_key'),
166 onPressed: () async {
@@ -158,17 +169,17 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
169
170 setState(() {
171 nameTextEditingController.text = rName;
161 - nameTextEditingController.selection = TextSelection.fromPosition(
162 - TextPosition(offset: nameTextEditingController.text.length));
172 + nameTextEditingController.selection =
173 + TextSelection.fromPosition(TextPosition(
174 + offset:
175 + nameTextEditingController.text.length));
176 });
177 },
178 icon: Container(
179 padding: const EdgeInsets.all(8),
180 decoration: BoxDecoration(
181 borderRadius: BorderRadius.circular(6.0),
169 - color: Theme
170 - .of(context)
171 - .hintColor,
182 + color: Theme.of(context).hintColor,
183 ),
184 width: 34,
185 height: 34,
@@ -194,14 +205,13 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
205 seedTextFieldKey: ValueKey('wallet_restore_from_seed_wallet_seeds_textfield_key'),
206 pasteButtonKey: ValueKey('wallet_restore_from_seed_wallet_seeds_paste_button_key'),
207 ),
197 - if (widget.type == WalletType.monero || widget.type == WalletType.wownero)
208 + if ([WalletType.monero, WalletType.wownero].contains(widget.type))
209 GestureDetector(
210 key: ValueKey('wallet_restore_from_seed_seedtype_picker_button_key'),
211 onTap: () async {
212 await showPopUp<void>(
213 context: context,
203 - builder: (_) =>
204 - Picker(
214 + builder: (_) => Picker(
215 items: _getItems(),
216 selectedAtIndex: isPolyseed
217 ? 1
@@ -226,33 +236,30 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
236 ),
237 ),
238 ),
229 - if (widget.displayWalletPassword)
230 - ...[BaseTextFormField(
239 + if (widget.displayWalletPassword) ...[
240 + BaseTextFormField(
241 key: ValueKey('password'),
242 controller: passwordTextEditingController,
233 - hintText: S
234 - .of(context)
235 - .password,
243 + hintText: S.of(context).password,
244 obscureText: true),
237 - BaseTextFormField(
238 - key: ValueKey('repeat_wallet_password'),
239 - controller: repeatedPasswordTextEditingController,
240 - hintText: S
241 - .of(context)
242 - .repeat_wallet_password,
243 - obscureText: true)
244 - ],
245 + BaseTextFormField(
246 + key: ValueKey('repeat_wallet_password'),
247 + controller: repeatedPasswordTextEditingController,
248 + hintText: S.of(context).repeat_wallet_password,
249 + obscureText: true)
250 + ],
251 if (widget.displayLanguageSelector)
252 if (!seedTypeController.value.text.contains("14") && widget.displayLanguageSelector)
253 GestureDetector(
254 onTap: () async {
255 await showPopUp<void>(
256 context: context,
251 - builder: (_) =>
252 - SeedLanguagePicker(
257 + builder: (_) => SeedLanguagePicker(
258 selected: language,
254 - onItemSelected: _changeLanguage,
255 - seedType: isPolyseed ? MoneroSeedType.polyseed : MoneroSeedType.legacy,
259 + onItemSelected: (lang) =>
260 + _changeLanguage(lang, isPolyseed || isBip39),
261 + seedType:
262 + widget.seedSettingsViewModel.moneroSeedType,
263 ));
264 },
265 child: Container(
@@ -274,7 +281,8 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
281 key: blockchainHeightKey,
282 blockHeightTextFieldKey: ValueKey('wallet_restore_from_seed_blockheight_textfield_key'),
283 onHeightOrDateEntered: widget.onHeightOrDateEntered,
277 - hasDatePicker: widget.type == WalletType.monero || widget.type == WalletType.wownero,
284 + hasDatePicker:
285 + [WalletType.monero, WalletType.wownero].contains(widget.type),
286 walletType: widget.type,
287 ),
288 ]));
@@ -282,28 +290,29 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
290
291 bool get isPolyseed =>
292 widget.seedSettingsViewModel.moneroSeedType == MoneroSeedType.polyseed &&
285 - (widget.type == WalletType.monero || widget.type == WalletType.wownero);
293 + [WalletType.monero, WalletType.wownero].contains(widget.type);
294 +
295 + bool get isBip39 =>
296 + widget.seedSettingsViewModel.moneroSeedType == MoneroSeedType.bip39 &&
297 + WalletType.monero == widget.type;
298
287 - Widget get expandIcon =>
288 - Container(
299 + Widget get expandIcon => Container(
300 padding: EdgeInsets.all(18),
301 width: 24,
302 height: 24,
303 child: Image.asset(
304 'assets/images/arrow_bottom_purple_icon.png',
305 height: 8,
295 - color: Theme
296 - .of(context)
297 - .hintColor,
306 + color: Theme.of(context).hintColor,
307 ),
308 );
309
301 - void _changeLanguage(String language) {
302 - final setLang = isPolyseed
310 + void _changeLanguage(String language, [bool useBip39Wordlist = false]) {
311 + final setLang = useBip39Wordlist
312 ? "POLYSEED_$language"
313 : seedTypeController.value.text.contains("14")
305 - ? "WOWSEED_" + language
306 - : language;
314 + ? "WOWSEED_" + language
315 + : language;
316 setState(() {
317 this.language = setLang;
318 seedWidgetStateKey.currentState!.changeSeedLanguage(setLang);
@@ -312,8 +321,8 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
321 });
322 }
323
315 - void _setLanguageLabel(String language) =>
316 - languageController.text = '${language.replaceAll("POLYSEED_", "")} (Seed language)';
324 + void _setLanguageLabel(String language) => languageController.text =
325 + '${language.replaceAll("POLYSEED_", "")} (Seed language)';
326
327 void _changeSeedType(MoneroSeedType item) {
328 _setSeedType(item);
@@ -328,9 +337,17 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
337 List<MoneroSeedType> _getItems() {
338 switch (widget.type) {
339 case WalletType.monero:
331 - return [MoneroSeedType.legacy, MoneroSeedType.polyseed];
340 + return [
341 + MoneroSeedType.legacy,
342 + MoneroSeedType.polyseed,
343 + MoneroSeedType.bip39
344 + ];
345 case WalletType.wownero:
333 - return [MoneroSeedType.legacy, MoneroSeedType.polyseed, MoneroSeedType.wowneroSeed];
346 + return [
347 + MoneroSeedType.legacy,
348 + MoneroSeedType.polyseed,
349 + MoneroSeedType.wowneroSeed
350 + ];
351 default:
352 return [MoneroSeedType.legacy];
353 }
lib/src/screens/restore/wallet_restore_page.dart
+30 -12
@@ -527,25 +527,42 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
527 }
528
529 bool _isValidSeed() {
530 - final seedPhrase =
531 - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.text;
530 + final seedPhrase = walletRestoreFromSeedFormKey
531 + .currentState!.seedWidgetStateKey.currentState!.text;
532 if (walletRestoreViewModel.isPolyseed(seedPhrase)) return true;
533
534 final seedWords = seedPhrase.split(' ');
535
536 - if (seedWords.length == 14 && walletRestoreViewModel.type == WalletType.wownero) return true;
537 - if (seedWords.length == 26 && walletRestoreViewModel.type == WalletType.zano) return true;
536 + if (seedWords.length == 14 &&
537 + walletRestoreViewModel.type == WalletType.wownero) return true;
538 + if (seedWords.length == 26 &&
539 + walletRestoreViewModel.type == WalletType.zano) return true;
540 +
541 + if (seedWords.length == 12 &&
542 + walletRestoreViewModel.type == WalletType.monero) {
543 + return walletRestoreFromSeedFormKey
544 + .currentState
545 + ?.blockchainHeightKey
546 + .currentState
547 + ?.restoreHeightController
548 + .text
549 + .isNotEmpty == true;
550 + }
551
539 - if ((walletRestoreViewModel.type == WalletType.monero ||
540 - walletRestoreViewModel.type == WalletType.wownero ||
541 - walletRestoreViewModel.type == WalletType.haven) &&
542 - seedWords.length != WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
543 - return false;
552 + if ([WalletType.monero, WalletType.wownero, WalletType.haven]
553 + .contains(walletRestoreViewModel.type) &&
554 + seedWords.length ==
555 + WalletRestoreViewModelBase.moneroSeedMnemonicLength) {
556 + return true;
557 }
558
559 // bip39:
560 final validBip39SeedLengths = [12, 18, 24];
548 - final nonBip39WalletTypes = [WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred];
561 + final nonBip39WalletTypes = [
562 + WalletType.wownero,
563 + WalletType.haven,
564 + WalletType.decred
565 + ];
566 // if it's a bip39 wallet and the length is not valid return false
567 if (!nonBip39WalletTypes.contains(walletRestoreViewModel.type) &&
568 !(validBip39SeedLengths.contains(seedWords.length))) {
@@ -558,8 +575,9 @@ class _WalletRestorePageBodyState extends State<_WalletRestorePageBody>
575 return false;
576 }
577
561 - final words =
562 - walletRestoreFromSeedFormKey.currentState!.seedWidgetStateKey.currentState!.words.toSet();
578 + final words = walletRestoreFromSeedFormKey
579 + .currentState!.seedWidgetStateKey.currentState!.words
580 + .toSet();
581 return seedWords.toSet().difference(words).toSet().isEmpty;
582 }
583
lib/src/widgets/seed_language_picker.dart
+1 -1
@@ -16,7 +16,7 @@ class SeedLanguagePickerOption {
16
17 final List<SeedLanguagePickerOption> seedLanguages = [
18 SeedLanguagePickerOption('English', S.current.seed_language_english,
19 - Image.asset('assets/images/flags/usa.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
19 + Image.asset('assets/images/flags/usa.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed, MoneroSeedType.bip39]),
20 SeedLanguagePickerOption('Chinese (Simplified)', S.current.seed_language_chinese,
21 Image.asset('assets/images/flags/chn.png'), [MoneroSeedType.legacy, MoneroSeedType.polyseed]),
22 SeedLanguagePickerOption('Chinese (Traditional)', S.current.seed_language_chinese_traditional,
lib/view_model/wallet_groups_display_view_model.dart
+6 -1
@@ -6,6 +6,7 @@ import 'package:cake_wallet/store/app_store.dart';
6 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
7 import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart';
8 import 'package:cake_wallet/wallet_types.g.dart';
9 +import 'package:cw_core/utils/print_verbose.dart';
10 import 'package:cw_core/wallet_info.dart';
11 import 'package:cw_core/wallet_type.dart';
12 import 'package:mobx/mobx.dart';
@@ -127,12 +128,16 @@ abstract class WalletGroupsDisplayViewModelBase with Store {
128
129 bool isNonSeedWallet = wallet.isNonSeedWallet;
130
131 + bool isNotMoneroBip39Wallet = wallet.type == WalletType.monero &&
132 + wallet.derivationInfo?.derivationType != DerivationType.bip39;
133 +
134 // Exclude if any of these conditions are true
135 return isNonBIP39Wallet ||
136 isNanoDerivationType ||
137 isElectrumDerivationType ||
138 isSameTypeAsSelectedWallet ||
135 - isNonSeedWallet;
139 + isNonSeedWallet ||
140 + isNotMoneroBip39Wallet;
141 });
142
143 if (shouldExcludeGroup) continue;
lib/view_model/wallet_keys_view_model.dart
+4 -3
@@ -63,14 +63,15 @@ abstract class WalletKeysViewModelBase with Store {
63 String get seed => _wallet.seed != null ? _wallet.seed! : '';
64
65 bool get isLegacySeedOnly =>
66 - (_wallet.type == WalletType.monero || _wallet.type == WalletType.wownero) &&
66 + [WalletType.monero, WalletType.wownero].contains(_wallet.type) &&
67 _wallet.seed != null &&
68 - !Polyseed.isValidSeed(_wallet.seed!);
68 + !(Polyseed.isValidSeed(_wallet.seed!) ||
69 + _wallet.seed!.split(' ').length == 12);
70
71 String get legacySeed {
72 if ((_wallet.type == WalletType.monero || _wallet.type == WalletType.wownero) &&
73 _wallet.seed != null &&
73 - Polyseed.isValidSeed(_wallet.seed!)) {
74 + (Polyseed.isValidSeed(_wallet.seed!) || _wallet.seed!.split(' ').length == 12)) {
75 final langName = PolyseedLang.getByPhrase(_wallet.seed!).nameEnglish;
76
77 if (_wallet.type == WalletType.monero) {
lib/view_model/wallet_new_vm.dart
+15 -6
@@ -49,6 +49,9 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
49 bool get hasLanguageSelector =>
50 [WalletType.monero, WalletType.haven, WalletType.wownero].contains(type);
51
52 + bool get showLanguageSelector =>
53 + newWalletArguments?.mnemonic == null && hasLanguageSelector;
54 +
55 int get seedPhraseWordsLength {
56 switch (type) {
57 case WalletType.monero:
@@ -81,7 +84,9 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
84 }
85 }
86
84 - bool get hasSeedType => [WalletType.monero, WalletType.wownero].contains(type);
87 + bool get hasSeedType =>
88 + newWalletArguments?.mnemonic == null &&
89 + [WalletType.monero, WalletType.wownero].contains(type);
90
91 @override
92 WalletCredentials getCredentials(dynamic _options) {
@@ -92,11 +97,15 @@ abstract class WalletNewVMBase extends WalletCreationVM with Store {
97 switch (type) {
98 case WalletType.monero:
99 return monero!.createMoneroNewWalletCredentials(
95 - name: name,
96 - language: options!.first as String,
97 - password: walletPassword,
98 - passphrase: passphrase,
99 - isPolyseed: options.last as bool);
100 + name: name,
101 + language: options!.first as String,
102 + password: walletPassword,
103 + passphrase: passphrase,
104 + seedType: newWalletArguments!.mnemonic != null
105 + ? MoneroSeedType.bip39.raw
106 + : (options.last as MoneroSeedType).raw,
107 + mnemonic: newWalletArguments!.mnemonic,
108 + );
109 case WalletType.bitcoin:
110 case WalletType.litecoin:
111 return bitcoin!.createBitcoinNewWalletCredentials(
res/values/strings_ar.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "البذور",
684 "seedtype_alert_content": "مشاركة البذور مع محافظ أخرى ممكن فقط مع BIP39 Seedtype.",
685 "seedtype_alert_title": "تنبيه البذور",
686 - "seedtype_legacy": "إرث (25 كلمة)",
687 - "seedtype_polyseed": "بوليسيد (16 كلمة)",
688 - "seedtype_wownero": "Wownero (14 كلمة)",
686 "select_backup_file": "حدد ملف النسخ الاحتياطي",
687 "select_buy_provider_notice": "حدد مزود شراء أعلاه. يمكنك تخطي هذه الشاشة عن طريق تعيين مزود شراء الافتراضي في إعدادات التطبيق.",
688 "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "سوف تتلقى(ooded )",
1025 "you_will_send": "تحويل من",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_bg.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Семенна тип",
684 "seedtype_alert_content": "Споделянето на семена с други портфейли е възможно само с BIP39 Seedtype.",
685 "seedtype_alert_title": "Сигнал за семена",
686 - "seedtype_legacy": "Наследство (25 думи)",
687 - "seedtype_polyseed": "Поли семе (16 думи)",
688 - "seedtype_wownero": "Wownero (14 думи)",
686 "select_backup_file": "Избор на резервно копие",
687 "select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
688 "select_destination": "Моля, изберете дестинация за архивния файл.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Ще получите(прогнозно )",
1025 "you_will_send": "Обръщане от",
1026 "yy": "гг"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_cs.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "SeedType",
684 "seedtype_alert_content": "Sdílení semen s jinými peněženkami je možné pouze u BIP39 SeedType.",
685 "seedtype_alert_title": "Upozornění seedtype",
686 - "seedtype_legacy": "Legacy (25 slov)",
687 - "seedtype_polyseed": "Polyseed (16 slov)",
688 - "seedtype_wownero": "Wownero (14 slov)",
686 "select_backup_file": "Vybrat soubor se zálohou",
687 "select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.",
688 "select_destination": "Vyberte cíl pro záložní soubor.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Obdržíte(odhadovaný )",
1025 "you_will_send": "Směnit z",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_de.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Seedtyp",
685 "seedtype_alert_content": "Das Teilen von Seeds mit anderen Wallet ist nur mit bip39 Seedype möglich.",
686 "seedtype_alert_title": "Seedype-Alarm",
687 - "seedtype_legacy": "Veraltet (25 Wörter)",
688 - "seedtype_polyseed": "Polyseed (16 Wörter)",
689 - "seedtype_wownero": "WOWNO (14 Wörter)",
687 "select_backup_file": "Sicherungsdatei auswählen",
688 "select_buy_provider_notice": "Wählen Sie oben einen Anbieter kaufen. Sie können diese Seite überspringen, indem Sie Ihren Standard-Kaufanbieter in den App-Einstellungen festlegen.",
689 "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
@@ -1030,4 +1027,4 @@
1027 "you_will_receive_estimated_amount": "Sie erhalten(geschätzt )",
1028 "you_will_send": "Konvertieren von",
1029 "yy": "YY"
1033 -}
\ No newline at end of file
1030 +}
res/values/strings_en.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Seedtype",
685 "seedtype_alert_content": "Sharing seeds with other wallets is only possible with BIP39 SeedType.",
686 "seedtype_alert_title": "SeedType Alert",
687 - "seedtype_legacy": "Legacy (25 words)",
688 - "seedtype_polyseed": "Polyseed (16 words)",
689 - "seedtype_wownero": "Wownero (14 words)",
687 "select_backup_file": "Select backup file",
688 "select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.",
689 "select_destination": "Please select destination for the backup file.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "You will receive (estimated)",
1026 "you_will_send": "Convert from",
1027 "yy": "YY"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_es.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Tipos de semillas",
685 "seedtype_alert_content": "Compartir semillas con otras billeteras solo es posible con semillas bip39 - un tipo específico de semilla.",
686 "seedtype_alert_title": "Alerta de tipo de semillas",
687 - "seedtype_legacy": "Semilla clásica-legacy (25 palabras)",
688 - "seedtype_polyseed": "Poli-semilla (16 palabras)",
689 - "seedtype_wownero": "Wownero (14 palabras)",
687 "select_backup_file": "Seleccionar archivo de respaldo",
688 "select_buy_provider_notice": "Selecciona un proveedor de compra arriba. Puede omitir esta pantalla configurando su proveedor de compra predeterminado en la configuración de la aplicación.",
689 "select_destination": "Selecciona el destino del archivo de copia de seguridad.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "Recibirá(estimado )",
1026 "you_will_send": "Convertir de",
1027 "yy": "YY"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_fr.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Type de graine",
684 "seedtype_alert_content": "Le partage de graines avec d'autres portefeuilles n'est possible qu'avec le type de graine BIP39.",
685 "seedtype_alert_title": "Alerte Type de Graine",
686 - "seedtype_legacy": "Legacy (25 words)",
687 - "seedtype_polyseed": "Polyseed (16 mots)",
688 - "seedtype_wownero": "WOWNERO (14 mots)",
686 "select_backup_file": "Sélectionnez le fichier de sauvegarde",
687 "select_buy_provider_notice": "Sélectionnez un fournisseur d'achat ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur d'achat par défaut dans les paramètres de l'application.",
688 "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Vous recevrez ( estimé )",
1025 "you_will_send": "Convertir depuis",
1026 "yy": "AA"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_ha.arb
+1 -4
@@ -685,9 +685,6 @@
685 "seedtype": "Seedtype",
686 "seedtype_alert_content": "Raba tsaba tare da sauran wallets yana yiwuwa ne kawai tare da Bip39 seedtype.",
687 "seedtype_alert_title": "Seedtype farke",
688 - "seedtype_legacy": "Legacy (25 kalmomi)",
689 - "seedtype_polyseed": "Polyseed (16 kalmomi)",
690 - "seedtype_wownero": "WowRero (kalmomi 14)",
688 "select_backup_file": "Zaɓi fayil ɗin madadin",
689 "select_buy_provider_notice": "Zaɓi mai ba da kyauta a sama. Zaka iya tsallake wannan allon ta hanyar saita mai ba da isasshen busasshen mai ba da isasshen busasshiyar saiti.",
690 "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
@@ -1029,4 +1026,4 @@
1026 "you_will_receive_estimated_amount": "Za ku (karɓi )",
1027 "you_will_send": "Maida daga",
1028 "yy": "YY"
1032 -}
\ No newline at end of file
1029 +}
res/values/strings_hi.arb
+1 -4
@@ -685,9 +685,6 @@
685 "seedtype": "बीज",
686 "seedtype_alert_content": "अन्य पर्स के साथ बीज साझा करना केवल BIP39 सीडटाइप के साथ संभव है।",
687 "seedtype_alert_title": "बीजगणित अलर्ट",
688 - "seedtype_legacy": "विरासत (25 शब्द)",
689 - "seedtype_polyseed": "पॉलीसीड (16 शब्द)",
690 - "seedtype_wownero": "Wownero (14 शब्द)",
688 "select_backup_file": "बैकअप फ़ाइल का चयन करें",
689 "select_buy_provider_notice": "ऊपर एक खरीद प्रदाता का चयन करें। आप इस स्क्रीन को ऐप सेटिंग्स में अपना डिफ़ॉल्ट बाय प्रदाता सेट करके छोड़ सकते हैं।",
690 "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
@@ -1029,4 +1026,4 @@
1026 "you_will_receive_estimated_amount": "आपको#अनुमानित ( प्राप्त होगा)",
1027 "you_will_send": "से रूपांतरित करें",
1028 "yy": "वाईवाई"
1032 -}
\ No newline at end of file
1029 +}
res/values/strings_hr.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Sjemenska vrsta",
684 "seedtype_alert_content": "Dijeljenje sjemena s drugim novčanicima moguće je samo s BIP39 sjemenom.",
685 "seedtype_alert_title": "Upozorenje o sjemenu",
686 - "seedtype_legacy": "Nasljeđe (25 riječi)",
687 - "seedtype_polyseed": "Poliseed (16 riječi)",
688 - "seedtype_wownero": "WANERO (14 riječi)",
686 "select_backup_file": "Odaberite datoteku sigurnosne kopije",
687 "select_buy_provider_notice": "Odaberite gornji davatelj kupnje. Ovaj zaslon možete preskočiti postavljanjem zadanog davatelja usluga kupnje u postavkama aplikacija.",
688 "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Primit ćete(procijenjeno )",
1025 "you_will_send": "Razmijeni iz",
1026 "yy": "GG"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_hy.arb
+1 -4
@@ -682,9 +682,6 @@
682 "seedtype": "Սերմի տեսակ",
683 "seedtype_alert_content": "Այլ դրամապանակներով սերմերի փոխանակումը հնարավոր է միայն BIP39 SEEDTYPE- ով:",
684 "seedtype_alert_title": "SEEDTYPE ALERT",
685 - "seedtype_legacy": "Legacy (25 բառ)",
686 - "seedtype_polyseed": "Polyseed (16 բառ)",
687 - "seedtype_wownero": "Wownero (14 բառ)",
685 "select_backup_file": "Ընտրել կրկնօրինակ ֆայլ",
686 "select_buy_provider_notice": "Ընտրեք գնման մատակարարը վերևում։ Դուք կարող եք բաց թողնել այս էկրանը ձեր լռելայն գնման մատակարարը հավելվածի կարգավորումներում սահմանելով",
687 "select_destination": "Խնդրում ենք ընտրել կրկնօրինակ ֆայլի նպատակակետը",
@@ -1025,4 +1022,4 @@
1022 "you_will_receive_estimated_amount": "Դուք կստանաք ( գնահատված )",
1023 "you_will_send": "Փոխանակեք",
1024 "yy": "ՏՏ"
1028 -}
\ No newline at end of file
1025 +}
res/values/strings_id.arb
+1 -4
@@ -686,9 +686,6 @@
686 "seedtype": "Seedtype",
687 "seedtype_alert_content": "Berbagi biji dengan dompet lain hanya dimungkinkan dengan BIP39 seedtype.",
688 "seedtype_alert_title": "Peringatan seedtype",
689 - "seedtype_legacy": "Legacy (25 kata)",
690 - "seedtype_polyseed": "Polyseed (16 kata)",
691 - "seedtype_wownero": "Wownero (14 kata)",
689 "select_backup_file": "Pilih file cadangan",
690 "select_buy_provider_notice": "Pilih penyedia beli di atas. Anda dapat melewatkan layar ini dengan mengatur penyedia pembelian default Anda di pengaturan aplikasi.",
691 "select_destination": "Silakan pilih tujuan untuk file cadangan.",
@@ -1030,4 +1027,4 @@
1027 "you_will_receive_estimated_amount": "Anda akan menerima(estimasi )",
1028 "you_will_send": "Konversi dari",
1029 "yy": "YY"
1033 -}
\ No newline at end of file
1030 +}
res/values/strings_it.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Seedtype",
685 "seedtype_alert_content": "La condivisione di semi con altri portafogli è possibile solo con Bip39 SeedType.",
686 "seedtype_alert_title": "Avviso seedType",
687 - "seedtype_legacy": "Legacy (25 parole)",
688 - "seedtype_polyseed": "Polyseed (16 parole)",
689 - "seedtype_wownero": "Wownero (14 parole)",
687 "select_backup_file": "Seleziona file di backup",
688 "select_buy_provider_notice": "Seleziona un provider di acquisto sopra. È possibile saltare questa schermata impostando il provider di acquisto predefinito nelle impostazioni dell'app.",
689 "select_destination": "Seleziona la destinazione per il file di backup.",
@@ -1029,4 +1026,4 @@
1026 "you_will_receive_estimated_amount": "Riceverai(stimato )",
1027 "you_will_send": "Conveti da",
1028 "yy": "YY"
1032 -}
\ No newline at end of file
1029 +}
res/values/strings_ja.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "SeedType",
685 "seedtype_alert_content": "他の財布と種子を共有することは、BIP39 SeedTypeでのみ可能です。",
686 "seedtype_alert_title": "SeedTypeアラート",
687 - "seedtype_legacy": "レガシー(25語)",
688 - "seedtype_polyseed": "ポリシード(16語)",
689 - "seedtype_wownero": "wownero(14ワード)",
687 "select_backup_file": "バックアップファイルを選択",
688 "select_buy_provider_notice": "上記の購入プロバイダーを選択してください。デフォルトの購入プロバイダーをアプリ設定で設定して、この画面をスキップできます。",
689 "select_destination": "バックアップファイルの保存先を選択してください。",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "あなたは(推定)を受け取ります",
1026 "you_will_send": "から変換",
1027 "yy": "YY"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_ko.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "시드 타입",
684 "seedtype_alert_content": "다른 지갑과 씨앗을 공유하는 것은 BIP39 SeedType에서만 가능합니다.",
685 "seedtype_alert_title": "종자 경보",
686 - "seedtype_legacy": "레거시 (25 단어)",
687 - "seedtype_polyseed": "다문 (16 단어)",
688 - "seedtype_wownero": "Wownero (14 단어)",
686 "select_backup_file": "백업 파일 선택",
687 "select_buy_provider_notice": "위의 구매 제공자를 선택하십시오. 앱 설정에서 기본 구매 제공자를 설정 하여이 화면을 건너 뛸 수 있습니다.",
688 "select_destination": "백업 파일의 대상을 선택하십시오.",
@@ -1028,4 +1025,4 @@
1025 "you_will_send": "다음에서 변환",
1026 "YY": "YY",
1027 "yy": "YY"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_my.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "မျိုးပွားခြင်း",
684 "seedtype_alert_content": "အခြားပိုက်ဆံအိတ်များနှင့်မျိုးစေ့များကိုမျှဝေခြင်းသည် BIP39 sebyspe ဖြင့်သာဖြစ်သည်။",
685 "seedtype_alert_title": "ပျိုးပင်သတိပေးချက်",
686 - "seedtype_legacy": "အမွေအနှစ် (စကားလုံး 25 လုံး)",
687 - "seedtype_polyseed": "polyseed (စကားလုံး 16 လုံး)",
688 - "seedtype_wownero": "Wownero (စကားလုံး 14 လုံး)",
686 "select_backup_file": "အရန်ဖိုင်ကို ရွေးပါ။",
687 "select_buy_provider_notice": "အပေါ်ကဝယ်သူတစ် ဦး ကိုရွေးချယ်ပါ။ သင်၏ default 0 ယ်သူအား app settings တွင် setting လုပ်ခြင်းဖြင့်ဤ screen ကိုကျော်သွားနိုင်သည်။",
688 "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "သင် ( ခန့်မှန်းခြေ ) လက်ခံရရှိလိမ့်မည်",
1025 "you_will_send": "မှပြောင်းပါ။",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_nl.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Zaadtype",
684 "seedtype_alert_content": "Het delen van zaden met andere portefeuilles is alleen mogelijk met BIP39 SeedType.",
685 "seedtype_alert_title": "Zaadtype alert",
686 - "seedtype_legacy": "Legacy (25 woorden)",
687 - "seedtype_polyseed": "Polyseed (16 woorden)",
688 - "seedtype_wownero": "WOWNERO (14 woorden)",
686 "select_backup_file": "Selecteer een back-upbestand",
687 "select_buy_provider_notice": "Selecteer hierboven een koopprovider. U kunt dit scherm overslaan door uw standaard kopenprovider in te stellen in app -instellingen.",
688 "select_destination": "Selecteer de bestemming voor het back-upbestand.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "U ontvangt(geschat )",
1026 "you_will_send": "Converteren van",
1027 "yy": "JJ"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_pl.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Sedtype",
684 "seedtype_alert_content": "Dzielenie się nasionami z innymi portfelami jest możliwe tylko z BIP39 sededType.",
685 "seedtype_alert_title": "Ustanowienie typu sedype",
686 - "seedtype_legacy": "Legacy (25 słów)",
687 - "seedtype_polyseed": "Polyseed (16 słów)",
688 - "seedtype_wownero": "Wowero (14 słów)",
686 "select_backup_file": "Wybierz plik kopii zapasowej",
687 "select_buy_provider_notice": "Wybierz powyższe dostawcę zakupu. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę zakupu w ustawieniach aplikacji.",
688 "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Otrzymasz(oszacowane )",
1025 "you_will_send": "Konwertuj z",
1026 "yy": "RR"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_pt.arb
+1 -4
@@ -685,9 +685,6 @@
685 "seedtype": "SeedType",
686 "seedtype_alert_content": "Compartilhar sementes com outras carteiras só é possível com o BIP39 SeedType.",
687 "seedtype_alert_title": "Alerta de SeedType",
688 - "seedtype_legacy": "Legado (25 palavras)",
689 - "seedtype_polyseed": "Polyseed (16 palavras)",
690 - "seedtype_wownero": "Wowrone (14 palavras)",
688 "select_backup_file": "Selecione o arquivo de backup",
689 "select_buy_provider_notice": "Selecione um provedor de compra acima. Você pode pular esta tela definindo seu provedor de compra padrão nas configurações de aplicativos.",
690 "select_destination": "Selecione o destino para o arquivo de backup.",
@@ -1030,4 +1027,4 @@
1027 "you_will_receive_estimated_amount": "Você receberá(estimado )",
1028 "you_will_send": "Converter de",
1029 "yy": "aa"
1033 -}
\ No newline at end of file
1030 +}
res/values/strings_ru.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "SEEDTYPE",
685 "seedtype_alert_content": "Обмен семенами с другими кошельками возможно только с BIP39 SeedType.",
686 "seedtype_alert_title": "SEEDTYPE ALERT",
687 - "seedtype_legacy": "Наследие (25 слов)",
688 - "seedtype_polyseed": "Полиса (16 слов)",
689 - "seedtype_wownero": "Wownero (14 слов)",
687 "select_backup_file": "Выберите файл резервной копии",
688 "select_buy_provider_notice": "Выберите поставщика покупки выше. Вы можете пропустить этот экран, установив поставщика покупки по умолчанию в настройках приложения.",
689 "select_destination": "Пожалуйста, выберите место для файла резервной копии.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "Вы получите(Оценку )",
1026 "you_will_send": "Конвертировать из",
1027 "yy": "ГГ"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_th.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "เมล็ดพันธุ์",
684 "seedtype_alert_content": "การแบ่งปันเมล็ดกับกระเป๋าเงินอื่น ๆ เป็นไปได้เฉพาะกับ bip39 seedtype",
685 "seedtype_alert_title": "การแจ้งเตือน seedtype",
686 - "seedtype_legacy": "มรดก (25 คำ)",
687 - "seedtype_polyseed": "โพลีส (16 คำ)",
688 - "seedtype_wownero": "wownero (14 คำ)",
686 "select_backup_file": "เลือกไฟล์สำรอง",
687 "select_buy_provider_notice": "เลือกผู้ให้บริการซื้อด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการซื้อเริ่มต้นในการตั้งค่าแอป",
688 "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "คุณจะได้รับ(โดยประมาณ )",
1025 "you_will_send": "แปลงจาก",
1026 "yy": "ปี"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_tl.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Seed type",
684 "seedtype_alert_content": "Ang pagbabahagi ng mga buto sa iba pang mga pitaka ay posible lamang sa bip39 seedtype.",
685 "seedtype_alert_title": "Alerto ng Seedtype",
686 - "seedtype_legacy": "Legacy (25 na salita)",
687 - "seedtype_polyseed": "Polyseed (16 na salita)",
688 - "seedtype_wownero": "Wownero (14 na salita)",
686 "select_backup_file": "Piliin ang backup na file",
687 "select_buy_provider_notice": "Pumili ng provider ng pagbili sa itaas. Maaari mong laktawan ang screen na ito sa pamamagitan ng pagtatakda ng iyong default na provider ng pagbili sa mga setting ng app.",
688 "select_destination": "Mangyaring piliin ang patutunguhan para sa backup na file.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "Makakatanggap ka ng(tinantyang)",
1025 "you_will_send": "I-convert mula sa",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_tr.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "Tohum",
684 "seedtype_alert_content": "Tohumları diğer cüzdanlarla paylaşmak sadece BIP39 tohumu ile mümkündür.",
685 "seedtype_alert_title": "SeedType uyarısı",
686 - "seedtype_legacy": "Miras (25 kelime)",
687 - "seedtype_polyseed": "Polyseed (16 kelime)",
688 - "seedtype_wownero": "Wownero (14 kelime)",
686 "select_backup_file": "Yedek dosyası seç",
687 "select_buy_provider_notice": "Yukarıda bir satın alma sağlayıcısı seçin. App ayarlarında varsayılan satın alma sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.",
688 "select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "(Tahmini ) alacaksınız",
1025 "you_will_send": "Biçiminden dönüştür:",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
res/values/strings_uk.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Насіннєвий тип",
685 "seedtype_alert_content": "Спільний доступ до інших гаманців можливе лише за допомогою BIP39 Seedtype.",
686 "seedtype_alert_title": "Попередження насінника",
687 - "seedtype_legacy": "Спадщина (25 слів)",
688 - "seedtype_polyseed": "Полісей (16 слів)",
689 - "seedtype_wownero": "Влонеро (14 слів)",
687 "select_backup_file": "Виберіть файл резервної копії",
688 "select_buy_provider_notice": "Виберіть постачальника купівлі вище. Ви можете пропустити цей екран, встановивши свого постачальника купівлі за замовчуванням у налаштуваннях додатків.",
689 "select_destination": "Виберіть місце призначення для файлу резервної копії.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "Ви отримаєте(оцінюється )",
1026 "you_will_send": "Конвертувати з",
1027 "yy": "YY"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_ur.arb
+1 -4
@@ -685,9 +685,6 @@
685 "seedtype": "سیڈ ٹائپ",
686 "seedtype_alert_content": "دوسرے بٹوے کے ساتھ بیجوں کا اشتراک صرف BIP39 بیج ٹائپ کے ساتھ ہی ممکن ہے۔",
687 "seedtype_alert_title": "سیڈ ٹائپ الرٹ",
688 - "seedtype_legacy": "میراث (25 الفاظ)",
689 - "seedtype_polyseed": "پالیسیڈ (16 الفاظ)",
690 - "seedtype_wownero": "واونرو (14 الفاظ)",
688 "select_backup_file": "بیک اپ فائل کو منتخب کریں۔",
689 "select_buy_provider_notice": "اوپر خریدنے والا خریدنے والا منتخب کریں۔ آپ ایپ کی ترتیبات میں اپنے پہلے سے طے شدہ خریدنے والے کو ترتیب دے کر اس اسکرین کو چھوڑ سکتے ہیں۔",
690 "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
@@ -1029,4 +1026,4 @@
1026 "you_will_receive_estimated_amount": "آپ(تخمینہ ) وصول کریں گے",
1027 "you_will_send": "سے تبدیل کریں۔",
1028 "yy": "YY"
1032 -}
\ No newline at end of file
1029 +}
res/values/strings_vi.arb
+1 -4
@@ -681,9 +681,6 @@
681 "seedtype": "Loại hạt giống",
682 "seedtype_alert_content": "Chia sẻ hạt giống với ví khác chỉ có thể với BIP39 SeedType.",
683 "seedtype_alert_title": "Cảnh báo hạt giống",
684 - "seedtype_legacy": "Di sản (25 từ)",
685 - "seedtype_polyseed": "Polyseed (16 từ)",
686 - "seedtype_wownero": "Wownero (14 từ)",
684 "select_backup_file": "Chọn tệp sao lưu",
685 "select_buy_provider_notice": "Chọn nhà cung cấp mua ở trên. Bạn có thể bỏ qua màn hình này bằng cách thiết lập nhà cung cấp mua mặc định trong cài đặt ứng dụng.",
686 "select_destination": "Vui lòng chọn đích cho tệp sao lưu.",
@@ -1024,4 +1021,4 @@
1021 "you_will_receive_estimated_amount": "Bạn sẽ nhận được(ước tính )",
1022 "you_will_send": "Chuyển đổi từ",
1023 "yy": "YY"
1027 -}
\ No newline at end of file
1024 +}
res/values/strings_yo.arb
+1 -4
@@ -684,9 +684,6 @@
684 "seedtype": "Irugbin-seetypu",
685 "seedtype_alert_content": "Pinpin awọn irugbin pẹlu awọn gedo miiran ṣee ṣe pẹlu Bip39 irugbin.",
686 "seedtype_alert_title": "Ṣajọpọ Seeytype",
687 - "seedtype_legacy": "Legacy (awọn ọrọ 25)",
688 - "seedtype_polyseed": "Polyseed (awọn ọrọ 16)",
689 - "seedtype_wownero": "Wowero (awọn ọrọ 14)",
687 "select_backup_file": "Select backup file",
688 "select_buy_provider_notice": "Yan olupese Ra loke. O le skii iboju yii nipa ṣiṣeto olupese rẹ ni awọn eto App.",
689 "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
@@ -1028,4 +1025,4 @@
1025 "you_will_receive_estimated_amount": "Iwọ yoo gba ( excimated )",
1026 "you_will_send": "Ṣe pàṣípààrọ̀ láti",
1027 "yy": "Ọd"
1031 -}
\ No newline at end of file
1028 +}
res/values/strings_zh.arb
+1 -4
@@ -683,9 +683,6 @@
683 "seedtype": "籽粒",
684 "seedtype_alert_content": "只有BIP39籽粒可以与其他钱包共享种子。",
685 "seedtype_alert_title": "籽粒警报",
686 - "seedtype_legacy": "遗产(25个单词)",
687 - "seedtype_polyseed": "多种物品(16个单词)",
688 - "seedtype_wownero": "沃恩罗(14个单词)",
686 "select_backup_file": "选择备份文件",
687 "select_buy_provider_notice": "在上面选择买入提供商。您可以通过在应用程序设置中设置默认的购买提供商来跳过此屏幕。",
688 "select_destination": "请选择备份文件的目的地。",
@@ -1027,4 +1024,4 @@
1024 "you_will_receive_estimated_amount": "您将收到(估计的)",
1025 "you_will_send": "转换自",
1026 "yy": "YY"
1030 -}
\ No newline at end of file
1027 +}
tool/configure.dart
+1 -1
@@ -406,7 +406,7 @@ abstract class Monero {
406 required int height});
407 WalletCredentials createMoneroRestoreWalletFromSeedCredentials({required String name, required String password, required String passphrase, required int height, required String mnemonic});
408 WalletCredentials createMoneroRestoreWalletFromHardwareCredentials({required String name, required String password, required int height, required ledger.LedgerConnection ledgerConnection});
409 - WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required bool isPolyseed, required String? passphrase, String? password});
409 +WalletCredentials createMoneroNewWalletCredentials({required String name, required String language, required int seedType, required String? passphrase, String? password, String? mnemonic});
410 Map<String, String> getKeys(Object wallet);
411 int? getRestoreHeight(Object wallet);
412 Object createMoneroTransactionCreationCredentials({required List<Output> outputs, required TransactionPriority priority});