dev
dart 222 lines 6.96 KB
Raw
1 import 'dart:io';
2
3 import 'package:cw_core/pathForWallet.dart';
4 import 'package:cw_core/encryption_file_utils.dart';
5 import 'package:cw_core/wallet_base.dart';
6 import 'package:cw_core/wallet_info.dart';
7 import 'package:cw_core/wallet_service.dart';
8 import 'package:cw_core/wallet_type.dart';
9 import 'package:cw_nano/nano_mnemonic.dart' as nm;
10 import 'package:cw_nano/nano_wallet.dart';
11 import 'package:cw_nano/nano_wallet_creation_credentials.dart';
12 import 'package:hive/hive.dart';
13 import 'package:bip39/bip39.dart' as bip39;
14 import 'package:nanodart/nanodart.dart';
15 import 'package:nanoutil/nanoutil.dart';
16
17 class NanoWalletService extends WalletService<
18 NanoNewWalletCredentials,
19 NanoRestoreWalletFromSeedCredentials,
20 NanoRestoreWalletFromKeysCredentials,
21 NanoNewWalletCredentials> {
22 NanoWalletService(this.isDirect);
23
24 final bool isDirect;
25
26 static bool walletFilesExist(String path) =>
27 !File(path).existsSync() && !File('$path.keys').existsSync();
28
29 @override
30 WalletType getType() => WalletType.nano;
31
32 @override
33 Future<WalletBase> create(NanoNewWalletCredentials credentials, {bool? isTestnet}) async {
34 final String mnemonic;
35 final derivationInfo =
36 credentials.derivationInfo ?? await credentials.walletInfo!.getDerivationInfo();
37 switch (derivationInfo.derivationType) {
38 case DerivationType.nano:
39 String seedKey = NanoSeeds.generateSeed();
40 mnemonic = credentials.mnemonic ?? NanoDerivations.standardSeedToMnemonic(seedKey);
41 break;
42 case DerivationType.bip39:
43 default:
44 final strength = credentials.seedPhraseLength == 24 ? 256 : 128;
45 mnemonic = credentials.mnemonic ?? bip39.generateMnemonic(strength: strength);
46 break;
47 }
48
49 final wallet = NanoWallet(
50 walletInfo: credentials.walletInfo!,
51 derivationInfo: derivationInfo,
52 mnemonic: mnemonic,
53 password: credentials.password!,
54 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
55 );
56 await wallet.init();
57 return wallet;
58 }
59
60 @override
61 Future<void> remove(String wallet) async {
62 final path = await pathForWalletDir(name: wallet, type: getType());
63 final file = Directory(path);
64 final isExist = file.existsSync();
65
66 if (isExist) {
67 await file.delete(recursive: true);
68 }
69
70 final walletInfo = await WalletInfo.get(wallet, getType());
71 if (walletInfo == null) {
72 throw Exception('Wallet not found');
73 }
74 await WalletInfo.delete(walletInfo);
75 }
76
77 @override
78 Future<void> rename(String currentName, String password, String newName) async {
79 final currentWalletInfo = await WalletInfo.get(currentName, getType());
80 if (currentWalletInfo == null) {
81 throw Exception('Wallet not found');
82 }
83
84 String randomWords =
85 (List<String>.from(nm.NanoMnemomics.WORDLIST)..shuffle()).take(24).join(' ');
86 final currentWallet = NanoWallet(
87 walletInfo: currentWalletInfo,
88 derivationInfo: await currentWalletInfo.getDerivationInfo(),
89 password: password,
90 mnemonic: randomWords,
91 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
92 );
93
94 await currentWallet.renameWalletFiles(newName);
95 await saveBackup(newName);
96
97 final newWalletInfo = currentWalletInfo;
98 newWalletInfo.id = WalletBase.idFor(newName, getType());
99 newWalletInfo.name = newName;
100
101 await newWalletInfo.save();
102 }
103
104 @override
105 Future<NanoWallet> restoreFromKeys(NanoRestoreWalletFromKeysCredentials credentials,
106 {bool? isTestnet}) async {
107 if (credentials.seedKey.contains(' ')) {
108 throw Exception("Invalid key!");
109 } else {
110 if (credentials.seedKey.length != 64 && credentials.seedKey.length != 128) {
111 throw Exception("Invalid key length!");
112 }
113 }
114
115 String? mnemonic;
116
117 // we can't derive the mnemonic from the key in all cases, only if it's a "nano" seed
118 if (credentials.seedKey.length == 64) {
119 try {
120 mnemonic = NanoDerivations.standardSeedToMnemonic(credentials.seedKey);
121 } catch (e) {
122 throw Exception("Wasn't a valid nano style seed!");
123 }
124 }
125 final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
126 // should never happen but just in case:
127 if (derivationInfo.derivationType == null) {
128 derivationInfo.derivationType = DerivationType.nano;
129 }
130
131 final wallet = await NanoWallet(
132 password: credentials.password!,
133 mnemonic: mnemonic ?? credentials.seedKey,
134 walletInfo: credentials.walletInfo!,
135 derivationInfo: derivationInfo,
136 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
137 );
138 await wallet.init();
139 await wallet.save();
140 return wallet;
141 }
142
143 @override
144 Future<NanoWallet> restoreFromHardwareWallet(NanoNewWalletCredentials credentials) {
145 throw UnimplementedError(
146 "Restoring a Nano wallet from a hardware wallet is not yet supported!");
147 }
148
149 @override
150 Future<NanoWallet> restoreFromSeed(NanoRestoreWalletFromSeedCredentials credentials,
151 {bool? isTestnet}) async {
152 if (credentials.mnemonic.contains(' ')) {
153 if (!bip39.validateMnemonic(credentials.mnemonic)) {
154 throw nm.NanoMnemonicIsIncorrectException();
155 }
156
157 if (!NanoMnemomics.validateMnemonic(credentials.mnemonic.split(' '))) {
158 throw nm.NanoMnemonicIsIncorrectException();
159 }
160 } else {
161 if (credentials.mnemonic.length != 64 && credentials.mnemonic.length != 128) {
162 throw Exception("Invalid seed length");
163 }
164 }
165
166 final derivationInfo = await credentials.walletInfo!.getDerivationInfo();
167 DerivationType derivationType = derivationInfo.derivationType ?? DerivationType.nano;
168
169 derivationInfo.derivationType = derivationType;
170 derivationInfo.save();
171
172 final wallet = await NanoWallet(
173 password: credentials.password!,
174 mnemonic: credentials.mnemonic,
175 walletInfo: credentials.walletInfo!,
176 derivationInfo: derivationInfo,
177 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
178 );
179
180 await wallet.init();
181 await wallet.save();
182 return wallet;
183 }
184
185 @override
186 Future<bool> isWalletExit(String name) async =>
187 File(await pathForWallet(name: name, type: getType())).existsSync();
188
189 @override
190 Future<NanoWallet> openWallet(String name, String password) async {
191 final walletInfo = await WalletInfo.get(name, getType());
192 if (walletInfo == null) {
193 throw Exception('Wallet not found');
194 }
195
196 try {
197 final wallet = await NanoWalletBase.open(
198 name: name,
199 password: password,
200 walletInfo: walletInfo,
201 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
202 );
203
204 await wallet.init();
205 await wallet.save();
206 saveBackup(name);
207 return wallet;
208 } catch (_) {
209 await restoreWalletFilesFromBackup(name);
210 final wallet = await NanoWalletBase.open(
211 name: name,
212 password: password,
213 walletInfo: walletInfo,
214 encryptionFileUtils: encryptionFileUtilsFor(isDirect),
215 );
216
217 await wallet.init();
218 await wallet.save();
219 return wallet;
220 }
221 }
222 }