fix regtests, fix load xaml error
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabView.xaml
WalletWasabi.Gui/Controls/WalletExplorer/SendTabView.xaml
</StackPanel>
<StackPanel IsVisible="{Binding IsCustomFee}" VerticalAlignment="Center" Orientation="Horizontal" Spacing="10">
<TextBlock Text="or set Fee:" />
- <controls:ExtendedTextBox Text="{Binding UserFeeText}" Watermark="Fee (sat/byte)" UseFloatingWatermark="True" MinWidth="124" MaxWidth="124" MinHeight="46" />
+ <controls:ExtendedTextBox Text="{Binding UserFeeText, Mode=TwoWay}" Watermark="Fee (sat/byte)" UseFloatingWatermark="True" MinWidth="124" MaxWidth="124" MinHeight="46" />
</StackPanel>
</StackPanel>
<StackPanel Spacing="10" Orientation="Horizontal">

MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
@@ -59,14 +59,13 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private bool _isBusy;
private bool _isHardwareBusy;
private int _caretIndex;
+ private ObservableAsPropertyHelper<bool> _isCustomFee;
private const string SendTransactionButtonTextString = "Send Transaction";
private const string WaitingForHardwareWalletButtonTextString = "Waiting for Hardware Wallet...";
private const string SendingTransactionButtonTextString = "Sending Transaction...";
private const string BuildTransactionButtonTextString = "Build Transaction";
private const string BuildingTransactionButtonTextString = "Building Transaction...";
- private const string FeeTooLowTextString = "Warning: Transactions with a fee rate below 1 sat/byte will not be relayed by the bitcoin network.";
- private const string FeeTooHighTextString = "Warning: Fee rate is extremely high.";
private ObservableCollection<SuggestionViewModel> _suggestions;
private FeeDisplayFormat _feeDisplayFormat;
@@ -204,10 +203,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(UpdateSuggestions);
- this.WhenAnyValue(x => x.FeeTarget)
- .ObserveOn(RxApp.MainThreadScheduler)
- .Subscribe(_ => FeeRateChanged(false));
-
MaxCommand = ReactiveCommand.Create(() => { IsMax = !IsMax; }, outputScheduler: RxApp.MainThreadScheduler);
this.WhenAnyValue(x => x.IsMax)
.ObserveOn(RxApp.MainThreadScheduler)
@@ -339,11 +334,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
BuildTransactionResult result;
if (useCustomFee)
{
- result = await Task.Run(() => Global.WalletService.BuildTransaction(Password, new[] { operation }, feeRate, allowUnconfirmed: true, allowedInputs: selectedCoinReferences));
+ result = await Task.Run(() => Global.WalletService.BuildTransaction(Password, new[] { operation }, feeTarget: null, feeRate: Money.Satoshis(feeRate.SatoshiPerByte), allowUnconfirmed: true, allowedInputs: selectedCoinReferences));
}
else
{
- result = await Task.Run(() => Global.WalletService.BuildTransaction(Password, new[] { operation }, FeeTarget, allowUnconfirmed: true, allowedInputs: selectedCoinReferences));
+ result = await Task.Run(() => Global.WalletService.BuildTransaction(Password, new[] { operation }, feeTarget: FeeTarget, feeRate: null, allowUnconfirmed: true, allowedInputs: selectedCoinReferences));
}
if (IsTransactionBuilder)
@@ -576,46 +571,6 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
SetFeesAndTexts(false);
}
- private void FeeRateChanged(bool setFeeTarget)
- {
- SetFeesAndTexts(!setFeeTarget);
- WarnCustomFee(setFeeTarget);
-
- if (setFeeTarget)
- {
- this.RaisePropertyChanged(nameof(FeeTarget));
- }
- else
- {
- this.RaisePropertyChanged(nameof(UserFeeText));
- }
- }
-
- private void WarnCustomFee(bool setFeeTarget)
- {
- if (decimal.TryParse(UserFeeText.Replace(',', '.'), out var feeRate))
- {
- if (feeRate < 1)
- {
- SetWarningMessage(FeeTooLowTextString);
- }
- else if (feeRate > 1000)
- {
- SetWarningMessage(FeeTooHighTextString);
- }
- else
- {
- SetWarningMessage("");
- }
-
- AllFeeEstimate allFeeEstimate = Global.Synchronizer?.AllFeeEstimate;
- if (allFeeEstimate != null)
- {
- _feeTarget = allFeeEstimate.GetFeeTarget(feeRate);
- }
- }
- }
-
private void SetFeesAndTexts(bool setFeeText)
{
AllFeeEstimate allFeeEstimate = Global.Synchronizer?.AllFeeEstimate;
@@ -721,6 +676,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
private void SetFees(AllFeeEstimate allFeeEstimate, int feeTarget)
{
SatoshiPerByteFeeRate = allFeeEstimate.GetFeeRate(feeTarget);
+ UserFeeText = SatoshiPerByteFeeRate.Satoshi.ToString();
IEnumerable<SmartCoin> selectedCoins = CoinList.Coins.Where(cvm => cvm.IsSelected).Select(x => x.Model);
@@ -892,11 +848,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
public string UserFeeText
{
get => _userFeeText;
- set
- {
- this.RaiseAndSetIfChanged(ref _userFeeText, value);
- FeeRateChanged(true);
- }
+ set => this.RaiseAndSetIfChanged(ref _userFeeText, value);
}
public int FeeTarget
@@ -1084,7 +1036,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
set => this.RaiseAndSetIfChanged(ref _amountWaterMarkText, value);
}
- public bool IsCustomFee => Global.UiConfig.CustomFee is true;
+ public bool IsCustomFee => _isCustomFee?.Value ?? false;
public ReactiveCommand<Unit, Unit> BuildTransactionCommand { get; }
@@ -1125,13 +1077,11 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => SetFeesAndTexts(false));
- base.OnOpen();
+ _isCustomFee = Global.UiConfig.WhenAnyValue(x => x.IsCustomFee)
+ .ToProperty(this, x => x.IsCustomFee, scheduler: RxApp.MainThreadScheduler)
+ .DisposeWith(Disposables);
- Global.UiConfig.WhenAnyValue(x => x.CustomFee).ObserveOn(RxApp.MainThreadScheduler).Subscribe(_ =>
- {
- this.RaisePropertyChanged(nameof(IsCustomFee));
- this.RaisePropertyChanged(nameof(UserFeeText));
- }).DisposeWith(Disposables);
+ base.OnOpen();
}
public override bool OnClose()

