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