11import 'dart:ffi' ;
22import 'dart:io' ;
33
4+ import 'package:collection/collection.dart' ;
45import 'package:cw_core/get_height_by_date.dart' ;
56import 'package:cw_core/monero_wallet_utils.dart' ;
67import 'package:cw_core/pathForWallet.dart' ;
@@ -14,29 +15,38 @@ import 'package:cw_core/wallet_type.dart';
1415import 'package:cw_monero/api/account_list.dart' ;
1516import 'package:cw_monero/api/wallet_manager.dart' as monero_wallet_manager;
1617import 'package:cw_monero/api/wallet_manager.dart' ;
18+ import 'package:cw_monero/bip39_seed.dart' ;
1719import 'package:cw_monero/ledger.dart' ;
1820import 'package:cw_monero/monero_wallet.dart' ;
19- import 'package:collection/collection.dart' ;
2021import 'package:hive/hive.dart' ;
2122import 'package:ledger_flutter_plus/ledger_flutter_plus.dart' ;
2223import 'package:monero/monero.dart' as monero;
2324import 'package:polyseed/polyseed.dart' ;
2425
26+ enum MoneroSeedType { polyseed, legacy, bip39 }
27+
2528class MoneroNewWalletCredentials extends WalletCredentials {
2629 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})
2836 : super (name: name, password: password);
2937
3038 final String language;
31- final bool isPolyseed ;
39+ final MoneroSeedType seedType ;
3240 final String ? passphrase;
41+ final String ? mnemonic;
3342}
3443
3544class 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})
4050 : super (name: name, password: password, height: height);
4151 LedgerConnection ledgerConnection;
4252}
@@ -60,13 +70,14 @@ class MoneroWalletLoadingException implements Exception {
6070}
6171
6272class 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 })
7081 : super (name: name, password: password, height: height);
7182
7283 final String language;
@@ -97,27 +108,42 @@ class MoneroWalletService extends WalletService<
97108 @override
98109 WalletType getType () => WalletType .monero;
99110
100- @override
101- Future <MoneroWallet > create (MoneroNewWalletCredentials credentials, {bool ? isTestnet}) async {
111+ @override
112+ Future <MoneroWallet > create (MoneroNewWalletCredentials credentials,
113+ {bool ? isTestnet}) async {
102114 try {
103115 final path = await pathForWallet (name: credentials.name, type: getType ());
104116
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) {
106128 final polyseed = Polyseed .create ();
107129 final lang = PolyseedLang .getByEnglishName (credentials.language);
108130
109- if (credentials.passphrase != null ) polyseed.crypt (credentials.passphrase! );
131+ if (credentials.passphrase != null )
132+ polyseed.crypt (credentials.passphrase! );
110133
111- final heightOverride =
112- getMoneroHeigthByDate ( date: DateTime .now ().subtract (Duration (days: 2 )));
134+ final heightOverride = getMoneroHeigthByDate (
135+ date: DateTime .now ().subtract (Duration (days: 2 )));
113136
114- return _restoreFromPolyseed (
115- path, credentials.password ! , polyseed, credentials.walletInfo! , lang,
137+ return _restoreFromPolyseed (path, credentials.password ! , polyseed,
138+ credentials.walletInfo! , lang,
116139 overrideHeight: heightOverride, passphrase: credentials.passphrase);
117140 }
118141
119142 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 ?? "" );
121147 final wallet = MoneroWallet (
122148 walletInfo: credentials.walletInfo! ,
123149 unspentCoinsInfo: unspentCoinsInfoSource,
@@ -145,7 +171,8 @@ class MoneroWalletService extends WalletService<
145171 }
146172
147173 @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 {
149176 try {
150177 final path = await pathForWallet (name: name, type: getType ());
151178
@@ -303,8 +330,28 @@ class MoneroWalletService extends WalletService<
303330 rethrow ;
304331 }
305332
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+
306352 try {
307353 final path = await pathForWallet (name: credentials.name, type: getType ());
354+
308355 monero_wallet_manager.restoreFromSeed (
309356 path: path,
310357 password: credentials.password! ,
@@ -325,6 +372,50 @@ class MoneroWalletService extends WalletService<
325372 }
326373 }
327374
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+
328419 Future <MoneroWallet > restoreFromPolyseed (
329420 MoneroRestoreWalletFromSeedCredentials credentials) async {
330421 try {
@@ -344,23 +435,21 @@ class MoneroWalletService extends WalletService<
344435 }
345436 }
346437
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,
349440 {PolyseedCoin coin = PolyseedCoin .POLYSEED_MONERO ,
350441 int ? overrideHeight,
351442 String ? passphrase}) async {
352-
353- if (polyseed.isEncrypted == false &&
354- (passphrase?? '' ) != "" ) {
443+ if (polyseed.isEncrypted == false && (passphrase ?? '' ) != "" ) {
355444 // Fallback to the different passphrase offset method, when a passphrase
356445 // was provided but the polyseed is not encrypted.
357446 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+
364453 final wallet = MoneroWallet (
365454 walletInfo: walletInfo,
366455 unspentCoinsInfo: unspentCoinsInfoSource,
@@ -437,7 +526,8 @@ class MoneroWalletService extends WalletService<
437526
438527 if (walletFilesExist (path)) await repairOldAndroidWallet (name);
439528
440- await monero_wallet_manager.openWalletAsync ({'path' : path, 'password' : password});
529+ await monero_wallet_manager
530+ .openWalletAsync ({'path' : path, 'password' : password});
441531 final walletInfo = walletInfoSource.values
442532 .firstWhere ((info) => info.id == WalletBase .idFor (name, getType ()));
443533 final wallet = MoneroWallet (
0 commit comments