From 25cd572b479cb0dfed52eacc8313272790132310 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:56:57 +0100 Subject: [PATCH 1/2] fix: invalid min amount when transferring funds --- src/ui/wallets/send_screen.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/ui/wallets/send_screen.rs b/src/ui/wallets/send_screen.rs index f9da86e3e..f9b092d6a 100644 --- a/src/ui/wallets/send_screen.rs +++ b/src/ui/wallets/send_screen.rs @@ -19,7 +19,7 @@ use crate::ui::{MessageType, RootScreenType, ScreenLike}; use dash_sdk::dashcore_rpc::dashcore::Address; use dash_sdk::dashcore_rpc::dashcore::address::NetworkUnchecked; use dash_sdk::dpp::address_funds::PlatformAddress; -use dash_sdk::dpp::balances::credits::Credits; +use dash_sdk::dpp::balances::credits::{CREDITS_PER_DUFF, Credits}; use dash_sdk::dpp::identity::core_script::CoreScript; use eframe::egui::{self, Context, RichText, Ui}; use egui::{Color32, Frame, Margin}; @@ -1155,14 +1155,17 @@ impl WalletSendScreen { ui.add_space(8.0); // Get max amount and hint based on source selection - let (max_amount_credits, max_hint) = match &self.selected_source { + let (max_amount_credits, max_hint, min_amount) = match &self.selected_source { Some(SourceSelection::CoreWallet) => { let max = self.selected_wallet.as_ref().and_then(|w| { w.read() .ok() - .map(|wallet| wallet.total_balance_duffs() * 1000) // duffs to credits + .map(|wallet| wallet.total_balance_duffs() * CREDITS_PER_DUFF) // duffs to credits }); - (max, None) + // We assume that min value is 0.00002 DASH, smaller amounts may be considered dust and rejected by Core. + let min_amount = Amount::new_dash(0.00002).value(); + + (max, None, Some(min_amount)) } Some(SourceSelection::PlatformAddresses(addresses)) => { // Parse destination to exclude it from max calculation (can't send to yourself) @@ -1198,9 +1201,13 @@ impl WalletSendScreen { } else { format!("~{} reserved for fees", Self::format_credits(max_fee)) }; - (Some(total.saturating_sub(max_fee)), Some(hint)) + ( + Some(total.saturating_sub(max_fee)), + Some(hint), + Some(max_fee), + ) } - None => (None, None), + None => (None, None, None), }; Frame::group(ui.style()) @@ -1215,9 +1222,10 @@ impl WalletSendScreen { .with_desired_width(150.0) }); - // Update max amount and hint dynamically + // Update max/min amount and hint dynamically amount_input.set_max_amount(max_amount_credits); amount_input.set_max_exceeded_hint(max_hint); + amount_input.set_min_amount(min_amount); let response = amount_input.show(ui); response.inner.update(&mut self.amount); @@ -2108,7 +2116,7 @@ impl WalletSendScreen { /// Advanced Core to Core send (multiple outputs) fn send_advanced_core_to_core(&mut self) -> Result { - let wallet = self + let wallet: Arc> = self .selected_wallet .as_ref() .ok_or("No wallet selected")? From 2884307a97cb0c9f20b75a5403a702944c0877b6 Mon Sep 17 00:00:00 2001 From: Lukasz Klimek <842586+lklimek@users.noreply.github.com> Date: Tue, 3 Feb 2026 16:23:36 +0100 Subject: [PATCH 2/2] chore: rabbit's feedback --- src/ui/wallets/send_screen.rs | 49 ++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/src/ui/wallets/send_screen.rs b/src/ui/wallets/send_screen.rs index f9b092d6a..ccf9063d3 100644 --- a/src/ui/wallets/send_screen.rs +++ b/src/ui/wallets/send_screen.rs @@ -396,6 +396,33 @@ impl WalletSendScreen { AddressType::Unknown } + fn min_output_amount( + &self, + input_type: AddressType, + output_type: AddressType, + ) -> Option { + let core_min = 5460_u64 * CREDITS_PER_DUFF; + let platform_min = self + .app_context + .platform_version() + .dpp + .state_transitions + .address_funds + .min_output_amount; + + match (input_type, output_type) { + (AddressType::Unknown, AddressType::Unknown) => None, + (AddressType::Core, AddressType::Core) => Some(core_min), + (AddressType::Platform, AddressType::Platform) => Some(platform_min), + (AddressType::Core, AddressType::Platform) => Some(56000000), // needed for asset locks + (AddressType::Platform, AddressType::Core) => Some(core_min.max(platform_min)), + (AddressType::Unknown, AddressType::Core) => Some(core_min), + (AddressType::Unknown, AddressType::Platform) => Some(platform_min), + (AddressType::Core, AddressType::Unknown) => Some(core_min), + (AddressType::Platform, AddressType::Unknown) => Some(platform_min), + } + } + /// Get available Platform addresses with balances /// Deduplicates addresses based on their canonical Bech32m string representation, /// preferring the entry with the highest nonce (most recent update) @@ -1155,17 +1182,15 @@ impl WalletSendScreen { ui.add_space(8.0); // Get max amount and hint based on source selection - let (max_amount_credits, max_hint, min_amount) = match &self.selected_source { + let (max_amount_credits, max_hint) = match &self.selected_source { Some(SourceSelection::CoreWallet) => { let max = self.selected_wallet.as_ref().and_then(|w| { w.read() .ok() .map(|wallet| wallet.total_balance_duffs() * CREDITS_PER_DUFF) // duffs to credits }); - // We assume that min value is 0.00002 DASH, smaller amounts may be considered dust and rejected by Core. - let min_amount = Amount::new_dash(0.00002).value(); - (max, None, Some(min_amount)) + (max, None) } Some(SourceSelection::PlatformAddresses(addresses)) => { // Parse destination to exclude it from max calculation (can't send to yourself) @@ -1201,14 +1226,18 @@ impl WalletSendScreen { } else { format!("~{} reserved for fees", Self::format_credits(max_fee)) }; - ( - Some(total.saturating_sub(max_fee)), - Some(hint), - Some(max_fee), - ) + (Some(total.saturating_sub(max_fee)), Some(hint)) } - None => (None, None, None), + None => (None, None), + }; + + let input_type = match self.selected_source { + Some(SourceSelection::CoreWallet) => AddressType::Core, + Some(SourceSelection::PlatformAddresses(_)) => AddressType::Platform, + None => AddressType::Unknown, }; + let output_type = self.detect_address_type(&self.destination_address); + let min_amount = self.min_output_amount(input_type, output_type); Frame::group(ui.style()) .fill(DashColors::surface(dark_mode))