Make sure there's difference between change and all remaining.
MODIFY
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
WalletWasabi.Gui/Controls/WalletExplorer/SendTabViewModel.cs
@@ -300,7 +300,7 @@ namespace WalletWasabi.Gui.Controls.WalletExplorer
FeeRate feeRate = FeeRate;
var label = Label;
- var intent = new PaymentIntent(new DestinationRequest(address, moneyRequest, label));
+ var intent = new PaymentIntent(address, moneyRequest, label);
try
{
MainWindowViewModel.Instance.StatusBar.TryAddStatus(StatusBarStatus.DequeuingSelectedCoins);

MODIFY
WalletWasabi.Tests/RegTests.cs
WalletWasabi.Tests/RegTests.cs
@@ -817,7 +817,7 @@ namespace WalletWasabi.Tests
}
var scp = new Key().ScriptPubKey;
- var res2 = wallet.BuildTransaction(password, new PaymentIntent(new DestinationRequest(scp, Money.Coins(0.05m), "foo")), 5, null, false);
+ var res2 = wallet.BuildTransaction(password, new PaymentIntent(new DestinationRequest(scp, Money.Coins(0.05m), label: "foo")), feeTarget: 5, allowUnconfirmed: false);
Assert.NotNull(res2.Transaction);
Assert.Single(res2.OuterWalletOutputs);
@@ -838,7 +838,7 @@ namespace WalletWasabi.Tests
Script receive = wallet.GetReceiveKey("Basic").P2wpkhScript;
Money amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 2;
- var res = wallet.BuildTransaction(password, new PaymentIntent(new DestinationRequest(receive, amountToSend, "foo")), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true);
+ var res = wallet.BuildTransaction(password, new PaymentIntent(new DestinationRequest(receive, amountToSend, label: "foo")), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true);
foreach (SmartCoin coin in res.SpentCoins)
{
@@ -886,7 +886,7 @@ namespace WalletWasabi.Tests
receive = wallet.GetReceiveKey("SubtractFeeFromAmount").P2wpkhScript;
amountToSend = wallet.Coins.Where(x => !x.Unavailable).Sum(x => x.Amount) / 3;
- res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, "foo"), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true, subtractFeeFromAmountIndex: 0);
+ res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, subtractFee: true, label: "foo"), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true);
Assert.Equal(2, res.InnerWalletOutputs.Count());
Assert.Empty(res.OuterWalletOutputs);
@@ -919,7 +919,7 @@ namespace WalletWasabi.Tests
#region LowFee
- res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, "foo"), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true);
+ res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), Constants.SevenDaysConfirmationTarget, allowUnconfirmed: true);
Assert.Equal(2, res.InnerWalletOutputs.Count());
Assert.Empty(res.OuterWalletOutputs);
@@ -952,7 +952,7 @@ namespace WalletWasabi.Tests
#region MediumFee
- res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, "foo"), Constants.OneDayConfirmationTarget, allowUnconfirmed: true);
+ res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), Constants.OneDayConfirmationTarget, allowUnconfirmed: true);
Assert.Equal(2, res.InnerWalletOutputs.Count());
Assert.Empty(res.OuterWalletOutputs);
@@ -985,7 +985,7 @@ namespace WalletWasabi.Tests
#region HighFee
- res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, "foo"), 2, allowUnconfirmed: true);
+ res = wallet.BuildTransaction(password, new PaymentIntent(receive, amountToSend, label: "foo"), 2, allowUnconfirmed: true);
Assert.Equal(2, res.InnerWalletOutputs.Count());
Assert.Empty(res.OuterWalletOutputs);
@@ -1033,12 +1033,6 @@ namespace WalletWasabi.Tests
Assert.Equal(receive, activeOutput.ScriptPubKey);
- Logger.LogInfo<RegTests>($"Fee: {res.Fee}");
- Logger.LogInfo<RegTests>($"FeePercentOfSent: {res.FeePercentOfSent} %");
- Logger.LogInfo<RegTests>($"SpendsUnconfirmed: {res.SpendsUnconfirmed}");
- Logger.LogInfo<RegTests>($"Active Output: {activeOutput.Amount.ToString(false, true)} {activeOutput.ScriptPubKey.GetDestinationAddress(network)}");
- Logger.LogInfo<RegTests>($"TxId: {res.Transaction.GetHash()}");
-
Assert.Single(res.Transaction.Transaction.Outputs);
var maxBuiltTxOutput = res.Transaction.Transaction.Outputs.Single();
Assert.Equal(receive, maxBuiltTxOutput.ScriptPubKey);
@@ -1100,8 +1094,8 @@ namespace WalletWasabi.Tests
res = wallet.BuildTransaction(
password,
new PaymentIntent(
- new DestinationRequest(new Key(), amountToSend, "outgoing"),
- new DestinationRequest(new Key(), amountToSend, "outgoing2")),
+ new DestinationRequest(new Key(), amountToSend, label: "outgoing"),
+ new DestinationRequest(new Key(), amountToSend, label: "outgoing2")),
Constants.SevenDaysConfirmationTarget,
allowUnconfirmed: true);
@@ -1171,8 +1165,8 @@ namespace WalletWasabi.Tests
res = wallet.BuildTransaction(
password,
new PaymentIntent(
- new DestinationRequest(k1, MoneyRequest.CreateAllRemaining()),
- new DestinationRequest(k2, Money.Coins(0.0003m), "outgoing")),
+ new DestinationRequest(k1, MoneyRequest.CreateChange()),
+ new DestinationRequest(k2, Money.Coins(0.0003m), label: "outgoing")),
2);
Assert.Contains(k1.ScriptPubKey, res.OuterWalletOutputs.Select(x => x.ScriptPubKey));
@@ -1184,7 +1178,7 @@ namespace WalletWasabi.Tests
res = wallet.BuildTransaction(
password,
- new PaymentIntent(new Key(), Money.Coins(0.0003m), "outgoing"),
+ new PaymentIntent(new Key(), Money.Coins(0.0003m), label: "outgoing"),
2);
Assert.True(res.FeePercentOfSent > 1);
@@ -1193,8 +1187,8 @@ namespace WalletWasabi.Tests
res = wallet.BuildTransaction(
password,
new PaymentIntent(
- new DestinationRequest(newChangeK.P2wpkhScript, MoneyRequest.CreateAllRemaining(), "boo"),
- new DestinationRequest(new Key(), Money.Coins(0.0003m), "outgoing")),
+ new DestinationRequest(newChangeK.P2wpkhScript, MoneyRequest.CreateChange(), "boo"),
+ new DestinationRequest(new Key(), Money.Coins(0.0003m), label: "outgoing")),
2);
Assert.True(res.FeePercentOfSent > 1);
@@ -1283,15 +1277,11 @@ namespace WalletWasabi.Tests
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, -10));
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, 2000));
- // subtractFeeFromAmountIndex has to be valid
- Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, 2, null, false, -10));
- Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, validIntent, 2, null, false, 1));
-
// toSend amount sum has to be in range 0 to 2099999997690000
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, invalidIntent, 2));
// toSend negative sum amount
- Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, new PaymentIntent(scp, Money.Satoshis(-10000), ""), 2));
+ Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(null, new PaymentIntent(scp, Money.Satoshis(-10000)), 2));
// toSend negative operation amount
Assert.Throws<ArgumentOutOfRangeException>(() => wallet.BuildTransaction(
@@ -1302,9 +1292,9 @@ namespace WalletWasabi.Tests
2));
// allowedInputs cannot be empty
- Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, validIntent, 2, null, false, null, new TxoRef[0]));
+ Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(null, validIntent, feeTarget: 2, allowedInputs: new TxoRef[0]));
- // "Only one element can contain Money.Zero
+ // "Only one element can contain the AllRemaining flag.
Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(
password,
new PaymentIntent(
@@ -1339,12 +1329,12 @@ namespace WalletWasabi.Tests
// subtract Fee from amount index with no enough money
var operations = new PaymentIntent(
- new DestinationRequest(scp, Money.Coins(1m)),
+ new DestinationRequest(scp, Money.Coins(1m), subtractFee: true),
new DestinationRequest(scp, Money.Coins(0.5m)));
- Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, 2, null, false, 0));
+ Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(password, operations, 2, null, false));
// No enough money (only one confirmed coin, no unconfirmed allowed)
- operations = new PaymentIntent(scp, Money.Coins(1.5m), "");
+ operations = new PaymentIntent(scp, Money.Coins(1.5m));
Assert.Throws<InsufficientBalanceException>(() => wallet.BuildTransaction(null, operations, 2));
// No enough money (only one confirmed coin, unconfirmed allowed)
@@ -1363,7 +1353,7 @@ namespace WalletWasabi.Tests
Assert.Equal(1, btx.SpentCoins.Count(c => c.Confirmed));
Assert.Equal(1, btx.SpentCoins.Count(c => !c.Confirmed));
- // Only one operation with Zero money
+ // Only one operation with AllRemainingFlag
Assert.Throws<ArgumentException>(() => wallet.BuildTransaction(
null,
@@ -1374,7 +1364,7 @@ namespace WalletWasabi.Tests
Logger.TurnOn();
- operations = new PaymentIntent(scp, Money.Coins(0.5m), "");
+ operations = new PaymentIntent(scp, Money.Coins(0.5m));
btx = wallet.BuildTransaction(password, operations, 2);
}
finally
@@ -1457,11 +1447,11 @@ namespace WalletWasabi.Tests
Assert.Single(wallet.Coins);
// Send money before reorg.
- var operations = new PaymentIntent(scp, Money.Coins(0.011m), "");
+ var operations = new PaymentIntent(scp, Money.Coins(0.011m));
var btx1 = wallet.BuildTransaction(password, operations, 2);
await wallet.SendTransactionAsync(btx1.Transaction);
- operations = new PaymentIntent(scp, Money.Coins(0.012m), "");
+ operations = new PaymentIntent(scp, Money.Coins(0.012m));
var btx2 = wallet.BuildTransaction(password, operations, 2, allowUnconfirmed: true);
await wallet.SendTransactionAsync(btx2.Transaction);
@@ -1480,11 +1470,11 @@ namespace WalletWasabi.Tests
// Send money after reorg.
// When we invalidate a block, the transactions set in the invalidated block
// are reintroduced when we generate a new block through the rpc call
- operations = new PaymentIntent(scp, Money.Coins(0.013m), "");
+ operations = new PaymentIntent(scp, Money.Coins(0.013m));
var btx3 = wallet.BuildTransaction(password, operations, 2);
await wallet.SendTransactionAsync(btx3.Transaction);
- operations = new PaymentIntent(scp, Money.Coins(0.014m), "");
+ operations = new PaymentIntent(scp, Money.Coins(0.014m));
var btx4 = wallet.BuildTransaction(password, operations, 2, allowUnconfirmed: true);
await wallet.SendTransactionAsync(btx4.Transaction);
@@ -1654,8 +1644,8 @@ namespace WalletWasabi.Tests
Assert.Equal((1 * Money.COIN) - tx1Res.Fee.Satoshi, totalWallet);
// Spend the unconfirmed and unspent coin (send it to ourself)
- operations = new PaymentIntent(key.PubKey.WitHash.ScriptPubKey, Money.Coins(0.5m), "");
- var tx2Res = wallet.BuildTransaction(password, operations, 2, allowUnconfirmed: true, subtractFeeFromAmountIndex: 0);
+ operations = new PaymentIntent(key.PubKey.WitHash.ScriptPubKey, Money.Coins(0.5m), subtractFee: true);
+ var tx2Res = wallet.BuildTransaction(password, operations, 2, allowUnconfirmed: true);
await wallet.SendTransactionAsync(tx2Res.Transaction);
while (wallet.Coins.Count != 4)
@@ -1698,7 +1688,7 @@ namespace WalletWasabi.Tests
// Test coin basic count.
var coinCount = wallet.Coins.Count;
var to = wallet.GetReceiveKey("foo");
- var res = wallet.BuildTransaction(password, new PaymentIntent(to.P2wpkhScript, Money.Coins(0.2345m), "bar"), 2, allowUnconfirmed: true);
+ var res = wallet.BuildTransaction(password, new PaymentIntent(to.P2wpkhScript, Money.Coins(0.2345m), label: "bar"), 2, allowUnconfirmed: true);
await wallet.SendTransactionAsync(res.Transaction);
Assert.Equal(coinCount + 2, wallet.Coins.Count);
Assert.Equal(2, wallet.Coins.Count(x => !x.Confirmed));

MODIFY
WalletWasabi/Models/TransactionBuilding/ChangeStrategy.cs
WalletWasabi/Models/TransactionBuilding/ChangeStrategy.cs
@@ -7,6 +7,7 @@ namespace WalletWasabi.Models.TransactionBuilding
public enum ChangeStrategy
{
Auto,
- Custom
+ Custom,
+ AllRemainingCustom
}
}

