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';
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
}
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;
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,
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
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!,
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 {
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,
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(