dynamic max fee rate value (#1395)

Serhii committed Apr 25, 2024 at 19:28 UTC 9ff6da3d5d2db04069deed46c35a15e4138d777e
12 files changed +54 -9
lib/bitcoin/cw_bitcoin.dart
+6
@@ -291,4 +291,10 @@ class CWBitcoin extends Bitcoin {
291 outputsCount,
292 );
293 }
294 +
295 + @override
296 + int getMaxCustomFeeRate(Object wallet) {
297 + final bitcoinWallet = wallet as ElectrumWallet;
298 + return (bitcoinWallet.feeRate(BitcoinTransactionPriority.fast) * 1.1).round();
299 + }
300 }
lib/src/screens/send/widgets/send_card.dart
+2
@@ -675,6 +675,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
675 final selectedItem = items.indexOf(sendViewModel.transactionPriority);
676 final customItemIndex = sendViewModel.getCustomPriorityIndex(items);
677 final isBitcoinWallet = sendViewModel.walletType == WalletType.bitcoin;
678 + final maxCustomFeeRate = sendViewModel.maxCustomFeeRate?.toDouble();
679 double? customFeeRate = isBitcoinWallet ? sendViewModel.customBitcoinFeeRate.toDouble() : null;
680
681 await showPopUp<void>(
@@ -689,6 +690,7 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
690 sendViewModel.displayFeeRate(priority, customFeeRate?.round()),
691 selectedAtIndex: selectedIdx,
692 customItemIndex: customItemIndex,
693 + maxValue: maxCustomFeeRate,
694 title: S.of(context).please_select,
695 headerEnabled: !isBitcoinWallet,
696 closeOnItemSelected: !isBitcoinWallet,
lib/src/screens/settings/other_settings_page.dart
+1
@@ -37,6 +37,7 @@ class OtherSettingsPage extends BasePage {
37 customItemIndex: _otherSettingsViewModel.customPriorityItemIndex,
38 onItemSelected: _otherSettingsViewModel.onDisplayBitcoinPrioritySelected,
39 customValue: _otherSettingsViewModel.customBitcoinFeeRate,
40 + maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(),
41 ) :
42 SettingsPickerCell(
43 title: S.current.settings_fee_priority,
lib/src/screens/settings/widgets/setting_priority_picker_cell.dart
+3
@@ -15,6 +15,7 @@ class SettingsPriorityPickerCell<ItemType> extends StandardListRow {
15 this.isGridView = false,
16 this.matchingCriteria,
17 this.customValue,
18 + this.maxValue,
19 this.customItemIndex,
20 this.onItemSelected})
21 : super(
@@ -34,6 +35,7 @@ class SettingsPriorityPickerCell<ItemType> extends StandardListRow {
35 displayItem: (ItemType item) => displayItem!(item, sliderValue.round()),
36 selectedAtIndex: selectedAtIndex,
37 customItemIndex: customItemIndex,
38 + maxValue: maxValue,
39 headerEnabled: false,
40 closeOnItemSelected: false,
41 mainAxisAlignment: MainAxisAlignment.center,
@@ -61,6 +63,7 @@ class SettingsPriorityPickerCell<ItemType> extends StandardListRow {
63 final bool isGridView;
64 final bool Function(ItemType, String)? matchingCriteria;
65 double? customValue;
66 + double? maxValue;
67 int? customItemIndex;
68
69 @override
lib/src/screens/transaction_details/rbf_details_list_fee_picker_item.dart
+2
@@ -10,6 +10,7 @@ class StandardPickerListItem<T> extends TransactionDetailsListItem {
10 required this.onItemSelected,
11 required this.selectedIdx,
12 required this.customItemIndex,
13 + this.maxValue,
14 required this.customValue})
15 : super(title: title, value: value);
16
@@ -18,6 +19,7 @@ class StandardPickerListItem<T> extends TransactionDetailsListItem {
19 final Function(double) onSliderChanged;
20 final Function(T) onItemSelected;
21 final int selectedIdx;
22 + final double? maxValue;
23 final int customItemIndex;
24 double customValue;
25 }
lib/src/screens/transaction_details/rbf_details_page.dart
+1
@@ -74,6 +74,7 @@ class RBFDetailsPage extends BasePage {
74 selectedIdx: item.selectedIdx,
75 customItemIndex: item.customItemIndex,
76 customValue: item.customValue,
77 + maxValue: item.maxValue,
78 );
79 }
80
lib/src/widgets/picker.dart
+19 -9
@@ -27,14 +27,21 @@ class Picker<Item> extends StatefulWidget {
27 this.headerEnabled = true,
28 this.closeOnItemSelected = true,
29 this.sliderValue,
30 + this.minValue,
31 + this.maxValue,
32 this.customItemIndex,
33 this.isWrapped = true,
34 this.borderColor,
35 this.onSliderChanged,
36 this.matchingCriteria,
35 - }) : assert(hintText == null ||
36 - matchingCriteria !=
37 - null); // make sure that if the search field is enabled then there is a searching criteria provided
37 + }) : assert(hintText == null || matchingCriteria != null) {
38 + // make sure that if the search field is enabled then there is a searching criteria provided
39 + if (sliderValue != null && maxValue != null) {
40 + if (sliderValue! > maxValue!) {
41 + sliderValue = maxValue;
42 + }
43 + }
44 + }
45
46 final int selectedAtIndex;
47 final List<Item> items;
@@ -49,12 +56,14 @@ class Picker<Item> extends StatefulWidget {
56 final String? hintText;
57 final bool headerEnabled;
58 final bool closeOnItemSelected;
52 - final double? sliderValue;
59 + double? sliderValue;
60 + final double? minValue;
61 final int? customItemIndex;
62 final bool isWrapped;
63 final Color? borderColor;
64 final Function(double)? onSliderChanged;
65 final bool Function(Item, String)? matchingCriteria;
66 + final double? maxValue;
67
68 @override
69 _PickerState<Item> createState() => _PickerState<Item>(items, images, onItemSelected);
@@ -138,7 +147,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
147 containerHeight = height * 0.75;
148 }
149
141 - final content = Column (
150 + final content = Column(
151 children: [
152 if (widget.title?.isNotEmpty ?? false)
153 Container(
@@ -211,8 +220,9 @@ class _PickerState<Item> extends State<Picker<Item>> {
220 fontWeight: FontWeight.w500,
221 fontFamily: 'Lato',
222 decoration: TextDecoration.none,
214 - color:
215 - Theme.of(context).extension<CakeTextTheme>()!.titleColor,
223 + color: Theme.of(context)
224 + .extension<CakeTextTheme>()!
225 + .titleColor,
226 ),
227 ),
228 )
@@ -491,8 +501,8 @@ class _PickerState<Item> extends State<Picker<Item>> {
501 child: Slider(
502 value: widget.sliderValue ?? 1,
503 onChanged: isActivated ? widget.onSliderChanged : null,
494 - min: 1,
495 - max: 100,
504 + min: widget.minValue ?? 1,
505 + max: widget.maxValue ?? 100,
506 divisions: 100,
507 ),
508 ),
lib/src/widgets/standard_picker_list.dart
+3
@@ -15,6 +15,7 @@ class StandardPickerList<T> extends StatefulWidget {
15 required this.selectedIdx,
16 required this.customItemIndex,
17 required this.customValue,
18 + this.maxValue,
19 }) : super(key: key);
20
21 final String title;
@@ -26,6 +27,7 @@ class StandardPickerList<T> extends StatefulWidget {
27 final String value;
28 final int selectedIdx;
29 final double customValue;
30 + final double? maxValue;
31
32 @override
33 _StandardPickerListState<T> createState() => _StandardPickerListState<T>();
@@ -59,6 +61,7 @@ class _StandardPickerListState<T> extends State<StandardPickerList<T>> {
61 displayItem: adaptedDisplayItem,
62 selectedAtIndex: selectedIdx,
63 customItemIndex: widget.customItemIndex,
64 + maxValue: widget.maxValue,
65 headerEnabled: false,
66 closeOnItemSelected: false,
67 mainAxisAlignment: MainAxisAlignment.center,
lib/view_model/send/send_view_model.dart
+7
@@ -166,6 +166,13 @@ abstract class SendViewModelBase extends WalletChangeListenerViewModel with Stor
166 return null;
167 }
168
169 + int? get maxCustomFeeRate {
170 + if (wallet.type == WalletType.bitcoin) {
171 + return bitcoin!.getMaxCustomFeeRate(wallet);
172 + }
173 + return null;
174 + }
175 +
176 @computed
177 int get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate;
178
lib/view_model/settings/other_settings_view_model.dart
+7
@@ -140,6 +140,13 @@ abstract class OtherSettingsViewModelBase with Store {
140 return customItem != null ? priorities.indexOf(customItem) : null;
141 }
142
143 + int? get maxCustomFeeRate {
144 + if (_wallet.type == WalletType.bitcoin) {
145 + return bitcoin!.getMaxCustomFeeRate(_wallet);
146 + }
147 + return null;
148 + }
149 +
150 @action
151 ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) =>
152 _settingsStore.defaultBuyProviders[walletType] = buyProviderType;
lib/view_model/transaction_details_view_model.dart
+2
@@ -348,12 +348,14 @@ abstract class TransactionDetailsViewModelBase with Store {
348 final customItem = priorities.firstWhereOrNull(
349 (element) => element == sendViewModel.bitcoinTransactionPriorityCustom);
350 final customItemIndex = customItem != null ? priorities.indexOf(customItem) : null;
351 + final maxCustomFeeRate = sendViewModel.maxCustomFeeRate?.toDouble();
352
353 RBFListItems.add(StandardPickerListItem(
354 title: S.current.estimated_new_fee,
355 value: bitcoin!.formatterBitcoinAmountToString(amount: newFee) + ' ${walletTypeToCryptoCurrency(wallet.type)}',
356 items: priorityForWalletType(wallet.type),
357 customValue: settingsStore.customBitcoinFeeRate.toDouble(),
358 + maxValue: maxCustomFeeRate,
359 selectedIdx: selectedItem,
360 customItemIndex: customItemIndex ?? 0,
361 displayItem: (dynamic priority, double sliderValue) =>
tool/configure.dart
+1
@@ -159,6 +159,7 @@ abstract class Bitcoin {
159 Future<bool> isChangeSufficientForFee(Object wallet, String txId, String newFee);
160 int getFeeAmountForPriority(Object wallet, TransactionPriority priority, int inputsCount, int outputsCount, {int? size});
161 int getFeeAmountWithFeeRate(Object wallet, int feeRate, int inputsCount, int outputsCount, {int? size});
162 + int getMaxCustomFeeRate(Object wallet);
163 }
164 """;
165