mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Add separate signing flow for PSBT
This commit is contained in:
committed by
Andrew Camilleri
parent
d9a8443081
commit
cd93a5ab6b
@@ -130,6 +130,14 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
switch (command)
|
||||
{
|
||||
case "export":
|
||||
var viewModel = new WalletPSBTExportViewModel {SigningContext = vm.SigningContext};
|
||||
viewModel.PSBT = psbt.ToBase64();
|
||||
viewModel.PSBTHex = psbt.ToHex();
|
||||
viewModel.CryptoCode = network.CryptoCode;
|
||||
await FetchTransactionDetails(derivationSchemeSettings, viewModel, network);
|
||||
return View("WalletPSBTExport", viewModel);
|
||||
|
||||
case "decode":
|
||||
vm.Decoded = psbt.ToString();
|
||||
ModelState.Remove(nameof(vm.PSBT));
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace BTCPayServer.Models.WalletViewModels
|
||||
{
|
||||
public class WalletPSBTExportViewModel : WalletPSBTReadyViewModel
|
||||
{
|
||||
public string CryptoCode { get; set; }
|
||||
public string PSBTHex { get; set; }
|
||||
public string PSBT { get; set; }
|
||||
|
||||
[Display(Name = "Upload PSBT from file")]
|
||||
public IFormFile UploadedPSBTFile { get; set; }
|
||||
}
|
||||
}
|
||||
80
BTCPayServer/Views/Wallets/WalletPSBTExport.cshtml
Normal file
80
BTCPayServer/Views/Wallets/WalletPSBTExport.cshtml
Normal file
@@ -0,0 +1,80 @@
|
||||
@model WalletPSBTExportViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(WalletsNavPages.PSBT, "PSBT Signing", Context.GetStoreData().StoreName);
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
<link href="~/vendor/vue-qrcode-reader/vue-qrcode-reader.css" rel="stylesheet" asp-append-version="true"/>
|
||||
}
|
||||
|
||||
@section PageFootContent {
|
||||
<bundle name="wwwroot/bundles/camera-bundle.min.js"></bundle>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
initQRShow("Scan the PSBT with your wallet", @Json.Serialize(Model.PSBTHex), "scan-qr-modal");
|
||||
initCameraScanningApp("Scan the PSBT from your wallet", function (data){
|
||||
$("textarea#ImportedPSBT").val(data);
|
||||
$("#Decode").click();
|
||||
}, "scanModal");
|
||||
});
|
||||
</script>
|
||||
}
|
||||
|
||||
<h4 class="mb-3">@ViewData["PageTitle"]</h4>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-9 col-xl-8">
|
||||
@if (Model.CanCalculateBalance)
|
||||
{
|
||||
<p>
|
||||
This transaction will change your balance:
|
||||
@if (Model.Positive)
|
||||
{
|
||||
<span class="text-success">@Model.BalanceChange</span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="text-danger">@Model.BalanceChange</span>
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
Review the details and export the PSBT for your wallet.
|
||||
Sign the PSBT with your wallet and import the signed version
|
||||
here for finalization and broadcastimg.
|
||||
</p>
|
||||
}
|
||||
<partial name="_PSBTInfo" model="Model" />
|
||||
|
||||
<h4 class="mt-5 mb-3">Export PSBT for signing</h4>
|
||||
<div class="form-group">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")">
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
<div class="d-flex">
|
||||
<button name="command" type="submit" class="btn btn-primary" value="save-psbt">Download PSBT file</button>
|
||||
<button name="command" type="button" class="btn btn-primary only-for-js ms-2" data-bs-toggle="modal" data-bs-target="#scan-qr-modal">Show QR for wallet camera</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h4 class="mt-5 mb-3">Import signed PSBT</h4>
|
||||
<form class="form-group" method="post" asp-action="WalletPSBT" asp-route-walletId="@Context.GetRouteValue("walletId")" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="ImportedPSBT" class="form-label">PSBT content</label>
|
||||
<textarea id="ImportedPSBT" name="PSBT" class="form-control" rows="5"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="UploadedPSBTFile" class="form-label"></label>
|
||||
<input asp-for="UploadedPSBTFile" type="file" class="form-control">
|
||||
</div>
|
||||
<div class="d-flex">
|
||||
<button type="submit" name="command" value="decode" class="btn btn-primary" id="Decode">Decode PSBT</button>
|
||||
<button type="button" id="scanqrcode" class="btn btn-primary only-for-js ms-2" data-bs-toggle="modal" data-bs-target="#scanModal">Scan wallet QR with camera</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<partial name="ShowQR"/>
|
||||
<partial name="CameraScanner"/>
|
||||
@@ -56,7 +56,7 @@
|
||||
}
|
||||
|
||||
<div class="list-group mt-4">
|
||||
<button type="submit" name="command" value="decode" class="list-group-item list-group-item-action" id="SignWithPSBT">
|
||||
<button type="submit" name="command" value="export" class="list-group-item list-group-item-action" id="SignWithPSBT">
|
||||
<div class="image">
|
||||
<vc:icon symbol="wallet-file"/>
|
||||
</div>
|
||||
|
||||
68
BTCPayServer/Views/Wallets/_PSBTInfo.cshtml
Normal file
68
BTCPayServer/Views/Wallets/_PSBTInfo.cshtml
Normal file
@@ -0,0 +1,68 @@
|
||||
@model WalletPSBTReadyViewModel
|
||||
|
||||
<h4 class="mt-4 mb-n3">Inputs</h4>
|
||||
<table class="table table-sm table-responsive-lg">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Index</th>
|
||||
<th class="text-end">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var input in Model.Inputs)
|
||||
{
|
||||
<tr>
|
||||
@if (input.Error != null)
|
||||
{
|
||||
<td>
|
||||
@input.Index <span class="fa fa-exclamation-triangle text-danger" title="@input.Error"></span>
|
||||
</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td>@input.Index</td>
|
||||
}
|
||||
|
||||
@if (input.Positive)
|
||||
{
|
||||
<td class="text-end text-success">@input.BalanceChange</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td class="text-end text-danger">@input.BalanceChange</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="mt-4 mb-n3">Outputs</h4>
|
||||
<table class="table table-sm table-responsive-lg">
|
||||
<thead class="thead-inverse">
|
||||
<tr>
|
||||
<th>Destination</th>
|
||||
<th class="text-end">Amount</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var destination in Model.Destinations)
|
||||
{
|
||||
<tr>
|
||||
<td>@destination.Destination</td>
|
||||
@if (destination.Positive)
|
||||
{
|
||||
<td class="text-end text-success">@destination.Balance</td>
|
||||
}
|
||||
else
|
||||
{
|
||||
<td class="text-end text-danger">@destination.Balance</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@if (Model.FeeRate != null)
|
||||
{
|
||||
<p class="text-muted text-end">Transaction fee rate: <b>@Model.FeeRate</b></p>
|
||||
}
|
||||
Reference in New Issue
Block a user