mirror of
https://github.com/aljazceru/BTCPayServerPlugins.git
synced 2025-12-16 23:24:25 +01:00
update to dotnet8
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="BTCPayServer: Bitcoin-HTTPS" type="LaunchSettings" factoryName=".NET Launch Settings Profile">
|
||||
<option name="LAUNCH_PROFILE_PROJECT_FILE_PATH" value="$PROJECT_DIR$/submodules/btcpayserver/BTCPayServer/BTCPayServer.csproj" />
|
||||
<option name="LAUNCH_PROFILE_TFM" value="net6.0" />
|
||||
<option name="LAUNCH_PROFILE_TFM" value="net8.0" />
|
||||
<option name="LAUNCH_PROFILE_NAME" value="Bitcoin-HTTPS" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="ConfigBuilder" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/ConfigBuilder/bin/Debug/net6.0/ConfigBuilder.exe" />
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/ConfigBuilder/bin/Debug/net8.0/ConfigBuilder.exe" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ConfigBuilder/bin/Debug/net6.0" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/ConfigBuilder/bin/Debug/net8.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
@@ -12,7 +12,7 @@
|
||||
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
|
||||
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net6.0" />
|
||||
<option name="PROJECT_TFM" value="net8.0" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
|
||||
@@ -33,8 +33,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Ticket
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.Wabisabi", "Plugins\BTCPayServer.Plugins.Wabisabi\BTCPayServer.Plugins.Wabisabi.csproj", "{0D438B7D-F996-4BF3-8F54-02CB9DF120D8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.LSP", "Plugins\BTCPayServer.Plugins.LSP\BTCPayServer.Plugins.LSP.csproj", "{304E4860-8420-4D71-B95A-3E2F75BEE9F6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.PluginPacker", "submodules\btcpayserver\BTCPayServer.PluginPacker\BTCPayServer.PluginPacker.csproj", "{2FDF2D41-8E75-49F6-9F4D-9E9AECE7922F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BTCPayServer.Plugins.NIP05", "Plugins\BTCPayServer.Plugins.NIP05\BTCPayServer.Plugins.NIP05.csproj", "{362D2175-9632-418E-95B1-5D638C52ECA6}"
|
||||
@@ -175,14 +173,6 @@ Global
|
||||
{0D438B7D-F996-4BF3-8F54-02CB9DF120D8}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0D438B7D-F996-4BF3-8F54-02CB9DF120D8}.Altcoins-Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0D438B7D-F996-4BF3-8F54-02CB9DF120D8}.Altcoins-Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Altcoins-Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Altcoins-Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Altcoins-Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{304E4860-8420-4D71-B95A-3E2F75BEE9F6}.Altcoins-Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{2FDF2D41-8E75-49F6-9F4D-9E9AECE7922F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{2FDF2D41-8E75-49F6-9F4D-9E9AECE7922F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{2FDF2D41-8E75-49F6-9F4D-9E9AECE7922F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
|
||||
@@ -11,10 +11,10 @@ foreach (var plugin in plugins)
|
||||
var buildConfigurationName = assemblyConfigurationAttribute?.Configuration;
|
||||
var x = Directory.GetDirectories(Path.Combine(plugin, "bin"));
|
||||
|
||||
p += $"{Path.GetFullPath(plugin)}/bin/{buildConfigurationName}/net6.0/{Path.GetFileName(plugin)}.dll;";
|
||||
p += $"{Path.GetFullPath(plugin)}/bin/{buildConfigurationName}/net8.0/{Path.GetFileName(plugin)}.dll;";
|
||||
// if (x.Any(s => s.EndsWith("Altcoins-Debug")))
|
||||
// {
|
||||
// p += $"{Path.GetFullPath(plugin)}/bin/Altcoins-Debug/net6.0/{Path.GetFileName(plugin)}.dll;";
|
||||
// p += $"{Path.GetFullPath(plugin)}/bin/Altcoins-Debug/net8.0/{Path.GetFileName(plugin)}.dll;";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Bitcoin Whitepaper</Product>
|
||||
<Description>This makes the Bitcoin whitepaper available on your BTCPay Server.</Description>
|
||||
<Version>1.0.3</Version>
|
||||
<Version>1.0.4</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace BTCPayServer.Plugins.BitcoinWhitepaper
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.7.4" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Data Erasure</Product>
|
||||
<Description>Allows you to erase user data from invoices after a period of time.</Description>
|
||||
<Version>1.0.1</Version>
|
||||
<Version>1.0.2</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace BTCPayServer.Plugins.DataErasure
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.8.0" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Dynamic Rate Limit</Product>
|
||||
<Description>Allows you to override the default rate limiting.</Description>
|
||||
<Version>1.0.0</Version>
|
||||
<Version>1.0.1</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -10,7 +10,7 @@ public class DynamicRateLimitsPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.8.0" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Dynamic Reports</Product>
|
||||
<Description>Allows you to create custom reports using SQL.</Description>
|
||||
<Version>1.0.0</Version>
|
||||
<Version>1.0.1</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -10,7 +10,7 @@ public class DynamicReportsPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.11.7" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>File Seller</Product>
|
||||
<Description>Allows you to sell files through the point of sale/crowdfund apps.</Description>
|
||||
<Version>1.0.3</Version>
|
||||
<Version>1.0.4</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ public class FileSellerPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.11.5" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>FixedFloat</Product>
|
||||
<Description>Allows you to embed a FixedFloat conversion screen to allow customers to pay with altcoins.</Description>
|
||||
<Version>1.1.5</Version>
|
||||
<Version>1.1.6</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace BTCPayServer.Plugins.FixedFloat
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.9.0" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
|
||||
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Plugin specific properties -->
|
||||
<PropertyGroup>
|
||||
<Product>LSP</Product>
|
||||
<Description>Allows you to become an LSP selling lightning channels with inbound liquidity</Description>
|
||||
<Version>1.0.0</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
<AddRazorSupportForMvc>true</AddRazorSupportForMvc>
|
||||
<PreserveCompilationContext>false</PreserveCompilationContext>
|
||||
<GenerateEmbeddedFilesManifest>true</GenerateEmbeddedFilesManifest>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- This will make sure that referencing BTCPayServer doesn't put any artifact in the published directory -->
|
||||
<ItemDefinitionGroup>
|
||||
<ProjectReference>
|
||||
<Properties>StaticWebAssetsEnabled=false</Properties>
|
||||
<Private>false</Private>
|
||||
<ExcludeAssets>runtime;native;build;buildTransitive;contentFiles</ExcludeAssets>
|
||||
</ProjectReference>
|
||||
</ItemDefinitionGroup>
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\**"/>
|
||||
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,301 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Constants;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Lightning;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using NBitcoin;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Plugins.LSP
|
||||
{
|
||||
[Authorize(AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Authorize(Policy = Policies.CanModifyStoreSettings, AuthenticationSchemes = AuthenticationSchemes.Cookie)]
|
||||
[Route("plugins/{storeId}/LSP")]
|
||||
public class LSPController : Controller
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[HttpGet("")]
|
||||
public async Task<IActionResult> View(string storeId)
|
||||
{
|
||||
var config = await _LSPService.GetLSPForStore(storeId);
|
||||
try
|
||||
{
|
||||
if (config?.Enabled is true)
|
||||
{
|
||||
return View(new LSPViewModel() {Settings = config});
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("")]
|
||||
public async Task<IActionResult> Purchase(string storeId, string email, uint inbound, bool privateChannel)
|
||||
{
|
||||
var config = await _LSPService.GetLSPForStore(storeId);
|
||||
try
|
||||
{
|
||||
if (config?.Enabled is not true || string.IsNullOrEmpty(email) || inbound < config.Minimum ||
|
||||
inbound > config.Maximum)
|
||||
{
|
||||
return RedirectToAction("View", new {storeId});
|
||||
}
|
||||
|
||||
var price = Math.Ceiling((config.FeePerSat == 0 ? 0 : (config.FeePerSat * inbound)) + config.BaseFee);
|
||||
var btcpayClient = await CreateClient(storeId);
|
||||
var redirectUrl = Request.GetAbsoluteUri(Url.Action("Connect",
|
||||
"LSP", new {storeId, invoiceId = "kukkskukkskukks"}));
|
||||
redirectUrl = redirectUrl.Replace("kukkskukkskukks", "{InvoiceId}");
|
||||
var inv = await btcpayClient.CreateInvoice(storeId,
|
||||
new CreateInvoiceRequest()
|
||||
{
|
||||
Amount = price,
|
||||
Currency = "sats",
|
||||
Type = InvoiceType.Standard,
|
||||
AdditionalSearchTerms = new[] {"LSP"},
|
||||
Checkout =
|
||||
{
|
||||
RequiresRefundEmail = true,
|
||||
RedirectAutomatically = price > 0,
|
||||
RedirectURL = redirectUrl,
|
||||
},
|
||||
Metadata = JObject.FromObject(new
|
||||
{
|
||||
buyerEmail = email,
|
||||
privateChannel,
|
||||
inbound,
|
||||
config.BaseFee,
|
||||
config.FeePerSat,
|
||||
orderId = "LSP"
|
||||
})
|
||||
});
|
||||
|
||||
while (inv.Amount == 0 && inv.Status == InvoiceStatus.New)
|
||||
{
|
||||
if (inv.Status == InvoiceStatus.New)
|
||||
inv = await btcpayClient.GetInvoice(inv.StoreId, inv.Id);
|
||||
}
|
||||
|
||||
if (inv.Status == InvoiceStatus.Settled)
|
||||
return RedirectToAction("Connect", new {storeId, invoiceId = inv.Id});
|
||||
return Redirect(inv.CheckoutLink);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
|
||||
return RedirectToAction("View", new {storeId});
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("connect")]
|
||||
public async Task<IActionResult> Connect(string storeId, string invoiceId)
|
||||
{
|
||||
var btcpayClient = await CreateClient(storeId);
|
||||
try
|
||||
{
|
||||
var config = await _LSPService.GetLSPForStore(storeId);
|
||||
var result = new LSPConnectPage() {InvoiceId = invoiceId, Settings = config};
|
||||
var invoice = await btcpayClient.GetInvoice(storeId, invoiceId);
|
||||
result.Status = invoice.Status;
|
||||
if (invoice.Status != InvoiceStatus.Settled) return View(result);
|
||||
if (invoice.Metadata.TryGetValue("lsp-channel-complete", out _))
|
||||
{
|
||||
return Redirect(invoice.CheckoutLink);
|
||||
}
|
||||
|
||||
|
||||
result.Invoice = invoice;
|
||||
result.LNURL = LNURL.LNURL.EncodeUri(new Uri(Request.GetAbsoluteUri(Url.Action(
|
||||
"LNURLChannelRequest",
|
||||
"LSP", new {storeId, invoiceId}))), "channelRequest", true).ToString();
|
||||
|
||||
return View(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<BTCPayServerClient> CreateClient(string storeId)
|
||||
{
|
||||
return await _btcPayServerClientFactory.Create(null, new[] {storeId},
|
||||
new DefaultHttpContext()
|
||||
{
|
||||
Request =
|
||||
{
|
||||
Scheme = "https", Host = Request.Host, Path = Request.Path, PathBase = Request.PathBase
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public class LSPConnectPage
|
||||
{
|
||||
public string LNURL;
|
||||
public string InvoiceId { get; set; }
|
||||
public InvoiceStatus Status { get; set; }
|
||||
public LSPSettings Settings { get; set; }
|
||||
public InvoiceData Invoice { get; set; }
|
||||
}
|
||||
|
||||
private readonly LSPService _LSPService;
|
||||
private readonly IBTCPayServerClientFactory _btcPayServerClientFactory;
|
||||
|
||||
public LSPController(IHttpClientFactory httpClientFactory,
|
||||
LSPService LSPService,
|
||||
IBTCPayServerClientFactory btcPayServerClientFactory)
|
||||
{
|
||||
_LSPService = LSPService;
|
||||
_btcPayServerClientFactory = btcPayServerClientFactory;
|
||||
}
|
||||
|
||||
[HttpGet("update")]
|
||||
public async Task<IActionResult> UpdateLSPSettings(string storeId)
|
||||
{
|
||||
LSPSettings vm = null;
|
||||
try
|
||||
{
|
||||
vm = await _LSPService.GetLSPForStore(storeId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
vm ??= new();
|
||||
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public async Task<IActionResult> UpdateLSPSettings(string storeId,
|
||||
LSPSettings vm,
|
||||
string command)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
|
||||
switch (command?.ToLowerInvariant())
|
||||
{
|
||||
case "save":
|
||||
await _LSPService.SetLSPForStore(storeId, vm);
|
||||
TempData["SuccessMessage"] = "LSP settings modified";
|
||||
return RedirectToAction(nameof(UpdateLSPSettings), new {storeId});
|
||||
|
||||
default:
|
||||
return View(vm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("lnurlc-callback")]
|
||||
public async Task<IActionResult> LNURLChannelRequestCallback(string storeId, string k1, string remoteId)
|
||||
{
|
||||
if (!NodeInfo.TryParse(remoteId, out var remoteNode))
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
var btcPayClient = await CreateClient(storeId);
|
||||
var invoice = await btcPayClient.GetInvoice(storeId, k1);
|
||||
if (invoice?.Status != InvoiceStatus.Settled || invoice.Metadata.TryGetValue("lsp-channel-complete", out _))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var settings = await _LSPService.GetLSPForStore(storeId);
|
||||
if (settings?.Enabled is not true)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
if (!invoice.Metadata.TryGetValue("posData", out var posData))
|
||||
{
|
||||
posData = JToken.Parse("{}");
|
||||
}
|
||||
|
||||
var inbound = invoice.Metadata["inbound"].Value<long>();
|
||||
try
|
||||
{
|
||||
await btcPayClient.ConnectToLightningNode(storeId, "BTC", new ConnectToNodeRequest(remoteNode));
|
||||
posData["LSP"] = JToken.FromObject(new Dictionary<string,object>());
|
||||
posData["LSP"]["Remote Node"] = remoteId;
|
||||
await btcPayClient.OpenLightningChannel(storeId, "BTC", new OpenLightningChannelRequest()
|
||||
{
|
||||
ChannelAmount = new Money(inbound, MoneyUnit.Satoshi),
|
||||
|
||||
NodeURI = remoteNode
|
||||
});
|
||||
posData["LSP"]["Channel Status"] = "Opened";
|
||||
invoice.Metadata["posData"] = posData;
|
||||
invoice.Metadata["lsp-channel-complete"] = true;
|
||||
await btcPayClient.UpdateInvoice(storeId, invoice.Id,
|
||||
new UpdateInvoiceRequest() {Metadata = invoice.Metadata});
|
||||
return Ok(new LNURL.LNUrlStatusResponse()
|
||||
{
|
||||
Status = "OK"
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
posData["Error"] =
|
||||
$"Channel could not be created. You should refund customer.{Environment.NewLine}{e.Message}";
|
||||
invoice.Metadata["posData"] = posData;
|
||||
await btcPayClient.UpdateInvoice(storeId, invoice.Id,
|
||||
new UpdateInvoiceRequest() {Metadata = invoice.Metadata});
|
||||
return Ok(new LNURL.LNUrlStatusResponse()
|
||||
{
|
||||
Status = "ERROR", Reason = $"Channel could not be created to {remoteId}"
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpGet("{invoiceId}/lnurlc")]
|
||||
public async Task<IActionResult> LNURLChannelRequest(string storeId, string invoiceId, string nodeUri)
|
||||
{
|
||||
var btcPayClient = await CreateClient(storeId);
|
||||
var invoice = await btcPayClient.GetInvoice(storeId, invoiceId);
|
||||
if (invoice?.Status != InvoiceStatus.Settled || invoice.Metadata.TryGetValue("lsp-channel-complete", out _))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
var settings = await _LSPService.GetLSPForStore(storeId);
|
||||
if (settings?.Enabled is not true)
|
||||
{
|
||||
return BadRequest();
|
||||
}
|
||||
return Ok(new LNURL.LNURLChannelRequest()
|
||||
{
|
||||
Tag = "channelRequest",
|
||||
K1 = invoiceId,
|
||||
Callback = new Uri(Request.GetAbsoluteUri(Url.Action("LNURLChannelRequestCallback",
|
||||
"LSP", new {storeId}))),
|
||||
Uri = nodeUri is null
|
||||
? (await btcPayClient.GetLightningNodeInfo(storeId, "BTC")).NodeURIs
|
||||
.OrderBy(nodeInfo => nodeInfo.IsTor).First()
|
||||
: NodeInfo.Parse(nodeUri)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Abstractions.Services;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace BTCPayServer.Plugins.LSP
|
||||
{
|
||||
public class LSPPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.7.4" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
applicationBuilder.AddSingleton<LSPService>();
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("LSP/StoreIntegrationLSPOption",
|
||||
"store-integrations-list"));
|
||||
applicationBuilder.AddSingleton<IUIExtension>(new UIExtension("LSP/LSPNav",
|
||||
"store-integrations-nav"));
|
||||
base.Execute(applicationBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
|
||||
namespace BTCPayServer.Plugins.LSP;
|
||||
|
||||
public class LSPService
|
||||
{
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly IStoreRepository _storeRepository;
|
||||
|
||||
public LSPService(IMemoryCache memoryCache,
|
||||
IStoreRepository storeRepository)
|
||||
{
|
||||
_memoryCache = memoryCache;
|
||||
_storeRepository = storeRepository;
|
||||
}
|
||||
|
||||
|
||||
public async Task<LSPSettings> GetLSPForStore(string storeId)
|
||||
{
|
||||
var k = $"{nameof(LSPSettings)}_{storeId}";
|
||||
return await _memoryCache.GetOrCreateAsync(k, async _ =>
|
||||
{
|
||||
var res = await _storeRepository.GetSettingAsync<LSPSettings>(storeId,
|
||||
nameof(LSPSettings));
|
||||
return res;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task SetLSPForStore(string storeId, LSPSettings lspSettings)
|
||||
{
|
||||
var k = $"{nameof(LSPSettings)}_{storeId}";
|
||||
await _storeRepository.UpdateSetting(storeId, nameof(LSPSettings), lspSettings);
|
||||
_memoryCache.Set(k, lspSettings);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
dotnet publish -c Altcoins-Release -o bin/publish/BTCPayServer.Plugins.LSP
|
||||
dotnet run -p ../../BTCPayServer.PluginPacker bin/publish/BTCPayServer.Plugins.LSP BTCPayServer.Plugins.LSP ../packed
|
||||
@@ -1,21 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
namespace BTCPayServer.Plugins.LSP;
|
||||
|
||||
public class LSPSettings
|
||||
{
|
||||
public bool Enabled { get; set; } = true;
|
||||
public long Minimum { get; set; } = 100000;
|
||||
public long Maximum { get; set; } = 10000000;
|
||||
public decimal FeePerSat { get; set; } = 0.01m;
|
||||
public long BaseFee { get; set; } = 0;
|
||||
public string CustomCSS { get; set; }
|
||||
public string Title { get; set; } = "Lightning Liquidity Peddler";
|
||||
public string Description { get; set; } = "<h3 class='w-100'>Get an inbound channel</h3><p>This will open a public channel to your node.</p>";
|
||||
}
|
||||
|
||||
public class LSPViewModel
|
||||
{
|
||||
public LSPSettings Settings { get; set; }
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
@using BTCPayServer.Client.Models
|
||||
@model BTCPayServer.Plugins.LSP.LSPController.LSPConnectPage
|
||||
@inject ContentSecurityPolicies contentSecurityPolicies
|
||||
@using BTCPayServer.Security
|
||||
@using NBitcoin
|
||||
@{
|
||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'");
|
||||
contentSecurityPolicies.AllowUnsafeHashes();
|
||||
Layout = "_LayoutSimple";
|
||||
var reloadPage = false;
|
||||
}
|
||||
|
||||
<style>
|
||||
footer {
|
||||
display: none;
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Settings.CustomCSS))
|
||||
{
|
||||
@Safe.Raw(Model.Settings.CustomCSS)
|
||||
}
|
||||
|
||||
</style>
|
||||
<div class="container d-flex h-100">
|
||||
<div class="justify-content-center align-self-center text-center mx-auto px-2 w-100 m-auto">
|
||||
<partial name="_StatusMessage"/>
|
||||
<div class="mb-4 d-print-none d-flex justify-content-center">
|
||||
|
||||
<h1 >Thank you!</h1>
|
||||
</div>
|
||||
@if (Model.Status == InvoiceStatus.Processing)
|
||||
{
|
||||
reloadPage = true;
|
||||
<div class="alert alert-info">
|
||||
The invoice has detected a payment but is still waiting to be settled. This page will refresh periodically until it is settled.
|
||||
</div>
|
||||
}
|
||||
else if (Model.Status != InvoiceStatus.Settled)
|
||||
{
|
||||
<div class="alert alert-danger">
|
||||
The invoice is not settled.
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
Model.Invoice.Metadata.TryGetValue("inbound", out var inbound);
|
||||
<div class="d-inline-flex flex-column" style="width:256px">
|
||||
<div class="qr-container mb-2">
|
||||
@await Component.InvokeAsync("QRCode", new {data = Model.LNURL.ToUpperInvariant()})
|
||||
</div>
|
||||
|
||||
<p class="mx-auto">Scan this QR with your wallet to proceed with opening the channel.</p>
|
||||
<a class="btn btn-secondary mt-3" href=@Model.LNURL>Open in wallet</a>
|
||||
<p class="text-muted">Opening a channel of at least @inbound.ToString() sats.</p>
|
||||
</div>
|
||||
}
|
||||
<div class="row">
|
||||
<div class="powered__by__btcpayserver col-12">
|
||||
Powered by <a target="_blank" href="https://github.com/btcpayserver/btcpayserver" rel="noreferrer noopener">BTCPay Server</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if (reloadPage)
|
||||
{
|
||||
<script type="text/javascript" nonce="@nonce">
|
||||
setTimeout(function(){
|
||||
|
||||
window.location.reload();
|
||||
}, 3000);
|
||||
</script>
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@model BTCPayServer.Plugins.LSP.LSPSettings
|
||||
@inject IScopeProvider ScopeProvider
|
||||
@{
|
||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["NavPartialName"] = "../UIStores/_Nav";
|
||||
ViewData.SetActivePage("LSP", "Update Store LSP Settings", null);
|
||||
}
|
||||
|
||||
<h2 class="mt-1 mb-4">@ViewData["Title"]</h2>
|
||||
<div class="row">
|
||||
<div class="col-xl-8 col-xxl-constrain">
|
||||
<form method="post">
|
||||
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
|
||||
<div class="form-group form-check">
|
||||
<input type="checkbox" class="form-check-input" asp-for="Enabled"/>
|
||||
<label asp-for="Enabled" class="form-check-label"></label>
|
||||
<span asp-validation-for="Enabled" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="Title" class="form-label" data-required>LSP Title</label>
|
||||
<input asp-for="Title" class="form-control" required/>
|
||||
<span asp-validation-for="Title" class="text-danger"></span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label asp-for="Description" class="form-label"></label>
|
||||
<textarea asp-for="Description" rows="10" cols="40" class="form-control richtext"></textarea>
|
||||
<span asp-validation-for="Description" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label asp-for="CustomCSS" class="form-label">Additional Custom CSS</label>
|
||||
<textarea asp-for="CustomCSS" rows="10" cols="40" class="form-control"></textarea>
|
||||
<span asp-validation-for="CustomCSS" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="form-group col-md-6 col-sm-12">
|
||||
<label asp-for="Minimum" class="form-label"></label>
|
||||
<input asp-for="Minimum" class="form-control" type="number" inputmode="numeric" min="1" style="max-width:16ch;"/>
|
||||
<span asp-validation-for="Minimum" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group col-md-6 col-sm-12">
|
||||
<label asp-for="Maximum" class="form-label"></label>
|
||||
<input asp-for="Maximum" class="form-control" type="number" inputmode="numeric" min="1" style="max-width:16ch;"/>
|
||||
<span asp-validation-for="Maximum" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group col-md-6 col-sm-12">
|
||||
<label asp-for="BaseFee" class="form-label"></label>
|
||||
<input asp-for="BaseFee" class="form-control" type="number" inputmode="numeric" style="max-width:16ch;"/>
|
||||
<span asp-validation-for="BaseFee" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group col-md-6 col-sm-12">
|
||||
<label asp-for="FeePerSat" class="form-label"></label>
|
||||
<input asp-for="FeePerSat" class="form-control" type="number" min="0" step="any" inputmode="numeric" style="max-width:16ch;"/>
|
||||
<span asp-validation-for="FeePerSat" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group mt-4">
|
||||
<input type="submit" value="Save" name="command" class="btn btn-primary"/>
|
||||
@if (this.ViewContext.ModelState.IsValid && Model.Enabled)
|
||||
{
|
||||
<a class="btn btn-secondary" href=" @Url.Action("View", "LSP", new {storeId})">
|
||||
Purchase page
|
||||
</a>
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section PageFootContent {
|
||||
<link href="~/vendor/summernote/summernote-bs5.css" rel="stylesheet" asp-append-version="true"/>
|
||||
<script src="~/vendor/summernote/summernote-bs5.js" asp-append-version="true"></script>
|
||||
<partial name="_ValidationScriptsPartial"/>
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
@using Microsoft.AspNetCore.Routing
|
||||
@using BTCPayServer.Plugins.LSP
|
||||
@model BTCPayServer.Plugins.LSP.LSPViewModel
|
||||
@inject ContentSecurityPolicies contentSecurityPolicies
|
||||
@inject IScopeProvider ScopeProvider
|
||||
@using BTCPayServer.Security
|
||||
@using NBitcoin
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@{
|
||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||
var nonce = RandomUtils.GetUInt256().ToString().Substring(0, 32);
|
||||
contentSecurityPolicies.Add("script-src", $"'nonce-{nonce}'");
|
||||
contentSecurityPolicies.AllowUnsafeHashes();
|
||||
Layout = "_LayoutSimple";
|
||||
|
||||
}
|
||||
<style>
|
||||
footer {
|
||||
display: none;
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(Model.Settings.CustomCSS))
|
||||
{
|
||||
@Safe.Raw(Model.Settings.CustomCSS)
|
||||
}
|
||||
|
||||
</style>
|
||||
<script nonce="@nonce">
|
||||
const baseFee = @Model.Settings.BaseFee ;
|
||||
const rate = @Model.Settings.FeePerSat ;
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
document.getElementById("inbound").addEventListener("input", ev => {
|
||||
compute(ev.target.value);
|
||||
});
|
||||
|
||||
function compute(inbound){
|
||||
const cost = Math.ceil(baseFee + ( rate ===0? 0 : (rate * inbound)));
|
||||
document.getElementById("cost").textContent = `Cost: ${cost} sats`;
|
||||
}
|
||||
compute(document.getElementById("inbound").value);
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="container d-flex h-100">
|
||||
<div class="justify-content-center align-self-center text-center mx-auto px-2 py-3 w-100 m-auto">
|
||||
<partial name="_StatusMessage"/>
|
||||
|
||||
<h1 >@Model.Settings.Title</h1>
|
||||
@if (!string.IsNullOrEmpty(Model.Settings.Description))
|
||||
{
|
||||
<div class="row" id="description">
|
||||
<div class="overflow-hidden col-12">@Safe.Raw(Model.Settings.Description)</div>
|
||||
</div>
|
||||
}
|
||||
<form method="post" asp-controller="LSP" asp-action="Purchase" asp-antiforgery="false" asp-route-storeId="@storeId">
|
||||
<div class="row g-2 mb-4 justify-content-center" id="form-email-container">
|
||||
|
||||
<div class="col-sm-12 col-md-8">
|
||||
<div class="form-floating">
|
||||
<input required type="email" name="email" class="form-control"/>
|
||||
<label >Email</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-8">
|
||||
<div class="form-floating">
|
||||
<input required type="number" id="inbound" name="inbound" value="@Model.Settings.Minimum" min="@Model.Settings.Minimum" max="@Model.Settings.Maximum" class="form-control"/>
|
||||
<label >Inbound Sats</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12 col-md-8">
|
||||
<p class="mb-0">Base fee: @Model.Settings.BaseFee sats, fee per inbound sat: @Model.Settings.FeePerSat sats</p>
|
||||
<p class="mb-2 w-100" id="cost"></p>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-6">
|
||||
<button type="submit" class="btn btn-primary btn-lg">Purchase</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="powered__by__btcpayserver col-12">
|
||||
Powered by <a target="_blank" href="https://github.com/btcpayserver/btcpayserver" rel="noreferrer noopener">BTCPay Server</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,19 +0,0 @@
|
||||
@using BTCPayServer.Plugins.LSP
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject IScopeProvider ScopeProvider
|
||||
@{
|
||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||
var isActive = !string.IsNullOrEmpty(storeId) && ViewContext.RouteData.Values.TryGetValue("Controller", out var controller) && controller is not null &&
|
||||
nameof(LSPController).StartsWith(controller?.ToString(), StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(storeId))
|
||||
{
|
||||
<li class="nav-item">
|
||||
<a asp-area="" asp-controller="LSP" asp-action="UpdateLSPSettings" asp-route-storeId="@storeId" class="nav-link js-scroll-trigger @(isActive? "active": string.Empty)">
|
||||
<svg role="img" class="icon">
|
||||
</svg>
|
||||
<span>LSP</span>
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
@using BTCPayServer.Client
|
||||
@using BTCPayServer.Plugins.LSP
|
||||
@using Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@using Microsoft.AspNetCore.Routing
|
||||
@using BTCPayServer.Abstractions.Contracts
|
||||
@inject BTCPayServerClient BTCPayServerClient
|
||||
@inject LSPService LSPService
|
||||
@inject IScopeProvider ScopeProvider
|
||||
@{
|
||||
var storeId = ScopeProvider.GetCurrentStoreId();
|
||||
|
||||
LSPSettings settings = null;
|
||||
if (!string.IsNullOrEmpty(storeId))
|
||||
{
|
||||
try
|
||||
{
|
||||
settings = await LSPService.GetLSPForStore(storeId);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@if (!string.IsNullOrEmpty(storeId))
|
||||
{
|
||||
<li class="list-group-item bg-tile ">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="d-flex flex-wrap flex-fill flex-column flex-sm-row">
|
||||
<strong class="me-3">
|
||||
LSP
|
||||
</strong>
|
||||
<span title="" class="d-flex me-3">
|
||||
<span class="text-secondary">Sell lightning channel inbound liquidity using BTCPay Server</span>
|
||||
</span>
|
||||
</span>
|
||||
<span class="d-flex align-items-center fw-semibold">
|
||||
@if (settings?.Enabled is true)
|
||||
{
|
||||
<span class="d-flex align-items-center text-success">
|
||||
<span class="me-2 btcpay-status btcpay-status--enabled"></span>
|
||||
Active
|
||||
</span>
|
||||
<span class="text-light ms-3 me-2">|</span>
|
||||
<a lass="btn btn-link px-1 py-1 fw-semibold" asp-controller="LSP" asp-action="UpdateLSPSettings" asp-route-storeId="@storeId">
|
||||
Modify
|
||||
</a>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span class="d-flex align-items-center text-danger">
|
||||
<span class="me-2 btcpay-status btcpay-status--disabled"></span>
|
||||
Disabled
|
||||
</span>
|
||||
<a class="btn btn-primary btn-sm ms-4 px-3 py-1 fw-semibold" asp-controller="LSP" asp-action="UpdateLSPSettings" asp-route-storeId="@storeId">
|
||||
Setup
|
||||
</a>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
@using BTCPayServer.Abstractions.Services
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@inject Safe Safe
|
||||
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Configurations>Debug;Release;Altcoins-Debug;Altcoins-Release</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
@@ -11,7 +11,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Liquid+</Product>
|
||||
<Description>Enhanced support for the liquid network.</Description>
|
||||
<Version>1.1.2</Version>
|
||||
<Version>1.1.3</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Plugin development properties -->
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace BTCPayServer.Plugins.LiquidPlus
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.11.0" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection services)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Nostr </Product>
|
||||
<Description>Allows you to verify your nostr account with NIP5 and zap like the rest of the crazies</Description>
|
||||
<Version>1.1.2</Version>
|
||||
<Version>1.1.3</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
@@ -35,7 +35,7 @@
|
||||
<ProjectReference Include="..\..\submodules\btcpayserver\BTCPayServer\BTCPayServer.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NNostr.Client" Version="0.0.37" />
|
||||
<PackageReference Include="NNostr.Client" Version="0.0.38" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources" />
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace BTCPayServer.Plugins.NIP05
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.11.0"}
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
|
||||
};
|
||||
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>Prism</Product>
|
||||
<Description>Automated value splits for Bitcoin.</Description>
|
||||
<Version>1.2.0</Version>
|
||||
<Version>1.2.1</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -11,7 +11,7 @@ public class PrismPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.11.5"}
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
|
||||
};
|
||||
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
|
||||
@@ -177,8 +177,7 @@ namespace BTCPayServer.Plugins.Prism
|
||||
.TryGetValue(network.CryptoCode,
|
||||
out var internalLightningNode))
|
||||
{
|
||||
lnClient = _lightningClientFactoryService.Create(internalLightningNode,
|
||||
network);
|
||||
lnClient = internalLightningNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
<!-- -->
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>SideShift</Product>
|
||||
<Description>Allows you to embed a SideShift conversion screen to allow customers to pay with altcoins.</Description>
|
||||
<Version>1.1.12</Version>
|
||||
<Version>1.1.13</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace BTCPayServer.Plugins.SideShift
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.10.0"}
|
||||
new() {Identifier = nameof(BTCPayServer), Condition = ">=1.12.0"}
|
||||
};
|
||||
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<PropertyGroup>
|
||||
<Product>TicketTailor</Product>
|
||||
<Description>Allows you to integrate with TicketTailor.com to sell tickets for Bitcoin</Description>
|
||||
<Version>1.0.13</Version>
|
||||
<Version>1.0.14</Version>
|
||||
</PropertyGroup>
|
||||
<!-- Plugin development properties -->
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace BTCPayServer.Plugins.TicketTailor
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.7.10" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
@@ -11,9 +11,9 @@
|
||||
|
||||
<!-- Plugin specific properties -->
|
||||
<PropertyGroup>
|
||||
<Product>Wabisabi Coinjoin</Product>
|
||||
<Product>Coinjoin</Product>
|
||||
<Description>Allows you to integrate your btcpayserver store with coinjoins.</Description>
|
||||
<Version>1.0.66</Version>
|
||||
<Version>1.0.67</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- Plugin development properties -->
|
||||
|
||||
@@ -472,8 +472,8 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
||||
{
|
||||
if (scriptInfos2.TryGetValue(s.ScriptPubKey, out var si))
|
||||
{
|
||||
var derivation = DerivationScheme.GetChild(si.Item2.Result.KeyPath).GetExtPubKeys().First()
|
||||
.PubKey;
|
||||
var derivation = DerivationScheme.GetChild(si.Item2.Result.KeyPath).GetExtPubKeys().First().PubKey;
|
||||
|
||||
var hdPubKey = new HdPubKey(derivation, kp.Derive(si.Item2.Result.KeyPath).KeyPath,
|
||||
LabelsArray.Empty,
|
||||
KeyState.Used);
|
||||
@@ -536,24 +536,24 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
||||
{
|
||||
attachments.AddRange(result.HandledPayments.Select(payment => new Attachment("payout", payment.Value.Identifier)));
|
||||
}
|
||||
await _walletRepository.AddWalletTransactionAttachment(
|
||||
new WalletId(StoreId, "BTC"),
|
||||
result.UnsignedCoinJoin.GetHash(),
|
||||
attachments);
|
||||
List<(WalletId wallet, string id, IEnumerable<Attachment> attachments, string type )> objects = new();
|
||||
|
||||
objects.Add((new WalletId(StoreId, "BTC"),
|
||||
result.UnsignedCoinJoin.GetHash().ToString(),
|
||||
attachments, "tx"));
|
||||
|
||||
var mixedCoins = smartTx.WalletOutputs.Where(coin =>
|
||||
coin.AnonymitySet > 1 && BlockchainAnalyzer.StdDenoms.Contains(coin.TxOut.Value.Satoshi));
|
||||
if (storeIdForutxo != StoreId)
|
||||
if (storeIdForutxo != StoreId)
|
||||
{
|
||||
await _walletRepository.AddWalletTransactionAttachment(
|
||||
new WalletId(storeIdForutxo, "BTC"),
|
||||
txHash,
|
||||
new List<Attachment>()
|
||||
|
||||
|
||||
objects.Add((new WalletId(storeIdForutxo, "BTC"),
|
||||
txHash.ToString(), new List<Attachment>()
|
||||
{
|
||||
new Attachment("coinjoin", result.RoundId.ToString(), JObject.FromObject(new CoinjoinData()
|
||||
{
|
||||
Transaction = txHash.ToString(),
|
||||
Transaction = txHash.ToString(),
|
||||
Round = result.RoundId.ToString(),
|
||||
CoinsOut = mixedCoins.Select(coin => new CoinjoinData.CoinjoinDataCoin()
|
||||
{
|
||||
@@ -564,83 +564,32 @@ public class BTCPayWallet : IWallet, IDestinationProvider
|
||||
CoordinatorName = coordinatorName
|
||||
})),
|
||||
new Attachment(coordinatorName, null, null)
|
||||
});
|
||||
}, "tx"));
|
||||
|
||||
}
|
||||
|
||||
foreach (var mixedCoin in mixedCoins)
|
||||
{
|
||||
objects.Add((new WalletId(storeIdForutxo, "BTC"),
|
||||
mixedCoin.Outpoint.ToString(),
|
||||
new[]
|
||||
{
|
||||
new Attachment("anonset", mixedCoin.AnonymitySet.ToString(), JObject.FromObject(new
|
||||
{
|
||||
Tooltip =
|
||||
$"This coin has an anonset score of {mixedCoin.AnonymitySet.ToString()} (anonset-{mixedCoin.AnonymitySet.ToString()})"
|
||||
}))
|
||||
}, "utxo"));
|
||||
|
||||
|
||||
foreach (var mixedCoin in mixedCoins)
|
||||
{
|
||||
await _walletRepository.AddWalletTransactionAttachment(new WalletId(storeIdForutxo, "BTC"),
|
||||
mixedCoin.Outpoint.ToString(),
|
||||
new[] {new Attachment("anonset", mixedCoin.AnonymitySet.ToString(), JObject.FromObject(new
|
||||
{
|
||||
Tooltip = $"This coin has an anonset score of {mixedCoin.AnonymitySet.ToString()} (anonset-{mixedCoin.AnonymitySet.ToString()})"
|
||||
}))}, "utxo");
|
||||
}
|
||||
|
||||
}
|
||||
_smartifier.SmartTransactions.AddOrReplace(txHash, Task.FromResult(smartTx));
|
||||
smartTx.WalletOutputs.ForEach(coin =>
|
||||
{
|
||||
|
||||
_smartifier.Coins.AddOrReplace(coin.Outpoint, Task.FromResult(coin));
|
||||
});
|
||||
|
||||
//
|
||||
// var kp = await ExplorerClient.GetMetadataAsync<RootedKeyPath>(DerivationScheme,
|
||||
// WellknownMetadataKeys.AccountKeyPath);
|
||||
//
|
||||
// var stopwatch = Stopwatch.StartNew();
|
||||
// Logger.LogInformation($"Registering coinjoin result for {StoreId}");
|
||||
//
|
||||
// var storeIdForutxo = WabisabiStoreSettings.PlebMode ||
|
||||
// string.IsNullOrEmpty(WabisabiStoreSettings.MixToOtherWallet)? StoreId: WabisabiStoreSettings.MixToOtherWallet;
|
||||
// var client = await BtcPayServerClientFactory.Create(null, StoreId);
|
||||
// BTCPayServerClient utxoClient = client;
|
||||
// DerivationStrategyBase utxoDerivationScheme = DerivationScheme;
|
||||
// if (storeIdForutxo != StoreId)
|
||||
// {
|
||||
// utxoClient = await BtcPayServerClientFactory.Create(null, storeIdForutxo);
|
||||
// var pm = await utxoClient.GetStoreOnChainPaymentMethod(storeIdForutxo, "BTC");
|
||||
// utxoDerivationScheme = ExplorerClient.Network.DerivationStrategyFactory.Parse(pm.DerivationScheme);
|
||||
// }
|
||||
// var kp = await ExplorerClient.GetMetadataAsync<RootedKeyPath>(DerivationScheme,
|
||||
// WellknownMetadataKeys.AccountKeyPath);
|
||||
//
|
||||
// //mark the tx as a coinjoin at a specific coordinator
|
||||
// var txObject = new AddOnChainWalletObjectRequest() {Id = result.UnsignedCoinJoin.GetHash().ToString(), Type = "tx"};
|
||||
//
|
||||
// var labels = new[]
|
||||
// {
|
||||
// new AddOnChainWalletObjectRequest() {Id = "coinjoin", Type = "label"},
|
||||
// new AddOnChainWalletObjectRequest() {Id = coordinatorName, Type = "label"}
|
||||
// };
|
||||
//
|
||||
|
||||
//
|
||||
// await client.AddOrUpdateOnChainWalletObject(StoreId, "BTC", txObject);
|
||||
// if(storeIdForutxo != StoreId)
|
||||
// await utxoClient.AddOrUpdateOnChainWalletObject(storeIdForutxo, "BTC", txObject);
|
||||
|
||||
// foreach (var label in labels)
|
||||
// {
|
||||
// await client.AddOrUpdateOnChainWalletObject(StoreId, "BTC", label);
|
||||
// await client.AddOrUpdateOnChainWalletLink(StoreId, "BTC", txObject, new AddOnChainWalletObjectLinkRequest()
|
||||
// {
|
||||
// Id = label.Id,
|
||||
// Type = label.Type
|
||||
// }, CancellationToken.None);
|
||||
//
|
||||
// if (storeIdForutxo != StoreId)
|
||||
// {await utxoClient.AddOrUpdateOnChainWalletObject(storeIdForutxo, "BTC", label);
|
||||
// await utxoClient.AddOrUpdateOnChainWalletLink(storeIdForutxo, "BTC", txObject, new AddOnChainWalletObjectLinkRequest()
|
||||
// {
|
||||
// Id = label.Id,
|
||||
// Type = label.Type
|
||||
// }, CancellationToken.None);
|
||||
// }
|
||||
// }
|
||||
});
|
||||
|
||||
await _walletRepository.AddWalletTransactionAttachments(objects.ToArray());
|
||||
|
||||
stopwatch.Stop();
|
||||
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
@using BTCPayServer.Components.TruncateCenter
|
||||
@using BTCPayServer.Payments
|
||||
@using BTCPayServer
|
||||
@using BTCPayServer.Services
|
||||
@model BTCPayServer.Plugins.Wabisabi.BTCPayWallet.CoinjoinData.CoinjoinDataCoin
|
||||
@inject BTCPayNetworkProvider BtcPayNetworkProvider
|
||||
@inject TransactionLinkProviders TransactionLinkProviders
|
||||
@{
|
||||
var op = OutPoint.Parse(Model.Outpoint);
|
||||
var text = Model.Outpoint;
|
||||
}
|
||||
<li class="bg-none list-group-item d-flex border-0 d-flex px-1 ">
|
||||
<div class="truncate-center-id border-0 p-0 d-flex ">
|
||||
<vc:truncate-center text="@text" link="@BitcoinPaymentType.Instance.GetTransactionLink(BtcPayNetworkProvider.BTC, op.Hash.ToString())" classes="truncate-center-id h-100 border-end-0"/>
|
||||
<vc:truncate-center text="@text" link="@TransactionLinkProviders.GetTransactionLink(new PaymentMethodId("BTC", BitcoinPaymentType.Instance), op.Hash.ToString())" classes="truncate-center-id h-100 border-end-0"/>
|
||||
<div class="card truncate-center-id " style="border-left-style: dashed; min-width: 110px;">
|
||||
<span class="text-nowrap">@Model.Amount BTC</span>
|
||||
<span class="text-nowrap">
|
||||
|
||||
@@ -2,13 +2,16 @@
|
||||
@using BTCPayServer.Abstractions.Extensions
|
||||
@using BTCPayServer.Payments
|
||||
@using BTCPayServer.Plugins.Wabisabi
|
||||
@using BTCPayServer.Services
|
||||
@using NBitcoin
|
||||
@using WalletWasabi.Blockchain.Analysis
|
||||
@model List<BTCPayServer.Plugins.Wabisabi.BTCPayWallet.CoinjoinData>
|
||||
@inject BTCPayNetworkProvider BtcPayNetworkProvider
|
||||
@inject TransactionLinkProviders TransactionLinkProviders
|
||||
@{
|
||||
var network = BtcPayNetworkProvider.BTC;
|
||||
var mainnet = BtcPayNetworkProvider.NetworkType == ChainName.Mainnet;
|
||||
var pmi = new PaymentMethodId( network.CryptoCode, PaymentTypes.BTCLike);
|
||||
|
||||
}
|
||||
|
||||
<div class="table-responsive" style=" transform: rotateX(180deg);">
|
||||
@@ -44,7 +47,7 @@
|
||||
<span class="text-break">@cjData.CoordinatorName</span>
|
||||
</td>
|
||||
<td>
|
||||
<vc:truncate-center text="@cjData.Transaction" link="@BitcoinPaymentType.Instance.GetTransactionLink(network, cjData.Transaction)" classes="truncate-center-id"/>
|
||||
<vc:truncate-center text="@cjData.Transaction" link="@TransactionLinkProviders.GetTransactionLink(pmi, cjData.Transaction)" classes="truncate-center-id"/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="card truncate-center-id">
|
||||
|
||||
@@ -452,8 +452,7 @@
|
||||
</form>
|
||||
@if (!liteMode)
|
||||
{
|
||||
|
||||
<partial name="Wabisabi/AddCoordinatorPrompt" model="@(new DiscoveredCoordinator())"/>
|
||||
<partial name="Wabisabi/AddCoordinatorPrompt" model="@(new DiscoveredCoordinator())"/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
@@ -30,7 +29,7 @@ public class WabisabiPlugin : BaseBTCPayServerPlugin
|
||||
{
|
||||
public override IBTCPayServerPlugin.PluginDependency[] Dependencies { get; } =
|
||||
{
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.11.5" }
|
||||
new() { Identifier = nameof(BTCPayServer), Condition = ">=1.12.0" }
|
||||
};
|
||||
public override void Execute(IServiceCollection applicationBuilder)
|
||||
{
|
||||
|
||||
Submodule submodules/btcpayserver updated: a921504bcf...7066a2a577
Submodule submodules/walletwasabi updated: f2fda44cd9...298bb95e09
Reference in New Issue
Block a user