update breez

This commit is contained in:
Kukks
2024-03-21 15:52:36 +01:00
parent 1c64038245
commit efd72fdae9
15 changed files with 280 additions and 94 deletions

View File

@@ -8,7 +8,7 @@
<!-- Plugin specific properties -->
<PropertyGroup>
<Product>Breez / Greenlight</Product>
<Description>Lightwight lightning baby!</Description>
<Description>Lightweight lightning baby!</Description>
<Version>1.0.0</Version>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
@@ -34,7 +34,7 @@
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Breez.Sdk" Version="0.2.10" />
<PackageReference Include="Breez.Sdk" Version="0.3.6" />
</ItemGroup>
</Project>

View File

@@ -1,12 +1,18 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Threading.Tasks;
using Breez.Sdk;
using BTCPayServer.Abstractions.Constants;
using BTCPayServer.Client;
using BTCPayServer.Data;
using BTCPayServer.Lightning;
using BTCPayServer.Models;
using BTCPayServer.Payments;
using BTCPayServer.Payments.Lightning;
using BTCPayServer.Services.Stores;
using BTCPayServer.Services.Wallets;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
@@ -17,21 +23,22 @@ using NBXplorer.DerivationStrategy;
namespace BTCPayServer.Plugins.Breez;
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Route("plugins/{storeId}/Breez")]
public class BreezController : Controller
{
private readonly BTCPayNetworkProvider _btcPayNetworkProvider;
private readonly BreezService _breezService;
private readonly BTCPayWalletProvider _btcWalletProvider;
private readonly StoreRepository _storeRepository;
public BreezController(BTCPayNetworkProvider btcPayNetworkProvider,
BreezService breezService,
BTCPayWalletProvider btcWalletProvider)
BTCPayWalletProvider btcWalletProvider, StoreRepository storeRepository)
{
_btcPayNetworkProvider = btcPayNetworkProvider;
_breezService = breezService;
_btcWalletProvider = btcWalletProvider;
_storeRepository = storeRepository;
}
@@ -43,6 +50,7 @@ public class BreezController : Controller
}
[HttpGet("swapin")]
[Authorize(Policy = Policies.CanCreateInvoice, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SwapIn(string storeId)
{
var client = _breezService.GetClient(storeId);
@@ -55,6 +63,7 @@ public class BreezController : Controller
}
[HttpGet("info")]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Info(string storeId)
{
var client = _breezService.GetClient(storeId);
@@ -67,6 +76,7 @@ public class BreezController : Controller
}
[HttpGet("sweep")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Sweep(string storeId)
{
var client = _breezService.GetClient(storeId);
@@ -79,6 +89,7 @@ public class BreezController : Controller
}
[HttpPost("sweep")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Sweep(string storeId, string address, uint satPerByte)
{
var client = _breezService.GetClient(storeId);
@@ -98,7 +109,7 @@ public class BreezController : Controller
try
{
var response = client.Sdk.Sweep(new SweepRequest(address, satPerByte));
var response = client.Sdk.RedeemOnchainFunds(new RedeemOnchainFundsRequest(address, satPerByte));
TempData[WellKnownTempData.SuccessMessage] = $"sweep successful: {response.txid}";
}
@@ -112,6 +123,7 @@ public class BreezController : Controller
}
[HttpGet("send")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Send(string storeId)
{
var client = _breezService.GetClient(storeId);
@@ -122,6 +134,7 @@ public class BreezController : Controller
return View((object) storeId);
}
[Authorize(Policy = Policies.CanCreateInvoice, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[Route("receive")]
public async Task<IActionResult> Receive(string storeId, ulong? amount)
{
@@ -130,11 +143,20 @@ public class BreezController : Controller
{
return RedirectToAction(nameof(Configure), new {storeId});
}
if (amount is not null)
try
{
var invoice = await client.CreateInvoice(LightMoney.FromUnit(amount.Value, LightMoneyUnit.Satoshi).MilliSatoshi, null, TimeSpan.Zero);
TempData["bolt11"] = invoice.BOLT11;
return RedirectToAction("Payments", "Breez", new {storeId });
if (amount is not null)
{
var invoice = await client.CreateInvoice(LightMoney.FromUnit(amount.Value, LightMoneyUnit.Satoshi).MilliSatoshi, null, TimeSpan.Zero);
TempData["bolt11"] = invoice.BOLT11;
return RedirectToAction("Payments", "Breez", new {storeId });
}
}
catch (Exception e)
{
TempData[WellKnownTempData.ErrorMessage] = $"{e.Message}";
}
@@ -142,6 +164,7 @@ public class BreezController : Controller
}
[HttpPost("send")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Send(string storeId, string address, ulong? amount)
{
var client = _breezService.GetClient(storeId);
@@ -203,6 +226,7 @@ public class BreezController : Controller
[HttpGet("swapout")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SwapOut(string storeId)
{
var client = _breezService.GetClient(storeId);
@@ -215,6 +239,7 @@ public class BreezController : Controller
}
[HttpPost("swapout")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SwapOut(string storeId, string address, ulong amount, uint satPerByte,
string feesHash)
{
@@ -247,6 +272,7 @@ public class BreezController : Controller
}
[HttpGet("swapin/{address}/refund")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SwapInRefund(string storeId, string address)
{
var client = _breezService.GetClient(storeId);
@@ -259,6 +285,7 @@ public class BreezController : Controller
}
[HttpPost("swapin/{address}/refund")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> SwapInRefund(string storeId, string address, string refundAddress, uint satPerByte)
{
var client = _breezService.GetClient(storeId);
@@ -280,27 +307,100 @@ public class BreezController : Controller
return RedirectToAction(nameof(SwapIn), new {storeId});
}
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
[HttpGet("configure")]
public async Task<IActionResult> Configure(string storeId)
{
return View(await _breezService.Get(storeId));
}
private static async Task<byte[]> ReadAsByteArrayAsync( Stream source)
{
// Optimization
if (source is MemoryStream memorySource)
return memorySource.ToArray();
using var memoryStream = new MemoryStream();
await source.CopyToAsync(memoryStream);
return memoryStream.ToArray();
}
[HttpPost("configure")]
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Configure(string storeId, string command, BreezSettings settings)
{
var store = HttpContext.GetStoreData();
var existing = store.GetSupportedPaymentMethods(_btcPayNetworkProvider).OfType<LightningSupportedPaymentMethod>()
.FirstOrDefault(method =>
method.PaymentId.PaymentType == LightningPaymentType.Instance &&
method.PaymentId.CryptoCode == "BTC");
if (command == "clear")
{
await _breezService.Set(storeId, null);
TempData[WellKnownTempData.SuccessMessage] = "Settings cleared successfully";
var client = _breezService.GetClient(storeId);
var isStoreSetToThisMicro = existing?.GetExternalLightningUrl() == client?.ToString();
if (client is not null && isStoreSetToThisMicro)
{
store.SetSupportedPaymentMethod(existing.PaymentId, null);
await _storeRepository.UpdateStore(store);
}
return RedirectToAction(nameof(Configure), new {storeId});
}
if (command == "save")
{
try
{
await _breezService.Set(storeId, settings);
if (string.IsNullOrEmpty(settings.Mnemonic))
{
ModelState.AddModelError(nameof(settings.Mnemonic), "Mnemonic is required");
return View(settings);
}
else
{
try
{
new Mnemonic(settings.Mnemonic);
}
catch (Exception e)
{
ModelState.AddModelError(nameof(settings.Mnemonic), "Invalid mnemonic");
return View(settings);
}
}
if (settings.GreenlightCredentials is not null)
{
await using var stream = settings.GreenlightCredentials .OpenReadStream();
using var archive = new ZipArchive(stream);
var deviceClientArchiveEntry = archive.GetEntry("client.crt");
var deviceKeyArchiveEntry = archive.GetEntry("client-key.pem");
if(deviceClientArchiveEntry is null || deviceKeyArchiveEntry is null)
{
ModelState.AddModelError(nameof(settings.GreenlightCredentials), "Invalid zip file (does not have client.crt or client-key.pem)");
return View(settings);
}
else
{
var deviceClient = await ReadAsByteArrayAsync(deviceClientArchiveEntry.Open());
var deviceKey = await ReadAsByteArrayAsync(deviceKeyArchiveEntry.Open());
var dir = _breezService.GetWorkDir(storeId);
Directory.CreateDirectory(dir);
await System.IO.File.WriteAllBytesAsync(Path.Combine(dir, "client.crt"), deviceClient);
await System.IO.File.WriteAllBytesAsync(Path.Combine(dir, "client-key.pem"), deviceKey);
await _breezService.Set(storeId, settings);
}
}
else
{
await _breezService.Set(storeId, settings);
}
}
catch (Exception e)
{
@@ -308,14 +408,27 @@ public class BreezController : Controller
return View(settings);
}
if(existing is null)
{
existing = new LightningSupportedPaymentMethod()
{
CryptoCode = "BTC"
};
var client = _breezService.GetClient(storeId);
existing.SetLightningUrl(client);
store.SetSupportedPaymentMethod(existing);
await _storeRepository.UpdateStore(store);
}
TempData[WellKnownTempData.SuccessMessage] = "Settings saved successfully";
return RedirectToAction(nameof(Configure), new {storeId});
return RedirectToAction(nameof(Info), new {storeId});
}
return NotFound();
}
[Route("payments")]
[Authorize(Policy = Policies.CanViewStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
public async Task<IActionResult> Payments(string storeId, PaymentsViewModel viewModel)
{
var client = _breezService.GetClient(storeId);
@@ -326,7 +439,7 @@ public class BreezController : Controller
viewModel ??= new PaymentsViewModel();
viewModel.Payments = client.Sdk.ListPayments(new ListPaymentsRequest(null, null, null, true,
viewModel.Payments = client.Sdk.ListPayments(new ListPaymentsRequest(null, null, null,null,true,
(uint?) viewModel.Skip, (uint?) viewModel.Count));
return View(viewModel);

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -22,12 +23,21 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
public readonly string PaymentKey;
public BreezLightningClient(string inviteCode, string apiKey, string workingDir, NBitcoin.Network network,
string mnemonic, string paymentKey)
Mnemonic mnemonic, string paymentKey)
{
apiKey??= "99010c6f84541bf582899db6728f6098ba98ca95ea569f4c63f2c2c9205ace57";
_network = network;
PaymentKey = paymentKey;
GreenlightCredentials glCreds = null;
if (File.Exists(Path.Combine(workingDir, "client.crt")) && File.Exists(Path.Combine(workingDir, "client-key.pem")))
{
var deviceCert = File.ReadAllBytes(Path.Combine(workingDir, "client.crt"));
var deviceKey = File.ReadAllBytes(Path.Combine(workingDir, "client-key.pem"));
glCreds = new GreenlightCredentials(deviceKey.ToList(), deviceCert.ToList());
}
var nodeConfig = new NodeConfig.Greenlight(
new GreenlightNodeConfig(null, inviteCode)
new GreenlightNodeConfig(glCreds, inviteCode)
);
var config = BreezSdkMethods.DefaultConfig(
network == NBitcoin.Network.Main ? EnvironmentType.PRODUCTION : EnvironmentType.STAGING,
@@ -40,8 +50,8 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
network == NBitcoin.Network.TestNet ? Network.TESTNET :
network == NBitcoin.Network.RegTest ? Network.REGTEST : Network.SIGNET
};
var seed = BreezSdkMethods.MnemonicToSeed(mnemonic);
Sdk = BreezSdkMethods.Connect(config, seed, this);
var seed = mnemonic.DeriveSeed();
Sdk = BreezSdkMethods.Connect(config, seed.ToList(), this);
}
public BlockingBreezServices Sdk { get; }
@@ -137,7 +147,7 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
CancellationToken cancellation = default)
{
return Sdk.ListPayments(new ListPaymentsRequest(new List<PaymentTypeFilter>(){PaymentTypeFilter.RECEIVED}, null, null,
request?.PendingOnly is not true, (uint?) request?.OffsetIndex, null))
null, request?.PendingOnly is not true, (uint?) request?.OffsetIndex, null))
.Select(FromPayment).ToArray();
}
@@ -155,7 +165,7 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
CancellationToken cancellation = default)
{
return Sdk.ListPayments(new ListPaymentsRequest(new List<PaymentTypeFilter>(){PaymentTypeFilter.RECEIVED}, null, null, null,
(uint?) request?.OffsetIndex, null))
null, (uint?) request?.OffsetIndex, null))
.Select(ToLightningPayment).ToArray();
}
@@ -164,6 +174,7 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
CancellationToken cancellation = default)
{
var expiryS = expiry == TimeSpan.Zero ? (uint?) null : Math.Max(0, (uint) expiry.TotalSeconds);
description??= "Invoice";
var p = Sdk.ReceivePayment(new ReceivePaymentRequest((ulong) amount.MilliSatoshi, description, null, null,
false, expiryS));
return FromPR(p);
@@ -207,10 +218,6 @@ public class BreezLightningClient : ILightningClient, IDisposable, EventListener
{
PeersCount = ni.connectedPeers.Count,
Alias = $"greenlight {ni.id}",
NodeInfoList =
{
new NodeInfo(new PubKey(ni.id), "blockstrean.com", 69)
}, //we have to fake this as btcpay currently requires this to enable the payment method
BlockHeight = (int) ni.blockHeight
};
}

View File

@@ -20,6 +20,7 @@ public class BreezLightningConnectionStringHandler : ILightningConnectionStringH
return null;
}
if (!kv.TryGetValue("key", out var key))
{
error = $"The key 'key' is mandatory for breez connection strings";

View File

@@ -56,10 +56,10 @@ public class BreezService:EventHostedServiceBase
await base.ProcessEvent(evt, cancellationToken);
}
private string GetWorkDir()
public string GetWorkDir(string storeId)
{
var dir = _dataDirectories.Value.DataDir;
return Path.Combine(dir, "Plugins", "Breez");
return Path.Combine(dir, "Plugins", "Breez",storeId);
}
TaskCompletionSource tcs = new();
@@ -103,9 +103,11 @@ public class BreezService:EventHostedServiceBase
try
{
var network = Network.Main; // _btcPayNetworkProvider.BTC.NBitcoinNetwork;
Directory.CreateDirectory(GetWorkDir());
var client = new BreezLightningClient(settings.InviteCode, settings.ApiKey, GetWorkDir(),
network, settings.Mnemonic, settings.PaymentKey);
var dir = GetWorkDir(storeId);
Directory.CreateDirectory(dir);
settings.PaymentKey ??= Guid.NewGuid().ToString();
var client = new BreezLightningClient(settings.InviteCode, settings.ApiKey, dir,
network, new Mnemonic(settings.Mnemonic), settings.PaymentKey);
if (storeId is not null)
{
_clients.AddOrReplace(storeId, client);
@@ -140,6 +142,7 @@ public class BreezService:EventHostedServiceBase
data.SetSupportedPaymentMethod(new PaymentMethodId("BTC", LightningPaymentType.Instance), null );
await _storeRepository.UpdateStore(data);
}
Directory.Delete(GetWorkDir(storeId), true);
}
else if(result is not null )

View File

@@ -1,13 +1,20 @@
#nullable enable
using System;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
namespace BTCPayServer.Plugins.Breez;
public class BreezSettings
{
public string InviteCode { get; set; }
public string Mnemonic { get; set; }
public string ApiKey { get; set; }
public string? InviteCode { get; set; }
public string? Mnemonic { get; set; }
public string? ApiKey { get; set; }
public string PaymentKey { get; set; } = Guid.NewGuid().ToString();
[JsonIgnore]
public IFormFile GreenlightCredentials { get; set; }
}

View File

@@ -6,8 +6,10 @@
@{
ViewData.SetActivePage("Breez", "Configure", "Configure");
var storeId = Context.GetCurrentStoreId();
var showAdvancedOptions = !string.IsNullOrEmpty(Model?.ApiKey) || !string.IsNullOrEmpty(Model?.InviteCode);
var active = (await BreezService.Get(storeId)) is not null;
}
<form method="post" asp-action="Configure" asp-controller="Breez" asp-route-storeId="@storeId">
<form method="post" asp-action="Configure" asp-controller="Breez" asp-route-storeId="@storeId" enctype="multipart/form-data">
<div class="row mb-4">
<div class="col-12">
<div class="d-flex align-items-center justify-content-between mb-3">
@@ -16,7 +18,7 @@
</h3>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<button name="command" type="submit" value="save" class="btn btn-primary">Save</button>
@if (await BreezService.Get(storeId) is not null)
@if (active)
{
<button name="command" type="submit" value="clear" class="btn btn-danger">Clear</button>
}
@@ -25,21 +27,55 @@
<div class="form-group">
<label asp-for="Mnemonic" class="form-label">Mnemonic</label>
<input asp-for="Mnemonic" class="form-control"/>
<input value="@Model?.Mnemonic" asp-for="Mnemonic" class="form-control" type="password" disabled="@active"/>
<span asp-validation-for="Mnemonic" class="text-danger"></span>
<span class="text-muted">A Bitcoin 12-word mnemonic seed phrase.<strong>BACK THIS UP SAFELY! GENERATE IT RANDOMLY! SERVER ADMINS HAVE ACCESS TO THIS!</strong></span>
</div>
<div class="form-group">
<label asp-for="ApiKey" class="form-label">ApiKey</label>
<input asp-for="ApiKey" class="form-control"/>
<span asp-validation-for="ApiKey" class="text-danger"></span>
@if (!active)
{
<div class="row">
<div class="col-6">
<div class="form-group">
<label asp-for="GreenlightCredentials" class="form-label">Greenlight credentials</label>
<input asp-for="GreenlightCredentials" type="file" class="form-control">
<span asp-validation-for="GreenlightCredentials" class="text-danger w-100 d-block"></span>
<a href="https://greenlight.blockstream.com/" target="_blank">Get Greenlight credentials directly from Blockstream</a>
</div>
</div>
<div class="col-6">
<div class="form-group">
<label asp-for="InviteCode" class="form-label">Invite Code</label>
<input asp-for="InviteCode" class="form-control"/>
<span asp-validation-for="InviteCode" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="InviteCode" class="form-label">InviteCode</label>
<input asp-for="InviteCode" class="form-control"/>
<span asp-validation-for="InviteCode" class="text-danger"></span>
<span class="text-muted">Alternatively, you can use an invite code.</span>
</div>
</div>
</div>
}
<button class="d-inline-flex align-items-center btn btn-link text-primary fw-semibold p-0 mb-3" type="button" id="AdvancedSettingsButton" data-bs-toggle="collapse" data-bs-target="#AdvancedSettings" aria-expanded="false" aria-controls="AdvancedSettings">
<vc:icon symbol="caret-down"/>
<span class="ms-1">Advanced settings</span>
</button>
<div id="AdvancedSettings" class="collapse @(showAdvancedOptions ? "show" : "")">
<div class="form-group">
<label asp-for="ApiKey" class="form-label">Breez API Key</label>
<input asp-for="ApiKey" class="form-control"/>
<span asp-validation-for="ApiKey" class="text-danger"></span>
</div>
@if (active)
{
<div class="form-group">
<label asp-for="InviteCode" class="form-label">Invite Code</label>
<input asp-for="InviteCode" class="form-control"/>
<span asp-validation-for="InviteCode" class="text-danger"></span>
</div>
}
</div>
<input type="hidden" asp-for="PaymentKey"/>

View File

@@ -1,6 +1,6 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Security
@using Microsoft.AspNetCore.Mvc.TagHelpers
@model object
@{
var storeId = Model switch
@@ -8,8 +8,7 @@
string s => s,
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
_ => Context.GetImplicitStoreId()
};
ViewData.SetActivePage("Breez", "Create Swapin Refund", "Info");
};
ViewData.SetActivePage("Breez", "Breez node info", "Info");
}
<partial name="Breez/BreezNodeInfo" model="@storeId"/>

View File

@@ -1,6 +1,10 @@
@using BTCPayServer.Components
@using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using BTCPayServer.Components.QRCode
@using BTCPayServer.Components.TruncateCenter
@model BTCPayServer.Plugins.Breez.PaymentsViewModel
@{
var storeId = Context.GetCurrentStoreId();
@@ -17,8 +21,8 @@
</h3>
<div class="d-flex gap-3 mt-3 mt-sm-0">
<a asp-action="Send" asp-controller="Breez" asp-route-storeId="@storeId" type="submit" class="btn btn-primary">Send</a>
<a asp-action="Receive" asp-controller="Breez" asp-route-storeId="@storeId" type="submit" class="btn btn-primary">Receive</a>
<a permission="@Policies.CanModifyStoreSettings" asp-action="Send" asp-controller="Breez" asp-route-storeId="@storeId" type="submit" class="btn btn-primary">Send</a>
<a permission="@Policies.CanCreateInvoice" asp-action="Receive" asp-controller="Breez" asp-route-storeId="@storeId" type="submit" class="btn btn-primary">Receive</a>
</div>
</div>

View File

@@ -2,6 +2,7 @@
@using BTCPayServer
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Abstractions.Contracts
@using BTCPayServer.Client
@using BTCPayServer.Components.QRCode
@using BTCPayServer.Components.TruncateCenter
@using BTCPayServer.Models.StoreViewModels
@@ -9,6 +10,7 @@
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security
@using BTCPayServer.Services
@using Microsoft.AspNetCore.Mvc.TagHelpers
@using NBitcoin
@inject BreezService BreezService
@inject BTCPayNetworkProvider BtcPayNetworkProvider
@@ -20,7 +22,7 @@
string s => s,
StoreDashboardViewModel dashboardModel => dashboardModel.StoreId,
_ => Context.GetImplicitStoreId()
};
};
var sdk = BreezService.GetClient(storeId)?.Sdk;
if (sdk is null)
return;
@@ -34,13 +36,12 @@
catch (Exception e)
{
}
var refundables = sdk.ListRefundables();
var deriv = Context.GetStoreData().GetDerivationSchemeSettings(BtcPayNetworkProvider, "BTC");
var ni = sdk.NodeInfo();
var f = sdk.RecommendedFees();
var pmi = new PaymentMethodId("BTC", PaymentTypes.BTCLike);
}
<datalist id="fees">
@@ -70,34 +71,35 @@
<vc:truncate-center text="@inProgressSwap.bitcoinAddress" padding="15" elastic="true" classes="form-control-plaintext" id="Address"/>
<label for="Address">Address</label>
</div>
<span class="text-muted">Please send an amount between <br/> @Money.Satoshis(inProgressSwap.minAllowedDeposit).ToDecimal(MoneyUnit.BTC) and @Money.Satoshis(inProgressSwap.maxAllowedDeposit).ToDecimal(MoneyUnit.BTC)BTC </span>
@if (deriv is not null)
{
var wallet = new WalletId(storeId, "BTC");
<a class="btn btn-link w-100" asp-controller="UIWallets" asp-action="WalletSend" asp-route-walletId="@wallet" asp-route-defaultDestination="@inProgressSwap.bitcoinAddress">Send using BTCPay Wallet</a>
}
@{
var onChainSats = ni.onchainBalanceMsat / 1000;
if (inProgressSwap.minAllowedDeposit <= (long) onChainSats)
<div>
<span class="text-muted">Please send an amount between <br/> @Money.Satoshis(inProgressSwap.minAllowedDeposit).ToDecimal(MoneyUnit.BTC) and @Money.Satoshis(inProgressSwap.maxAllowedDeposit).ToDecimal(MoneyUnit.BTC)BTC </span>
@if (deriv is not null)
{
<div class="w-100">
<form asp-action="Sweep" asp-route-storeId="@storeId">
<input type="hidden" value="@inProgressSwap.bitcoinAddress" name="address"/>
<button class="btn btn-link w-100" type="submit"> Sweep onchain funds to swap in</button>
</form>
</div>
var wallet = new WalletId(storeId, "BTC");
<a class="btn btn-link w-100" permission="@Policies.CanModifyStoreSettings" asp-controller="UIWallets" asp-action="WalletSend" asp-route-walletId="@wallet" asp-route-defaultDestination="@inProgressSwap.bitcoinAddress">Send using BTCPay Wallet</a>
}
}
@{
var onChainSats = ni.onchainBalanceMsat / 1000;
if (inProgressSwap.minAllowedDeposit <= (long) onChainSats)
{
<div class="w-100">
<form asp-action="Sweep" asp-route-storeId="@storeId">
<input type="hidden" value="@inProgressSwap.bitcoinAddress" name="address"/>
<button class="btn btn-link w-100" type="submit"> Sweep onchain funds to swap in</button>
</form>
</div>
}
}
</div>
</div>
@if (inProgressSwap.unconfirmedSats + inProgressSwap.confirmedSats + inProgressSwap.paidSats > 0)
@if (inProgressSwap.unconfirmedSats + inProgressSwap.confirmedSats + (inProgressSwap.paidMsat * 1000) > 0)
{
<div class="card truncate-center-id">
<span class="text-nowrap">@inProgressSwap.unconfirmedSats sats unconfirmed</span>
<span class="text-nowrap">@inProgressSwap.confirmedSats sats confirmed</span>
<span class="text-nowrap">@inProgressSwap.paidSats sats paid</span>
<span class="text-nowrap">@(inProgressSwap.paidMsat * 1000) sats paid</span>
</div>
}
@if (inProgressSwap.unconfirmedTxIds.Any())
@@ -144,12 +146,12 @@
<td>@DateTimeOffset.FromUnixTimeSeconds(refund.createdAt)</td>
<td>@refund.bitcoinAddress</td>
<td>
@if (refund.unconfirmedSats + refund.confirmedSats + refund.paidSats > 0)
@if (refund.unconfirmedSats + refund.confirmedSats + refund.paidMsat > 0)
{
<div class="card truncate-center-id">
<span class="text-nowrap">@refund.unconfirmedSats sats unconfirmed</span>
<span class="text-nowrap">@refund.confirmedSats sats confirmed</span>
<span class="text-nowrap">@refund.paidSats sats paid</span>
<span class="text-nowrap">@(refund.paidMsat * 1000) sats paid</span>
</div>
}
@if (refund.unconfirmedTxIds.Any())

View File

@@ -1,7 +1,9 @@
@using BTCPayServer.Abstractions.Extensions
@using BTCPayServer.Client
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security
@using Microsoft.AspNetCore.Mvc.TagHelpers
@inject BreezService BreezService
@{
@@ -22,12 +24,12 @@
<div class="nav">
@if (sdk is not null)
{
<a asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Info")">Info</a>
<a asp-action="Payments" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Payments")">Payments</a>
<a asp-action="SwapIn" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapIn")">Swap In</a>
<a asp-action="SwapOut" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapOut")">Swap Out</a>
<a permission="@Policies.CanViewStoreSettings" asp-action="Info" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Info")">Info</a>
<a permission="@Policies.CanViewStoreSettings" asp-action="Payments" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Payments")">Payments</a>
<a permission="@Policies.CanCreateInvoice" asp-action="SwapIn" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapIn")">Swap In</a>
<a permission="@Policies.CanModifyStoreSettings" asp-action="SwapOut" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "SwapOut")">Swap Out</a>
}
<a asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Configure")">Configuration</a>
<a permission="@Policies.CanModifyStoreSettings" asp-action="Configure" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez", null, "Configure")">Configuration</a>
</div>
</nav>
</div>

View File

@@ -9,7 +9,7 @@
@if (!string.IsNullOrEmpty(storeId))
{
<li class="nav-item">
<a permission="@Policies.CanModifyStoreSettings" asp-controller="Breez" asp-action="Index" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez")" id="Nav-Breez">
<a permission="@Policies.CanViewStoreSettings" asp-controller="Breez" asp-action="Index" asp-route-storeId="@storeId" class="nav-link @ViewData.IsActivePage("Breez")" id="Nav-Breez">
<svg style="width: 15px; margin-left: 3px; margin-right: 7px;" viewBox="0 0 64 64" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="favicon-64-2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">

View File

@@ -2,6 +2,7 @@
@using BTCPayServer.Models.StoreViewModels
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Security
@using BTCPayServer.Client
@inject BreezService BreezService
@{
string storeId = null;
@@ -42,7 +43,7 @@
{
<div class="d-flex w-100 align-items-center justify-content-start gap-3">
<span class="btcpay-status btcpay-status--enabled"></span>
<h6 class="text-truncate">@lspInfo.name connected</h6>
<h6 class="text-truncate">@lspInfo.name LSP connected</h6>
</div>
}
<div class="store-number">
@@ -50,7 +51,7 @@
<h6>On-Chain Balance</h6>
@if (Model is StoreDashboardViewModel && nodeState.onchainBalanceMsat > 0)
{
<div>
<div permission="@Policies.CanModifyStoreSettings">
<a asp-action="Sweep" asp-controller="Breez" asp-route-storeId="@storeId">Sweep</a>
</div>
}
@@ -66,7 +67,7 @@
<h6>Lightning Balance</h6>
@if (Model is StoreDashboardViewModel)
{
<div>
<div permission="@Policies.CanModifyStoreSettings">
<a asp-action="SwapIn" asp-controller="Breez" asp-route-storeId="@storeId">Swap in</a>
@if (nodeState.channelsBalanceMsat > 0)
{

View File

@@ -15,7 +15,7 @@
if (sdk is null)
return;
data = sdk.ListPayments(new ListPaymentsRequest(null, null, null, null, 0, 10));
data = sdk.ListPayments(new ListPaymentsRequest(null, null, null, null, true, 0, 10));
}
var isDashboard = Model is StoreDashboardViewModel;
@@ -47,7 +47,7 @@
<th class="w-125px">Id</th>
<th class="w-125px">Timestamp</th>
<th class="w-125px">Type</th>
<th class="w-125px">Amount</th>
<th class="w-125px">Amount</th>a
<th class="text-nowrap">Fee</th>
<th class="text-nowrap">Status</th>
<th class="text-nowrap">Description</th>
@@ -57,25 +57,25 @@
@foreach (var payment in data)
{
<tr>
<td>
<td class="smMaxWidth text-truncate">
<span class="text-break">@payment.id</span>
<span >@payment.id</span>
</td>
<td>
<span class="text-break">@DateTimeOffset.FromUnixTimeSeconds(payment.paymentTime).ToTimeAgo()</span>
<span >@DateTimeOffset.FromUnixTimeSeconds(payment.paymentTime).ToTimeAgo()</span>
</td>
<td>
<span class="text-break">>@payment.paymentType.ToString().ToLowerInvariant().Replace("_", " ")</span>
<span >@payment.paymentType.ToString().ToLowerInvariant().Replace("_", " ")</span>
</td>
<td>
<span class="text-break">>@LightMoney.MilliSatoshis(payment.amountMsat).ToDecimal(LightMoneyUnit.BTC) BTC</span>
<span >@LightMoney.MilliSatoshis(payment.amountMsat).ToDecimal(LightMoneyUnit.BTC) BTC</span>
</td>
<td>
<span class="text-break">>@LightMoney.MilliSatoshis(payment.feeMsat).ToDecimal(LightMoneyUnit.BTC) BTC</span>
<span >@LightMoney.MilliSatoshis(payment.feeMsat).ToDecimal(LightMoneyUnit.BTC) BTC</span>
</td>
<td>
<span class="text-break">@payment.status.ToString().ToLowerInvariant()</span>
<span >@payment.status.ToString().ToLowerInvariant()</span>
</td>
<td>
<span class="text-break">@payment.description</span>

View File

@@ -1,5 +1,6 @@
@inject BreezService BreezService;
@using BTCPayServer.Plugins.Breez
@using BTCPayServer.Client
@model BTCPayServer.Models.StoreViewModels.LightningNodeViewModel
@if (Model.CryptoCode != "BTC")
@@ -9,5 +10,15 @@
@{
var client = BreezService.GetClient(Model.StoreId);
}
<input value="Custom" type="radio" id="LightningNodeType-Breez" data-bs-toggle="pill" data-bs-target="#BreezSetup" role="tab" aria-controls="BreezSetup" aria-selected="false" name="LightningNodeType" disabled="@(client is null)">
<label for="LightningNodeType-Breez">Use Breez wallet</label>
@if (client is null)
{
<a asp-action="Configure" asp-controller="Breez" permission="@Policies.CanModifyStoreSettings" asp-route-storeId="@Model.StoreId" value="Custom" type="radio" id="LightningNodeType-Breez" role="tab" aria-controls="BreezSetup" aria-selected="false" name="LightningNodeType">
<label for="LightningNodeType-Breez">Configure Breez</label>
</a>
}
else
{
<input value="Custom" type="radio" id="LightningNodeType-Breez" data-bs-toggle="pill" data-bs-target="#BreezSetup" role="tab" aria-controls="BreezSetup" aria-selected="false" name="LightningNodeType" disabled="@(client is null)">
<label for="LightningNodeType-Breez">Use Breez wallet</label>
}