From 10499814bb9d1f58d78a80b971b5be0bbf299266 Mon Sep 17 00:00:00 2001 From: rafael_xmr Date: Tue, 29 Jul 2025 09:50:11 -0300 Subject: [PATCH] warn sp dust (#2402) * feat: warn sp dust * Update lib/view_model/send/send_view_model.dart * fix condition --------- Co-authored-by: Omar Hatem --- cw_bitcoin/lib/electrum_wallet.dart | 26 +++++++++++++++++++-- lib/src/screens/send/widgets/send_card.dart | 2 +- lib/view_model/send/send_view_model.dart | 26 +++++++++++++++++---- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index 28d0e323a..eb03514ae 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -780,7 +780,11 @@ abstract class ElectrumWalletBase } if (outputs.length == 1) { - outputs[0] = BitcoinOutput(address: outputs.last.address, value: BigInt.from(amount)); + outputs[0] = BitcoinOutput( + address: outputs.last.address, + value: BigInt.from(amount), + isSilentPayment: hasSilentPayment, + ); } return EstimatedTxResult( @@ -888,7 +892,17 @@ abstract class ElectrumWalletBase ); updatedOutputs.clear(); - updatedOutputs.addAll(temp); + for (int i = 0; i < temp.length; i++) { + final output = temp[i]; + final oldOutput = outputs[i]; + + updatedOutputs.add(BitcoinOutput( + address: output.address, + value: output.value, + isSilentPayment: oldOutput.isSilentPayment, + isChange: output.isChange, + )); + } if (fee == 0) { throw BitcoinTransactionNoFeeException(); @@ -1088,6 +1102,14 @@ abstract class ElectrumWalletBase ); } + for (final output in updatedOutputs) { + // TODO: get from server + // if (output.isSilentPayment && output.value.toInt() > silentPaymentsMin) { + if (output.isSilentPayment && output.value.toInt() <= 1000) { + throw BitcoinTransactionNoDustException(); + } + } + if (walletInfo.isHardwareWallet) { final transaction = await buildHardwareWalletTransaction( utxos: estimatedTx.utxos, diff --git a/lib/src/screens/send/widgets/send_card.dart b/lib/src/screens/send/widgets/send_card.dart index d8b3517aa..8c372b161 100644 --- a/lib/src/screens/send/widgets/send_card.dart +++ b/lib/src/screens/send/widgets/send_card.dart @@ -258,7 +258,7 @@ class SendCardState extends State with AutomaticKeepAliveClientMixin output.setSendAll(await sendViewModel.sendingBalance), ), diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index a767065ec..22df67bf9 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -182,9 +182,9 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor if (pendingTransaction != null) { final currency = pendingTransactionFeeCurrency(walletType); final fiat = calculateFiatAmount( - price: _fiatConversationStore.prices[currency]!, - cryptoAmount: pendingTransaction!.feeFormattedValue, - ); + price: _fiatConversationStore.prices[currency]!, + cryptoAmount: pendingTransaction!.feeFormattedValue, + ); return fiat; } else { return '0.00'; @@ -210,8 +210,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor CryptoCurrency get currency => wallet.currency; - Validator get amountValidator => - AmountValidator(currency: walletTypeToCryptoCurrency(wallet.type)); + Validator amountValidator(Output output) => AmountValidator( + currency: walletTypeToCryptoCurrency(wallet.type), + minValue: isSendToSilentPayments(output) + ? + // TODO: get from server + // bitcoin!.silentPaymentsMinAmount + '0.00001' + : null, + ); Validator get allAmountValidator => AllAmountValidator(); @@ -289,6 +296,15 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor // If silent payments scanning, can still send payments (wallet.type == WalletType.bitcoin && wallet.syncStatus is SyncingSyncStatus); + bool isSendToSilentPayments(Output output) => + wallet.type == WalletType.bitcoin && + (RegExp(AddressValidator.silentPaymentAddressPatternMainnet).hasMatch(output.address) || + RegExp(AddressValidator.silentPaymentAddressPatternMainnet) + .hasMatch(output.extractedAddress) || + (output.parsedAddress.addresses.isNotEmpty && + RegExp(AddressValidator.silentPaymentAddressPatternMainnet) + .hasMatch(output.parsedAddress.addresses[0]))); + @computed List