From d411c3b08980dcda6d0c0653cc7f861c0ec57ebb Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 9 Sep 2026 13:33:46 +0200 Subject: [PATCH 1/3] fix: rebalance configuration with validation for budget and limits --- src/Pages/Nodes.razor | 103 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 91 insertions(+), 12 deletions(-) diff --git a/src/Pages/Nodes.razor b/src/Pages/Nodes.razor index f2a72c52..05dc38e7 100644 --- a/src/Pages/Nodes.razor +++ b/src/Pages/Nodes.razor @@ -477,32 +477,76 @@ - Rebalance Fee Budget (sats) - Max sats spendable on rebalance fees per refresh period. 0 = unset. - + + Rebalance Fee Budget (sats) + Max sats spendable on rebalance fees per refresh period. 0 = unset. + + + Rebalance fee budget must be ≥ 0 + + + - Budget Refresh Interval (days) - Period after which the rebalance fee budget resets. 0 = unset. - + + Budget Refresh Interval (days) + Period after which the rebalance fee budget resets. 0 = unset. + + + Budget refresh interval must be ≥ 0 days + + + - Max Rebalances In Flight - Maximum concurrent (Pending/InFlight) rebalances for this node. 0 = unset. - + + Max Rebalances In Flight + Maximum concurrent (Pending/InFlight) rebalances for this node. 0 = unset. + + + Max rebalances in flight must be ≥ 0 + + + - Max Cost-to-Earn Ratio - Profitability gate: spend at most this fraction of a channel's earn rate on rebalancing it (e.g. 0.5 = 50%). 0 = unset. - + + Max Cost-to-Earn Ratio + Profitability gate: spend at most this fraction of a channel's earn rate on rebalancing it (e.g. 0.5 = 50%). 0 = unset. + + + Max cost-to-earn ratio must be ≥ 0 + + + @@ -1070,5 +1114,40 @@ } } + // NumericEdit never renders min/max, and NumericPicker ignores them unless MinMaxLimitsOverride + // is set — hence Floor in the markup, plus these validators, which are what block the save. + private static void ValidateNonNegative(ValidatorEventArgs arg, string label) + { + var value = arg.Value switch + { + long l => l, + int i => i, + double d => d, + decimal m => (double)m, + _ => 0d + }; + + if (value < 0) + { + arg.Status = ValidationStatus.Error; + arg.ErrorText = $"{label} must be ≥ 0"; + return; + } + + arg.Status = ValidationStatus.Success; + } + + private void ValidateRebalanceBudget(ValidatorEventArgs arg) + => ValidateNonNegative(arg, "Rebalance fee budget"); + + private void ValidateRebalanceBudgetRefresh(ValidatorEventArgs arg) + => ValidateNonNegative(arg, "Budget refresh interval"); + + private void ValidateMaxRebalancesInFlight(ValidatorEventArgs arg) + => ValidateNonNegative(arg, "Max rebalances in flight"); + + private void ValidateMaxRebalanceCostToEarnRatio(ValidatorEventArgs arg) + => ValidateNonNegative(arg, "Max cost-to-earn ratio"); + } \ No newline at end of file From 84888030910a1391858d581a1d0d28b503f9621d Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 9 Sep 2026 13:56:55 +0200 Subject: [PATCH 2/3] refactor: streamline rebalance configuration by removing redundant validation logic --- src/Pages/Nodes.razor | 126 +++++++++++------------------------------- 1 file changed, 33 insertions(+), 93 deletions(-) diff --git a/src/Pages/Nodes.razor b/src/Pages/Nodes.razor index 05dc38e7..52b14176 100644 --- a/src/Pages/Nodes.razor +++ b/src/Pages/Nodes.razor @@ -472,81 +472,58 @@ + @* MinMaxLimitsOverride is required: Blazorise defaults it to Ignore, which makes Min inert. *@ @if (_selectedNodeForLiquidity.AutoRebalanceEnabled) { - - Rebalance Fee Budget (sats) - Max sats spendable on rebalance fees per refresh period. 0 = unset. - - - Rebalance fee budget must be ≥ 0 - - - + Rebalance Fee Budget (sats) + Max sats spendable on rebalance fees per refresh period. 0 = unset. + - - Budget Refresh Interval (days) - Period after which the rebalance fee budget resets. 0 = unset. - - - Budget refresh interval must be ≥ 0 days - - - + Budget Refresh Interval (days) + Period after which the rebalance fee budget resets. 0 = unset. + - - Max Rebalances In Flight - Maximum concurrent (Pending/InFlight) rebalances for this node. 0 = unset. - - - Max rebalances in flight must be ≥ 0 - - - + Max Rebalances In Flight + Maximum concurrent (Pending/InFlight) rebalances for this node. 0 = unset. + - - Max Cost-to-Earn Ratio - Profitability gate: spend at most this fraction of a channel's earn rate on rebalancing it (e.g. 0.5 = 50%). 0 = unset. - - - Max cost-to-earn ratio must be ≥ 0 - - - + Max Cost-to-Earn Ratio + Profitability gate: spend at most this fraction of a channel's earn rate on rebalancing it (e.g. 0.5 = 50%). 0 = unset. + @@ -1113,41 +1090,4 @@ return; } } - - // NumericEdit never renders min/max, and NumericPicker ignores them unless MinMaxLimitsOverride - // is set — hence Floor in the markup, plus these validators, which are what block the save. - private static void ValidateNonNegative(ValidatorEventArgs arg, string label) - { - var value = arg.Value switch - { - long l => l, - int i => i, - double d => d, - decimal m => (double)m, - _ => 0d - }; - - if (value < 0) - { - arg.Status = ValidationStatus.Error; - arg.ErrorText = $"{label} must be ≥ 0"; - return; - } - - arg.Status = ValidationStatus.Success; - } - - private void ValidateRebalanceBudget(ValidatorEventArgs arg) - => ValidateNonNegative(arg, "Rebalance fee budget"); - - private void ValidateRebalanceBudgetRefresh(ValidatorEventArgs arg) - => ValidateNonNegative(arg, "Budget refresh interval"); - - private void ValidateMaxRebalancesInFlight(ValidatorEventArgs arg) - => ValidateNonNegative(arg, "Max rebalances in flight"); - - private void ValidateMaxRebalanceCostToEarnRatio(ValidatorEventArgs arg) - => ValidateNonNegative(arg, "Max cost-to-earn ratio"); - - } \ No newline at end of file From 7a023f6028ed2829deaff5a0ee3b329b620d4e16 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 9 Sep 2026 17:21:58 +0200 Subject: [PATCH 3/3] fix: add validation for rebalance fee budget to prevent skipping nodes without a budget --- src/Pages/Nodes.razor | 45 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Pages/Nodes.razor b/src/Pages/Nodes.razor index 52b14176..369953ce 100644 --- a/src/Pages/Nodes.razor +++ b/src/Pages/Nodes.razor @@ -478,14 +478,20 @@ - Rebalance Fee Budget (sats) - Max sats spendable on rebalance fees per refresh period. 0 = unset. - + + Rebalance Fee Budget (sats) + Max sats spendable on rebalance fees per refresh period. Required: the rebalancer skips a node with no budget. + + + Set a budget above 0, or the rebalancer will skip this node + + + @@ -969,7 +975,13 @@ private async Task SaveAndCloseLiquidityManagementModal() { - if (_nodeLiquidityValidationsRef != null && await _nodeLiquidityValidationsRef.ValidateAll() && _selectedNodeForLiquidity != null) + if (_nodeLiquidityValidationsRef != null && !await _nodeLiquidityValidationsRef.ValidateAll()) + { + ToastService.ShowError("Please fix the errors"); + return; + } + + if (_nodeLiquidityValidationsRef != null && _selectedNodeForLiquidity != null) { // Convert BTC back to sats and percent back to ratio (0-1) @@ -1090,4 +1102,19 @@ return; } } + + // AutoRebalanceJob skips any node whose budget is null or 0, silently and forever, so the page + // that owns the field refuses to save the gate and an empty budget together. + private void ValidateRebalanceBudget(ValidatorEventArgs arg) + { + arg.Status = ValidationStatus.Success; + + if (_selectedNodeForLiquidity?.AutoRebalanceEnabled != true) return; + + if (arg.Value is not long budget || budget <= 0) + { + arg.Status = ValidationStatus.Error; + arg.ErrorText = "Automated rebalancing needs a fee budget above 0; the rebalancer skips nodes without one."; + } + } } \ No newline at end of file