MODIFY
WalletWasabi.Gui/Tabs/SettingsViewModel.cs
WalletWasabi.Gui/Tabs/SettingsViewModel.cs
@@ -50,7 +50,7 @@ namespace WalletWasabi.Gui.Tabs
public SettingsViewModel(Global global) : base(global, "Settings")
{
Autocopy = Global.UiConfig?.Autocopy is true;
- CustomFee = Global.UiConfig?.CustomFee is true;
+ CustomFee = Global.UiConfig?.IsCustomFee is true;
// Use global config's data as default filler until the real data is filled out by the loading of the config onopen.
var globalConfig = Global.Config;
@@ -76,7 +76,7 @@ namespace WalletWasabi.Gui.Tabs
this.WhenAnyValue(x => x.CustomFee)
.ObserveOn(RxApp.TaskpoolScheduler)
- .Subscribe(x => Global.UiConfig.CustomFee = x);
+ .Subscribe(x => Global.UiConfig.IsCustomFee = x);
OpenConfigFileCommand = ReactiveCommand.Create(OpenConfigFile);
@@ -168,7 +168,7 @@ namespace WalletWasabi.Gui.Tabs
.ToProperty(this, x => x.IsPinSet, scheduler: RxApp.MainThreadScheduler)
.DisposeWith(Disposables);
- Global.UiConfig.WhenAnyValue(x => x.LockScreenPinHash, x => x.Autocopy, x => x.CustomFee)
+ Global.UiConfig.WhenAnyValue(x => x.LockScreenPinHash, x => x.Autocopy, x => x.IsCustomFee)
.Throttle(TimeSpan.FromSeconds(1))
.ObserveOn(RxApp.TaskpoolScheduler)
.Subscribe(async _ => await Global.UiConfig.ToFileAsync())

MODIFY
WalletWasabi.Gui/UiConfig.cs
WalletWasabi.Gui/UiConfig.cs
@@ -24,7 +24,7 @@ namespace WalletWasabi.Gui
private bool _lurkingWifeMode;
private bool _lockScreenActive;
private string _lockScreenPinHash;
- private bool _customFee;
+ private bool _isCustomFee;
[JsonProperty(PropertyName = "WindowState")]
[JsonConverter(typeof(WindowStateAfterStartJsonConverter))]
@@ -51,11 +51,11 @@ namespace WalletWasabi.Gui
public bool Autocopy { get; internal set; }
[DefaultValue(false)]
- [JsonProperty(PropertyName = "CustomFee", DefaultValueHandling = DefaultValueHandling.Populate)]
- public bool CustomFee
+ [JsonProperty(PropertyName = "IsCustomFee", DefaultValueHandling = DefaultValueHandling.Populate)]
+ public bool IsCustomFee
{
- get => _customFee;
- set => RaiseAndSetIfChanged(ref _customFee, value);
+ get => _isCustomFee;
+ set => RaiseAndSetIfChanged(ref _isCustomFee, value);
}
[DefaultValue(false)]

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -816,7 +816,7 @@ namespace WalletWasabi.Tests
}
var scp = new Key().ScriptPubKey;
- var res2 = wallet.BuildTransaction(password, new[] { new WalletService.Operation(scp, Money.Coins(0.05m), "foo") }, 5, false);
+ var res2 = wallet.BuildTransaction(password, new[] { new WalletService.Operation(scp, Money.Coins(0.05m), "foo") }, 5, null, false);
Assert.NotNull(res2.Transaction);
Assert.Single(res2.OuterWalletOutputs);
@@ -1131,7 +1131,7 @@ namespace WalletWasabi.Tests
// covers:
// disallow unconfirmed with allowed inputs
// feeTarget < 2 // NOTE: need to correct alowing 0 and 1
- res = wallet.BuildTransaction(password, toSend, 0, false, allowedInputs: allowedInputs);
+ res = wallet.BuildTransaction(password, toSend, 0, null, false, allowedInputs: allowedInputs);
activeOutput = res.InnerWalletOutputs.Single(x => x.ScriptPubKey == receive);
Assert.Single(res.InnerWalletOutputs);
@@ -1252,8 +1252,8 @@ namespace WalletWasabi.Tests
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validOperationList, 2000));
// subtractFeeFromAmountIndex has to be valid
- Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validOperationList, 2, false, -10));
- Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validOperationList, 2, false, 1));
+ Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validOperationList, 2, null, false, -10));
+ Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validOperationList, 2, null, false, 1));
// toSend amount sum has to be in range 0 to 2099999997690000
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, invalidOperationList, 2));
@@ -1271,7 +1271,7 @@ namespace WalletWasabi.Tests
Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, operations, 2));
// allowedInputs cannot be empty
- Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, validOperationList, 2, false, null, null, new TxoRef[0]));
+ Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, validOperationList, 2, null, false, null, null, new TxoRef[0]));
// "Only one element can contain Money.Zero
var toSendWithTwoZeros = new[]
@@ -1279,11 +1279,11 @@ namespace WalletWasabi.Tests
new WalletService.Operation(scp, Money.Zero, "zero"),
new WalletService.Operation(scp, Money.Zero, "zero")
};
- Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(password, toSendWithTwoZeros, Constants.SevenDaysConfirmationTarget, false));
+ Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(password, toSendWithTwoZeros, Constants.SevenDaysConfirmationTarget, null, false));
// cannot specify spend all and custom change
var spendAll = new[] { new WalletService.Operation(scp, Money.Zero, "spendAll") };
- Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(password, spendAll, Constants.SevenDaysConfirmationTarget, false, customChange: new Key().ScriptPubKey));
+ Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(password, spendAll, Constants.SevenDaysConfirmationTarget, null, false, customChange: new Key().ScriptPubKey));
// Get some money, make it confirm.
var key = wallet.GetReceiveKey("foo label");
@@ -1314,24 +1314,24 @@ namespace WalletWasabi.Tests
new WalletService.Operation(scp, Money.Coins(1m), ""),
new WalletService.Operation(scp, Money.Coins(0.5m), "")
};
- Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, 2, false, 0));
+ Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, 2, null, false, 0));
// No enough money (only one confirmed coin, no unconfirmed allowed)
operations = new[] { new WalletService.Operation(scp, Money.Coins(1.5m), "") };
Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2));
// No enough money (only one confirmed coin, unconfirmed allowed)
- Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2, true));
+ Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2, null, true));
// Add new money with no confirmation
var txId2 = await rpc.SendToAddressAsync(key.GetP2wpkhAddress(network), Money.Coins(2m));
await Task.Delay(1000); // Wait tx to arrive and get processed.
// Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are NOT allowed)
- Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2, false));
+ Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2, null, false));
// Enough money (one confirmed coin and one unconfirmed coin, unconfirmed are allowed)
- var btx = wallet.BuildTransaction(password, operations, 2, true);
+ var btx = wallet.BuildTransaction(password, operations, 2, null, true);
Assert.Equal(2, btx.SpentCoins.Count());
Assert.Equal(1, btx.SpentCoins.Count(c => c.Confirmed));
Assert.Equal(1, btx.SpentCoins.Count(c => !c.Confirmed));
@@ -1345,7 +1345,7 @@ namespace WalletWasabi.Tests
Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, operations, 2));
// `Custom change` and `spend all` cannot be specified at the same time
- Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, operations, 2, false, null, Script.Empty));
+ Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, operations, 2, null, false, null, Script.Empty));
Logger.TurnOn();
operations = new[] { new WalletService.Operation(scp, Money.Coins(0.5m), "") };