MODIFY
WalletWasabi/Models/TransactionBuilding/DestinationRequest.cs
WalletWasabi/Models/TransactionBuilding/DestinationRequest.cs
@@ -12,7 +12,7 @@ namespace WalletWasabi.Models.TransactionBuilding
public MoneyRequest Amount { get; }
public string Label { get; }
- public DestinationRequest(Script scriptPubKey, Money amount, string label = "") : this(scriptPubKey, MoneyRequest.Create(amount), label)
+ public DestinationRequest(Script scriptPubKey, Money amount, bool subtractFee = false, string label = "") : this(scriptPubKey, MoneyRequest.Create(amount, subtractFee), label)
{
}
@@ -20,7 +20,7 @@ namespace WalletWasabi.Models.TransactionBuilding
{
}
- public DestinationRequest(IDestination destination, Money amount, string label = "") : this(destination, MoneyRequest.Create(amount), label)
+ public DestinationRequest(IDestination destination, Money amount, bool subtractFee = false, string label = "") : this(destination, MoneyRequest.Create(amount, subtractFee), label)
{
}

MODIFY
WalletWasabi/Models/TransactionBuilding/MoneyRequest.cs
WalletWasabi/Models/TransactionBuilding/MoneyRequest.cs
@@ -7,16 +7,19 @@ namespace WalletWasabi.Models.TransactionBuilding
{
public class MoneyRequest
{
- public static MoneyRequest Create(Money amount) => new MoneyRequest(amount, MoneyRequestType.Value);
+ public static MoneyRequest Create(Money amount, bool subtractFee = false) => new MoneyRequest(amount, MoneyRequestType.Value, subtractFee);
- public static MoneyRequest CreateAllRemaining() => new MoneyRequest(null, MoneyRequestType.AllRemaining);
+ public static MoneyRequest CreateChange(bool subtractFee = true) => new MoneyRequest(null, MoneyRequestType.Change, subtractFee);
+
+ public static MoneyRequest CreateAllRemaining(bool subtractFee = true) => new MoneyRequest(null, MoneyRequestType.AllRemaining, subtractFee);
public Money Amount { get; }
public MoneyRequestType Type { get; }
+ public bool SubtractFee { get; }
- private MoneyRequest(Money amount, MoneyRequestType type)
+ private MoneyRequest(Money amount, MoneyRequestType type, bool subtractFee)
{
- if (type == MoneyRequestType.AllRemaining)
+ if (type == MoneyRequestType.AllRemaining || type == MoneyRequestType.Change)
{
if (amount != null)
{
@@ -41,6 +44,7 @@ namespace WalletWasabi.Models.TransactionBuilding
Amount = amount;
Type = type;
+ SubtractFee = subtractFee;
}
}
}

MODIFY
WalletWasabi/Models/TransactionBuilding/MoneyRequestType.cs
WalletWasabi/Models/TransactionBuilding/MoneyRequestType.cs
@@ -7,6 +7,7 @@ namespace WalletWasabi.Models.TransactionBuilding
public enum MoneyRequestType
{
Value,
+ Change,
AllRemaining
}
}

MODIFY
WalletWasabi/Models/TransactionBuilding/PaymentIntent.cs
WalletWasabi/Models/TransactionBuilding/PaymentIntent.cs
@@ -14,7 +14,7 @@ namespace WalletWasabi.Models.TransactionBuilding
public Money TotalAmount { get; }
public int Count => Requests.Count();
- public PaymentIntent(Script scriptPubKey, Money amount, string label = "") : this(scriptPubKey, MoneyRequest.Create(amount), label)
+ public PaymentIntent(Script scriptPubKey, Money amount, bool subtractFee = false, string label = "") : this(scriptPubKey, MoneyRequest.Create(amount, subtractFee), label)
{
}
@@ -22,7 +22,7 @@ namespace WalletWasabi.Models.TransactionBuilding
{
}
- public PaymentIntent(IDestination destination, Money amount, string label = "") : this(destination, MoneyRequest.Create(amount), label)
+ public PaymentIntent(IDestination destination, Money amount, bool subtractFee = false, string label = "") : this(destination, MoneyRequest.Create(amount, subtractFee), label)
{
}
@@ -42,18 +42,43 @@ namespace WalletWasabi.Models.TransactionBuilding
Guard.NotNull(nameof(request), request);
}
- var allSpendCount = requests.Count(x => x.Amount.Type == MoneyRequestType.AllRemaining);
- if (allSpendCount == 0)
+ var subtractFeeCount = requests.Count(x => x.Amount.SubtractFee);
+ if (subtractFeeCount > 1)
+ {
+ // Note: It'd be possible tod implement that the fees to be subtracted equally from more outputs, but I guess nobody would use it.
+ throw new ArgumentException($"Only one request can specify fee subtraction.");
+ }
+
+ var allRemainingCount = requests.Count(x => x.Amount.Type == MoneyRequestType.AllRemaining);
+ var changeCount = requests.Count(x => x.Amount.Type == MoneyRequestType.Change);
+ int specialCount = allRemainingCount + changeCount;
+ if (specialCount == 0)
{
ChangeStrategy = ChangeStrategy.Auto;
}
- else if (allSpendCount == 1)
+ else if (specialCount == 1)
+ {
+ if (subtractFeeCount != 1)
+ {
+ throw new ArgumentException($"You must specifiy fee subtraction strategy if custom change is specified.");
+ }
+
+ if (allRemainingCount == 1)
+ {
+ ChangeStrategy = ChangeStrategy.AllRemainingCustom;
+ }
+ else if (changeCount == 1)
{
ChangeStrategy = ChangeStrategy.Custom;
}
else
{
- throw new ArgumentException($"Only one request can contain an all remaining money request.");
+ throw new NotSupportedException("This is impossible.");
+ }
+ }
+ else
+ {
+ throw new ArgumentException($"Only one request can contain an all remaining money or change request.");
}
Requests = requests;
@@ -61,11 +86,18 @@ namespace WalletWasabi.Models.TransactionBuilding
TotalAmount = requests.Where(x => x.Amount.Type == MoneyRequestType.Value).Sum(x => x.Amount.Amount);
}
- public bool TryGetCustomChange(out DestinationRequest customChange)
+ public bool TryGetCustomRequest(out DestinationRequest request)
+ {
+ request = Requests.SingleOrDefault(x => x.Amount.Type == MoneyRequestType.Change || x.Amount.Type == MoneyRequestType.AllRemaining);
+
+ return request != null;
+ }
+
+ public bool TryGetFeeSubtractionRequest(out DestinationRequest request)
{
- customChange = Requests.FirstOrDefault(x => x.Amount.Type == MoneyRequestType.AllRemaining);
+ request = Requests.SingleOrDefault(x => x.Amount.SubtractFee);
- return customChange != null;
+ return request != null;
}
}
}

