calculate with output cost too
MODIFY
WalletWasabi.Fluent/ViewModels/Wallets/Send/ChangeAvoidanceSuggestionViewModel.cs
WalletWasabi.Fluent/ViewModels/Wallets/Send/ChangeAvoidanceSuggestionViewModel.cs
@@ -7,7 +7,6 @@ using NBitcoin;
using WalletWasabi.Blockchain.TransactionBuilding;
using WalletWasabi.Blockchain.TransactionOutputs;
using WalletWasabi.Fluent.Helpers;
-using WalletWasabi.Logging;
using WalletWasabi.Wallets;
namespace WalletWasabi.Fluent.ViewModels.Wallets.Send;
@@ -53,7 +52,7 @@ public partial class ChangeAvoidanceSuggestionViewModel : SuggestionViewModel
{
List<SmartCoin> availableCoins = wallet.Coins.Unspent().ToList();
- if (ChangelessTransactionCoinSelector.TryGetCoins(availableCoins, transactionInfo.FeeRate, transactionInfo.Amount, out IEnumerable<SmartCoin>? selection, cancellationToken))
+ if (ChangelessTransactionCoinSelector.TryGetCoins(availableCoins, transactionInfo.FeeRate, new TxOut(transactionInfo.Amount, destination), out IEnumerable<SmartCoin>? selection, cancellationToken))
{
BuildTransactionResult transaction = TransactionHelpers.BuildChangelessTransaction(
wallet,

MODIFY
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
WalletWasabi.Tests/UnitTests/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelectorTests.cs
@@ -28,7 +28,9 @@ public class ChangelessTransactionCoinSelectorTests
List<SmartCoin> coins = GenerateDummySmartCoins(key, 6_025, 6_561, 8_192, 13_122, 50_000, 100_000, 196_939, 524_288);
var target = Money.Satoshis(150_000);
- bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), target, out IEnumerable<SmartCoin>? selectedCoins);
+ var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
+
+ bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), txOut, out IEnumerable<SmartCoin>? selectedCoins);
Assert.True(found);
long[] solution = selectedCoins!.Select(x => x.Amount.Satoshi).ToArray();
@@ -47,7 +49,9 @@ public class ChangelessTransactionCoinSelectorTests
List<SmartCoin> coins = GenerateDummySmartCoins(key, 150_000);
var target = Money.Satoshis(100_000);
- bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), target, out IEnumerable<SmartCoin>? selectedCoins);
+ var txOut = new TxOut(target, BitcoinFactory.CreateBitcoinAddress(Network.TestNet, key));
+
+ bool found = ChangelessTransactionCoinSelector.TryGetCoins(coins, new FeeRate(satoshiPerByte: 4), txOut, out IEnumerable<SmartCoin>? selectedCoins);
Assert.False(found);
}

MODIFY
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
WalletWasabi/Blockchain/TransactionBuilding/ChangelessTransactionCoinSelector.cs
@@ -19,13 +19,15 @@ public static class ChangelessTransactionCoinSelector
/// </summary>
/// <param name="availableCoins">Coins owned by the user.</param>
/// <param name="feeRate">Current fee rate to take into account effective values of available coins.</param>
- /// <param name="targetAmount">Amount the user wants to pay.</param>
+ /// <param name="txOut">Amount the user wants to pay + the type of the output address</param>
/// <returns><c>true</c> if a solution was found, <c>false</c> otherwise.</returns>
- /// <remarks>The implementation gives only the guarantee that user can pay at most 25% more than <paramref name="targetAmount"/>.</remarks>
- public static bool TryGetCoins(IEnumerable<SmartCoin> availableCoins, FeeRate feeRate, Money targetAmount, [NotNullWhen(true)] out IEnumerable<SmartCoin>? selectedCoins, CancellationToken cancellationToken = default)
+ /// <remarks>The implementation gives only the guarantee that user can pay at most 25% more than <paramref name="txOut.Value"/>.</remarks>
+ public static bool TryGetCoins(IEnumerable<SmartCoin> availableCoins, FeeRate feeRate, TxOut txOut, [NotNullWhen(true)] out IEnumerable<SmartCoin>? selectedCoins, CancellationToken cancellationToken = default)
{
selectedCoins = null;
- var target = targetAmount.Satoshi;
+
+ // target = target amount + output cost
+ var target = txOut.Value.Satoshi + feeRate.GetFee(txOut.ScriptPubKey.EstimateOutputVsize()).Satoshi;
// Keys are effective values of smart coins in satoshis.
IOrderedEnumerable<SmartCoin> sortedCoins = availableCoins.OrderByDescending(x => x.EffectiveValue(feeRate).Satoshi);

calculate with output cost too