dev
dart 409 lines 12.1 KB
Raw
1 part of 'nano.dart';
2
3 class CWNanoAccountList extends NanoAccountList {
4 CWNanoAccountList(this._wallet);
5 final Object _wallet;
6
7 @override
8 @computed
9 ObservableList<NanoAccount> get accounts {
10 final nanoWallet = _wallet as NanoWallet;
11 final accounts = nanoWallet.walletAddresses.accountList.accounts
12 .map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
13 .toList();
14 return ObservableList<NanoAccount>.of(accounts);
15 }
16
17 @override
18 void update(Object wallet) {
19 final nanoWallet = wallet as NanoWallet;
20 nanoWallet.walletAddresses.accountList.update(null);
21 }
22
23 @override
24 void refresh(Object wallet) {
25 final nanoWallet = wallet as NanoWallet;
26 nanoWallet.walletAddresses.accountList.refresh();
27 }
28
29 @override
30 Future<List<NanoAccount>> getAll(Object wallet) async {
31 final nanoWallet = wallet as NanoWallet;
32 return (await nanoWallet.walletAddresses.accountList.getAll())
33 .map((acc) => NanoAccount(id: acc.id, label: acc.label, balance: acc.balance))
34 .toList();
35 }
36
37 @override
38 Future<void> addAccount(Object wallet, {required String label}) async {
39 final nanoWallet = wallet as NanoWallet;
40 await nanoWallet.walletAddresses.accountList.addAccount(label: label);
41 }
42
43 @override
44 Future<void> setLabelAccount(Object wallet,
45 {required int accountIndex, required String label}) async {
46 final nanoWallet = wallet as NanoWallet;
47 await nanoWallet.walletAddresses.accountList
48 .setLabelAccount(accountIndex: accountIndex, label: label);
49 }
50 }
51
52 class CWNano extends Nano {
53 @override
54 NanoAccountList getAccountList(Object wallet) {
55 return CWNanoAccountList(wallet);
56 }
57
58 @override
59 Account getCurrentAccount(Object wallet) {
60 final nanoWallet = wallet as NanoWallet;
61 final acc = nanoWallet.walletAddresses.account;
62 return Account(id: acc!.id, label: acc.label, balance: acc.balance);
63 }
64
65 @override
66 void setCurrentAccount(Object wallet, int id, String label, String? balance) {
67 final nanoWallet = wallet as NanoWallet;
68 nanoWallet.walletAddresses.account = NanoAccount(id: id, label: label, balance: balance);
69 nanoWallet.regenerateAddress();
70 }
71
72 @override
73 List<String> getNanoWordList(String language) {
74 return NanoMnemomics.WORDLIST;
75 }
76
77 @override
78 WalletService createNanoWalletService(bool isDirect) {
79 return NanoWalletService(isDirect);
80 }
81
82 @override
83 Map<String, String> getKeys(Object wallet) {
84 final nanoWallet = wallet as NanoWallet;
85 final keys = nanoWallet.keys;
86 return <String, String>{
87 "seedKey": keys.seedKey,
88 };
89 }
90
91 @override
92 WalletCredentials createNanoNewWalletCredentials({
93 required String name,
94 WalletInfo? walletInfo,
95 String? password,
96 String? mnemonic,
97 String? passphrase,
98 }) =>
99 NanoNewWalletCredentials(
100 name: name,
101 password: password,
102 mnemonic: mnemonic,
103 walletInfo: walletInfo,
104 passphrase: passphrase,
105 );
106
107 @override
108 WalletCredentials createNanoRestoreWalletFromSeedCredentials({
109 required String name,
110 required String password,
111 required String mnemonic,
112 required DerivationType derivationType,
113 String? passphrase,
114 }) {
115 if (mnemonic.split(" ").length == 12 && derivationType != DerivationType.bip39) {
116 throw Exception("Invalid mnemonic for derivation type!");
117 }
118
119 return NanoRestoreWalletFromSeedCredentials(
120 name: name,
121 password: password,
122 mnemonic: mnemonic,
123 derivationType: derivationType,
124 passphrase: passphrase,
125 );
126 }
127
128 @override
129 WalletCredentials createNanoRestoreWalletFromKeysCredentials({
130 required String name,
131 required String password,
132 required String seedKey,
133 required DerivationType derivationType,
134 }) {
135 if (seedKey.length == 128 && derivationType != DerivationType.bip39) {
136 throw Exception("Invalid seed key length for derivation type!");
137 }
138
139 return NanoRestoreWalletFromKeysCredentials(
140 name: name,
141 password: password,
142 seedKey: seedKey,
143 derivationType: derivationType,
144 );
145 }
146
147 @override
148 Object createNanoTransactionCredentials(List<Output> outputs) {
149 return NanoTransactionCredentials(
150 outputs
151 .map((out) => OutputInfo(
152 fiatAmount: out.fiatAmount,
153 cryptoAmount: out.cryptoAmountMoney,
154 address: out.address,
155 note: out.note,
156 sendAll: out.sendAll,
157 extractedAddress: out.extractedAddress,
158 isParsedAddress: out.isParsedAddress,
159 ))
160 .toList(),
161 );
162 }
163
164 @override
165 Future<void> changeRep(Object wallet, String address) async {
166 if ((wallet as NanoWallet).transactionHistory.transactions.isEmpty) {
167 throw Exception("Can't change representative without an existing transaction history");
168 }
169 return wallet.changeRep(address);
170 }
171
172 @override
173 Future<bool> updateTransactions(Object wallet) async {
174 return (wallet as NanoWallet).updateTransactions();
175 }
176
177 @override
178 String getRepresentative(Object wallet) {
179 return (wallet as NanoWallet).representative;
180 }
181
182 @override
183 Future<List<N2Node>> getN2Reps(Object wallet) async {
184 return (wallet as NanoWallet).getN2Reps();
185 }
186
187 @override
188 bool isRepOk(Object wallet) {
189 return (wallet as NanoWallet).isRepOk;
190 }
191 }
192
193 class CWNanoUtil extends NanoUtil {
194 @override
195 bool isValidBip39Seed(String seed) {
196 return NanoDerivations.isValidBip39Seed(seed);
197 }
198
199 // number util:
200
201 static const int maxDecimalDigits = 6; // Max digits after decimal
202 BigInt rawPerNano = BigInt.parse("1000000000000000000000000000000");
203 BigInt rawPerNyano = BigInt.parse("1000000000000000000000000");
204 BigInt rawPerBanano = BigInt.parse("100000000000000000000000000000");
205 BigInt rawPerXMR = BigInt.parse("1000000000000");
206 BigInt convertXMRtoNano = BigInt.parse("1000000000000000000");
207
208 @override
209 String getRawAsUsableString(String? raw, BigInt rawPerCur) {
210 return NanoAmounts.getRawAsUsableString(raw, rawPerCur);
211 }
212
213 @override
214 String getRawAccuracy(String? raw, BigInt rawPerCur) {
215 return NanoAmounts.getRawAccuracy(raw, rawPerCur);
216 }
217
218 @override
219 String getAmountAsRaw(String amount, BigInt rawPerCur) {
220 return NanoAmounts.getAmountAsRaw(amount, rawPerCur);
221 }
222
223 @override
224 Future<AccountInfoResponse?> getInfoFromSeedOrMnemonic(
225 DerivationType derivationType, {
226 String? seedKey,
227 String? mnemonic,
228 required Node node,
229 }) async {
230 NanoClient nanoClient = NanoClient();
231 nanoClient.connect(node);
232 String? publicAddress;
233
234 if (seedKey == null && mnemonic == null) {
235 throw Exception("One of seed key OR mnemonic must be provided!");
236 }
237
238 try {
239 if (seedKey != null) {
240 if (seedKey.length == 64) {
241 try {
242 mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey);
243 } catch (e) {
244 printV("not a valid 'nano' seed key");
245 }
246 }
247 if (derivationType == DerivationType.bip39) {
248 publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
249 } else if (derivationType == DerivationType.nano) {
250 publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
251 }
252 }
253
254 if (derivationType == DerivationType.bip39) {
255 if (mnemonic != null) {
256 seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
257 publicAddress = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
258 }
259 }
260
261 if (derivationType == DerivationType.nano) {
262 if (mnemonic != null) {
263 seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
264 publicAddress = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
265 }
266 }
267 } catch (_) {}
268
269 if (publicAddress == null) {
270 // we couldn't derive a public address for the derivation type provided
271 // i.e. a bip39 seed was provided and we were instructed to derive a "nano" type address
272 return null;
273 }
274
275 AccountInfoResponse? accountInfo = await nanoClient.getAccountInfo(publicAddress);
276 if (accountInfo == null) {
277 accountInfo = AccountInfoResponse(
278 frontier: "", balance: "0", representative: "", confirmationHeight: 0);
279 }
280 accountInfo.address = publicAddress;
281 return accountInfo;
282 }
283
284 @override
285 Future<List<DerivationType>> compareDerivationMethods({
286 String? mnemonic,
287 String? privateKey,
288 required Node node,
289 }) async {
290 String? seedKey = privateKey;
291
292 if (mnemonic?.split(' ').length == 12) {
293 return [DerivationType.bip39];
294 }
295 if (seedKey?.length == 128) {
296 return [DerivationType.bip39];
297 } else if (seedKey?.length == 64) {
298 try {
299 mnemonic = NanoDerivations.standardSeedToMnemonic(seedKey!);
300 } catch (e) {
301 printV("not a valid 'nano' seed key");
302 }
303 }
304
305 late String publicAddressStandard;
306 late String publicAddressBip39;
307
308 try {
309 NanoClient nanoClient = NanoClient();
310 nanoClient.connect(node);
311
312 if (mnemonic != null) {
313 seedKey = await NanoDerivations.hdMnemonicListToSeed(mnemonic.split(' '));
314 publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
315
316 seedKey = await NanoDerivations.standardMnemonicToSeed(mnemonic);
317 publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
318 } else if (seedKey != null) {
319 try {
320 publicAddressBip39 = await NanoDerivations.hdSeedToAddress(seedKey, index: 0);
321 } catch (e) {
322 return [DerivationType.nano];
323 }
324 try {
325 publicAddressStandard = await NanoDerivations.standardSeedToAddress(seedKey, index: 0);
326 } catch (e) {
327 return [DerivationType.bip39];
328 }
329 }
330
331 // check if account has a history:
332 AccountInfoResponse? bip39Info;
333 AccountInfoResponse? standardInfo;
334
335 try {
336 bip39Info = await nanoClient.getAccountInfo(publicAddressBip39);
337 } catch (e) {
338 bip39Info = null;
339 }
340 try {
341 standardInfo = await nanoClient.getAccountInfo(publicAddressStandard);
342 } catch (e) {
343 standardInfo = null;
344 }
345
346 // one of these is *probably* null:
347 if (bip39Info == null && standardInfo != null) {
348 return [DerivationType.nano];
349 } else if (standardInfo == null && bip39Info != null) {
350 return [DerivationType.bip39];
351 }
352
353 // we don't know for sure:
354 return [DerivationType.nano, DerivationType.bip39];
355 } catch (e) {
356 return [DerivationType.nano, DerivationType.bip39];
357 }
358 }
359
360 @override
361 Future<List<DerivationInfo>> getDerivationsFromMnemonic({
362 String? mnemonic,
363 String? seedKey,
364 required Node node,
365 }) async {
366 List<DerivationInfo> list = [];
367
368 List<DerivationType> possibleDerivationTypes = await compareDerivationMethods(
369 mnemonic: mnemonic,
370 privateKey: seedKey,
371 node: node,
372 );
373 if (possibleDerivationTypes.length == 1) {
374 return [DerivationInfo(derivationType: possibleDerivationTypes.first)];
375 }
376
377 AccountInfoResponse? bip39Info = await nanoUtil!.getInfoFromSeedOrMnemonic(
378 DerivationType.bip39,
379 mnemonic: mnemonic,
380 seedKey: seedKey,
381 node: node,
382 );
383 AccountInfoResponse? standardInfo = await nanoUtil!.getInfoFromSeedOrMnemonic(
384 DerivationType.nano,
385 mnemonic: mnemonic,
386 seedKey: seedKey,
387 node: node,
388 );
389
390 if (standardInfo?.confirmationHeight != null && standardInfo!.confirmationHeight > 0) {
391 list.add(DerivationInfo(
392 derivationType: DerivationType.nano,
393 balance: nanoUtil!.getRawAsUsableString(standardInfo.balance, nanoUtil!.rawPerNano),
394 address: standardInfo.address!,
395 transactionsCount: standardInfo.confirmationHeight,
396 ));
397 }
398
399 if (bip39Info?.confirmationHeight != null && bip39Info!.confirmationHeight > 0) {
400 list.add(DerivationInfo(
401 derivationType: DerivationType.bip39,
402 balance: nanoUtil!.getRawAsUsableString(bip39Info.balance, nanoUtil!.rawPerNano),
403 address: bip39Info.address!,
404 transactionsCount: bip39Info.confirmationHeight,
405 ));
406 }
407 return list;
408 }
409 }