MODIFY
WalletWasabi/Models/AllFeeEstimate.cs
WalletWasabi/Models/AllFeeEstimate.cs
@@ -46,28 +46,9 @@ namespace WalletWasabi.Models
.Last(x => x.Key <= feeTarget) // The last should be the largest feeTarget.
.Value;
- satoshiPerByte = satoshiPerByte < 2 ? 2 : satoshiPerByte; // Lowest it will be is 2 sats/byte
-
return Money.Satoshis(satoshiPerByte);
}
- public int GetFeeTarget(decimal feeRate)
- {
- // Where the feeRate is still under or equal to the requested rate.
- try
- {
- int feeTarget = Estimations
- .Last(x => x.Value >= feeRate) // The last should be the largest feeTarget.
- .Key;
-
- return feeTarget;
- }
- catch (Exception)
- {
- return (int)GetFeeRate(Constants.SevenDaysConfirmationTarget); // feeRate is too high, so return highest value
- }
- }
-
#region Equality
public override bool Equals(object obj) => obj is AllFeeEstimate feeEstimate && this == feeEstimate;

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -1025,53 +1025,28 @@ namespace WalletWasabi.Services
/// <param name="allowUnconfirmed">Allow to spend unconfirmed transactions, if necessary.</param>
/// <param name="allowedInputs">Only these inputs allowed to be used to build the transaction. The wallet must know the corresponding private keys.</param>
/// <param name="subtractFeeFromAmountIndex">If null, fee is substracted from the change. Otherwise it denotes the index in the toSend array.</param>
- /// <param name="feeTarget">The target number of blocks to estimate the fee</param>
+ /// <param name="feeTarget">The target number of blocks to estimate the fee.</param>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public BuildTransactionResult BuildTransaction(string password,
Operation[] toSend,
- int feeTarget,
+ int? feeTarget = null,
+ Money feeRate = null,
bool allowUnconfirmed = false,
int? subtractFeeFromAmountIndex = null,
Script customChange = null,
IEnumerable<TxoRef> allowedInputs = null)
{
- Guard.InRangeAndNotNull(nameof(feeTarget), feeTarget, 0, Constants.SevenDaysConfirmationTarget); // Allow 0 and 1, and correct later.
- if (feeTarget < 2) // Correct 0 and 1 to 2.
- {
- feeTarget = 2;
- }
- Logger.LogInfo<WalletService>("Calculating dynamic transaction fee...");
- FeeRate satsPerByte;
- using (var client = new WasabiClient(Synchronizer.WasabiClient.TorClient.DestinationUriAction, Synchronizer.WasabiClient.TorClient.TorSocks5EndPoint))
- {
- Money rate = Synchronizer.GetFeeRate(feeTarget);
- Money sanityCheckedFeeRate = Math.Max(rate, 2); // Use the sanity check that under 2 satoshi per bytes should not be displayed. To correct possible rounding errors.
- satsPerByte = new FeeRate((decimal)sanityCheckedFeeRate.Satoshi);
- }
+ password = password ?? ""; // Correction.
+ toSend = Guard.NotNullOrEmpty(nameof(toSend), toSend);
- return BuildTransaction(password, toSend, satsPerByte, allowUnconfirmed, subtractFeeFromAmountIndex, customChange, allowedInputs);
+ if ((feeRate is null && feeTarget is null)
+ || (feeRate != null && feeTarget != null))
+ {
+ throw new NotSupportedException($"Either {nameof(feeRate)} or {nameof(feeTarget)} must be null. Not both.");
}
- /// <param name="toSend">If Money.Zero then spends all available amount. Does not generate change.</param>
- /// <param name="allowUnconfirmed">Allow to spend unconfirmed transactions, if necessary.</param>
- /// <param name="allowedInputs">Only these inputs allowed to be used to build the transaction. The wallet must know the corresponding private keys.</param>
- /// <param name="subtractFeeFromAmountIndex">If null, fee is substracted from the change. Otherwise it denotes the index in the toSend array.</param>
- /// <param name="feeRate">The fee rate for the transaction</param>
- /// <exception cref="ArgumentException"></exception>
- /// <exception cref="ArgumentNullException"></exception>
- /// <exception cref="ArgumentOutOfRangeException"></exception>
- public BuildTransactionResult BuildTransaction(string password,
- Operation[] toSend,
- FeeRate feeRate,
- bool allowUnconfirmed = false,
- int? subtractFeeFromAmountIndex = null,
- Script customChange = null,
- IEnumerable<TxoRef> allowedInputs = null)
- {
- password = password ?? ""; // Correction.
- toSend = Guard.NotNullOrEmpty(nameof(toSend), toSend);
if (toSend.Any(x => x is null))
{
throw new ArgumentNullException($"{nameof(toSend)} cannot contain null element.");
@@ -1096,6 +1071,17 @@ namespace WalletWasabi.Services
{
throw new ArgumentException($"{nameof(customChange)} and send all to destination cannot be specified at the same time.");
}
+
+ if (feeTarget.HasValue)
+ {
+ Guard.InRangeAndNotNull(nameof(feeTarget), feeTarget.Value, 0, Constants.SevenDaysConfirmationTarget); // Allow 0 and 1, and correct later.
+
+ if (feeTarget < 2) // Correct 0 and 1 to 2.
+ {
+ feeTarget = 2;
+ }
+ }
+
if (subtractFeeFromAmountIndex != null) // If not null, make sure not out of range. If null fee is substracted from the change.
{
if (subtractFeeFromAmountIndex < 0)
@@ -1107,10 +1093,6 @@ namespace WalletWasabi.Services
throw new ArgumentOutOfRangeException($"{nameof(subtractFeeFromAmountIndex)} can be maximum {nameof(toSend)}.Length - 1. {nameof(subtractFeeFromAmountIndex)}: {subtractFeeFromAmountIndex}, {nameof(toSend)}.Length - 1: {toSend.Length - 1}.");
}
}
- if (feeRate is null)
- {
- throw new ArgumentNullException($"{nameof(feeRate)} cannot be null.");
- }
// Get allowed coins to spend.
List<SmartCoin> allowedSmartCoinInputs; // Inputs that can be used to build the transaction.
@@ -1130,6 +1112,26 @@ namespace WalletWasabi.Services
allowedSmartCoinInputs = allowUnconfirmed ? Coins.Where(x => !x.Unavailable).ToList() : Coins.Where(x => !x.Unavailable && x.Confirmed).ToList();
}
+ // 4. Get and calculate fee
+ Logger.LogInfo<WalletService>("Calculating dynamic transaction fee...");
+
+ Money feePerBytes = null;
+ using (var client = new WasabiClient(Synchronizer.WasabiClient.TorClient.DestinationUriAction, Synchronizer.WasabiClient.TorClient.TorSocks5EndPoint))
+ {
+ Money fr;
+ if (feeTarget.HasValue)
+ {
+ fr = Synchronizer.GetFeeRate(feeTarget.Value);
+ }
+ else
+ {
+ fr = feeRate;
+ }
+
+ Money sanityCheckedFeeRate = Math.Max(fr, 2); // Use the sanity check that under 2 satoshi per bytes should not be displayed. To correct possible rounding errors.
+ feePerBytes = new Money(sanityCheckedFeeRate);
+ }
+
bool spendAll = spendAllCount == 1;
int inNum;
if (spendAll)
@@ -1139,7 +1141,7 @@ namespace WalletWasabi.Services
else
{
int expectedMinTxSize = 1 * Constants.P2wpkhInputSizeInBytes + 1 * Constants.OutputSizeInBytes + 10;
- inNum = SelectCoinsToSpend(allowedSmartCoinInputs, toSend.Select(x => x.Amount).Sum() + Money.Satoshis(feeRate.SatoshiPerByte * expectedMinTxSize)).Count();
+ inNum = SelectCoinsToSpend(allowedSmartCoinInputs, toSend.Select(x => x.Amount).Sum() + feePerBytes * expectedMinTxSize).Count();
}
// https://bitcoincore.org/en/segwit_wallet_dev/#transaction-fee-estimation
@@ -1147,7 +1149,7 @@ namespace WalletWasabi.Services
int outNum = spendAll ? toSend.Length : toSend.Length + 1; // number of addresses to send + 1 for change
int vSize = NBitcoinHelpers.CalculateVsizeAssumeSegwit(inNum, outNum);
Logger.LogInfo<WalletService>($"Estimated tx size: {vSize} vbytes.");
- Money fee = Money.Satoshis(feeRate.SatoshiPerByte * vSize);
+ Money fee = feePerBytes * vSize;
Logger.LogInfo<WalletService>($"Fee: {fee.Satoshi} Satoshi.");
// 5. How much to spend?

fix regtests, fix load xaml error