mirror of
https://github.com/cypherstack/stack_wallet.git
synced 2024-11-16 17:27:39 +00:00
Merge pull request #595 from cypherstack/custom_fee_selection
Custom fee selection
This commit is contained in:
commit
86630f2e85
57 changed files with 1573 additions and 311 deletions
|
@ -160,7 +160,7 @@ class ElectrumX {
|
|||
"JSONRPC response\n"
|
||||
" command: $command\n"
|
||||
" args: $args\n"
|
||||
" error: $response.data",
|
||||
" error: ${response.data}",
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -468,6 +468,31 @@ class _ConfirmTransactionViewState
|
|||
],
|
||||
),
|
||||
),
|
||||
if (transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
RoundedWhiteContainer(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"sats/vByte",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 4,
|
||||
),
|
||||
Text(
|
||||
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
|
||||
style: STextStyles.itemSubtitle12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
|
@ -906,6 +931,43 @@ class _ConfirmTransactionViewState
|
|||
),
|
||||
),
|
||||
),
|
||||
if (isDesktop &&
|
||||
!widget.isPaynymTransaction &&
|
||||
transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
),
|
||||
child: Text(
|
||||
"sats/vByte",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context),
|
||||
),
|
||||
),
|
||||
if (isDesktop &&
|
||||
!widget.isPaynymTransaction &&
|
||||
transactionInfo["fee"] is int &&
|
||||
transactionInfo["vSize"] is int)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
top: 10,
|
||||
left: 32,
|
||||
right: 32,
|
||||
),
|
||||
child: RoundedContainer(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 18,
|
||||
),
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldDefaultBG,
|
||||
child: Text(
|
||||
"~${(transactionInfo["fee"] / transactionInfo["vSize"]).toInt()}",
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (!isDesktop) const Spacer(),
|
||||
SizedBox(
|
||||
height: isDesktop ? 23 : 12,
|
||||
|
|
|
@ -56,6 +56,7 @@ import 'package:stackwallet/widgets/animated_text.dart';
|
|||
import 'package:stackwallet/widgets/background.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/fee_slider.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/qrcode_icon.dart';
|
||||
|
@ -284,6 +285,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
case FeeRateType.slow:
|
||||
feeRate = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
feeRate = -1;
|
||||
}
|
||||
|
||||
Amount fee;
|
||||
|
@ -299,6 +302,8 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
case FeeRateType.slow:
|
||||
specialMoneroId = MoneroTransactionPriority.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("custom fee not available for monero");
|
||||
}
|
||||
|
||||
fee = await manager.estimateFeeFor(amount, specialMoneroId.raw!);
|
||||
|
@ -494,6 +499,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
isSegwit: widget.accountLite!.segwit,
|
||||
amount: amount,
|
||||
args: {
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"feeRate": feeRate,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
|
@ -508,7 +514,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
txDataFuture = (manager.wallet as FiroWallet).prepareSendPublic(
|
||||
address: _address!,
|
||||
amount: amount,
|
||||
args: {"feeRate": ref.read(feeRateTypeStateProvider)},
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
txDataFuture = manager.prepareSend(
|
||||
|
@ -516,6 +525,7 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
amount: amount,
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
selectedUTXOs.isNotEmpty)
|
||||
|
@ -593,6 +603,10 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
|
||||
bool get isPaynymSend => widget.accountLite != null;
|
||||
|
||||
bool isCustomFee = false;
|
||||
|
||||
int customFeeRate = 1;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
coin = widget.coin;
|
||||
|
@ -1830,17 +1844,23 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
if (coin != Coin.epicCash)
|
||||
if (coin != Coin.epicCash &&
|
||||
coin != Coin.nano &&
|
||||
coin != Coin.banano)
|
||||
Text(
|
||||
"Transaction fee (estimated)",
|
||||
style: STextStyles.smallMed12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (coin != Coin.epicCash)
|
||||
if (coin != Coin.epicCash &&
|
||||
coin != Coin.nano &&
|
||||
coin != Coin.banano)
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
if (coin != Coin.epicCash)
|
||||
if (coin != Coin.epicCash &&
|
||||
coin != Coin.nano &&
|
||||
coin != Coin.banano)
|
||||
Stack(
|
||||
children: [
|
||||
TextField(
|
||||
|
@ -1898,6 +1918,15 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
fractionDigits: coin.decimals,
|
||||
),
|
||||
updateChosen: (String fee) {
|
||||
if (fee == "custom") {
|
||||
if (!isCustomFee) {
|
||||
setState(() {
|
||||
isCustomFee = true;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_setCurrentFee(
|
||||
fee,
|
||||
true,
|
||||
|
@ -1905,6 +1934,9 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
setState(() {
|
||||
_calculateFeesFuture =
|
||||
Future(() => fee);
|
||||
if (isCustomFee) {
|
||||
isCustomFee = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
|
@ -1928,11 +1960,11 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.done &&
|
||||
snapshot.hasData) {
|
||||
_setCurrentFee(
|
||||
snapshot.data! as String,
|
||||
snapshot.data!,
|
||||
false,
|
||||
);
|
||||
return Text(
|
||||
"~${snapshot.data! as String}",
|
||||
"~${snapshot.data!}",
|
||||
style: STextStyles
|
||||
.itemSubtitle(
|
||||
context),
|
||||
|
@ -1984,12 +2016,13 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
.done &&
|
||||
snapshot.hasData) {
|
||||
_setCurrentFee(
|
||||
snapshot.data!
|
||||
as String,
|
||||
snapshot.data!,
|
||||
false,
|
||||
);
|
||||
return Text(
|
||||
"~${snapshot.data! as String}",
|
||||
isCustomFee
|
||||
? ""
|
||||
: "~${snapshot.data!}",
|
||||
style: STextStyles
|
||||
.itemSubtitle(
|
||||
context),
|
||||
|
@ -2025,6 +2058,18 @@ class _SendViewState extends ConsumerState<SendView> {
|
|||
)
|
||||
],
|
||||
),
|
||||
if (isCustomFee)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 12,
|
||||
top: 16,
|
||||
),
|
||||
child: FeeSlider(
|
||||
onSatVByteChanged: (rate) {
|
||||
customFeeRate = rate;
|
||||
},
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
|
|
|
@ -161,6 +161,9 @@ class _TransactionFeeSelectionSheetState
|
|||
}
|
||||
}
|
||||
return ref.read(feeSheetSessionCacheProvider).slow[amount]!;
|
||||
|
||||
default:
|
||||
return Amount.zero;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -567,8 +570,6 @@ class _TransactionFeeSelectionSheetState
|
|||
.watch(feeRateTypeStateProvider.state)
|
||||
.state,
|
||||
onChanged: (x) {
|
||||
//todo: check if print needed
|
||||
// debugPrint(x.toString());
|
||||
ref
|
||||
.read(feeRateTypeStateProvider.state)
|
||||
.state = FeeRateType.slow;
|
||||
|
@ -672,6 +673,79 @@ class _TransactionFeeSelectionSheetState
|
|||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
if (manager.coin.isElectrumXCoin)
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
final state =
|
||||
ref.read(feeRateTypeStateProvider.state).state;
|
||||
if (state != FeeRateType.custom) {
|
||||
ref.read(feeRateTypeStateProvider.state).state =
|
||||
FeeRateType.custom;
|
||||
}
|
||||
widget.updateChosen("custom");
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: 20,
|
||||
height: 20,
|
||||
child: Radio(
|
||||
activeColor: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.radioButtonIconEnabled,
|
||||
value: FeeRateType.custom,
|
||||
groupValue: ref
|
||||
.watch(feeRateTypeStateProvider.state)
|
||||
.state,
|
||||
onChanged: (x) {
|
||||
ref
|
||||
.read(
|
||||
feeRateTypeStateProvider.state)
|
||||
.state = FeeRateType.custom;
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
width: 12,
|
||||
),
|
||||
Flexible(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
FeeRateType.custom.prettyName,
|
||||
style:
|
||||
STextStyles.titleBold12(context),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 2,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (manager.coin.isElectrumXCoin)
|
||||
const SizedBox(
|
||||
height: 24,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -714,6 +788,8 @@ class _TransactionFeeSelectionSheetState
|
|||
);
|
||||
}
|
||||
return null;
|
||||
case FeeRateType.custom:
|
||||
return null;
|
||||
}
|
||||
} catch (e, s) {
|
||||
Logging.instance.log("$e $s", level: LogLevel.Warning);
|
||||
|
|
|
@ -360,6 +360,8 @@ class _TokenSendViewState extends ConsumerState<TokenSendView> {
|
|||
case FeeRateType.slow:
|
||||
feeRate = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
feeRate = -1;
|
||||
}
|
||||
|
||||
final Amount fee = wallet.estimateFeeFor(feeRate);
|
||||
|
|
|
@ -179,6 +179,8 @@ class _DesktopFeeDropDownState extends ConsumerState<DesktopFeeDropDown> {
|
|||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.slow[amount]!;
|
||||
default:
|
||||
return Amount.zero;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,14 @@ import 'dart:async';
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:bip47/bip47.dart';
|
||||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:decimal/decimal.dart';
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
||||
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||
|
@ -46,16 +47,20 @@ import 'package:stackwallet/utilities/barcode_scanner_interface.dart';
|
|||
import 'package:stackwallet/utilities/clipboard_interface.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/logger.dart';
|
||||
import 'package:stackwallet/utilities/prefs.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/utilities/util.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/conditional_parent.dart';
|
||||
import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_fee_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/primary_button.dart';
|
||||
import 'package:stackwallet/widgets/desktop/secondary_button.dart';
|
||||
import 'package:stackwallet/widgets/fee_slider.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/addressbook_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart';
|
||||
import 'package:stackwallet/widgets/icon_widgets/x_icon.dart';
|
||||
|
@ -116,6 +121,17 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
|
||||
bool get isPaynymSend => widget.accountLite != null;
|
||||
|
||||
bool isCustomFee = false;
|
||||
int customFeeRate = 1;
|
||||
(FeeRateType, String?, String?)? feeSelectionResult;
|
||||
|
||||
final stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
Future<void> previewSend() async {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
@ -284,6 +300,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
isSegwit: widget.accountLite!.segwit,
|
||||
amount: amount,
|
||||
args: {
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"feeRate": feeRate,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
|
@ -300,6 +317,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
amount: amount,
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||
|
@ -313,6 +331,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
amount: amount,
|
||||
args: {
|
||||
"feeRate": ref.read(feeRateTypeStateProvider),
|
||||
"satsPerVByte": isCustomFee ? customFeeRate : null,
|
||||
"UTXOs": (manager.hasCoinControlSupport &&
|
||||
coinControlEnabled &&
|
||||
ref.read(desktopUseUTXOs).isNotEmpty)
|
||||
|
@ -562,12 +581,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: const [
|
||||
"Loading balance",
|
||||
"Loading balance.",
|
||||
"Loading balance..",
|
||||
"Loading balance...",
|
||||
],
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.itemSubtitle(context),
|
||||
);
|
||||
}
|
||||
|
@ -1369,94 +1383,178 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
|
|||
}
|
||||
},
|
||||
),
|
||||
// const SizedBox(
|
||||
// height: 20,
|
||||
// ),
|
||||
// Text(
|
||||
// "Note (optional)",
|
||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldActiveSearchIconRight,
|
||||
// ),
|
||||
// textAlign: TextAlign.left,
|
||||
// ),
|
||||
// const SizedBox(
|
||||
// height: 10,
|
||||
// ),
|
||||
// ClipRRect(
|
||||
// borderRadius: BorderRadius.circular(
|
||||
// Constants.size.circularBorderRadius,
|
||||
// ),
|
||||
// child: TextField(
|
||||
// minLines: 1,
|
||||
// maxLines: 5,
|
||||
// autocorrect: Util.isDesktop ? false : true,
|
||||
// enableSuggestions: Util.isDesktop ? false : true,
|
||||
// controller: noteController,
|
||||
// focusNode: _noteFocusNode,
|
||||
// style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
// color: Theme.of(context)
|
||||
// .extension<StackColors>()!
|
||||
// .textFieldActiveText,
|
||||
// height: 1.8,
|
||||
// ),
|
||||
// onChanged: (_) => setState(() {}),
|
||||
// decoration: standardInputDecoration(
|
||||
// "Type something...",
|
||||
// _noteFocusNode,
|
||||
// context,
|
||||
// desktopMed: true,
|
||||
// ).copyWith(
|
||||
// contentPadding: const EdgeInsets.only(
|
||||
// left: 16,
|
||||
// top: 11,
|
||||
// bottom: 12,
|
||||
// right: 5,
|
||||
// ),
|
||||
// suffixIcon: noteController.text.isNotEmpty
|
||||
// ? Padding(
|
||||
// padding: const EdgeInsets.only(right: 0),
|
||||
// child: UnconstrainedBox(
|
||||
// child: Row(
|
||||
// children: [
|
||||
// TextFieldIconButton(
|
||||
// child: const XIcon(),
|
||||
// onTap: () async {
|
||||
// setState(() {
|
||||
// noteController.text = "";
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// )
|
||||
// : null,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
if (!isPaynymSend)
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
Text(
|
||||
"Transaction fee (${coin == Coin.ethereum ? "max" : "estimated"})",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
ConditionalParent(
|
||||
condition: coin.isElectrumXCoin,
|
||||
builder: (child) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
child,
|
||||
CustomTextButton(
|
||||
text: "Edit",
|
||||
onTap: () async {
|
||||
feeSelectionResult = await showDialog<
|
||||
(
|
||||
FeeRateType,
|
||||
String?,
|
||||
String?,
|
||||
)?>(
|
||||
context: context,
|
||||
builder: (_) => DesktopFeeDialog(
|
||||
walletId: walletId,
|
||||
),
|
||||
);
|
||||
|
||||
if (feeSelectionResult != null) {
|
||||
if (isCustomFee &&
|
||||
feeSelectionResult!.$1 != FeeRateType.custom) {
|
||||
isCustomFee = false;
|
||||
} else if (!isCustomFee &&
|
||||
feeSelectionResult!.$1 == FeeRateType.custom) {
|
||||
isCustomFee = true;
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Text(
|
||||
"Transaction fee"
|
||||
"${isCustomFee ? "" : " (${coin == Coin.ethereum ? "max" : "estimated"})"}",
|
||||
style: STextStyles.desktopTextExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
if (!([Coin.nano, Coin.banano, Coin.epicCash].contains(coin)))
|
||||
DesktopFeeDropDown(
|
||||
walletId: walletId,
|
||||
if (!isCustomFee)
|
||||
(feeSelectionResult?.$2 == null)
|
||||
? FutureBuilder(
|
||||
future: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value.getManager(walletId).fees,
|
||||
),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
return DesktopFeeItem(
|
||||
feeObject: snapshot.data,
|
||||
feeRateType: FeeRateType.average,
|
||||
walletId: walletId,
|
||||
feeFor: ({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) async {
|
||||
if (ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] ==
|
||||
null) {
|
||||
final manager = ref
|
||||
.read(walletsChangeNotifierProvider)
|
||||
.getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(amount,
|
||||
MoneroTransactionPriority.regular.raw!);
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] = fee;
|
||||
} else if ((coin == Coin.firo ||
|
||||
coin == Coin.firoTestNet) &&
|
||||
ref
|
||||
.read(
|
||||
publicPrivateBalanceStateProvider
|
||||
.state)
|
||||
.state !=
|
||||
"Private") {
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount] =
|
||||
await manager.estimateFeeFor(
|
||||
amount, feeRate);
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(feeSheetSessionCacheProvider)
|
||||
.average[amount]!;
|
||||
},
|
||||
isSelected: true,
|
||||
);
|
||||
} else {
|
||||
return Row(
|
||||
children: [
|
||||
AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(
|
||||
context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
)
|
||||
: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
feeSelectionResult?.$2 ?? "",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text(
|
||||
feeSelectionResult?.$3 ?? "",
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (isCustomFee)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
bottom: 12,
|
||||
top: 16,
|
||||
),
|
||||
child: FeeSlider(
|
||||
onSatVByteChanged: (rate) {
|
||||
customFeeRate = rate;
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 36,
|
||||
|
|
|
@ -14,7 +14,7 @@ import 'package:decimal/decimal.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/contact_address_entry.dart';
|
||||
import 'package:stackwallet/models/isar/models/contact_entry.dart';
|
||||
import 'package:stackwallet/models/paynym/paynym_account_lite.dart';
|
||||
import 'package:stackwallet/models/send_view_auto_fill_data.dart';
|
||||
import 'package:stackwallet/pages/send_view/confirm_transaction_view.dart';
|
||||
|
|
|
@ -287,8 +287,8 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
@override
|
||||
Future<int> get maxFee async {
|
||||
final fee = (await fees).fast as String;
|
||||
final satsFee =
|
||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
final satsFee = Decimal.parse(fee) *
|
||||
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
return satsFee.floor().toBigInt().toInt();
|
||||
}
|
||||
|
||||
|
@ -1070,9 +1070,60 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -1087,6 +1138,8 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2201,6 +2254,7 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2308,18 +2362,22 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
if (satsPerVByte == null) {
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
}
|
||||
}
|
||||
|
||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||
|
@ -2373,15 +2431,19 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
}
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
final feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2576,6 +2638,7 @@ class BitcoinWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
|
|
@ -955,9 +955,60 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -972,6 +1023,8 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2187,6 +2240,7 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2303,10 +2357,12 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
if (feeForOneOutput < (vSizeForOneOutput + 1)) {
|
||||
feeForOneOutput = (vSizeForOneOutput + 1);
|
||||
}
|
||||
|
@ -2350,20 +2406,21 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
satoshisBeingUsed - satoshiAmountToSend - 1,
|
||||
], // dust limit is the minimum amount a change output should be
|
||||
))["vSize"] as int;
|
||||
//todo: check if print needed
|
||||
// debugPrint("vSizeForOneOutput $vSizeForOneOutput");
|
||||
// debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
var feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
var feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2575,6 +2632,7 @@ class BitcoinCashWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
|
|
@ -941,9 +941,60 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -958,6 +1009,8 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2092,6 +2145,7 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2199,10 +2253,12 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
if (feeForOneOutput < (vSizeForOneOutput + 1) * 1000) {
|
||||
feeForOneOutput = (vSizeForOneOutput + 1) * 1000;
|
||||
}
|
||||
|
@ -2248,15 +2304,19 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
var feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
var feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
var feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
var feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2463,6 +2523,7 @@ class DogecoinWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
|
|
@ -1409,6 +1409,7 @@ class ECashWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -1517,18 +1518,22 @@ class ECashWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
if (satsPerVByte == null) {
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
}
|
||||
}
|
||||
|
||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||
|
@ -1586,15 +1591,19 @@ class ECashWallet extends CoinServiceAPI
|
|||
}
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
final feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -1795,6 +1804,7 @@ class ECashWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
@ -1977,8 +1987,6 @@ class ECashWallet extends CoinServiceAPI
|
|||
final tx = builder.build();
|
||||
final txHex = tx.toHex();
|
||||
final vSize = tx.virtualSize();
|
||||
//todo: check if print needed
|
||||
Logger.print("ecash raw hex: $txHex");
|
||||
|
||||
return {"hex": txHex, "vSize": vSize};
|
||||
}
|
||||
|
@ -2549,9 +2557,60 @@ class ECashWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -2566,6 +2625,9 @@ class ECashWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
|
|
@ -1029,8 +1029,55 @@ class FiroWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final result = await coinSelection(
|
||||
amount.raw.toInt(),
|
||||
-1,
|
||||
address,
|
||||
isSendAll,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -1045,6 +1092,8 @@ class FiroWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -1296,6 +1345,7 @@ class FiroWallet extends CoinServiceAPI
|
|||
int selectedTxFeeRate,
|
||||
String _recipientAddress,
|
||||
bool isSendAll, {
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -1385,10 +1435,12 @@ class FiroWallet extends CoinServiceAPI
|
|||
recipients: [_recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
if (feeForOneOutput < vSizeForOneOutput + 1) {
|
||||
feeForOneOutput = vSizeForOneOutput + 1;
|
||||
|
@ -1429,20 +1481,21 @@ class FiroWallet extends CoinServiceAPI
|
|||
satoshisBeingUsed - satoshiAmountToSend - 1,
|
||||
], // dust limit is the minimum amount a change output should be
|
||||
))["vSize"] as int;
|
||||
//todo: check if print needed
|
||||
debugPrint("vSizeForOneOutput $vSizeForOneOutput");
|
||||
debugPrint("vSizeForTwoOutPuts $vSizeForTwoOutPuts");
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
var feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
var feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
var feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
var feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -1641,9 +1694,15 @@ class FiroWallet extends CoinServiceAPI
|
|||
level: LogLevel.Warning);
|
||||
// try adding more outputs
|
||||
if (spendableOutputs.length > inputsBeingConsumed) {
|
||||
return coinSelection(satoshiAmountToSend, selectedTxFeeRate,
|
||||
_recipientAddress, isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1, utxos: utxos);
|
||||
return coinSelection(
|
||||
satoshiAmountToSend,
|
||||
selectedTxFeeRate,
|
||||
_recipientAddress,
|
||||
isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
satsPerVByte: satsPerVByte,
|
||||
utxos: utxos,
|
||||
);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
|
|
@ -232,8 +232,8 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
@override
|
||||
Future<int> get maxFee async {
|
||||
final fee = (await fees).fast as String;
|
||||
final satsFee =
|
||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
final satsFee = Decimal.parse(fee) *
|
||||
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
return satsFee.floor().toBigInt().toInt();
|
||||
}
|
||||
|
||||
|
@ -1058,9 +1058,60 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -1075,6 +1126,8 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2298,6 +2351,7 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2403,18 +2457,22 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
if (satsPerVByte == null) {
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
}
|
||||
}
|
||||
|
||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||
|
@ -2455,15 +2513,19 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
))["vSize"] as int;
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
final feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2659,6 +2721,7 @@ class LitecoinWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
|
|
@ -458,6 +458,8 @@ class MoneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
case FeeRateType.slow:
|
||||
feePriority = MoneroTransactionPriority.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
|
||||
Future<PendingTransaction>? awaitPendingTransaction;
|
||||
|
|
|
@ -224,8 +224,8 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
@override
|
||||
Future<int> get maxFee async {
|
||||
final fee = (await fees).fast as String;
|
||||
final satsFee =
|
||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
final satsFee = Decimal.parse(fee) *
|
||||
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
return satsFee.floor().toBigInt().toInt();
|
||||
}
|
||||
|
||||
|
@ -1048,9 +1048,60 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -1065,6 +1116,8 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2274,6 +2327,7 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2379,18 +2433,22 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
if (satsPerVByte == null) {
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
}
|
||||
}
|
||||
|
||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||
|
@ -2431,15 +2489,19 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
))["vSize"] as int;
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
final feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2635,6 +2697,7 @@ class NamecoinWallet extends CoinServiceAPI
|
|||
return coinSelection(
|
||||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
satsPerVByte: satsPerVByte,
|
||||
recipientAddress: recipientAddress,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
|
|
|
@ -219,8 +219,8 @@ class ParticlWallet extends CoinServiceAPI
|
|||
@override
|
||||
Future<int> get maxFee async {
|
||||
final fee = (await fees).fast as String;
|
||||
final satsFee =
|
||||
Decimal.parse(fee) * Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
final satsFee = Decimal.parse(fee) *
|
||||
Decimal.fromInt(Constants.satsPerCoin(coin).toInt());
|
||||
return satsFee.floor().toBigInt().toInt();
|
||||
}
|
||||
|
||||
|
@ -975,9 +975,60 @@ class ParticlWallet extends CoinServiceAPI
|
|||
}) async {
|
||||
try {
|
||||
final feeRateType = args?["feeRate"];
|
||||
final customSatsPerVByte = args?["satsPerVByte"] as int?;
|
||||
final feeRateAmount = args?["feeRateAmount"];
|
||||
final utxos = args?["UTXOs"] as Set<isar_models.UTXO>?;
|
||||
if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
|
||||
if (customSatsPerVByte != null) {
|
||||
// check for send all
|
||||
bool isSendAll = false;
|
||||
if (amount == balance.spendable) {
|
||||
isSendAll = true;
|
||||
}
|
||||
|
||||
final bool coinControl = utxos != null;
|
||||
|
||||
final result = await coinSelection(
|
||||
satoshiAmountToSend: amount.raw.toInt(),
|
||||
selectedTxFeeRate: -1,
|
||||
satsPerVByte: customSatsPerVByte,
|
||||
recipientAddress: address,
|
||||
isSendAll: isSendAll,
|
||||
utxos: utxos?.toList(),
|
||||
coinControl: coinControl,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("PREPARE SEND RESULT: $result", level: LogLevel.Info);
|
||||
if (result is int) {
|
||||
switch (result) {
|
||||
case 1:
|
||||
throw Exception("Insufficient balance!");
|
||||
case 2:
|
||||
throw Exception("Insufficient funds to pay for transaction fee!");
|
||||
default:
|
||||
throw Exception("Transaction failed with error code $result");
|
||||
}
|
||||
} else {
|
||||
final hex = result["hex"];
|
||||
if (hex is String) {
|
||||
final fee = result["fee"] as int;
|
||||
final vSize = result["vSize"] as int;
|
||||
|
||||
Logging.instance.log("txHex: $hex", level: LogLevel.Info);
|
||||
Logging.instance.log("fee: $fee", level: LogLevel.Info);
|
||||
Logging.instance.log("vsize: $vSize", level: LogLevel.Info);
|
||||
// fee should never be less than vSize sanity check
|
||||
if (fee < vSize) {
|
||||
throw Exception(
|
||||
"Error in fee calculation: Transaction fee cannot be less than vSize");
|
||||
}
|
||||
return result as Map<String, dynamic>;
|
||||
} else {
|
||||
throw Exception("sent hex is not a String!!!");
|
||||
}
|
||||
}
|
||||
} else if (feeRateType is FeeRateType || feeRateAmount is int) {
|
||||
late final int rate;
|
||||
if (feeRateType is FeeRateType) {
|
||||
int fee = 0;
|
||||
|
@ -992,6 +1043,8 @@ class ParticlWallet extends CoinServiceAPI
|
|||
case FeeRateType.slow:
|
||||
fee = feeObject.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
rate = fee;
|
||||
} else {
|
||||
|
@ -2441,6 +2494,7 @@ class ParticlWallet extends CoinServiceAPI
|
|||
required String recipientAddress,
|
||||
required bool coinControl,
|
||||
required bool isSendAll,
|
||||
int? satsPerVByte,
|
||||
int additionalOutputs = 0,
|
||||
List<isar_models.UTXO>? utxos,
|
||||
}) async {
|
||||
|
@ -2546,18 +2600,22 @@ class ParticlWallet extends CoinServiceAPI
|
|||
recipients: [recipientAddress],
|
||||
satoshiAmounts: [satoshisBeingUsed - 1],
|
||||
))["vSize"] as int;
|
||||
int feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
int feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
if (satsPerVByte == null) {
|
||||
final int roughEstimate = roughFeeEstimate(
|
||||
spendableOutputs.length,
|
||||
1,
|
||||
selectedTxFeeRate,
|
||||
).raw.toInt();
|
||||
if (feeForOneOutput < roughEstimate) {
|
||||
feeForOneOutput = roughEstimate;
|
||||
}
|
||||
}
|
||||
|
||||
final int amount = satoshiAmountToSend - feeForOneOutput;
|
||||
|
@ -2598,15 +2656,19 @@ class ParticlWallet extends CoinServiceAPI
|
|||
))["vSize"] as int;
|
||||
|
||||
// Assume 1 output, only for recipient and no change
|
||||
final feeForOneOutput = estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForOneOutput = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForOneOutput)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForOneOutput,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
// Assume 2 outputs, one for recipient and one for change
|
||||
final feeForTwoOutputs = estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
final feeForTwoOutputs = satsPerVByte != null
|
||||
? (satsPerVByte * vSizeForTwoOutPuts)
|
||||
: estimateTxFee(
|
||||
vSize: vSizeForTwoOutPuts,
|
||||
feeRatePerKB: selectedTxFeeRate,
|
||||
);
|
||||
|
||||
Logging.instance
|
||||
.log("feeForTwoOutputs: $feeForTwoOutputs", level: LogLevel.Info);
|
||||
|
@ -2803,6 +2865,7 @@ class ParticlWallet extends CoinServiceAPI
|
|||
satoshiAmountToSend: satoshiAmountToSend,
|
||||
selectedTxFeeRate: selectedTxFeeRate,
|
||||
recipientAddress: recipientAddress,
|
||||
satsPerVByte: satsPerVByte,
|
||||
isSendAll: isSendAll,
|
||||
additionalOutputs: additionalOutputs + 1,
|
||||
utxos: utxos,
|
||||
|
|
|
@ -486,6 +486,8 @@ class WowneroWallet extends CoinServiceAPI with WalletCache, WalletDB {
|
|||
case FeeRateType.slow:
|
||||
feePriority = MoneroTransactionPriority.slow;
|
||||
break;
|
||||
default:
|
||||
throw ArgumentError("Invalid use of custom fee");
|
||||
}
|
||||
|
||||
Future<PendingTransaction>? awaitPendingTransaction;
|
||||
|
|
|
@ -84,17 +84,12 @@ class Wallets extends ChangeNotifier {
|
|||
}
|
||||
final List<Tuple2<Coin, List<ChangeNotifierProvider<Manager>>>> result = [];
|
||||
|
||||
for (final coin in map.keys) {
|
||||
result.add(Tuple2(coin, map[coin]!));
|
||||
for (final coin in Coin.values) {
|
||||
if (map[coin] != null) {
|
||||
result.add(Tuple2(coin, map[coin]!));
|
||||
}
|
||||
}
|
||||
|
||||
// result.sort((a, b) => a.item1.prettyName.compareTo(b.item1.prettyName));
|
||||
result.sort((a, b) => a.item1.prettyName == "Bitcoin"
|
||||
? -1
|
||||
: b.item1.prettyName == "Monero"
|
||||
? 1
|
||||
: a.item1.prettyName.compareTo(b.item1.prettyName));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
enum FeeRateType { fast, average, slow }
|
||||
enum FeeRateType { fast, average, slow, custom }
|
||||
|
||||
extension FeeRateTypeExt on FeeRateType {
|
||||
String get prettyName {
|
||||
|
@ -19,6 +19,8 @@ extension FeeRateTypeExt on FeeRateType {
|
|||
return "Average";
|
||||
case FeeRateType.slow:
|
||||
return "Slow";
|
||||
case FeeRateType.custom:
|
||||
return "Custom";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
412
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
412
lib/widgets/desktop/desktop_fee_dialog.dart
Normal file
|
@ -0,0 +1,412 @@
|
|||
import 'package:cw_core/monero_transaction_priority.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:stackwallet/models/models.dart';
|
||||
import 'package:stackwallet/pages/send_view/sub_widgets/transaction_fee_selection_sheet.dart';
|
||||
import 'package:stackwallet/pages/token_view/token_view.dart';
|
||||
import 'package:stackwallet/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/desktop_fee_dropdown.dart';
|
||||
import 'package:stackwallet/providers/global/wallets_provider.dart';
|
||||
import 'package:stackwallet/providers/wallet/public_private_balance_state_provider.dart';
|
||||
import 'package:stackwallet/services/coins/firo/firo_wallet.dart';
|
||||
import 'package:stackwallet/themes/stack_colors.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount.dart';
|
||||
import 'package:stackwallet/utilities/amount/amount_formatter.dart';
|
||||
import 'package:stackwallet/utilities/constants.dart';
|
||||
import 'package:stackwallet/utilities/enums/coin_enum.dart';
|
||||
import 'package:stackwallet/utilities/enums/fee_rate_type_enum.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
import 'package:stackwallet/widgets/animated_text.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog.dart';
|
||||
import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart';
|
||||
|
||||
class DesktopFeeDialog extends ConsumerStatefulWidget {
|
||||
const DesktopFeeDialog({
|
||||
Key? key,
|
||||
required this.walletId,
|
||||
this.isToken = false,
|
||||
}) : super(key: key);
|
||||
|
||||
final String walletId;
|
||||
final bool isToken;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopFeeDialog> createState() => _DesktopFeeDialogState();
|
||||
}
|
||||
|
||||
class _DesktopFeeDialogState extends ConsumerState<DesktopFeeDialog> {
|
||||
late final String walletId;
|
||||
|
||||
FeeObject? feeObject;
|
||||
FeeRateType feeRateType = FeeRateType.average;
|
||||
|
||||
Future<Amount> feeFor({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) async {
|
||||
switch (feeRateType) {
|
||||
case FeeRateType.fast:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.fast[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.fast.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).fast[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).fast[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.fast[amount]!;
|
||||
|
||||
case FeeRateType.average:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.average[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.regular.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).average[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).average[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.average[amount]!;
|
||||
|
||||
case FeeRateType.slow:
|
||||
if (ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.slow[amount] ==
|
||||
null) {
|
||||
if (widget.isToken == false) {
|
||||
final manager =
|
||||
ref.read(walletsChangeNotifierProvider).getManager(walletId);
|
||||
|
||||
if (coin == Coin.monero || coin == Coin.wownero) {
|
||||
final fee = await manager.estimateFeeFor(
|
||||
amount, MoneroTransactionPriority.slow.raw!);
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] = fee;
|
||||
} else if ((coin == Coin.firo || coin == Coin.firoTestNet) &&
|
||||
ref.read(publicPrivateBalanceStateProvider.state).state !=
|
||||
"Private") {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
await (manager.wallet as FiroWallet)
|
||||
.estimateFeeForPublic(amount, feeRate);
|
||||
} else {
|
||||
ref.read(feeSheetSessionCacheProvider).slow[amount] =
|
||||
await manager.estimateFeeFor(amount, feeRate);
|
||||
}
|
||||
} else {
|
||||
final tokenWallet = ref.read(tokenServiceProvider)!;
|
||||
final fee = tokenWallet.estimateFeeFor(feeRate);
|
||||
ref.read(tokenFeeSessionCacheProvider).slow[amount] = fee;
|
||||
}
|
||||
}
|
||||
return ref
|
||||
.read(widget.isToken
|
||||
? tokenFeeSessionCacheProvider
|
||||
: feeSheetSessionCacheProvider)
|
||||
.slow[amount]!;
|
||||
default:
|
||||
return Amount.zero;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
walletId = widget.walletId;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DesktopDialog(
|
||||
maxWidth: 450,
|
||||
maxHeight: double.infinity,
|
||||
child: FutureBuilder(
|
||||
future: ref.watch(
|
||||
walletsChangeNotifierProvider.select(
|
||||
(value) => value.getManager(walletId).fees,
|
||||
),
|
||||
),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
feeObject = snapshot.data!;
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 32),
|
||||
child: Text(
|
||||
"Choose fee",
|
||||
style: STextStyles.desktopH3(context),
|
||||
),
|
||||
),
|
||||
const DesktopDialogCloseButton(),
|
||||
],
|
||||
),
|
||||
...FeeRateType.values.map(
|
||||
(e) => Padding(
|
||||
padding: const EdgeInsets.only(
|
||||
left: 32,
|
||||
right: 32,
|
||||
bottom: 16,
|
||||
),
|
||||
child: DesktopFeeItem(
|
||||
feeObject: feeObject,
|
||||
feeRateType: e,
|
||||
walletId: walletId,
|
||||
feeFor: feeFor,
|
||||
isSelected: false,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DesktopFeeItem extends ConsumerStatefulWidget {
|
||||
const DesktopFeeItem({
|
||||
Key? key,
|
||||
required this.feeObject,
|
||||
required this.feeRateType,
|
||||
required this.walletId,
|
||||
required this.feeFor,
|
||||
required this.isSelected,
|
||||
}) : super(key: key);
|
||||
|
||||
final FeeObject? feeObject;
|
||||
final FeeRateType feeRateType;
|
||||
final String walletId;
|
||||
final Future<Amount> Function({
|
||||
required Amount amount,
|
||||
required FeeRateType feeRateType,
|
||||
required int feeRate,
|
||||
required Coin coin,
|
||||
}) feeFor;
|
||||
final bool isSelected;
|
||||
|
||||
@override
|
||||
ConsumerState<DesktopFeeItem> createState() => _DesktopFeeItemState();
|
||||
}
|
||||
|
||||
class _DesktopFeeItemState extends ConsumerState<DesktopFeeItem> {
|
||||
String? feeString;
|
||||
String? timeString;
|
||||
|
||||
static const stringsToLoopThrough = [
|
||||
"Calculating",
|
||||
"Calculating.",
|
||||
"Calculating..",
|
||||
"Calculating...",
|
||||
];
|
||||
|
||||
String estimatedTimeToBeIncludedInNextBlock(
|
||||
int targetBlockTime, int estimatedNumberOfBlocks) {
|
||||
int time = targetBlockTime * estimatedNumberOfBlocks;
|
||||
|
||||
int hours = (time / 3600).floor();
|
||||
if (hours > 1) {
|
||||
return "~$hours hours";
|
||||
} else if (hours == 1) {
|
||||
return "~$hours hour";
|
||||
}
|
||||
|
||||
// less than an hour
|
||||
|
||||
final string = (time / 60).toStringAsFixed(1);
|
||||
|
||||
if (string == "1.0") {
|
||||
return "~1 minute";
|
||||
} else {
|
||||
if (string.endsWith(".0")) {
|
||||
return "~${(time / 60).floor()} minutes";
|
||||
}
|
||||
return "~$string minutes";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint("BUILD: $runtimeType : ${widget.feeRateType}");
|
||||
|
||||
return MaterialButton(
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(
|
||||
(
|
||||
widget.feeRateType,
|
||||
feeString,
|
||||
timeString,
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Builder(
|
||||
builder: (_) {
|
||||
if (widget.feeRateType == FeeRateType.custom) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
widget.feeRateType.prettyName,
|
||||
style:
|
||||
STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
final manager = ref.watch(walletsChangeNotifierProvider
|
||||
.select((value) => value.getManager(widget.walletId)));
|
||||
|
||||
if (widget.feeObject == null) {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context).copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return FutureBuilder(
|
||||
future: widget.feeFor(
|
||||
coin: manager.coin,
|
||||
feeRateType: widget.feeRateType,
|
||||
feeRate: widget.feeRateType == FeeRateType.fast
|
||||
? widget.feeObject!.fast
|
||||
: widget.feeRateType == FeeRateType.slow
|
||||
? widget.feeObject!.slow
|
||||
: widget.feeObject!.medium,
|
||||
amount: ref.watch(sendAmountProvider.state).state,
|
||||
),
|
||||
builder: (_, AsyncSnapshot<Amount> snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
snapshot.hasData) {
|
||||
feeString = "${widget.feeRateType.prettyName} "
|
||||
"(~${ref.watch(pAmountFormatter(manager.coin)).format(
|
||||
snapshot.data!,
|
||||
indicatePrecisionLoss: false,
|
||||
)})";
|
||||
|
||||
timeString = manager.coin == Coin.ethereum
|
||||
? ""
|
||||
: estimatedTimeToBeIncludedInNextBlock(
|
||||
Constants.targetBlockTimeInSeconds(manager.coin),
|
||||
widget.feeRateType == FeeRateType.fast
|
||||
? widget.feeObject!.numberOfBlocksFast
|
||||
: widget.feeRateType == FeeRateType.slow
|
||||
? widget.feeObject!.numberOfBlocksSlow
|
||||
: widget.feeObject!.numberOfBlocksAverage,
|
||||
);
|
||||
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
feeString!,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
if (widget.feeObject != null)
|
||||
Text(
|
||||
timeString!,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveSearchIconRight,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return AnimatedText(
|
||||
stringsToLoopThrough: stringsToLoopThrough,
|
||||
style: STextStyles.desktopTextExtraExtraSmall(context)
|
||||
.copyWith(
|
||||
color: Theme.of(context)
|
||||
.extension<StackColors>()!
|
||||
.textFieldActiveText,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
56
lib/widgets/fee_slider.dart
Normal file
56
lib/widgets/fee_slider.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:stackwallet/utilities/text_styles.dart';
|
||||
|
||||
class FeeSlider extends StatefulWidget {
|
||||
const FeeSlider({
|
||||
super.key,
|
||||
required this.onSatVByteChanged,
|
||||
});
|
||||
|
||||
final void Function(int) onSatVByteChanged;
|
||||
|
||||
@override
|
||||
State<FeeSlider> createState() => _FeeSliderState();
|
||||
}
|
||||
|
||||
class _FeeSliderState extends State<FeeSlider> {
|
||||
static const int min = 1;
|
||||
static const int max = 4;
|
||||
|
||||
double sliderValue = 0;
|
||||
|
||||
int rate = min;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"sat/vByte",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
Text(
|
||||
"$rate",
|
||||
style: STextStyles.smallMed12(context),
|
||||
),
|
||||
],
|
||||
),
|
||||
Slider(
|
||||
value: sliderValue,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
sliderValue = value;
|
||||
rate = pow(sliderValue * (max - min) + min, 4).toInt();
|
||||
});
|
||||
widget.onSatVByteChanged(rate);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1761,7 +1761,7 @@ packages:
|
|||
path: wakelock_windows
|
||||
ref: "2a9bca63a540771f241d688562351482b2cf234c"
|
||||
resolved-ref: "2a9bca63a540771f241d688562351482b2cf234c"
|
||||
url: "https://github.com/timsneath/wakelock"
|
||||
url: "https://github.com/diegotori/wakelock"
|
||||
source: git
|
||||
version: "0.2.2"
|
||||
wallet:
|
||||
|
|
|
@ -182,7 +182,7 @@ dependency_overrides:
|
|||
# required for dart 3, at least until a fix is merged upstream
|
||||
wakelock_windows:
|
||||
git:
|
||||
url: https://github.com/timsneath/wakelock
|
||||
url: https://github.com/diegotori/wakelock
|
||||
ref: 2a9bca63a540771f241d688562351482b2cf234c
|
||||
path: wakelock_windows
|
||||
|
||||
|
|
|
@ -987,7 +987,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i27.BitcoinWallet {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
_i23.Future<List<_i18.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -1408,6 +1408,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i27.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i18.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1420,6 +1421,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i27.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
@ -2837,7 +2839,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -3205,7 +3207,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -269,7 +269,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
|
|||
@override
|
||||
_i13.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i13.Coin.banano,
|
||||
returnValue: _i13.Coin.bitcoin,
|
||||
) as _i13.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -230,7 +230,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i11.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i11.Coin.banano,
|
||||
returnValue: _i11.Coin.bitcoin,
|
||||
) as _i11.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -228,7 +228,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i11.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i11.Coin.banano,
|
||||
returnValue: _i11.Coin.bitcoin,
|
||||
) as _i11.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -548,7 +548,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
|
|||
@override
|
||||
_i9.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i9.Coin.banano,
|
||||
returnValue: _i9.Coin.bitcoin,
|
||||
) as _i9.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -335,7 +335,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -548,7 +548,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
|
|||
@override
|
||||
_i9.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i9.Coin.banano,
|
||||
returnValue: _i9.Coin.bitcoin,
|
||||
) as _i9.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -389,7 +389,7 @@ class MockManager extends _i1.Mock implements _i13.Manager {
|
|||
@override
|
||||
_i11.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i11.Coin.banano,
|
||||
returnValue: _i11.Coin.bitcoin,
|
||||
) as _i11.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -317,7 +317,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
|
|||
@override
|
||||
_i10.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i10.Coin.banano,
|
||||
returnValue: _i10.Coin.bitcoin,
|
||||
) as _i10.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -317,7 +317,7 @@ class MockManager extends _i1.Mock implements _i12.Manager {
|
|||
@override
|
||||
_i10.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i10.Coin.banano,
|
||||
returnValue: _i10.Coin.bitcoin,
|
||||
) as _i10.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -569,7 +569,7 @@ class MockManager extends _i1.Mock implements _i15.Manager {
|
|||
@override
|
||||
_i9.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i9.Coin.banano,
|
||||
returnValue: _i9.Coin.bitcoin,
|
||||
) as _i9.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -333,7 +333,7 @@ class MockManager extends _i1.Mock implements _i10.Manager {
|
|||
@override
|
||||
_i8.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i8.Coin.banano,
|
||||
returnValue: _i8.Coin.bitcoin,
|
||||
) as _i8.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -104,7 +104,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -103,7 +103,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -102,7 +102,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -144,7 +144,7 @@ class MockManager extends _i1.Mock implements _i9.Manager {
|
|||
@override
|
||||
_i10.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i10.Coin.banano,
|
||||
returnValue: _i10.Coin.bitcoin,
|
||||
) as _i10.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -104,7 +104,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i7.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i7.Coin.banano,
|
||||
returnValue: _i7.Coin.bitcoin,
|
||||
) as _i7.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -217,7 +217,7 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet {
|
|||
@override
|
||||
_i12.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i12.Coin.banano,
|
||||
returnValue: _i12.Coin.bitcoin,
|
||||
) as _i12.Coin);
|
||||
@override
|
||||
_i11.Future<List<String>> get mnemonic => (super.noSuchMethod(
|
||||
|
@ -487,6 +487,7 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet {
|
|||
int? selectedTxFeeRate,
|
||||
String? _recipientAddress,
|
||||
bool? isSendAll, {
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i13.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -499,6 +500,7 @@ class MockFiroWallet extends _i1.Mock implements _i10.FiroWallet {
|
|||
isSendAll,
|
||||
],
|
||||
{
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
|
|
@ -782,7 +782,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -1202,6 +1202,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i17.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1214,6 +1215,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
@ -2831,7 +2833,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -3199,7 +3201,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -866,7 +866,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i28.BitcoinWallet {
|
|||
@override
|
||||
_i21.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i21.Coin.banano,
|
||||
returnValue: _i21.Coin.bitcoin,
|
||||
) as _i21.Coin);
|
||||
@override
|
||||
_i22.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -1287,6 +1287,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i28.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i17.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1299,6 +1300,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i28.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
@ -2077,7 +2079,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i21.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i21.Coin.banano,
|
||||
returnValue: _i21.Coin.bitcoin,
|
||||
) as _i21.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -2445,7 +2447,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i19.CoinServiceAPI {
|
|||
@override
|
||||
_i21.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i21.Coin.banano,
|
||||
returnValue: _i21.Coin.bitcoin,
|
||||
) as _i21.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -466,7 +466,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i18.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i18.Coin.banano,
|
||||
returnValue: _i18.Coin.bitcoin,
|
||||
) as _i18.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -838,7 +838,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i7.CoinServiceAPI {
|
|||
@override
|
||||
_i18.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i18.Coin.banano,
|
||||
returnValue: _i18.Coin.bitcoin,
|
||||
) as _i18.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -1230,7 +1230,7 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet {
|
|||
@override
|
||||
_i18.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i18.Coin.banano,
|
||||
returnValue: _i18.Coin.bitcoin,
|
||||
) as _i18.Coin);
|
||||
@override
|
||||
_i19.Future<List<String>> get mnemonic => (super.noSuchMethod(
|
||||
|
@ -1500,6 +1500,7 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet {
|
|||
int? selectedTxFeeRate,
|
||||
String? _recipientAddress,
|
||||
bool? isSendAll, {
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i22.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1512,6 +1513,7 @@ class MockFiroWallet extends _i1.Mock implements _i23.FiroWallet {
|
|||
isSendAll,
|
||||
],
|
||||
{
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
|
|
@ -521,7 +521,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
|
|||
@override
|
||||
_i20.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i20.Coin.banano,
|
||||
returnValue: _i20.Coin.bitcoin,
|
||||
) as _i20.Coin);
|
||||
@override
|
||||
_i21.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -941,6 +941,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i17.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -953,6 +954,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i24.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
|
|
@ -776,7 +776,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -1196,6 +1196,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i17.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1208,6 +1209,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i26.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
@ -2186,7 +2188,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -2554,7 +2556,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
|
@ -878,7 +878,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i29.BitcoinWallet {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
_i23.Future<List<_i17.UTXO>> get utxos => (super.noSuchMethod(
|
||||
|
@ -1299,6 +1299,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i29.BitcoinWallet {
|
|||
required String? recipientAddress,
|
||||
required bool? coinControl,
|
||||
required bool? isSendAll,
|
||||
int? satsPerVByte,
|
||||
int? additionalOutputs = 0,
|
||||
List<_i17.UTXO>? utxos,
|
||||
}) =>
|
||||
|
@ -1311,6 +1312,7 @@ class MockBitcoinWallet extends _i1.Mock implements _i29.BitcoinWallet {
|
|||
#recipientAddress: recipientAddress,
|
||||
#coinControl: coinControl,
|
||||
#isSendAll: isSendAll,
|
||||
#satsPerVByte: satsPerVByte,
|
||||
#additionalOutputs: additionalOutputs,
|
||||
#utxos: utxos,
|
||||
},
|
||||
|
@ -2289,7 +2291,7 @@ class MockManager extends _i1.Mock implements _i6.Manager {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
@ -2657,7 +2659,7 @@ class MockCoinServiceAPI extends _i1.Mock implements _i20.CoinServiceAPI {
|
|||
@override
|
||||
_i22.Coin get coin => (super.noSuchMethod(
|
||||
Invocation.getter(#coin),
|
||||
returnValue: _i22.Coin.banano,
|
||||
returnValue: _i22.Coin.bitcoin,
|
||||
) as _i22.Coin);
|
||||
@override
|
||||
bool get isRefreshing => (super.noSuchMethod(
|
||||
|
|
Loading…
Reference in a new issue