MODIFY
WalletWasabi/Services/WalletService.cs
WalletWasabi/Services/WalletService.cs
@@ -869,7 +869,6 @@ 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. Null if feeRate is being used instead.</param>
/// <param name="feeRate">The fee rate for the transaction. Null if feeTarget is being used instead.</param>
/// <exception cref="ArgumentException"></exception>
@@ -880,7 +879,6 @@ namespace WalletWasabi.Services
int? feeTarget = null,
FeeRate feeRate = null,
bool allowUnconfirmed = false,
- int? subtractFeeFromAmountIndex = null,
IEnumerable<TxoRef> allowedInputs = null)
{
password = password ?? ""; // Correction.
@@ -908,19 +906,6 @@ namespace WalletWasabi.Services
}
}
- if (subtractFeeFromAmountIndex != null) // If not null, make sure not out of range. If null fee is substracted from the change.
- {
- if (subtractFeeFromAmountIndex < 0)
- {
- throw new ArgumentOutOfRangeException($"{nameof(subtractFeeFromAmountIndex)} cannot be smaller than 0.");
- }
- int maxIndex = payments.Count - 1;
- if (subtractFeeFromAmountIndex > maxIndex)
- {
- throw new ArgumentOutOfRangeException($"{nameof(subtractFeeFromAmountIndex)} can be maximum {maxIndex}. Actual: {subtractFeeFromAmountIndex}.");
- }
- }
-
// Get allowed coins to spend.
List<SmartCoin> allowedSmartCoinInputs; // Inputs that can be used to build the transaction.
if (allowedInputs != null) // If allowedInputs are specified then select the coins from them.
@@ -951,62 +936,49 @@ namespace WalletWasabi.Services
builder.SetCoinSelector(new SmartCoinSelector(smartCoinsByOutpoint));
builder.AddCoins(allowedSmartCoinInputs.Select(c => c.GetCoin()));
- for (int i = 0; i < payments.Count; i++) // clone
+ foreach (var request in payments.Requests.Where(x => x.Amount.Type == MoneyRequestType.Value))
{
- var toSend = payments.Requests.ToArray();
- var destination = toSend[i].Destination;
- var amountRequest = toSend[i].Amount;
- var label = toSend[i].Label;
- if (amountRequest.Type == MoneyRequestType.AllRemaining)
- {
- builder.SendAllRemaining(destination);
- }
- else if (amountRequest.Type == MoneyRequestType.Value)
- {
- builder.Send(destination, amountRequest.Amount);
- }
- else
- {
- throw new NotSupportedException($"{nameof(amountRequest.Type)} is not supported: {amountRequest.Type}.");
- }
+ var amountRequest = request.Amount;
- if (subtractFeeFromAmountIndex is int inputIndex && inputIndex == i)
+ builder.Send(request.Destination, amountRequest.Amount);
+ if (amountRequest.SubtractFee)
{
builder.SubtractFees();
}
}
HdPubKey changeHdPubKey = null;
- if (payments.TryGetCustomChange(out DestinationRequest custChange))
+
+ if (payments.TryGetCustomRequest(out DestinationRequest custChange))
{
- changeHdPubKey = KeyManager.GetKeyForScriptPubKey(custChange.Destination.ScriptPubKey);
- }
+ var changeScript = custChange.Destination.ScriptPubKey;
+ changeHdPubKey = KeyManager.GetKeyForScriptPubKey(changeScript);
- try
+ var changeStrategy = payments.ChangeStrategy;
+ if (changeStrategy == ChangeStrategy.Custom)
{
- // We try building without change, in some case, it is not needed
- builder.SendEstimatedFees(feePerBytes);
+ builder.SetChange(changeScript);
}
- catch (InvalidOperationException) // A change address should be specified
- {
- if (payments.ChangeStrategy == ChangeStrategy.Auto)
+ else if (changeStrategy == ChangeStrategy.AllRemainingCustom)
{
- // Let's check is we need change by using a dummy
- KeyManager.AssertCleanKeysIndexed(isInternal: true);
- KeyManager.AssertLockedInternalKeysIndexed(14);
- changeHdPubKey = KeyManager.GetKeys(KeyState.Clean, true).RandomElement();
- builder.SetChange(changeHdPubKey.P2wpkhScript);
+ builder.SendAllRemaining(changeScript);
}
- else if (payments.ChangeStrategy == ChangeStrategy.Custom)
+ else
{
- builder.SetChange(custChange.Destination);
+ throw new NotSupportedException(payments.ChangeStrategy.ToString());
+ }
}
else
{
- throw new NotSupportedException($"{nameof(payments.ChangeStrategy)} is not supported: {payments.ChangeStrategy}.");
+ KeyManager.AssertCleanKeysIndexed(isInternal: true);
+ KeyManager.AssertLockedInternalKeysIndexed(14);
+ changeHdPubKey = KeyManager.GetKeys(KeyState.Clean, true).RandomElement();
+
+ builder.SetChange(changeHdPubKey.P2wpkhScript);
}
+
builder.SendEstimatedFees(feePerBytes);
- }
+
var psbt = builder.BuildPSBT(false);
string changeLabel = null;

Make sure there's difference between change and all remaining.