feat: add ability to add comment to the transaction on the send view (#7265)
What changed, and why it matters
This commit adds a simple optional comment field to the wallet send screen in BTCPay Server. Users can type a short note (up to 200 characters) when preparing a transaction, and that note is saved alongside the transaction after it is broadcast. There is no security issue visible in the change.
No security action required. Routine review/accept.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces a Comment property on WalletSendModel and SigningContextModel, surfaces a textarea in WalletSend.cshtml, carries the value through hidden form fields in SigningContext.cshtml, and persists it via WalletRepository.SetWalletObjectComment after successful broadcast. The input is bounded by MaxLength(200) and rendered through normal ASP.NET MVC model binding/validation. No unsafe output encoding bypass, injection path, or authorization change is present in the diff.
Changed components
BTCPayServer/Controllers/UIWalletsController.PSBT.csBTCPayServer/Controllers/UIWalletsController.csBTCPayServer/Models/WalletViewModels/SigningContextModel.csBTCPayServer/Models/WalletViewModels/WalletSendModel.csBTCPayServer/Views/UIWallets/SigningContext.cshtmlBTCPayServer/Views/UIWallets/WalletSend.cshtmlInspect captured patch +21 / −1
diff --git a/BTCPayServer/Controllers/UIWalletsController.PSBT.cs b/BTCPayServer/Controllers/UIWalletsController.PSBT.cs
index 82de020..a3b02bf 100644
--- a/BTCPayServer/Controllers/UIWalletsController.PSBT.cs
+++ b/BTCPayServer/Controllers/UIWalletsController.PSBT.cs
@@ -576,6 +576,12 @@ namespace BTCPayServer.Controllers
TempData[WellKnownTempData.SuccessMessage] = StringLocalizer["Transaction broadcasted successfully ({0})", transaction.GetHash()].Value;
}
+ if (!string.IsNullOrEmpty(vm.SigningContext?.Comment))
+ {
+ var txObjId = new WalletObjectId(walletId, WalletObjectData.Types.Tx, transaction.GetHash().ToString());
+ await WalletRepository.SetWalletObjectComment(txObjId, vm.SigningContext.Comment);
+ }
+
if (vm.SigningContext.PendingTransactionId is not null)
{
await _pendingTransactionService.Broadcasted(GetPendingTxId(walletId, vm.SigningContext.PendingTransactionId));
diff --git a/BTCPayServer/Controllers/UIWalletsController.cs b/BTCPayServer/Controllers/UIWalletsController.cs
index 7b5293f..ca6425c 100644
--- a/BTCPayServer/Controllers/UIWalletsController.cs
+++ b/BTCPayServer/Controllers/UIWalletsController.cs
@@ -1290,7 +1290,8 @@ namespace BTCPayServer.Controllers
PayJoinBIP21 = vm.PayJoinBIP21,
EnforceLowR = psbtResponse.Suggestions?.ShouldEnforceLowR,
ChangeAddress = psbtResponse.ChangeAddress?.ToString(),
- PSBT = psbt.ToHex()
+ PSBT = psbt.ToHex(),
+ Comment = vm.Comment
};
if (!psbt.IsReadyToSign() && command == "sign")
@@ -1467,6 +1468,7 @@ namespace BTCPayServer.Controllers
redirectVm.FormParameters.Add("SigningContext.ChangeAddress", signingContext.ChangeAddress);
redirectVm.FormParameters.Add("SigningContext.PendingTransactionId", signingContext.PendingTransactionId);
redirectVm.FormParameters.Add("SigningContext.BalanceChangeFromReplacement", signingContext.BalanceChangeFromReplacement.ToString());
+ redirectVm.FormParameters.Add("SigningContext.Comment", signingContext.Comment);
}
private IActionResult RedirectToWalletPSBT(WalletPSBTViewModel vm)
diff --git a/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs b/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs
index a1a4be0..66db599 100644
--- a/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs
+++ b/BTCPayServer/Models/WalletViewModels/SigningContextModel.cs
@@ -20,5 +20,6 @@ namespace BTCPayServer.Models.WalletViewModels
public string PendingTransactionId { get; set; }
public long BalanceChangeFromReplacement { get; set; }
+ public string Comment { get; set; }
}
}
diff --git a/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs b/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs
index 213aa1f..ac2fe09 100644
--- a/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs
+++ b/BTCPayServer/Models/WalletViewModels/WalletSendModel.cs
@@ -70,6 +70,10 @@ namespace BTCPayServer.Models.WalletViewModels
public string ReturnUrl { get; set; }
public bool IsMultiSigOnServer { get; set; }
+ [Display(Name = "Transaction Comment")]
+ [MaxLength(200)]
+ public string Comment { get; set; }
+
public class InputSelectionOption
{
public IEnumerable<TransactionTagModel> Labels { get; set; }
diff --git a/BTCPayServer/Views/UIWallets/SigningContext.cshtml b/BTCPayServer/Views/UIWallets/SigningContext.cshtml
index e85df08..59422d7 100644
--- a/BTCPayServer/Views/UIWallets/SigningContext.cshtml
+++ b/BTCPayServer/Views/UIWallets/SigningContext.cshtml
@@ -9,4 +9,5 @@
<input type="hidden" asp-for="ChangeAddress" value="@Model.ChangeAddress" />
<input type="hidden" asp-for="PendingTransactionId" value="@Model.PendingTransactionId" />
<input type="hidden" asp-for="BalanceChangeFromReplacement" value="@Model.BalanceChangeFromReplacement" />
+ <input type="hidden" asp-for="Comment" value="@Model.Comment" />
}
diff --git a/BTCPayServer/Views/UIWallets/WalletSend.cshtml b/BTCPayServer/Views/UIWallets/WalletSend.cshtml
index 9930ffb..c26da97 100644
--- a/BTCPayServer/Views/UIWallets/WalletSend.cshtml
+++ b/BTCPayServer/Views/UIWallets/WalletSend.cshtml
@@ -188,6 +188,12 @@
}
</div>
+ <div class="form-group">
+ <label asp-for="Comment" class="form-label"></label>
+ <textarea asp-for="Comment" class="form-control" rows="2" maxlength="200" placeholder="@StringLocalizer["Optional note about this transaction"]"></textarea>
+ <span asp-validation-for="Comment" class="text-danger"></span>
+ </div>
+
<div class="my-4">
<button class="d-inline-flex align-items-center btn btn-link text-primary fw-semibold p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
<vc:icon symbol="caret-down"/>
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.