CW-489 Skip Warning if all monero utxo are selected (#1106)

Konstantin Ullrich committed Sep 28, 2023 at 19:49 UTC dc36c31197485aff33980d66583cce366aca2cb9
1 file changed +72 -97
cw_monero/lib/monero_wallet.dart
+72 -97
@@ -37,10 +37,10 @@ const moneroBlockSize = 1000;
37
38 class MoneroWallet = MoneroWalletBase with _$MoneroWallet;
39
40 -abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
41 - MoneroTransactionHistory, MoneroTransactionInfo> with Store {
42 - MoneroWalletBase({required WalletInfo walletInfo,
43 - required Box<UnspentCoinsInfo> unspentCoinsInfo})
40 +abstract class MoneroWalletBase
41 + extends WalletBase<MoneroBalance, MoneroTransactionHistory, MoneroTransactionInfo> with Store {
42 + MoneroWalletBase(
43 + {required WalletInfo walletInfo, required Box<UnspentCoinsInfo> unspentCoinsInfo})
44 : balance = ObservableMap<CryptoCurrency, MoneroBalance>.of({
45 CryptoCurrency.xmr: MoneroBalance(
46 fullBalance: monero_wallet.getFullBalance(accountIndex: 0),
@@ -112,12 +112,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
112
113 Future<void> init() async {
114 await walletAddresses.init();
115 - balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(
116 - <CryptoCurrency, MoneroBalance>{
117 - currency: MoneroBalance(
118 - fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id),
119 - unlockedBalance: monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id))
120 - });
115 + balance = ObservableMap<CryptoCurrency, MoneroBalance>.of(<CryptoCurrency, MoneroBalance>{
116 + currency: MoneroBalance(
117 + fullBalance: monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id),
118 + unlockedBalance:
119 + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id))
120 + });
121 _setListeners();
122 await updateTransactions();
123
@@ -125,15 +125,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
125 monero_wallet.setRecoveringFromSeed(isRecovery: walletInfo.isRecovery);
126
127 if (monero_wallet.getCurrentHeight() <= 1) {
128 - monero_wallet.setRefreshFromBlockHeight(
129 - height: walletInfo.restoreHeight);
128 + monero_wallet.setRefreshFromBlockHeight(height: walletInfo.restoreHeight);
129 }
130 }
131
133 - _autoSaveTimer = Timer.periodic(
134 - Duration(seconds: _autoSaveInterval),
135 - (_) async => await save());
132 + _autoSaveTimer =
133 + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save());
134 }
135 +
136 @override
137 Future<void>? updateBalance() => null;
138
@@ -153,7 +152,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
152 login: node.login,
153 password: node.password,
154 useSSL: node.isSSL,
156 - isLightWallet: false, // FIXME: hardcoded value
155 + isLightWallet: false,
156 + // FIXME: hardcoded value
157 socksProxyAddress: node.socksProxyAddress);
158
159 monero_wallet.setTrustedDaemon(node.trusted);
@@ -189,7 +189,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
189 final outputs = _credentials.outputs;
190 final hasMultiDestination = outputs.length > 1;
191 final unlockedBalance =
192 - monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
192 + monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
193 var allInputsAmount = 0;
194
195 PendingTransactionDescription pendingTransactionDescription;
@@ -208,56 +208,42 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
208 inputs.add(utx.keyImage);
209 }
210 }
211 -
212 - if (inputs.isEmpty) {
213 - throw MoneroTransactionNoInputsException(0);
214 - }
211 + final spendAllCoins = inputs.length == unspentCoins.length;
212
213 if (hasMultiDestination) {
217 - if (outputs.any((item) => item.sendAll
218 - || (item.formattedCryptoAmount ?? 0) <= 0)) {
214 + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) {
215 throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
216 }
217
222 - final int totalAmount = outputs.fold(0, (acc, value) =>
223 - acc + (value.formattedCryptoAmount ?? 0));
218 + final int totalAmount =
219 + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0));
220
221 final estimatedFee = calculateEstimatedFee(_credentials.priority, totalAmount);
222 if (unlockedBalance < totalAmount) {
223 throw MoneroTransactionCreationException('You do not have enough XMR to send this amount.');
224 }
225
230 - if (allInputsAmount < totalAmount + estimatedFee) {
226 + if (!spendAllCoins && (allInputsAmount < totalAmount + estimatedFee)) {
227 throw MoneroTransactionNoInputsException(inputs.length);
228 }
229
230 final moneroOutputs = outputs.map((output) {
235 - final outputAddress = output.isParsedAddress
236 - ? output.extractedAddress
237 - : output.address;
231 + final outputAddress = output.isParsedAddress ? output.extractedAddress : output.address;
232
239 - return MoneroOutput(
240 - address: outputAddress!,
241 - amount: output.cryptoAmount!.replaceAll(',', '.'));
233 + return MoneroOutput(
234 + address: outputAddress!, amount: output.cryptoAmount!.replaceAll(',', '.'));
235 }).toList();
236
244 - pendingTransactionDescription =
245 - await transaction_history.createTransactionMultDest(
237 + pendingTransactionDescription = await transaction_history.createTransactionMultDest(
238 outputs: moneroOutputs,
239 priorityRaw: _credentials.priority.serialize(),
240 accountIndex: walletAddresses.account!.id,
241 preferredInputs: inputs);
242 } else {
243 final output = outputs.first;
252 - final address = output.isParsedAddress
253 - ? output.extractedAddress
254 - : output.address;
255 - final amount = output.sendAll
256 - ? null
257 - : output.cryptoAmount!.replaceAll(',', '.');
258 - final formattedAmount = output.sendAll
259 - ? null
260 - : output.formattedCryptoAmount;
244 + final address = output.isParsedAddress ? output.extractedAddress : output.address;
245 + final amount = output.sendAll ? null : output.cryptoAmount!.replaceAll(',', '.');
246 + final formattedAmount = output.sendAll ? null : output.formattedCryptoAmount;
247
248 if ((formattedAmount != null && unlockedBalance < formattedAmount) ||
249 (formattedAmount == null && unlockedBalance <= 0)) {
@@ -268,8 +254,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
254 }
255
256 final estimatedFee = calculateEstimatedFee(_credentials.priority, formattedAmount);
271 - if ((formattedAmount != null && allInputsAmount < (formattedAmount + estimatedFee)) ||
272 - (formattedAmount == null && allInputsAmount != unlockedBalance)) {
257 + if (!spendAllCoins &&
258 + ((formattedAmount != null && allInputsAmount < (formattedAmount + estimatedFee)) ||
259 + formattedAmount == null)) {
260 throw MoneroTransactionNoInputsException(inputs.length);
261 }
262
@@ -327,10 +314,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
314
315 try {
316 // -- rename the waller folder --
330 - final currentWalletDir =
331 - Directory(await pathForWalletDir(name: name, type: type));
332 - final newWalletDirPath =
333 - await pathForWalletDir(name: newWalletName, type: type);
317 + final currentWalletDir = Directory(await pathForWalletDir(name: name, type: type));
318 + final newWalletDirPath = await pathForWalletDir(name: newWalletName, type: type);
319 await currentWalletDir.rename(newWalletDirPath);
320
321 // -- use new waller folder to rename files with old names still --
@@ -340,8 +325,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
325 final currentKeysFile = File('$renamedWalletPath.keys');
326 final currentAddressListFile = File('$renamedWalletPath.address.txt');
327
343 - final newWalletPath =
344 - await pathForWallet(name: newWalletName, type: type);
328 + final newWalletPath = await pathForWallet(name: newWalletName, type: type);
329
330 if (currentCacheFile.existsSync()) {
331 await currentCacheFile.rename(newWalletPath);
@@ -359,8 +343,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
343 final currentKeysFile = File('$currentWalletPath.keys');
344 final currentAddressListFile = File('$currentWalletPath.address.txt');
345
362 - final newWalletPath =
363 - await pathForWallet(name: newWalletName, type: type);
346 + final newWalletPath = await pathForWallet(name: newWalletName, type: type);
347
348 // Copies current wallet files into new wallet name's dir and files
349 if (currentCacheFile.existsSync()) {
@@ -426,8 +409,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
409
410 if (unspentCoins.isNotEmpty) {
411 unspentCoins.forEach((coin) {
429 - final coinInfoList = unspentCoinsInfo.values.where((element) =>
430 - element.walletId.contains(id) && element.hash.contains(coin.hash));
412 + final coinInfoList = unspentCoinsInfo.values
413 + .where((element) => element.walletId.contains(id) && element.hash.contains(coin.hash));
414
415 if (coinInfoList.isNotEmpty) {
416 final coinInfo = coinInfoList.first;
@@ -447,16 +430,15 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
430
431 Future<void> _addCoinInfo(MoneroUnspent coin) async {
432 final newInfo = UnspentCoinsInfo(
450 - walletId: id,
451 - hash: coin.hash,
452 - isFrozen: coin.isFrozen,
453 - isSending: coin.isSending,
454 - noteRaw: coin.note,
455 - address: coin.address,
456 - value: coin.value,
457 - vout: 0,
458 - keyImage: coin.keyImage
459 - );
433 + walletId: id,
434 + hash: coin.hash,
435 + isFrozen: coin.isFrozen,
436 + isSending: coin.isSending,
437 + noteRaw: coin.note,
438 + address: coin.address,
439 + value: coin.value,
440 + vout: 0,
441 + keyImage: coin.keyImage);
442
443 await unspentCoinsInfo.add(newInfo);
444 }
@@ -464,8 +446,8 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
446 Future<void> _refreshUnspentCoinsInfo() async {
447 try {
448 final List<dynamic> keys = <dynamic>[];
467 - final currentWalletUnspentCoins = unspentCoinsInfo.values
468 - .where((element) => element.walletId.contains(id));
449 + final currentWalletUnspentCoins =
450 + unspentCoinsInfo.values.where((element) => element.walletId.contains(id));
451
452 if (currentWalletUnspentCoins.isNotEmpty) {
453 currentWalletUnspentCoins.forEach((element) {
@@ -486,16 +468,14 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
468 }
469
470 String getTransactionAddress(int accountIndex, int addressIndex) =>
489 - monero_wallet.getAddress(
490 - accountIndex: accountIndex,
491 - addressIndex: addressIndex);
471 + monero_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex);
472
473 @override
474 Future<Map<String, MoneroTransactionInfo>> fetchTransactions() async {
475 transaction_history.refreshTransactions();
496 - return _getAllTransactionsOfAccount(walletAddresses.account?.id).fold<Map<String, MoneroTransactionInfo>>(
497 - <String, MoneroTransactionInfo>{},
498 - (Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
476 + return _getAllTransactionsOfAccount(walletAddresses.account?.id)
477 + .fold<Map<String, MoneroTransactionInfo>>(<String, MoneroTransactionInfo>{},
478 + (Map<String, MoneroTransactionInfo> acc, MoneroTransactionInfo tx) {
479 acc[tx.id] = tx;
480 return acc;
481 });
@@ -523,12 +503,11 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
503 return monero_wallet.getSubaddressLabel(accountIndex, addressIndex);
504 }
505
526 - List<MoneroTransactionInfo> _getAllTransactionsOfAccount(int? accountIndex) =>
527 - transaction_history
528 - .getAllTransactions()
529 - .map((row) => MoneroTransactionInfo.fromRow(row))
530 - .where((element) => element.accountIndex == (accountIndex ?? 0))
531 - .toList();
506 + List<MoneroTransactionInfo> _getAllTransactionsOfAccount(int? accountIndex) => transaction_history
507 + .getAllTransactions()
508 + .map((row) => MoneroTransactionInfo.fromRow(row))
509 + .where((element) => element.accountIndex == (accountIndex ?? 0))
510 + .toList();
511
512 void _setListeners() {
513 _listener?.stop();
@@ -550,8 +529,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
529 }
530
531 int _getHeightDistance(DateTime date) {
553 - final distance =
554 - DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
532 + final distance = DateTime.now().millisecondsSinceEpoch - date.millisecondsSinceEpoch;
533 final daysTmp = (distance / 86400).round();
534 final days = daysTmp < 1 ? 1 : daysTmp;
535
@@ -582,11 +560,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
560 }
561 }
562
585 - Future<void> _askForUpdateTransactionHistory() async =>
586 - await updateTransactions();
563 + Future<void> _askForUpdateTransactionHistory() async => await updateTransactions();
564
588 - int _getFullBalance() =>
589 - monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
565 + int _getFullBalance() => monero_wallet.getFullBalance(accountIndex: walletAddresses.account!.id);
566
567 int _getUnlockedBalance() =>
568 monero_wallet.getUnlockedBalance(accountIndex: walletAddresses.account!.id);
@@ -595,8 +571,7 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
571 var frozenBalance = 0;
572
573 for (var coin in unspentCoinsInfo.values) {
598 - if (coin.isFrozen)
599 - frozenBalance += coin.value;
574 + if (coin.isFrozen) frozenBalance += coin.value;
575 }
576
577 return frozenBalance;
@@ -617,9 +592,9 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
592 syncStatus = SyncedSyncStatus();
593
594 if (!_hasSyncAfterStartup) {
620 - _hasSyncAfterStartup = true;
621 - await save();
622 - }
595 + _hasSyncAfterStartup = true;
596 + await save();
597 + }
598
599 if (walletInfo.isRecovery) {
600 await setAsRecovered();
@@ -644,12 +619,12 @@ abstract class MoneroWalletBase extends WalletBase<MoneroBalance,
619
620 void _updateSubAddress(bool enableAutoGenerate, {Account? account}) {
621 if (enableAutoGenerate) {
647 - walletAddresses.updateUnusedSubaddress(
648 - accountIndex: account?.id ?? 0,
649 - defaultLabel: account?.label ?? '',
650 - );
651 - } else {
652 - walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
653 - }
622 + walletAddresses.updateUnusedSubaddress(
623 + accountIndex: account?.id ?? 0,
624 + defaultLabel: account?.label ?? '',
625 + );
626 + } else {
627 + walletAddresses.updateSubaddressList(accountIndex: account?.id ?? 0);
628 + }
629 }
630 }