mirror of
https://github.com/btcpayserver/btcpayserver.git
synced 2025-02-20 13:34:37 +01:00
Improve wallet nav (#3921)
* Fix rescan nav highlighting * Wallet send wizard * Wallet receive wizard * Consistent wizard back button behaviour
This commit is contained in:
parent
f30ddbf175
commit
181d4d5ea4
23 changed files with 606 additions and 429 deletions
|
@ -544,6 +544,7 @@ namespace BTCPayServer.Tests
|
|||
}
|
||||
}
|
||||
Driver.Navigate().Refresh();
|
||||
Driver.FindElement(By.Id("CancelWizard")).Click();
|
||||
return addressStr;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ using BTCPayServer.Abstractions.Models;
|
|||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Lightning.CLightning;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
|
@ -94,9 +93,8 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.SetCheckbox(By.Id("selectAllCheckbox"), true);
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("BumpFee")).Click();
|
||||
var err = s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error);
|
||||
Assert.Contains("any UTXO available", err.Text);
|
||||
Assert.Contains($"/stores/{s.StoreId}/invoices", s.Driver.Url);
|
||||
Assert.Contains("any UTXO available", s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error).Text);
|
||||
|
||||
// But we should be able to bump from the wallet's page
|
||||
s.GoToWallet(navPages: WalletsNavPages.Transactions);
|
||||
|
@ -104,8 +102,8 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
s.Driver.FindElement(By.Id("BumpFee")).Click();
|
||||
s.Driver.FindElement(By.Id("BroadcastTransaction")).Click();
|
||||
s.FindAlertMessage();
|
||||
Assert.Contains($"/wallets/{s.WalletId}", s.Driver.Url);
|
||||
Assert.Contains("Transaction broadcasted successfully", s.FindAlertMessage().Text);
|
||||
}
|
||||
|
||||
[Fact(Timeout = TestTimeout)]
|
||||
|
@ -1046,6 +1044,7 @@ namespace BTCPayServer.Tests
|
|||
//you cannot use the Sign with NBX option without saving private keys when generating the wallet.
|
||||
Assert.DoesNotContain("nbx-seed", s.Driver.PageSource);
|
||||
|
||||
s.Driver.FindElement(By.Id("CancelWizard")).Click();
|
||||
s.Driver.FindElement(By.Id("WalletNav-Receive")).Click();
|
||||
//generate a receiving address
|
||||
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
|
||||
|
@ -1070,6 +1069,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
|
||||
Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
|
||||
receiveAddr = s.Driver.FindElement(By.Id("address")).GetAttribute("value");
|
||||
s.Driver.FindElement(By.Id("CancelWizard")).Click();
|
||||
|
||||
//change the wallet and ensure old address is not there and generating a new one does not result in the prev one
|
||||
s.GenerateWallet(cryptoCode, "", true);
|
||||
|
@ -1164,6 +1164,7 @@ namespace BTCPayServer.Tests
|
|||
Assert.Equal(parsedBip21.Address.ToString(),
|
||||
s.Driver.FindElement(By.Id("Outputs_0__DestinationAddress")).GetAttribute("value"));
|
||||
|
||||
s.Driver.FindElement(By.Id("CancelWizard")).Click();
|
||||
s.GoToWalletSettings(cryptoCode);
|
||||
var settingsUrl = s.Driver.Url;
|
||||
s.Driver.FindElement(By.Id("ActionsDropdownToggle")).Click();
|
||||
|
@ -1299,6 +1300,7 @@ namespace BTCPayServer.Tests
|
|||
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
|
||||
s.FindAlertMessage();
|
||||
|
||||
s.GoToWallet(null, WalletsNavPages.Transactions);
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.Navigate().Refresh();
|
||||
|
|
|
@ -13,6 +13,7 @@ using BTCPayServer.Models;
|
|||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Payments.PayJoin.Sender;
|
||||
using BTCPayServer.Services;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NBitcoin;
|
||||
using NBitcoin.Payment;
|
||||
|
@ -23,7 +24,6 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
public partial class UIWalletsController
|
||||
{
|
||||
|
||||
[NonAction]
|
||||
public async Task<CreatePSBTResponse> CreatePSBT(BTCPayNetwork network, DerivationSchemeSettings derivationSettings, WalletSendModel sendModel, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -132,16 +132,17 @@ namespace BTCPayServer.Controllers
|
|||
return View("PostRedirect", new PostRedirectViewModel
|
||||
{
|
||||
AspController = "UIWallets",
|
||||
AspAction = nameof(UIWalletsController.WalletSign),
|
||||
AspAction = nameof(WalletSign),
|
||||
RouteParameters = {
|
||||
{ "walletId", walletId.ToString() },
|
||||
{ "returnUrl", returnUrl }
|
||||
{ "walletId", walletId.ToString() }
|
||||
},
|
||||
FormParameters =
|
||||
{
|
||||
{ "walletId", walletId.ToString() },
|
||||
{ "psbt", psbt.ToHex() }
|
||||
}
|
||||
{
|
||||
{ "walletId", walletId.ToString() },
|
||||
{ "psbt", psbt.ToHex() },
|
||||
{ "backUrl", returnUrl },
|
||||
{ "returnUrl", returnUrl }
|
||||
}
|
||||
});
|
||||
} catch (Exception ex) {
|
||||
TempData[WellKnownTempData.ErrorMessage] = ex.Message;
|
||||
|
@ -152,23 +153,29 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpPost("{walletId}/sign")]
|
||||
public async Task<IActionResult> WalletSign([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletPSBTViewModel vm, string returnUrl = null, string command = null)
|
||||
WalletId walletId, WalletPSBTViewModel vm, string command = null)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
if (returnUrl is null)
|
||||
returnUrl = Url.Action(nameof(WalletTransactions), new { walletId });
|
||||
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
||||
|
||||
vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath;
|
||||
|
||||
if (psbt is null || vm.InvalidPSBT)
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.PSBT), "Invalid PSBT");
|
||||
return View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl));
|
||||
return View("WalletSigningOptions", new WalletSigningOptionsModel
|
||||
{
|
||||
SigningContext = vm.SigningContext,
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
}
|
||||
switch (command)
|
||||
{
|
||||
case "vault":
|
||||
return ViewVault(walletId, vm.SigningContext);
|
||||
return ViewVault(walletId, vm);
|
||||
case "seed":
|
||||
return SignWithSeed(walletId, vm.SigningContext);
|
||||
return SignWithSeed(walletId, vm.SigningContext, vm.ReturnUrl, vm.BackUrl);
|
||||
case "decode":
|
||||
return await WalletPSBT(walletId, vm, "decode");
|
||||
default:
|
||||
|
@ -185,21 +192,36 @@ namespace BTCPayServer.Controllers
|
|||
WellknownMetadataKeys.MasterHDKey);
|
||||
if (extKey != null)
|
||||
{
|
||||
return await SignWithSeed(walletId,
|
||||
new SignWithSeedViewModel { SeedOrKey = extKey, SigningContext = vm.SigningContext });
|
||||
return await SignWithSeed(walletId, new SignWithSeedViewModel
|
||||
{
|
||||
SeedOrKey = extKey,
|
||||
SigningContext = vm.SigningContext,
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return View("WalletSigningOptions", new WalletSigningOptionsModel(vm.SigningContext, returnUrl));
|
||||
return View("WalletSigningOptions", new WalletSigningOptionsModel
|
||||
{
|
||||
SigningContext = vm.SigningContext,
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet("{walletId}/psbt")]
|
||||
public async Task<IActionResult> WalletPSBT([ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId)
|
||||
WalletId walletId, string returnUrl)
|
||||
{
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode);
|
||||
var vm = new WalletPSBTViewModel();
|
||||
vm.CryptoCode = network.CryptoCode;
|
||||
var referer = HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath;
|
||||
var vm = new WalletPSBTViewModel
|
||||
{
|
||||
BackUrl = string.IsNullOrEmpty(returnUrl) ? null : referer,
|
||||
ReturnUrl = returnUrl ?? referer,
|
||||
CryptoCode = network.CryptoCode
|
||||
};
|
||||
|
||||
var derivationSchemeSettings = GetDerivationSchemeSettings(walletId);
|
||||
if (derivationSchemeSettings == null)
|
||||
|
@ -222,6 +244,8 @@ namespace BTCPayServer.Controllers
|
|||
return NotFound();
|
||||
|
||||
vm.NBXSeedAvailable = await CanUseHotWallet() && derivationSchemeSettings.IsHotWallet;
|
||||
vm.BackUrl ??= HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath;
|
||||
|
||||
var psbt = await vm.GetPSBT(network.NBitcoinNetwork);
|
||||
if (vm.InvalidPSBT)
|
||||
{
|
||||
|
@ -257,20 +281,27 @@ namespace BTCPayServer.Controllers
|
|||
return RedirectToWalletPSBT(new WalletPSBTViewModel
|
||||
{
|
||||
PSBT = psbt.ToBase64(),
|
||||
FileName = vm.FileName
|
||||
FileName = vm.FileName,
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
|
||||
case "combine":
|
||||
ModelState.Remove(nameof(vm.PSBT));
|
||||
return View(nameof(WalletPSBTCombine), new WalletPSBTCombineViewModel { OtherPSBT = psbt.ToBase64() });
|
||||
return View(nameof(WalletPSBTCombine), new WalletPSBTCombineViewModel
|
||||
{
|
||||
OtherPSBT = psbt.ToBase64(),
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
|
||||
case "broadcast":
|
||||
return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel
|
||||
{
|
||||
return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel
|
||||
{
|
||||
SigningContext = new SigningContextModel(psbt)
|
||||
});
|
||||
}
|
||||
SigningContext = new SigningContextModel(psbt),
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
|
||||
default:
|
||||
return View("WalletPSBTDecoded", vm);
|
||||
|
@ -452,7 +483,7 @@ namespace BTCPayServer.Controllers
|
|||
});
|
||||
vm.SigningContext.PSBT = proposedPayjoin.ToBase64();
|
||||
vm.SigningContext.OriginalPSBT = psbt.ToBase64();
|
||||
return ViewVault(walletId, vm.SigningContext);
|
||||
return ViewVault(walletId, vm);
|
||||
}
|
||||
}
|
||||
catch (PayjoinReceiverException ex)
|
||||
|
@ -523,17 +554,18 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
TempData[WellKnownTempData.SuccessMessage] = $"Transaction broadcasted successfully ({transaction.GetHash()})";
|
||||
}
|
||||
var returnUrl = this.HttpContext.Request.Query["returnUrl"].FirstOrDefault();
|
||||
if (returnUrl is not null)
|
||||
if (!string.IsNullOrEmpty(vm.ReturnUrl))
|
||||
{
|
||||
return LocalRedirect(returnUrl);
|
||||
return LocalRedirect(vm.ReturnUrl);
|
||||
}
|
||||
return RedirectToAction(nameof(WalletTransactions), new { walletId = walletId.ToString() });
|
||||
}
|
||||
case "analyze-psbt":
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel()
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel
|
||||
{
|
||||
PSBT = psbt.ToBase64()
|
||||
PSBT = psbt.ToBase64(),
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
case "decode":
|
||||
await FetchTransactionDetails(derivationSchemeSettings, vm, network);
|
||||
|
@ -568,9 +600,10 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
sourcePSBT = sourcePSBT.Combine(psbt);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "PSBT Successfully combined!";
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel()
|
||||
return RedirectToWalletPSBT(new WalletPSBTViewModel
|
||||
{
|
||||
PSBT = sourcePSBT.ToBase64()
|
||||
PSBT = sourcePSBT.ToBase64(),
|
||||
ReturnUrl = vm.ReturnUrl
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ using NBitcoin;
|
|||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Services.Wallets.Export;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Client;
|
||||
using NBXplorer.DerivationStrategy;
|
||||
|
@ -349,7 +350,8 @@ namespace BTCPayServer.Controllers
|
|||
}
|
||||
|
||||
[HttpGet("{walletId}/receive")]
|
||||
public IActionResult WalletReceive([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId)
|
||||
public IActionResult WalletReceive([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
|
||||
[FromQuery] string returnUrl = null)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
|
@ -369,26 +371,26 @@ namespace BTCPayServer.Controllers
|
|||
Request.GetAbsoluteUri(Url.Action(nameof(PayJoinEndpointController.Submit), "PayJoinEndpoint",
|
||||
new { walletId.CryptoCode })));
|
||||
}
|
||||
return View(new WalletReceiveViewModel()
|
||||
return View(new WalletReceiveViewModel
|
||||
{
|
||||
CryptoCode = walletId.CryptoCode,
|
||||
Address = address?.ToString(),
|
||||
CryptoImage = GetImage(paymentMethod.PaymentId, network),
|
||||
PaymentLink = bip21.ToString()
|
||||
PaymentLink = bip21.ToString(),
|
||||
ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{walletId}/receive")]
|
||||
[HttpPost("{walletId}/receive")]
|
||||
public async Task<IActionResult> WalletReceive([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
|
||||
WalletReceiveViewModel viewModel, string command)
|
||||
WalletReceiveViewModel vm, string command)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
DerivationSchemeSettings paymentMethod = GetDerivationSchemeSettings(walletId);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
switch (command)
|
||||
|
@ -397,7 +399,7 @@ namespace BTCPayServer.Controllers
|
|||
var address = await _walletReceiveService.UnReserveAddress(walletId);
|
||||
if (!string.IsNullOrEmpty(address))
|
||||
{
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel()
|
||||
TempData.SetStatusMessageModel(new StatusMessageModel
|
||||
{
|
||||
AllowDismiss = true,
|
||||
Message = $"Address {address} was unreserved.",
|
||||
|
@ -414,7 +416,7 @@ namespace BTCPayServer.Controllers
|
|||
await SendFreeMoney(cheater, walletId, paymentMethod);
|
||||
break;
|
||||
}
|
||||
return RedirectToAction(nameof(WalletReceive), new { walletId });
|
||||
return RedirectToAction(nameof(WalletReceive), new { walletId, returnUrl = vm.ReturnUrl });
|
||||
}
|
||||
|
||||
private async Task SendFreeMoney(Cheater cheater, WalletId walletId, DerivationSchemeSettings paymentMethod)
|
||||
|
@ -458,8 +460,9 @@ namespace BTCPayServer.Controllers
|
|||
|
||||
[HttpGet("{walletId}/send")]
|
||||
public async Task<IActionResult> WalletSend(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, string defaultDestination = null, string defaultAmount = null, string[] bip21 = null)
|
||||
[ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
|
||||
string defaultDestination = null, string defaultAmount = null, string[] bip21 = null,
|
||||
[FromQuery] string returnUrl = null)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
|
@ -475,7 +478,12 @@ namespace BTCPayServer.Controllers
|
|||
rateRules.Spread = 0.0m;
|
||||
var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, storeData.DefaultCurrency);
|
||||
double.TryParse(defaultAmount, out var amount);
|
||||
var model = new WalletSendModel() { CryptoCode = walletId.CryptoCode };
|
||||
|
||||
var model = new WalletSendModel
|
||||
{
|
||||
CryptoCode = walletId.CryptoCode,
|
||||
ReturnUrl = returnUrl ?? HttpContext.Request.GetTypedHeaders().Referer?.AbsolutePath
|
||||
};
|
||||
if (bip21?.Any() is true)
|
||||
{
|
||||
foreach (var link in bip21)
|
||||
|
@ -590,6 +598,7 @@ namespace BTCPayServer.Controllers
|
|||
var network = NetworkProvider.GetNetwork<BTCPayNetwork>(walletId?.CryptoCode);
|
||||
if (network == null || network.ReadonlyWallet)
|
||||
return NotFound();
|
||||
|
||||
vm.SupportRBF = network.SupportRBF;
|
||||
vm.NBXSeedAvailable = await GetSeed(walletId, network) != null;
|
||||
if (!string.IsNullOrEmpty(bip21))
|
||||
|
@ -857,7 +866,12 @@ namespace BTCPayServer.Controllers
|
|||
switch (command)
|
||||
{
|
||||
case "sign":
|
||||
return await WalletSign(walletId, new WalletPSBTViewModel() { SigningContext = signingContext });
|
||||
return await WalletSign(walletId, new WalletPSBTViewModel
|
||||
{
|
||||
SigningContext = signingContext,
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
case "analyze-psbt":
|
||||
var name =
|
||||
$"Send-{string.Join('_', vm.Outputs.Select(output => $"{output.Amount}->{output.DestinationAddress}{(output.SubtractFeesFromOutput ? "-Fees" : string.Empty)}"))}.psbt";
|
||||
|
@ -920,24 +934,30 @@ namespace BTCPayServer.Controllers
|
|||
ModelState.Clear();
|
||||
}
|
||||
|
||||
private IActionResult ViewVault(WalletId walletId, SigningContextModel signingContext)
|
||||
private IActionResult ViewVault(WalletId walletId, WalletPSBTViewModel vm)
|
||||
{
|
||||
return View(nameof(WalletSendVault),
|
||||
new WalletSendVaultModel()
|
||||
new WalletSendVaultModel
|
||||
{
|
||||
SigningContext = signingContext,
|
||||
SigningContext = vm.SigningContext,
|
||||
WalletId = walletId.ToString(),
|
||||
WebsocketPath = this.Url.Action(nameof(UIVaultController.VaultBridgeConnection), "UIVault",
|
||||
new { walletId = walletId.ToString() })
|
||||
WebsocketPath = Url.Action(nameof(UIVaultController.VaultBridgeConnection), "UIVault",
|
||||
new { walletId = walletId.ToString() }),
|
||||
ReturnUrl = vm.ReturnUrl,
|
||||
BackUrl = vm.BackUrl
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{walletId}/vault")]
|
||||
[HttpPost("{walletId}/vault")]
|
||||
public IActionResult WalletSendVault([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
|
||||
WalletSendVaultModel model)
|
||||
{
|
||||
return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel() { SigningContext = model.SigningContext });
|
||||
return RedirectToWalletPSBTReady(new WalletPSBTReadyViewModel
|
||||
{
|
||||
SigningContext = model.SigningContext,
|
||||
ReturnUrl = model.ReturnUrl,
|
||||
BackUrl = model.BackUrl
|
||||
});
|
||||
}
|
||||
|
||||
private IActionResult RedirectToWalletPSBTReady(WalletPSBTReadyViewModel vm)
|
||||
|
@ -962,9 +982,13 @@ namespace BTCPayServer.Controllers
|
|||
redirectVm.FormParameters.Remove("command");
|
||||
redirectVm.FormParameters.Add("command", "broadcast");
|
||||
}
|
||||
if (this.HttpContext.Request.Query["returnUrl"].FirstOrDefault() is string returnUrl)
|
||||
if (vm.ReturnUrl != null)
|
||||
{
|
||||
redirectVm.RouteParameters.Add("returnUrl", returnUrl);
|
||||
redirectVm.FormParameters.Add("returnUrl", vm.ReturnUrl);
|
||||
}
|
||||
if (vm.BackUrl != null)
|
||||
{
|
||||
redirectVm.FormParameters.Add("backUrl", vm.BackUrl);
|
||||
}
|
||||
return View("PostRedirect", redirectVm);
|
||||
}
|
||||
|
@ -987,17 +1011,29 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
AspController = "UIWallets",
|
||||
AspAction = nameof(WalletPSBT),
|
||||
RouteParameters = { { "walletId", this.RouteData?.Values["walletId"]?.ToString() } },
|
||||
FormParameters = { { "psbt", vm.PSBT }, { "fileName", vm.FileName }, { "command", "decode" }, }
|
||||
RouteParameters = { { "walletId", RouteData.Values["walletId"]?.ToString() } },
|
||||
FormParameters =
|
||||
{
|
||||
{ "psbt", vm.PSBT },
|
||||
{ "fileName", vm.FileName },
|
||||
{ "backUrl", vm.BackUrl },
|
||||
{ "returnUrl", vm.ReturnUrl },
|
||||
{ "command", "decode" }
|
||||
}
|
||||
};
|
||||
return View("PostRedirect", redirectVm);
|
||||
}
|
||||
|
||||
[HttpGet("{walletId}/psbt/seed")]
|
||||
public IActionResult SignWithSeed([ModelBinder(typeof(WalletIdModelBinder))] WalletId walletId,
|
||||
SigningContextModel signingContext)
|
||||
SigningContextModel signingContext, string returnUrl, string backUrl)
|
||||
{
|
||||
return View(nameof(SignWithSeed), new SignWithSeedViewModel { SigningContext = signingContext });
|
||||
return View(nameof(SignWithSeed), new SignWithSeedViewModel
|
||||
{
|
||||
SigningContext = signingContext,
|
||||
ReturnUrl = returnUrl,
|
||||
BackUrl = backUrl
|
||||
});
|
||||
}
|
||||
|
||||
[HttpPost("{walletId}/psbt/seed")]
|
||||
|
@ -1082,7 +1118,9 @@ namespace BTCPayServer.Controllers
|
|||
{
|
||||
SigningKey = signingKey.GetWif(network.NBitcoinNetwork).ToString(),
|
||||
SigningKeyPath = rootedKeyPath?.ToString(),
|
||||
SigningContext = viewModel.SigningContext
|
||||
SigningContext = viewModel.SigningContext,
|
||||
ReturnUrl = viewModel.ReturnUrl,
|
||||
BackUrl = viewModel.BackUrl
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1225,12 +1263,14 @@ namespace BTCPayServer.Controllers
|
|||
i++;
|
||||
}
|
||||
|
||||
parameters.Add("returnUrl", Url.Action(nameof(WalletTransactions), new { walletId }));
|
||||
var backUrl = Url.Action(nameof(WalletTransactions), new { walletId });
|
||||
parameters.Add("returnUrl", backUrl);
|
||||
parameters.Add("backUrl", backUrl);
|
||||
return View("PostRedirect",
|
||||
new PostRedirectViewModel
|
||||
{
|
||||
AspController = "UIWallets",
|
||||
AspAction = nameof(UIWalletsController.WalletCPFP),
|
||||
AspAction = nameof(WalletCPFP),
|
||||
RouteParameters = { { "walletId", walletId.ToString() } },
|
||||
FormParameters = parameters
|
||||
});
|
||||
|
@ -1324,6 +1364,7 @@ namespace BTCPayServer.Controllers
|
|||
public string CryptoCode { get; set; }
|
||||
public string Address { get; set; }
|
||||
public string PaymentLink { get; set; }
|
||||
public string? ReturnUrl { get; set; }
|
||||
}
|
||||
|
||||
public class SendToAddressResult
|
||||
|
|
|
@ -14,6 +14,9 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
|
||||
[Display(Name = "Optional seed passphrase")]
|
||||
public string Passphrase { get; set; }
|
||||
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
public ExtKey GetExtKey(Network network)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
@ -12,6 +13,9 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
public string PSBT { get; set; }
|
||||
[Display(Name = "Upload PSBT from file...")]
|
||||
public IFormFile UploadedPSBTFile { get; set; }
|
||||
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
public PSBT GetSourcePSBT(Network network)
|
||||
{
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NBitcoin;
|
||||
|
@ -31,6 +32,8 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
public List<DestinationViewModel> Destinations { get; set; } = new List<DestinationViewModel>();
|
||||
public List<InputViewModel> Inputs { get; set; } = new List<InputViewModel>();
|
||||
public string FeeRate { get; set; }
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
internal void SetErrors(IList<PSBTError> errors)
|
||||
{
|
||||
|
|
|
@ -51,6 +51,7 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
return psbt;
|
||||
}
|
||||
public bool InvalidPSBT { get; set; }
|
||||
|
||||
async Task<PSBT> GetPSBTCore(Network network)
|
||||
{
|
||||
if (UploadedPSBTFile != null)
|
||||
|
|
|
@ -67,6 +67,9 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
|
||||
[Display(Name = "UTXOs to spend from")]
|
||||
public IEnumerable<string> SelectedInputs { get; set; }
|
||||
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
|
||||
public class InputSelectionOption
|
||||
{
|
||||
|
|
|
@ -4,6 +4,8 @@ namespace BTCPayServer.Models.WalletViewModels
|
|||
{
|
||||
public string WalletId { get; set; }
|
||||
public string WebsocketPath { get; set; }
|
||||
public SigningContextModel SigningContext { get; set; } = new SigningContextModel();
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
public SigningContextModel SigningContext { get; set; } = new ();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Models.WalletViewModels
|
||||
{
|
||||
public class WalletSigningOptionsModel
|
||||
{
|
||||
public WalletSigningOptionsModel(
|
||||
SigningContextModel signingContext,
|
||||
string returnUrl)
|
||||
{
|
||||
SigningContext = signingContext;
|
||||
ReturnUrl = returnUrl;
|
||||
}
|
||||
|
||||
public SigningContextModel SigningContext { get; }
|
||||
public string ReturnUrl { get; }
|
||||
public SigningContextModel SigningContext { get; set; }
|
||||
public string BackUrl { get; set; }
|
||||
public string ReturnUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<script id="VaultConnection" type="text/template">
|
||||
<div class="vault-feedback vault-feedback1 mb-2 d-flex">
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-1"></span>
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-grow"></span>
|
||||
</div>
|
||||
<div class="vault-feedback vault-feedback2 mb-2 d-flex">
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-1"></span>
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-grow"></span>
|
||||
</div>
|
||||
<div class="vault-feedback vault-feedback3 mb-2 d-flex">
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-1"></span>
|
||||
<span class="vault-feedback-icon mt-1 me-2"></span> <span class="vault-feedback-content flex-grow"></span>
|
||||
</div>
|
||||
<div id="pin-input" class="mt-4" style="display: none;">
|
||||
<div class="row">
|
||||
|
|
|
@ -19,5 +19,3 @@
|
|||
}
|
||||
|
||||
@RenderBody()
|
||||
|
||||
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
@using BTCPayServer.Controllers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model SignWithSeedViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign PSBT", walletId);
|
||||
var returnUrl = this.Context.Request.Query["returnUrl"].FirstOrDefault();
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
@if (returnUrl is string)
|
||||
{
|
||||
<a href="@returnUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
<a href="@returnUrl" class="cancel">
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
|
@ -37,8 +40,10 @@
|
|||
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
|
||||
<form method="post" asp-action="SignWithSeed" asp-route-walletId="@walletId" asp-route-returnUrl="@returnUrl">
|
||||
<form method="post" asp-action="SignWithSeed" asp-route-walletId="@walletId">
|
||||
<partial name="SigningContext" for="SigningContext"/>
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="form-group">
|
||||
<label asp-for="SeedOrKey" class="form-label"></label>
|
||||
<input asp-for="SeedOrKey" class="form-control" autocomplete="off" autocorrect="off" autocapitalize="off"/>
|
||||
|
|
|
@ -1,11 +1,29 @@
|
|||
@model WalletPSBTViewModel
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Controllers
|
||||
@using BTCPayServer.TagHelpers
|
||||
@using BundlerMinifier.TagHelpers
|
||||
@model WalletPSBTViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Decode PSBT", walletId);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
<link href="~/vendor/vue-qrcode-reader/vue-qrcode-reader.css" rel="stylesheet" asp-append-version="true"/>
|
||||
}
|
||||
|
@ -23,42 +41,43 @@
|
|||
</script>
|
||||
}
|
||||
|
||||
<h3 class="mb-3">@ViewData["Title"]</h3>
|
||||
<header class="text-center">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
<p class="lead text-secondary mt-3 mx-md-5">You can decode a PSBT by either pasting its content, uploading the file or scanning the wallet QR code.</p>
|
||||
</header>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
@if (Model.Errors != null && Model.Errors.Count != 0)
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
@foreach (var error in Model.Errors)
|
||||
{
|
||||
<span>@error</span>
|
||||
<br/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
<div class="my-5">
|
||||
@if (Model.Errors != null && Model.Errors.Count != 0)
|
||||
{
|
||||
<div class="alert alert-danger alert-dismissible" role="alert">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close">
|
||||
<vc:icon symbol="close" />
|
||||
</button>
|
||||
@foreach (var error in Model.Errors)
|
||||
{
|
||||
<span>@error</span>
|
||||
<br/>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
||||
<p>You can decode a PSBT by either pasting its content, uploading the file or scanning the wallet QR code.</p>
|
||||
<form class="form-group" method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label asp-for="PSBT" class="form-label"></label>
|
||||
<textarea class="form-control" rows="5" asp-for="PSBT"></textarea>
|
||||
<span asp-validation-for="PSBT" class="text-danger"></span>
|
||||
</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 mt-2" id="Decode">Decode PSBT</button>
|
||||
<button type="button" id="scanqrcode" class="btn btn-secondary only-for-js ms-3 mt-2" data-bs-toggle="modal" data-bs-target="#scanModal">Scan wallet QR with camera</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
<form class="form-group" method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" enctype="multipart/form-data">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="form-group">
|
||||
<label asp-for="PSBT" class="form-label"></label>
|
||||
<textarea class="form-control" rows="5" asp-for="PSBT"></textarea>
|
||||
<span asp-validation-for="PSBT" class="text-danger"></span>
|
||||
</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 mt-2" id="Decode">Decode PSBT</button>
|
||||
<button type="button" id="scanqrcode" class="btn btn-secondary only-for-js ms-3 mt-2" data-bs-toggle="modal" data-bs-target="#scanModal">Scan wallet QR with camera</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<partial name="ShowQR"/>
|
||||
<partial name="CameraScanner"/>
|
||||
|
|
|
@ -1,12 +1,22 @@
|
|||
@using BTCPayServer.Controllers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model WalletPSBTCombineViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, "Combine PSBT", walletId);
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
<a asp-action="WalletSend" asp-route-walletId="@Context.GetRouteValue("walletId")" class="cancel">
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
@ -17,6 +27,8 @@
|
|||
|
||||
<form class="form-group" method="post" asp-action="WalletPSBTCombine" asp-route-walletId="@Context.GetRouteValue("walletId")" enctype="multipart/form-data">
|
||||
<input type="hidden" asp-for="OtherPSBT"/>
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="form-group">
|
||||
<label asp-for="PSBT" class="form-label"></label>
|
||||
<textarea class="form-control" rows="5" asp-for="PSBT"></textarea>
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
@using BTCPayServer.Controllers
|
||||
@using BTCPayServer.TagHelpers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BundlerMinifier.TagHelpers
|
||||
@model WalletPSBTViewModel
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
var isReady = !Model.HasErrors;
|
||||
var isSignable = !isReady;
|
||||
var needsExport = !isSignable && !isReady;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.PSBT, isReady ? "Confirm broadcasting this transaction" : "Transaction Details", walletId);
|
||||
var returnUrl = this.Context.Request.Query["returnUrl"].FirstOrDefault();
|
||||
}
|
||||
|
||||
@section PageHeadContent {
|
||||
|
@ -38,12 +43,15 @@
|
|||
}
|
||||
|
||||
@section Navbar {
|
||||
@if (returnUrl is string)
|
||||
{
|
||||
<a href="@returnUrl" class="cancel">
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
||||
<header class="text-center mb-3">
|
||||
|
@ -59,17 +67,21 @@
|
|||
<input type="hidden" asp-for="NBXSeedAvailable"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
<input type="hidden" asp-for="FileName"/>
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="d-flex flex-column flex-sm-row flex-wrap justify-content-center align-items-sm-center">
|
||||
<button type="submit" id="SignTransaction" name="command" value="sign" asp-route-returnUrl="@returnUrl" class="btn btn-primary">Sign transaction</button>
|
||||
<button type="submit" id="SignTransaction" name="command" value="sign" class="btn btn-primary">Sign transaction</button>
|
||||
</div>
|
||||
</form>
|
||||
}
|
||||
else if (isReady)
|
||||
{
|
||||
<form method="post" asp-action="WalletPSBTReady" asp-route-walletId="@walletId" asp-route-returnUrl="@returnUrl" class="my-5">
|
||||
<form method="post" asp-action="WalletPSBTReady" asp-route-walletId="@walletId" class="my-5">
|
||||
<input type="hidden" asp-for="SigningKey" />
|
||||
<input type="hidden" asp-for="SigningKeyPath" />
|
||||
<partial name="SigningContext" for="SigningContext" />
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="d-flex flex-column flex-sm-row flex-wrap justify-content-center align-items-sm-center">
|
||||
@if (!string.IsNullOrEmpty(Model.SigningContext?.PayJoinBIP21))
|
||||
{
|
||||
|
@ -105,6 +117,8 @@ else
|
|||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" class="mb-2">
|
||||
<input type="hidden" asp-for="CryptoCode"/>
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="d-flex flex-column flex-sm-row flex-wrap align-items-sm-center">
|
||||
<button name="command" type="submit" class="btn btn-primary mb-3 mb-sm-0 me-sm-2" value="save-psbt">Download PSBT file</button>
|
||||
<button name="command" type="button" class="btn btn-primary mb-3 mb-sm-0 me-sm-2 only-for-js" data-bs-toggle="modal" data-bs-target="#scan-qr-modal">Show QR for wallet camera</button>
|
||||
|
@ -152,6 +166,8 @@ else
|
|||
<div id="PSBTOptionsImportContent" class="accordion-collapse collapse" aria-labelledby="PSBTOptionsImportHeader" data-bs-parent="#PSBTOptions">
|
||||
<div class="accordion-body">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" enctype="multipart/form-data" class="mb-2">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<div class="form-group">
|
||||
<label for="ImportedPSBT" class="form-label">PSBT content</label>
|
||||
<textarea id="ImportedPSBT" name="PSBT" class="form-control" rows="5"></textarea>
|
||||
|
@ -180,6 +196,8 @@ else
|
|||
<div class="accordion-body">
|
||||
<form method="post" asp-action="WalletPSBT" asp-route-walletId="@walletId" class="mb-2">
|
||||
<input type="hidden" asp-for="PSBT"/>
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<p class="mb-2">For exporting the signed PSBT and transaction information to a wallet, update the PSBT.</p>
|
||||
<button id="update-psbt" type="submit" name="command" value="update" class="btn btn-secondary">Update PSBT</button>
|
||||
<p class="mt-4 mb-2">For batching transactions, you can combine this PSBT with another one.</p>
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@inject BTCPayServer.Services.BTCPayServerEnvironment env
|
||||
@using BTCPayServer.Controllers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Components.QRCode
|
||||
@model BTCPayServer.Controllers.WalletReceiveViewModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
var returnUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Receive, $"Receive {Model.CryptoCode}", walletId);
|
||||
}
|
||||
|
||||
|
@ -12,88 +16,86 @@
|
|||
<link href="~/main/qrcode.css" rel="stylesheet" asp-append-version="true"/>
|
||||
}
|
||||
|
||||
<div class="row no-gutters">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<form method="post" asp-action="WalletReceive">
|
||||
@if (string.IsNullOrEmpty(Model.Address))
|
||||
@section Navbar {
|
||||
<a href="@returnUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
</header>
|
||||
|
||||
<form method="post" asp-action="WalletReceive" class="my-5">
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
@if (string.IsNullOrEmpty(Model.Address))
|
||||
{
|
||||
<div class="d-grid gap-3 col-sm-10 col-md-8 col-lg-7 col-xxl-6 mx-auto">
|
||||
<button id="generateButton" class="btn btn-primary" type="submit" name="command" value="generate-new-address">Generate next available @Model.CryptoCode address</button>
|
||||
@if (env.CheatMode)
|
||||
{
|
||||
<div class="d-flex flex-wrap gap-3">
|
||||
<button id="generateButton" class="btn btn-primary" type="submit" name="command" value="generate-new-address">Generate next available @Model.CryptoCode address</button>
|
||||
@if (env.CheatMode)
|
||||
{
|
||||
<button type="submit" name="command" value="fill-wallet" class="btn btn-info">Cheat Mode: Send transactions to this wallet</button>
|
||||
}
|
||||
</div>
|
||||
<button type="submit" name="command" value="fill-wallet" class="btn btn-info">Cheat Mode: Send transactions to this wallet</button>
|
||||
}
|
||||
else
|
||||
{
|
||||
<h3 class="mb-4">@Model.CryptoCode Address</h3>
|
||||
<noscript>
|
||||
<div class="m-sm-0 p-sm-0">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control " readonly="readonly" asp-for="Address" id="address"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" readonly="readonly" asp-for="PaymentLink" id="payment-link"/>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 col-sm-6">
|
||||
<button type="submit" name="command" value="generate-new-address" class="btn btn-primary w-100">Generate another address</button>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-4 mt-sm-0">
|
||||
<button type="submit" name="command" value="unreserve-current-address" class="btn btn-secondary w-100">Unreserve this address</button>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<noscript>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control " readonly="readonly" asp-for="Address" id="address"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" readonly="readonly" asp-for="PaymentLink" id="payment-link"/>
|
||||
</div>
|
||||
<div class="row mt-4">
|
||||
<div class="col-12 col-sm-6">
|
||||
<button type="submit" name="command" value="generate-new-address" class="btn btn-primary w-100">Generate another address</button>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-4 mt-sm-0">
|
||||
<button type="submit" name="command" value="unreserve-current-address" class="btn btn-secondary w-100">Unreserve this address</button>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
<div class="only-for-js col-sm-10 col-xxl-8 mx-auto" id="app">
|
||||
<div class="tab-content text-center">
|
||||
<div class="tab-pane" id="link-tab" role="tabpanel">
|
||||
<div class="qr-container mb-3">
|
||||
<img src="@Model.CryptoImage" class="qr-icon" alt="@Model.CryptoCode"/>
|
||||
<vc:qr-code data="@Model.PaymentLink"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group" data-clipboard="@Model.PaymentLink">
|
||||
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@Model.PaymentLink" id="payment-link"/>
|
||||
<button type="button" class="btn btn-outline-secondary p-2" style="width:7em;" data-clipboard-confirm>
|
||||
<vc:icon symbol="copy"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
<div class="only-for-js m-sm-0 p-sm-0" id="app">
|
||||
<div class="mb-5">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane" id="link-tab" role="tabpanel">
|
||||
<div class="qr-container mb-3">
|
||||
<img src="@Model.CryptoImage" class="qr-icon" alt="@Model.CryptoCode"/>
|
||||
<vc:qr-code data="@Model.PaymentLink"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group" data-clipboard="@Model.PaymentLink">
|
||||
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@Model.PaymentLink" id="payment-link"/>
|
||||
<button type="button" class="btn btn-outline-secondary p-2" style="width:7em;" data-clipboard-confirm>
|
||||
<vc:icon symbol="copy"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane show active" id="address-tab" role="tabpanel">
|
||||
<div class="qr-container mb-3">
|
||||
<img src="@Model.CryptoImage" class="qr-icon" alt="@Model.CryptoCode"/>
|
||||
<vc:qr-code data="@Model.Address"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group" data-clipboard="@Model.Address">
|
||||
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@Model.Address" id="address"/>
|
||||
<button type="button" class="input-group-text btn btn-outline-secondary p-2" style="width:7em;" data-clipboard-confirm>
|
||||
<vc:icon symbol="copy"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<a class="btcpay-pill active" data-bs-toggle="tab" href="#address-tab">Address</a>
|
||||
<a class="btcpay-pill " data-bs-toggle="tab" href="#link-tab">Link</a>
|
||||
</div>
|
||||
<div class="tab-pane show active" id="address-tab" role="tabpanel">
|
||||
<div class="qr-container mb-3">
|
||||
<img src="@Model.CryptoImage" class="qr-icon" alt="@Model.CryptoCode"/>
|
||||
<vc:qr-code data="@Model.Address"/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group" data-clipboard="@Model.Address">
|
||||
<input type="text" class="form-control" style="cursor:copy" readonly="readonly" value="@Model.Address" id="address"/>
|
||||
<button type="button" class="input-group-text btn btn-outline-secondary p-2" style="width:7em;" data-clipboard-confirm>
|
||||
<vc:icon symbol="copy"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-6">
|
||||
<button type="submit" name="command" value="generate-new-address" class="btn btn-primary w-100">Generate another address</button>
|
||||
</div>
|
||||
<div class="col-12 col-sm-6 mt-4 mt-sm-0">
|
||||
<button type="submit" name="command" value="unreserve-current-address" class="btn btn-secondary w-100">Unreserve this address</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="nav justify-content-center">
|
||||
<a class="btcpay-pill active" data-bs-toggle="tab" href="#address-tab">Address</a>
|
||||
<a class="btcpay-pill " data-bs-toggle="tab" href="#link-tab">Link</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid gap-3 col-sm-8 col-sm-6 col-lg-5 mx-auto mt-5">
|
||||
<button type="submit" name="command" value="generate-new-address" class="btn btn-primary w-100">Generate another address</button>
|
||||
<button type="submit" name="command" value="unreserve-current-address" class="btn btn-secondary w-100">Unreserve this address</button>
|
||||
</div>
|
||||
}
|
||||
</form>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePage(WalletsNavPages.Rescan, "Rescan Wallet", walletId);
|
||||
ViewData.SetActivePage(WalletsNavPages.Settings, "Rescan Wallet", walletId);
|
||||
}
|
||||
|
||||
<h3 class="mb-3">@ViewData["Title"]</h3>
|
||||
|
|
|
@ -1,14 +1,32 @@
|
|||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@inject BTCPayServer.Security.ContentSecurityPolicies csp
|
||||
@using Microsoft.AspNetCore.Mvc.ModelBinding
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.TagHelpers
|
||||
@using BundlerMinifier.TagHelpers
|
||||
@using BTCPayServer.Controllers
|
||||
@model WalletSendModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, $"Send {Model.CryptoCode}", walletId);
|
||||
csp.Add("worker-src", "blob:");
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
||||
@section PageHeadContent
|
||||
{
|
||||
<link href="~/vendor/vue-qrcode-reader/vue-qrcode-reader.css" rel="stylesheet" asp-append-version="true"/>
|
||||
|
@ -29,214 +47,219 @@
|
|||
|
||||
<partial name="CameraScanner"/>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain @(!Model.InputSelection && Model.Outputs.Count == 1 ? "transaction-output-form" : "")">
|
||||
<form method="post" asp-action="WalletSend" asp-route-walletId="@walletId">
|
||||
<input type="hidden" asp-for="InputSelection" />
|
||||
<input type="hidden" asp-for="FiatDivisibility" />
|
||||
<input type="hidden" asp-for="CryptoDivisibility" />
|
||||
<input type="hidden" asp-for="NBXSeedAvailable" />
|
||||
<input type="hidden" asp-for="Fiat" />
|
||||
<input type="hidden" asp-for="Rate" />
|
||||
<input type="hidden" asp-for="CurrentBalance" />
|
||||
<input type="hidden" asp-for="ImmatureBalance" />
|
||||
<input type="hidden" asp-for="CryptoCode" />
|
||||
<input type="hidden" name="BIP21" id="BIP21" />
|
||||
|
||||
<ul class="text-danger">
|
||||
@foreach (var errors in ViewData.ModelState.Where(pair => pair.Key == string.Empty && pair.Value.ValidationState == ModelValidationState.Invalid))
|
||||
{
|
||||
foreach (var error in errors.Value.Errors)
|
||||
{
|
||||
<li>@error.ErrorMessage</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
<header class="text-center">
|
||||
<h1>@ViewData["Title"]</h1>
|
||||
</header>
|
||||
|
||||
@if (Model.Outputs.Count == 1)
|
||||
<form method="post" asp-action="WalletSend" asp-route-walletId="@walletId" class="my-5">
|
||||
<input type="hidden" asp-for="InputSelection" />
|
||||
<input type="hidden" asp-for="FiatDivisibility" />
|
||||
<input type="hidden" asp-for="CryptoDivisibility" />
|
||||
<input type="hidden" asp-for="NBXSeedAvailable" />
|
||||
<input type="hidden" asp-for="Fiat" />
|
||||
<input type="hidden" asp-for="Rate" />
|
||||
<input type="hidden" asp-for="CurrentBalance" />
|
||||
<input type="hidden" asp-for="ImmatureBalance" />
|
||||
<input type="hidden" asp-for="CryptoCode" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" name="BIP21" id="BIP21" />
|
||||
|
||||
@if (!ViewContext.ModelState.IsValid)
|
||||
{
|
||||
<ul class="text-danger">
|
||||
@foreach (var errors in ViewData.ModelState.Where(pair => pair.Key == string.Empty && pair.Value.ValidationState == ModelValidationState.Invalid))
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[0].DestinationAddress" class="form-label"></label>
|
||||
<button type="submit" name="command" value="add-output" class="d-inline-block ms-2 btn text-primary btn-link p-0 mb-2">
|
||||
<span class="fa fa-plus"></span> Add another destination
|
||||
</button>
|
||||
</div>
|
||||
<input asp-for="Outputs[0].DestinationAddress" class="form-control font-monospace" autofocus autocomplete="off" />
|
||||
<span asp-validation-for="Outputs[0].DestinationAddress" class="text-danger"></span>
|
||||
foreach (var error in errors.Value.Errors)
|
||||
{
|
||||
<li>@error.ErrorMessage</li>
|
||||
}
|
||||
}
|
||||
</ul>
|
||||
}
|
||||
|
||||
@if (Model.Outputs.Count == 1)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[0].DestinationAddress" class="form-label"></label>
|
||||
<button type="submit" name="command" value="add-output" class="d-inline-block ms-2 btn text-primary btn-link p-0 mb-2">
|
||||
<span class="fa fa-plus"></span> Add another destination
|
||||
</button>
|
||||
</div>
|
||||
<input asp-for="Outputs[0].DestinationAddress" class="form-control font-monospace" autofocus autocomplete="off" />
|
||||
<span asp-validation-for="Outputs[0].DestinationAddress" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[0].Amount" class="form-label"></label>
|
||||
<button type="submit" name="command" value="toggle-input-selection" class="d-inline-block ms-2 btn text-primary btn-link p-0 mb-2" id="toggleInputSelection"><span class="fa fa-@(Model.InputSelection ? "eye-slash" : "eye") "></span> @(Model.InputSelection ? "Hide" : "Show") coin selection</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input asp-for="Outputs[0].Amount" type="number" inputmode="decimal" step="any" min="0" asp-format="{0}" class="form-control output-amount hide-number-spin" />
|
||||
<div class="input-group-text fiat-value" style="display:none;">
|
||||
<span class="input-group-text p-0 border-0">=</span>
|
||||
<input type="number" inputmode="decimal" class="input-group-text fiat-value-edit-input py-0 border-0 hide-number-spin" min="0" step="any" style="max-width:100px" />
|
||||
<span class="input-group-text p-0 border-0">@Model.Fiat</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[0].Amount" class="form-label"></label>
|
||||
<button type="submit" name="command" value="toggle-input-selection" class="d-inline-block ms-2 btn text-primary btn-link p-0 mb-2" id="toggleInputSelection"><span class="fa fa-@(Model.InputSelection ? "eye-slash" : "eye") "></span> @(Model.InputSelection ? "Hide" : "Show") coin selection</button>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<input asp-for="Outputs[0].Amount" type="number" inputmode="decimal" step="any" min="0" asp-format="{0}" class="form-control output-amount hide-number-spin" />
|
||||
<div class="input-group-text fiat-value" style="display:none;">
|
||||
<span class="input-group-text p-0 border-0">=</span>
|
||||
<input type="number" inputmode="decimal" class="input-group-text fiat-value-edit-input py-0 border-0 hide-number-spin" min="0" step="any" style="max-width:100px" />
|
||||
<span class="input-group-text p-0 border-0">@Model.Fiat</span>
|
||||
</div>
|
||||
<span asp-validation-for="Outputs[0].Amount" class="text-danger"></span>
|
||||
<p class="form-text text-secondary mb-0 crypto-info">
|
||||
Your available balance is
|
||||
<button type="button" class="crypto-balance-link btn btn-link p-0 align-baseline">@Model.CurrentBalance</button> <span>@Model.CryptoCode</span>.
|
||||
@if (Model.ImmatureBalance > 0)
|
||||
{
|
||||
<span><br><span class="text-warning">⚠</span> @Model.ImmatureBalance @Model.CryptoCode are still immature and require additional confirmations.</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group list-group-flush">
|
||||
@for (var index = 0; index < Model.Outputs.Count; index++)
|
||||
{
|
||||
<input type="hidden" asp-for="Outputs[index].PayoutId" />
|
||||
<div class="list-group-item d-block px-0 pt-0 pb-3 mb-3">
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[index].DestinationAddress" class="form-label"></label>
|
||||
<button type="submit" name="command" value="@($"remove-output:{index}")" class="d-inline-block ms-2 btn text-danger btn-link p-0 mb-2">
|
||||
<span class="fa fa-times"></span> Remove Destination
|
||||
</button>
|
||||
</div>
|
||||
<input asp-for="Outputs[index].DestinationAddress" class="form-control" autocomplete="off"/>
|
||||
<span asp-validation-for="Outputs[index].DestinationAddress" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Outputs[index].Amount" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="Outputs[index].Amount" type="number" min="0" step="any" asp-format="{0}" class="form-control output-amount hide-number-spin" />
|
||||
<div class="input-group-text fiat-value" style="display:none;">
|
||||
<span class="input-group-text p-0 border-0">=</span>
|
||||
<input type="number" inputmode="decimal" class="input-group-text fiat-value-edit-input py-0 border-0 hide-number-spin" min="0" step="any" style="max-width:100px" />
|
||||
<span class="input-group-text p-0 border-0">@Model.Fiat</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="form-text text-secondary crypto-info mb-2">
|
||||
Your available balance is
|
||||
<button type="button" class="crypto-balance-link btn btn-link p-0 align-baseline">@Model.CurrentBalance</button> <span>@Model.CryptoCode</span>.
|
||||
@if (Model.ImmatureBalance > 0)
|
||||
{
|
||||
<span><br>Note: @Model.ImmatureBalance @Model.CryptoCode are still immature and require additional confirmations.</span>
|
||||
}
|
||||
</p>
|
||||
<span asp-validation-for="Outputs[index].Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" asp-for="Outputs[index].SubtractFeesFromOutput" class="form-check-input subtract-fees" />
|
||||
<label asp-for="Outputs[index].SubtractFeesFromOutput" class="form-check-label"></label>
|
||||
<span asp-validation-for="Outputs[index].SubtractFeesFromOutput" class="text-danger"></span>
|
||||
</div>
|
||||
<span asp-validation-for="Outputs[0].Amount" class="text-danger"></span>
|
||||
<p class="form-text text-secondary mb-0 crypto-info">
|
||||
Your available balance is
|
||||
<button type="button" class="crypto-balance-link btn btn-link p-0 align-baseline">@Model.CurrentBalance</button> <span>@Model.CryptoCode</span>.
|
||||
@if (Model.ImmatureBalance > 0)
|
||||
{
|
||||
<span><br><span class="text-warning">⚠</span> @Model.ImmatureBalance @Model.CryptoCode are still immature and require additional confirmations.</span>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="list-group list-group-flush">
|
||||
@for (var index = 0; index < Model.Outputs.Count; index++)
|
||||
</div>
|
||||
<div class="d-grid gap-3 d-md-block mt-n2">
|
||||
<button type="submit" name="command" value="add-output" class="btn btn-secondary me-md-1"><span class="fa fa-plus"></span> Add another destination</button>
|
||||
<button type="submit" name="command" value="toggle-input-selection" class="btn btn-secondary" id="toggleInputSelection"><span class="fa fa-@(Model.InputSelection ? "eye-slash" : "eye") "></span> @(Model.InputSelection ? "Hide" : "Show") coin selection</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.InputSelection)
|
||||
{
|
||||
<partial name="CoinSelection" />
|
||||
}
|
||||
|
||||
<div class="form-group my-4">
|
||||
<label asp-for="FeeSatoshiPerByte" class="form-label"></label>
|
||||
<input asp-for="FeeSatoshiPerByte" type="number" inputmode="numeric" min="0" step="any" class="form-control" style="max-width:14ch;" />
|
||||
<span asp-validation-for="FeeSatoshiPerByte" class="text-danger"></span>
|
||||
<span id="FeeRate-Error" class="text-danger"></span>
|
||||
@if (Model.RecommendedSatoshiPerByte.Any())
|
||||
{
|
||||
<div class="text-start mt-4 d-flex align-items-sm-center flex-column flex-sm-row">
|
||||
<span class="text-secondary me-3">
|
||||
Confirm in the next
|
||||
</span>
|
||||
<div class="btn-group btn-group-toggle feerate-options mt-2 mt-sm-0" role="group" data-bs-toggle="buttons">
|
||||
@for (var index = 0; index < Model.RecommendedSatoshiPerByte.Count; index++)
|
||||
{
|
||||
<input type="hidden" asp-for="Outputs[index].PayoutId" />
|
||||
<div class="list-group-item transaction-output-form px-0 pt-0 pb-3 mb-3">
|
||||
<div class="form-group">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<label asp-for="Outputs[index].DestinationAddress" class="form-label"></label>
|
||||
<button type="submit" name="command" value="@($"remove-output:{index}")" class="d-inline-block ms-2 btn text-danger btn-link p-0 mb-2">
|
||||
<span class="fa fa-times"></span> Remove Destination
|
||||
</button>
|
||||
</div>
|
||||
<input asp-for="Outputs[index].DestinationAddress" class="form-control" autocomplete="off"/>
|
||||
<span asp-validation-for="Outputs[index].DestinationAddress" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Outputs[index].Amount" class="form-label"></label>
|
||||
<div class="input-group">
|
||||
<input asp-for="Outputs[index].Amount" type="number" min="0" step="any" asp-format="{0}" class="form-control output-amount hide-number-spin" />
|
||||
<div class="input-group-text fiat-value" style="display:none;">
|
||||
<span class="input-group-text p-0 border-0">=</span>
|
||||
<input type="number" inputmode="decimal" class="input-group-text fiat-value-edit-input py-0 border-0 hide-number-spin" min="0" step="any" style="max-width:100px" />
|
||||
<span class="input-group-text p-0 border-0">@Model.Fiat</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="form-text text-secondary crypto-info mb-2">
|
||||
Your available balance is
|
||||
<button type="button" class="crypto-balance-link btn btn-link p-0 align-baseline">@Model.CurrentBalance</button> <span>@Model.CryptoCode</span>.
|
||||
@if (Model.ImmatureBalance > 0)
|
||||
{
|
||||
<span><br>Note: @Model.ImmatureBalance @Model.CryptoCode are still immature and require additional confirmations.</span>
|
||||
}
|
||||
</p>
|
||||
<span asp-validation-for="Outputs[index].Amount" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input type="checkbox" asp-for="Outputs[index].SubtractFeesFromOutput" class="form-check-input subtract-fees" />
|
||||
<label asp-for="Outputs[index].SubtractFeesFromOutput" class="form-check-label"></label>
|
||||
<span asp-validation-for="Outputs[index].SubtractFeesFromOutput" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
var feeRateOption = Model.RecommendedSatoshiPerByte[index];
|
||||
<button type="button" class="btn btn-sm btn-secondary crypto-fee-link" value="@feeRateOption.FeeRate" data-bs-toggle="tooltip" title="@feeRateOption.FeeRate sat/b">
|
||||
@feeRateOption.Target.TimeString()
|
||||
</button>
|
||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].Target" />
|
||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].FeeRate" />
|
||||
}
|
||||
</div>
|
||||
<div class="d-grid gap-3 d-md-block mt-n2">
|
||||
<button type="submit" name="command" value="add-output" class="btn btn-secondary me-md-1"><span class="fa fa-plus"></span> Add another destination</button>
|
||||
<button type="submit" name="command" value="toggle-input-selection" class="btn btn-secondary" id="toggleInputSelection"><span class="fa fa-@(Model.InputSelection ? "eye-slash" : "eye") "></span> @(Model.InputSelection ? "Hide" : "Show") coin selection</button>
|
||||
</div>
|
||||
}
|
||||
|
||||
@if (Model.InputSelection)
|
||||
{
|
||||
<partial name="CoinSelection" />
|
||||
}
|
||||
|
||||
<div class="form-group my-4">
|
||||
<label asp-for="FeeSatoshiPerByte" class="form-label"></label>
|
||||
<input asp-for="FeeSatoshiPerByte" type="number" inputmode="numeric" min="0" step="any" class="form-control" style="max-width:14ch;" />
|
||||
<span asp-validation-for="FeeSatoshiPerByte" class="text-danger"></span>
|
||||
<span id="FeeRate-Error" class="text-danger"></span>
|
||||
@if (Model.RecommendedSatoshiPerByte.Any())
|
||||
{
|
||||
<div class="text-start mt-4 d-flex align-items-sm-center flex-column flex-sm-row">
|
||||
<span class="text-secondary me-3">
|
||||
Confirm in the next
|
||||
</span>
|
||||
<div class="btn-group btn-group-toggle feerate-options mt-2 mt-sm-0" role="group" data-bs-toggle="buttons">
|
||||
@for (var index = 0; index < Model.RecommendedSatoshiPerByte.Count; index++)
|
||||
{
|
||||
var feeRateOption = Model.RecommendedSatoshiPerByte[index];
|
||||
<button type="button" class="btn btn-sm btn-secondary crypto-fee-link" value="@feeRateOption.FeeRate" data-bs-toggle="tooltip" title="@feeRateOption.FeeRate sat/b">
|
||||
@feeRateOption.Target.TimeString()
|
||||
</button>
|
||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].Target" />
|
||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte[index].FeeRate" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@if (Model.Outputs.Count == 1)
|
||||
{
|
||||
}
|
||||
</div>
|
||||
@if (Model.Outputs.Count == 1)
|
||||
{
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" asp-for="Outputs[0].SubtractFeesFromOutput" class="form-check-input subtract-fees" />
|
||||
<label asp-for="Outputs[0].SubtractFeesFromOutput" class="form-check-label"></label>
|
||||
<span asp-validation-for="Outputs[0].SubtractFeesFromOutput" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="my-4">
|
||||
<button class="btn btn-link text-primary p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
<div id="AdvancedSettings" class="collapse">
|
||||
<div class="pt-3 pb-1">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input type="checkbox" asp-for="Outputs[0].SubtractFeesFromOutput" class="form-check-input subtract-fees" />
|
||||
<label asp-for="Outputs[0].SubtractFeesFromOutput" class="form-check-label"></label>
|
||||
<span asp-validation-for="Outputs[0].SubtractFeesFromOutput" class="text-danger"></span>
|
||||
<input asp-for="NoChange" class="form-check-input" />
|
||||
<label asp-for="NoChange" class="form-check-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/#dont-create-utxo-change" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="my-4">
|
||||
<button class="btn btn-link text-primary p-0" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
|
||||
Advanced settings
|
||||
</button>
|
||||
<div id="AdvancedSettings" class="collapse">
|
||||
<div class="pt-3 pb-1">
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="NoChange" class="form-check-input" />
|
||||
<label asp-for="NoChange" class="form-check-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/#dont-create-utxo-change" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="AlwaysIncludeNonWitnessUTXO" class="form-check-input"/>
|
||||
<label asp-for="AlwaysIncludeNonWitnessUTXO" class="form-check-label"></label>
|
||||
<a href="https://medium.com/@@jmacato/wasabi-wallets-advisory-for-trezor-users-7d942c727f92" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.SupportRBF)
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="AllowFeeBump" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/#rbf-replace-by-fee" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="AllowFeeBump" class="form-select w-auto">
|
||||
<option value="Maybe">Randomize for higher privacy</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.PayJoinBIP21))
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="PayJoinBIP21" class="form-label"></label>
|
||||
<input asp-for="PayJoinBIP21" class="form-control" />
|
||||
<span asp-validation-for="PayJoinBIP21" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check">
|
||||
<input asp-for="AlwaysIncludeNonWitnessUTXO" class="form-check-input"/>
|
||||
<label asp-for="AlwaysIncludeNonWitnessUTXO" class="form-check-label"></label>
|
||||
<a href="https://medium.com/@@jmacato/wasabi-wallets-advisory-for-trezor-users-7d942c727f92" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@if (Model.SupportRBF)
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="AllowFeeBump" class="form-label"></label>
|
||||
<a href="https://docs.btcpayserver.org/Wallet/#rbf-replace-by-fee" target="_blank" rel="noreferrer noopener">
|
||||
<span class="fa fa-question-circle-o text-secondary" title="More information..."></span>
|
||||
</a>
|
||||
<select asp-for="AllowFeeBump" class="form-select w-auto">
|
||||
<option value="Maybe">Randomize for higher privacy</option>
|
||||
<option value="Yes">Yes</option>
|
||||
<option value="No">No</option>
|
||||
</select>
|
||||
</div>
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.PayJoinBIP21))
|
||||
{
|
||||
<div class="form-group">
|
||||
<label asp-for="PayJoinBIP21" class="form-label"></label>
|
||||
<input asp-for="PayJoinBIP21" class="form-control" />
|
||||
<span asp-validation-for="PayJoinBIP21" class="text-danger"></span>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="form-group d-flex gap-3 mt-2">
|
||||
<button type="submit" id="SignTransaction" name="command" value="sign" class="btn btn-primary">Sign transaction</button>
|
||||
<button type="submit" id="ScheduleTransaction" name="command" value="schedule" class="btn btn-secondary">Schedule transaction</button>
|
||||
<a class="btn btn-secondary" asp-controller="UIWallets" asp-action="WalletPSBT" asp-route-walletId="@walletId" id="PSBT">PSBT</a>
|
||||
<button type="button" id="bip21parse" class="btn btn-secondary" title="Paste BIP21/Address"><i class="fa fa-paste"></i></button>
|
||||
<button type="button" id="scanqrcode" class="btn btn-secondary only-for-js" data-bs-toggle="modal" data-bs-target="#scanModal" title="Scan BIP21/Address with camera"><i class="fa fa-camera"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group d-grid d-sm-flex flex-wrap gap-3 mt-2">
|
||||
<button type="submit" id="SignTransaction" name="command" value="sign" class="btn btn-primary">Sign transaction</button>
|
||||
<button type="submit" id="ScheduleTransaction" name="command" value="schedule" class="btn btn-secondary">Schedule transaction</button>
|
||||
<a class="btn btn-secondary" asp-controller="UIWallets" asp-action="WalletPSBT" asp-route-walletId="@walletId" asp-route-returnUrl="@Model.ReturnUrl" id="PSBT">PSBT</a>
|
||||
<button type="button" id="bip21parse" class="btn btn-secondary" title="Paste BIP21/Address"><i class="fa fa-paste"></i></button>
|
||||
<button type="button" id="scanqrcode" class="btn btn-secondary only-for-js" data-bs-toggle="modal" data-bs-target="#scanModal" title="Scan BIP21/Address with camera"><i class="fa fa-camera"></i></button>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
@using BTCPayServer.Controllers
|
||||
@using BTCPayServer.TagHelpers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model WalletSendVaultModel
|
||||
@{
|
||||
var walletId = Context.GetRouteValue("walletId").ToString();
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", walletId);
|
||||
var returnUrl = this.Context.Request.Query["returnUrl"].FirstOrDefault();
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
@if (returnUrl is string)
|
||||
{
|
||||
<a href="@returnUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
<a href="@returnUrl" class="cancel">
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
}
|
||||
|
||||
<header class="text-center">
|
||||
|
@ -31,9 +35,11 @@
|
|||
</div>
|
||||
|
||||
<div id="body" class="my-4">
|
||||
<form id="broadcastForm" asp-action="WalletSendVault" asp-route-walletId="@walletId" asp-route-returnUrl="@returnUrl" method="post" style="display:none;">
|
||||
<form id="broadcastForm" asp-action="WalletSendVault" asp-route-walletId="@walletId" method="post" style="display:none;">
|
||||
<input type="hidden" id="WalletId" asp-for="WalletId" />
|
||||
<input type="hidden" asp-for="WebsocketPath" />
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
<partial name="SigningContext" for="SigningContext" />
|
||||
</form>
|
||||
<div id="vaultPlaceholder"></div>
|
||||
|
|
|
@ -1,17 +1,24 @@
|
|||
@using BTCPayServer.Controllers
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@model WalletSigningOptionsModel
|
||||
@inject BTCPayNetworkProvider BTCPayNetworkProvider
|
||||
@addTagHelper *, BundlerMinifier.TagHelpers
|
||||
@{
|
||||
var walletId = WalletId.Parse(Context.GetRouteValue("walletId").ToString());
|
||||
var cancelUrl = Model.ReturnUrl ?? Url.Action(nameof(UIWalletsController.WalletTransactions), new { walletId });
|
||||
var backUrl = Model.BackUrl != null ? $"{Model.BackUrl}?returnUrl={Model.ReturnUrl}" : null;
|
||||
Layout = "_LayoutWizard";
|
||||
ViewData.SetActivePage(WalletsNavPages.Send, "Sign the transaction", walletId.ToString());
|
||||
}
|
||||
|
||||
@section Navbar {
|
||||
<a href="@Model.ReturnUrl">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
<a href="@Model.ReturnUrl" class="cancel">
|
||||
@if (backUrl != null)
|
||||
{
|
||||
<a href="@backUrl" id="GoBack">
|
||||
<vc:icon symbol="back" />
|
||||
</a>
|
||||
}
|
||||
<a href="@cancelUrl" id="CancelWizard" class="cancel">
|
||||
<vc:icon symbol="close" />
|
||||
</a>
|
||||
}
|
||||
|
@ -21,8 +28,10 @@
|
|||
<p class="lead text-secondary mt-3">You can sign the transaction using one of the following methods.</p>
|
||||
</header>
|
||||
|
||||
<form method="post" asp-action="WalletSign" asp-route-walletId="@walletId" asp-route-returnUrl="@Model.ReturnUrl">
|
||||
<form method="post" asp-action="WalletSign" asp-route-walletId="@walletId">
|
||||
<partial name="SigningContext" for="SigningContext" />
|
||||
<input type="hidden" asp-for="ReturnUrl" />
|
||||
<input type="hidden" asp-for="BackUrl" />
|
||||
|
||||
@if (BTCPayNetworkProvider.GetNetwork<BTCPayNetwork>(walletId.CryptoCode).VaultSupported)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,6 @@ namespace BTCPayServer.Views.Wallets
|
|||
Index,
|
||||
Send,
|
||||
Transactions,
|
||||
Rescan,
|
||||
PSBT,
|
||||
Receive,
|
||||
Settings
|
||||
|
|
Loading…
Add table
Reference in a new issue