Compare commits
181 Commits
v1.11.5
...
v1.12.4-rc
Author | SHA1 | Date | |
---|---|---|---|
b03f8db06b | |||
27e70a169e | |||
6a1d17dda2 | |||
31bc6dd48c | |||
6054315d84 | |||
2a7059ddeb | |||
e2e7e59722 | |||
8b373bda8e | |||
d6806dc1f6 | |||
a753698ae7 | |||
3eec9cb0bb | |||
cd8ef0c1ff | |||
bd196ad963 | |||
1ad93838c9 | |||
a9252fd741 | |||
376067324b | |||
dd7ab2f647 | |||
1d6d146fb2 | |||
3ae1f13323 | |||
0b0a8f8218 | |||
f070b22355 | |||
c5a0e28420 | |||
70e9ea1d5e | |||
89d294524a | |||
5e25ee2996 | |||
5935dbf1d1 | |||
f7542c988d | |||
e90414bded | |||
78882dcff0 | |||
1ac1443070 | |||
b5405e9313 | |||
c7eef01fd5 | |||
26f61d35bb | |||
765776c429 | |||
9f54074d03 | |||
f23078df1c | |||
a35bf54a02 | |||
4867698ac9 | |||
e84e575017 | |||
c585a0b276 | |||
ad89139e07 | |||
ebc053aca5 | |||
96da7f0322 | |||
8ae9e59d9d | |||
c94dc87cb8 | |||
20512a59b3 | |||
b3f9216c54 | |||
1cda0360e9 | |||
7f75117bfa | |||
5a70345499 | |||
5114a3a2ea | |||
93ab219124 | |||
61bf6d33b2 | |||
3fc687a2d4 | |||
8da04fd7e2 | |||
cb54f8f6d1 | |||
6ecfe073e7 | |||
ea2648f08f | |||
40adf7acd2 | |||
850af216bd | |||
bf6200d55c | |||
93bb85ffaa | |||
2fa7745886 | |||
2714907aef | |||
0d61e45cc6 | |||
541cef55b8 | |||
e3863ac076 | |||
0e2379caa6 | |||
a17c486f81 | |||
e4aaff5e34 | |||
97fda9d362 | |||
7a06423bc7 | |||
26374ef476 | |||
6324a1a1e8 | |||
b751e23e93 | |||
72ee65843d | |||
d413dd9257 | |||
433adf4668 | |||
d78267d7ee | |||
0c16492d1c | |||
eda437995f | |||
379286c366 | |||
3f344f2c0c | |||
d050c8e3b2 | |||
b13f140b86 | |||
7066a2a577 | |||
a8ebaa6784 | |||
60ff7e86b8 | |||
44b7ed0e6e | |||
afed3a0899 | |||
28265b30d2 | |||
a97172cea6 | |||
605741182d | |||
2c94a87be4 | |||
6f98d5aa20 | |||
3d08e70101 | |||
bdf56c0a6f | |||
b9b3860e6b | |||
b0554bbf17 | |||
b31f1812d2 | |||
04292d09e1 | |||
1081eab9db | |||
4023b24209 | |||
bac9ab08d1 | |||
75bf8a5086 | |||
3ffae30b95 | |||
2fda9cf539 | |||
c8b9a425b8 | |||
62865d7d88 | |||
3afd24fcd7 | |||
fd582aad75 | |||
f9155772f5 | |||
2e4313bf18 | |||
5ad320ee4b | |||
d46543ae16 | |||
2f23bad3bc | |||
6d288271cd | |||
b4daa76aeb | |||
5bd8067328 | |||
9ccc42f556 | |||
3ee4f43eb5 | |||
d1bf47a5c0 | |||
ccf9cfa332 | |||
773f8a9aea | |||
dd62e166a1 | |||
2fb72d5aa6 | |||
46f0818765 | |||
96569ae4aa | |||
f2b1e5f93e | |||
2326894a2b | |||
c15f02ddbf | |||
7708084331 | |||
696a414e95 | |||
c16dfb2dcb | |||
c979c4774c | |||
e82281d273 | |||
27c22d5e33 | |||
6acc545b66 | |||
609ec0989f | |||
b702621a04 | |||
89041a6744 | |||
c485c109e6 | |||
29a49d5f71 | |||
a5fafc4864 | |||
a921504bcf | |||
027154a4d3 | |||
bf1a1368ff | |||
097ffbf8a3 | |||
ec076d1560 | |||
8dadfa2111 | |||
c8ee6ead0b | |||
018e4c501d | |||
99a0b70cfa | |||
314a1352ec | |||
901e6be21e | |||
d58dde950e | |||
8ac18b74df | |||
2846c38ff5 | |||
d44efce225 | |||
d3dca7e808 | |||
41e3828eea | |||
9e76b4d28e | |||
ef03497350 | |||
e5a2aeb145 | |||
229a4ea56c | |||
f20e6d3768 | |||
1d210eb6e3 | |||
d8422a979f | |||
0cf6d39f02 | |||
076c20a3b7 | |||
0cfb0ba890 | |||
44a7e9387e | |||
e71954ee34 | |||
9cd9e84be6 | |||
25af9c4227 | |||
72a99bf9a6 | |||
f1228523cb | |||
a45d368115 | |||
16433dc183 | |||
0a956fdc73 | |||
75396f491b |
@ -31,79 +31,23 @@ jobs:
|
||||
- run:
|
||||
command: |
|
||||
curl -X POST -H "Authorization: token $GH_PAT" -H "Accept: application/vnd.github.everest-preview+json" -H "Content-Type: application/json" https://api.github.com/repos/btcpayserver/btcpayserver-doc/dispatches --data '{"event_type": "build_docs"}'
|
||||
|
||||
# publish jobs require $DOCKERHUB_REPO, $DOCKERHUB_USER, $DOCKERHUB_PASS defined
|
||||
amd64:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
docker:
|
||||
docker:
|
||||
- image: cimg/base:stable
|
||||
steps:
|
||||
- setup_remote_docker
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-amd64 -f amd64.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 -f amd64.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-amd64
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64
|
||||
|
||||
arm32v7:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull --build-arg CONFIGURATION_NAME=Altcoins-Release -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 -f arm32v7.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm32v7
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7
|
||||
|
||||
arm64v8:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
command: |
|
||||
sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
GIT_COMMIT=$(git rev-parse HEAD)
|
||||
#
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --pull -t $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker build --build-arg GIT_COMMIT=${GIT_COMMIT} --build-arg CONFIGURATION_NAME=Altcoins-Release --pull -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 -f arm64v8.Dockerfile .
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
|
||||
sudo docker push $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
|
||||
|
||||
multiarch:
|
||||
machine:
|
||||
image: ubuntu-2004:202201-02
|
||||
steps:
|
||||
- run:
|
||||
command: |
|
||||
sudo docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
#
|
||||
LATEST_TAG=${CIRCLE_TAG:1} #trim v from tag
|
||||
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-arm64v8
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-amd64 --os linux --arch amd64
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm32v7 --os linux --arch arm --variant v7
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG $DOCKERHUB_REPO:$LATEST_TAG-arm64v8 --os linux --arch arm64 --variant v8
|
||||
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG -p
|
||||
|
||||
sudo docker manifest create --amend $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-amd64 --os linux --arch amd64
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm32v7 --os linux --arch arm --variant v7
|
||||
sudo docker manifest annotate $DOCKERHUB_REPO:$LATEST_TAG-altcoins $DOCKERHUB_REPO:$LATEST_TAG-altcoins-arm64v8 --os linux --arch arm64 --variant v8
|
||||
sudo docker manifest push $DOCKERHUB_REPO:$LATEST_TAG-altcoins -p
|
||||
|
||||
docker login --username=$DOCKERHUB_USER --password=$DOCKERHUB_PASS
|
||||
docker buildx create --use
|
||||
DOCKER_BUILDX_OPTS="--platform linux/amd64,linux/arm64,linux/arm/v7 --build-arg GIT_COMMIT=${GIT_COMMIT} --push"
|
||||
docker buildx build $DOCKER_BUILDX_OPTS -t $DOCKERHUB_REPO:$LATEST_TAG .
|
||||
docker buildx build $DOCKER_BUILDX_OPTS -t $DOCKERHUB_REPO:$LATEST_TAG-altcoins --build-arg CONFIGURATION_NAME=Altcoins-Release .
|
||||
workflows:
|
||||
version: 2
|
||||
build_and_test:
|
||||
@ -120,7 +64,7 @@ workflows:
|
||||
# only act on version tags
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- amd64:
|
||||
- docker:
|
||||
filters:
|
||||
# ignore any commit on any branch by default
|
||||
branches:
|
||||
@ -130,25 +74,3 @@ workflows:
|
||||
# OR features on specific versions like v1.0.0.88-lndseedbackup-1
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- arm32v7:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- arm64v8:
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
- multiarch:
|
||||
requires:
|
||||
- amd64
|
||||
- arm32v7
|
||||
- arm64v8
|
||||
filters:
|
||||
branches:
|
||||
ignore: /.*/
|
||||
tags:
|
||||
only: /(v[1-9]+(\.[0-9]+)*(-[a-z0-9-]+)?)|(v[a-z0-9-]+)/
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -298,4 +298,5 @@ Packed Plugins
|
||||
Plugins/packed
|
||||
|
||||
BTCPayServer/wwwroot/swagger/v1/openapi.json
|
||||
BTCPayServer/appsettings.dev.json
|
||||
BTCPayServer/appsettings.dev.json
|
||||
BTCPayServer.Tests/monero_wallet
|
||||
|
@ -31,11 +31,11 @@
|
||||
<None Include="icon.png" Pack="true" PackagePath="\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="HtmlSanitizer" Version="5.0.372" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.9" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.7" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="6.0.1" />
|
||||
<PackageReference Include="HtmlSanitizer" Version="8.0.723" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.0" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0-beta.2" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Client\BTCPayServer.Client.csproj" />
|
||||
|
@ -84,6 +84,7 @@ namespace BTCPayServer.Abstractions.Contracts
|
||||
.UseNpgsql(_options.Value.ConnectionString, o =>
|
||||
{
|
||||
o.EnableRetryOnFailure(10);
|
||||
o.SetPostgresVersion(12, 0);
|
||||
if (!string.IsNullOrEmpty(_schemaPrefix))
|
||||
{
|
||||
o.MigrationsHistoryTable(_schemaPrefix);
|
||||
|
@ -20,6 +20,15 @@ namespace BTCPayServer.Abstractions.Extensions
|
||||
Relative
|
||||
}
|
||||
|
||||
public static void SetBlazorAllowed(this ViewDataDictionary viewData, bool allowed)
|
||||
{
|
||||
viewData["BlazorAllowed"] = allowed;
|
||||
}
|
||||
public static bool IsBlazorAllowed(this ViewDataDictionary viewData)
|
||||
{
|
||||
return viewData["BlazorAllowed"] is not false;
|
||||
}
|
||||
|
||||
public static void SetActivePage<T>(this ViewDataDictionary viewData, T activePage, string title = null, string activeId = null)
|
||||
where T : IConvertible
|
||||
{
|
||||
|
@ -5,7 +5,6 @@ using System.Reflection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Npgsql.Internal.TypeHandlers.GeometricHandlers;
|
||||
|
||||
namespace BTCPayServer.Abstractions.Form;
|
||||
|
||||
@ -105,31 +104,7 @@ public class Form
|
||||
}
|
||||
}
|
||||
|
||||
public void SetValues(JObject values)
|
||||
{
|
||||
var fields = GetAllFields().ToDictionary(k => k.FullName, k => k.Field);
|
||||
SetValues(fields, new List<string>(), values);
|
||||
}
|
||||
|
||||
private void SetValues(Dictionary<string, Field> fields, List<string> path, JObject values)
|
||||
{
|
||||
foreach (var prop in values.Properties())
|
||||
{
|
||||
List<string> propPath = new List<string>(path.Count + 1);
|
||||
propPath.AddRange(path);
|
||||
propPath.Add(prop.Name);
|
||||
if (prop.Value.Type == JTokenType.Object)
|
||||
{
|
||||
SetValues(fields, propPath, (JObject)prop.Value);
|
||||
}
|
||||
else if (prop.Value.Type == JTokenType.String)
|
||||
{
|
||||
var fullName = string.Join('_', propPath.Where(s => !string.IsNullOrEmpty(s)));
|
||||
if (fields.TryGetValue(fullName, out var f) && !f.Constant)
|
||||
f.Value = prop.Value.Value<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
using System.Web;
|
||||
using Ganss.XSS;
|
||||
using Ganss.Xss;
|
||||
using Microsoft.AspNetCore.Html;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
|
@ -2,12 +2,13 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer.Abstractions.TagHelpers;
|
||||
|
||||
[HtmlTargetElement(Attributes = "[permission]")]
|
||||
[HtmlTargetElement(Attributes = "[not-permission]" )]
|
||||
[HtmlTargetElement(Attributes = "[not-permission]")]
|
||||
public class PermissionTagHelper : TagHelper
|
||||
{
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
@ -22,29 +23,72 @@ public class PermissionTagHelper : TagHelper
|
||||
public string Permission { get; set; }
|
||||
public string NotPermission { get; set; }
|
||||
public string PermissionResource { get; set; }
|
||||
|
||||
public bool AndMode { get; set; } = false;
|
||||
|
||||
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Permission) && string.IsNullOrEmpty(NotPermission))
|
||||
var permissions = Permission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||||
var notPermissions = NotPermission?.Split(',', StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
|
||||
|
||||
if (!permissions.Any() && !notPermissions.Any())
|
||||
return;
|
||||
if (_httpContextAccessor.HttpContext is null)
|
||||
return;
|
||||
|
||||
var expectedResult = !string.IsNullOrEmpty(Permission);
|
||||
var key = $"{Permission??NotPermission}_{PermissionResource}";
|
||||
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var o) ||
|
||||
o is not AuthorizationResult res)
|
||||
bool shouldRender = true; // Assume tag should be rendered unless a check fails
|
||||
|
||||
// Process 'Permission' - User must have these permissions
|
||||
if (permissions.Any())
|
||||
{
|
||||
res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
|
||||
PermissionResource,
|
||||
Permission);
|
||||
_httpContextAccessor.HttpContext.Items.Add(key, res);
|
||||
bool finalResult = AndMode;
|
||||
foreach (var perm in permissions)
|
||||
{
|
||||
var key = $"{perm}_{PermissionResource}";
|
||||
AuthorizationResult res = await GetOrAddAuthorizationResult(key, perm);
|
||||
|
||||
if (AndMode)
|
||||
finalResult &= res.Succeeded;
|
||||
else
|
||||
finalResult |= res.Succeeded;
|
||||
|
||||
if (!AndMode && finalResult) break;
|
||||
}
|
||||
|
||||
shouldRender = finalResult;
|
||||
}
|
||||
if (expectedResult != res.Succeeded)
|
||||
|
||||
// Process 'NotPermission' - User must not have these permissions
|
||||
if (shouldRender && notPermissions.Any())
|
||||
{
|
||||
foreach (var notPerm in notPermissions)
|
||||
{
|
||||
var key = $"{notPerm}_{PermissionResource}";
|
||||
AuthorizationResult res = await GetOrAddAuthorizationResult(key, notPerm);
|
||||
|
||||
if (res.Succeeded) // If the user has a 'NotPermission', they should not see the tag
|
||||
{
|
||||
shouldRender = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!shouldRender)
|
||||
{
|
||||
output.SuppressOutput();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<AuthorizationResult> GetOrAddAuthorizationResult(string key, string permission)
|
||||
{
|
||||
if (!_httpContextAccessor.HttpContext.Items.TryGetValue(key, out var cachedResult))
|
||||
{
|
||||
var res = await _authorizationService.AuthorizeAsync(_httpContextAccessor.HttpContext.User,
|
||||
PermissionResource, permission);
|
||||
_httpContextAccessor.HttpContext.Items[key] = res;
|
||||
return res;
|
||||
}
|
||||
|
||||
return cachedResult as AuthorizationResult;
|
||||
}
|
||||
}
|
||||
|
@ -30,8 +30,8 @@
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.3.21" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.24" />
|
||||
<PackageReference Include="BTCPayServer.Lightning.Common" Version="1.5.1" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.34" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -20,6 +20,12 @@ namespace BTCPayServer.Client
|
||||
return await HandleResponse<PullPaymentData>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<RegisterBoltcardResponse> RegisterBoltcard(string pullPaymentId, RegisterBoltcardRequest request, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var response = await _httpClient.SendAsync(CreateHttpRequest($"api/v1/pull-payments/{HttpUtility.UrlEncode(pullPaymentId)}/boltcards", bodyPayload: request, method: HttpMethod.Post), cancellationToken);
|
||||
return await HandleResponse<RegisterBoltcardResponse>(response);
|
||||
}
|
||||
|
||||
public virtual async Task<PullPaymentData[]> GetPullPayments(string storeId, bool includeArchived = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Dictionary<string, object> query = new Dictionary<string, object>();
|
||||
|
@ -27,8 +27,10 @@ namespace BTCPayServer.Client.Models
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public PosViewType DefaultView { get; set; }
|
||||
public bool ShowCustomAmount { get; set; } = false;
|
||||
public bool ShowDiscount { get; set; } = true;
|
||||
public bool EnableTips { get; set; } = true;
|
||||
public bool ShowDiscount { get; set; } = false;
|
||||
public bool ShowSearch { get; set; } = true;
|
||||
public bool ShowCategories { get; set; } = true;
|
||||
public bool EnableTips { get; set; } = false;
|
||||
public string CustomAmountPayButtonText { get; set; } = null;
|
||||
public string FixedAmountPayButtonText { get; set; } = null;
|
||||
public string TipText { get; set; } = null;
|
||||
@ -40,7 +42,6 @@ namespace BTCPayServer.Client.Models
|
||||
public bool? Archived { get; set; } = null;
|
||||
public string FormId { get; set; } = null;
|
||||
public string EmbeddedCSS { get; set; } = null;
|
||||
public CheckoutType? CheckoutType { get; set; } = null;
|
||||
}
|
||||
|
||||
public enum CrowdfundResetEvery
|
||||
|
@ -1,12 +1,12 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
namespace BTCPayServer.Client.Models;
|
||||
public enum InvoiceExceptionStatus
|
||||
{
|
||||
public enum InvoiceExceptionStatus
|
||||
{
|
||||
None,
|
||||
PaidLate,
|
||||
PaidPartial,
|
||||
Marked,
|
||||
Invalid,
|
||||
PaidOver
|
||||
}
|
||||
None,
|
||||
PaidLate,
|
||||
PaidPartial,
|
||||
Marked,
|
||||
Invalid,
|
||||
PaidOver
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,12 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class LNURLPayPaymentMethodBaseData
|
||||
{
|
||||
public bool UseBech32Scheme { get; set; }
|
||||
|
||||
[JsonProperty("lud12Enabled")]
|
||||
public bool LUD12Enabled { get; set; }
|
||||
|
||||
public LNURLPayPaymentMethodBaseData()
|
||||
|
@ -16,11 +16,12 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
}
|
||||
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme)
|
||||
public LNURLPayPaymentMethodData(string cryptoCode, bool enabled, bool useBech32Scheme, bool lud12Enabled)
|
||||
{
|
||||
Enabled = enabled;
|
||||
CryptoCode = cryptoCode;
|
||||
UseBech32Scheme = useBech32Scheme;
|
||||
LUD12Enabled = lud12Enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ namespace BTCPayServer.Client.Models
|
||||
public string DefaultView { get; set; }
|
||||
public bool ShowCustomAmount { get; set; }
|
||||
public bool ShowDiscount { get; set; }
|
||||
public bool ShowSearch { get; set; }
|
||||
public bool ShowCategories { get; set; }
|
||||
public bool EnableTips { get; set; }
|
||||
public string Currency { get; set; }
|
||||
public object Items { get; set; }
|
||||
|
39
BTCPayServer.Client/Models/RegisterBoltcardRequest.cs
Normal file
39
BTCPayServer.Client/Models/RegisterBoltcardRequest.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using NBitcoin.JsonConverters;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public enum OnExistingBehavior
|
||||
{
|
||||
KeepVersion,
|
||||
UpdateVersion
|
||||
}
|
||||
public class RegisterBoltcardRequest
|
||||
{
|
||||
[JsonConverter(typeof(HexJsonConverter))]
|
||||
[JsonProperty("UID")]
|
||||
public byte[] UID { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public OnExistingBehavior? OnExisting { get; set; }
|
||||
}
|
||||
public class RegisterBoltcardResponse
|
||||
{
|
||||
[JsonProperty("LNURLW")]
|
||||
public string LNURLW { get; set; }
|
||||
public int Version { get; set; }
|
||||
[JsonProperty("K0")]
|
||||
public string K0 { get; set; }
|
||||
[JsonProperty("K1")]
|
||||
public string K1 { get; set; }
|
||||
[JsonProperty("K2")]
|
||||
public string K2 { get; set; }
|
||||
[JsonProperty("K3")]
|
||||
public string K3 { get; set; }
|
||||
[JsonProperty("K4")]
|
||||
public string K4 { get; set; }
|
||||
}
|
||||
}
|
@ -37,8 +37,11 @@ namespace BTCPayServer.Client.Models
|
||||
public bool AnyoneCanCreateInvoice { get; set; }
|
||||
public string DefaultCurrency { get; set; }
|
||||
public bool RequiresRefundEmail { get; set; }
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public CheckoutType CheckoutType { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public CheckoutType? CheckoutType { get; set; }
|
||||
|
||||
public bool LightningAmountInSatoshi { get; set; }
|
||||
public bool LightningPrivateRouteHints { get; set; }
|
||||
public bool OnChainWithLnInvoiceFallback { get; set; }
|
||||
@ -73,6 +76,17 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public bool PayJoinEnabled { get; set; }
|
||||
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? AutoDetectLanguage { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowPayInWalletButton { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? ShowStoreHeader { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? CelebratePayment { get; set; }
|
||||
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
|
||||
public bool? PlaySoundOnPayment { get; set; }
|
||||
|
||||
public InvoiceData.ReceiptOptions Receipt { get; set; }
|
||||
|
||||
|
||||
|
@ -11,8 +11,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public bool Everything { get; set; } = true;
|
||||
|
||||
[JsonProperty(ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
|
||||
public WebhookEventType[] SpecificEvents { get; set; } = Array.Empty<WebhookEventType>();
|
||||
public string[] SpecificEvents { get; set; } = Array.Empty<string>();
|
||||
}
|
||||
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
@ -9,7 +9,7 @@ namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class WebhookEvent
|
||||
{
|
||||
public readonly static JsonSerializerSettings DefaultSerializerSettings;
|
||||
public static readonly JsonSerializerSettings DefaultSerializerSettings;
|
||||
static WebhookEvent()
|
||||
{
|
||||
DefaultSerializerSettings = new JsonSerializerSettings();
|
||||
@ -45,8 +45,7 @@ namespace BTCPayServer.Client.Models
|
||||
}
|
||||
}
|
||||
public bool IsRedelivery { get; set; }
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WebhookEventType Type { get; set; }
|
||||
public string Type { get; set; }
|
||||
[JsonConverter(typeof(NBitcoin.JsonConverters.DateTimeToUnixTimeConverter))]
|
||||
public DateTimeOffset Timestamp { get; set; }
|
||||
[JsonExtensionData]
|
||||
|
@ -1,13 +1,20 @@
|
||||
namespace BTCPayServer.Client.Models
|
||||
namespace BTCPayServer.Client.Models;
|
||||
|
||||
public static class WebhookEventType
|
||||
{
|
||||
public enum WebhookEventType
|
||||
{
|
||||
InvoiceCreated,
|
||||
InvoiceReceivedPayment,
|
||||
InvoiceProcessing,
|
||||
InvoiceExpired,
|
||||
InvoiceSettled,
|
||||
InvoiceInvalid,
|
||||
InvoicePaymentSettled,
|
||||
}
|
||||
public const string InvoiceCreated = nameof(InvoiceCreated);
|
||||
public const string InvoiceReceivedPayment = nameof(InvoiceReceivedPayment);
|
||||
public const string InvoiceProcessing = nameof(InvoiceProcessing);
|
||||
public const string InvoiceExpired = nameof(InvoiceExpired);
|
||||
public const string InvoiceSettled = nameof(InvoiceSettled);
|
||||
public const string InvoiceInvalid = nameof(InvoiceInvalid);
|
||||
public const string InvoicePaymentSettled = nameof(InvoicePaymentSettled);
|
||||
public const string PayoutCreated = nameof(PayoutCreated);
|
||||
public const string PayoutApproved = nameof(PayoutApproved);
|
||||
public const string PayoutUpdated = nameof(PayoutUpdated);
|
||||
public const string PaymentRequestUpdated = nameof(PaymentRequestUpdated);
|
||||
public const string PaymentRequestCreated = nameof(PaymentRequestCreated);
|
||||
public const string PaymentRequestArchived = nameof(PaymentRequestArchived);
|
||||
public const string PaymentRequestStatusChanged = nameof(PaymentRequestStatusChanged);
|
||||
|
||||
}
|
||||
|
@ -1,44 +1,74 @@
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Client.Models
|
||||
{
|
||||
public class WebhookInvoiceEvent : WebhookEvent
|
||||
public class WebhookPayoutEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPayoutEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("payout", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 2)] public string PayoutId { get; set; }
|
||||
[JsonProperty(Order = 3)] public string PullPaymentId { get; set; }
|
||||
[JsonProperty(Order = 4)] [JsonConverter(typeof(StringEnumConverter))]public PayoutState PayoutState { get; set; }
|
||||
}
|
||||
public class WebhookPaymentRequestEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookPaymentRequestEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("paymentrequest", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 2)] public string PaymentRequestId { get; set; }
|
||||
[JsonProperty(Order = 3)] [JsonConverter(typeof(StringEnumConverter))]public PaymentRequestData.PaymentRequestStatus Status { get; set; }
|
||||
}
|
||||
|
||||
public abstract class StoreWebhookEvent : WebhookEvent
|
||||
{
|
||||
[JsonProperty(Order = 1)] public string StoreId { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceEvent : StoreWebhookEvent
|
||||
{
|
||||
public WebhookInvoiceEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceEvent(WebhookEventType evtType)
|
||||
{
|
||||
this.Type = evtType;
|
||||
public WebhookInvoiceEvent(string evtType, string storeId)
|
||||
{
|
||||
if (!evtType.StartsWith("invoice", StringComparison.InvariantCultureIgnoreCase))
|
||||
throw new ArgumentException("Invalid event type", nameof(evtType));
|
||||
Type = evtType;
|
||||
StoreId = storeId;
|
||||
}
|
||||
|
||||
[JsonProperty(Order = 1)] public string StoreId { get; set; }
|
||||
[JsonProperty(Order = 2)] public string InvoiceId { get; set; }
|
||||
[JsonProperty(Order = 3)] public JObject Metadata { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceSettledEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceSettledEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceSettledEvent(string storeId) : base(WebhookEventType.InvoiceSettled, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
public bool ManuallyMarked { get; set; }
|
||||
public bool OverPaid { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoiceInvalidEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceInvalidEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceInvalidEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceInvalidEvent(string storeId) : base(WebhookEventType.InvoiceInvalid, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,11 +77,7 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public class WebhookInvoiceProcessingEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceProcessingEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceProcessingEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceProcessingEvent(string storeId) : base(WebhookEventType.InvoiceProcessing, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
@ -60,38 +86,25 @@ namespace BTCPayServer.Client.Models
|
||||
|
||||
public class WebhookInvoiceReceivedPaymentEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceReceivedPaymentEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceReceivedPaymentEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceReceivedPaymentEvent(string type, string storeId) : base(type, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
public bool AfterExpiration { get; set; }
|
||||
public string PaymentMethod { get; set; }
|
||||
public InvoicePaymentMethodDataModel.Payment Payment { get; set; }
|
||||
public bool OverPaid { get; set; }
|
||||
}
|
||||
|
||||
public class WebhookInvoicePaymentSettledEvent : WebhookInvoiceReceivedPaymentEvent
|
||||
{
|
||||
public WebhookInvoicePaymentSettledEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoicePaymentSettledEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoicePaymentSettledEvent(string storeId) : base(WebhookEventType.InvoicePaymentSettled, storeId)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class WebhookInvoiceExpiredEvent : WebhookInvoiceEvent
|
||||
{
|
||||
public WebhookInvoiceExpiredEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public WebhookInvoiceExpiredEvent(WebhookEventType evtType) : base(evtType)
|
||||
public WebhookInvoiceExpiredEvent(string storeId) : base(WebhookEventType.InvoiceExpired, storeId)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ namespace BTCPayServer.Client
|
||||
public const string CanModifyStoreWebhooks = "btcpay.store.webhooks.canmodifywebhooks";
|
||||
public const string CanModifyStoreSettingsUnscoped = "btcpay.store.canmodifystoresettings:";
|
||||
public const string CanViewStoreSettings = "btcpay.store.canviewstoresettings";
|
||||
public const string CanViewReports = "btcpay.store.canviewreports";
|
||||
public const string CanViewInvoices = "btcpay.store.canviewinvoices";
|
||||
public const string CanCreateInvoice = "btcpay.store.cancreateinvoice";
|
||||
public const string CanModifyInvoices = "btcpay.store.canmodifyinvoices";
|
||||
@ -34,7 +35,10 @@ namespace BTCPayServer.Client
|
||||
public const string CanDeleteUser = "btcpay.user.candeleteuser";
|
||||
public const string CanManagePullPayments = "btcpay.store.canmanagepullpayments";
|
||||
public const string CanArchivePullPayments = "btcpay.store.canarchivepullpayments";
|
||||
public const string CanManagePayouts = "btcpay.store.canmanagepayouts";
|
||||
public const string CanViewPayouts = "btcpay.store.canviewpayouts";
|
||||
public const string CanCreatePullPayments = "btcpay.store.cancreatepullpayments";
|
||||
public const string CanViewPullPayments = "btcpay.store.canviewpullpayments";
|
||||
public const string CanCreateNonApprovedPullPayments = "btcpay.store.cancreatenonapprovedpullpayments";
|
||||
public const string CanViewCustodianAccounts = "btcpay.store.canviewcustodianaccounts";
|
||||
public const string CanManageCustodianAccounts = "btcpay.store.canmanagecustodianaccounts";
|
||||
@ -53,6 +57,7 @@ namespace BTCPayServer.Client
|
||||
yield return CanModifyServerSettings;
|
||||
yield return CanModifyStoreSettings;
|
||||
yield return CanViewStoreSettings;
|
||||
yield return CanViewReports;
|
||||
yield return CanViewPaymentRequests;
|
||||
yield return CanModifyPaymentRequests;
|
||||
yield return CanModifyProfile;
|
||||
@ -72,6 +77,7 @@ namespace BTCPayServer.Client
|
||||
yield return CanManagePullPayments;
|
||||
yield return CanArchivePullPayments;
|
||||
yield return CanCreatePullPayments;
|
||||
yield return CanViewPullPayments;
|
||||
yield return CanCreateNonApprovedPullPayments;
|
||||
yield return CanViewCustodianAccounts;
|
||||
yield return CanManageCustodianAccounts;
|
||||
@ -79,6 +85,8 @@ namespace BTCPayServer.Client
|
||||
yield return CanWithdrawFromCustodianAccounts;
|
||||
yield return CanTradeCustodianAccount;
|
||||
yield return CanManageUsers;
|
||||
yield return CanManagePayouts;
|
||||
yield return CanViewPayouts;
|
||||
}
|
||||
}
|
||||
public static bool IsValidPolicy(string policy)
|
||||
@ -252,11 +260,13 @@ namespace BTCPayServer.Client
|
||||
Policies.CanViewStoreSettings,
|
||||
Policies.CanModifyStoreWebhooks,
|
||||
Policies.CanModifyPaymentRequests,
|
||||
Policies.CanManagePayouts,
|
||||
Policies.CanUseLightningNodeInStore);
|
||||
|
||||
PolicyHasChild(policyMap,Policies.CanManageUsers, Policies.CanCreateUser);
|
||||
PolicyHasChild(policyMap,Policies.CanManagePullPayments, Policies.CanCreatePullPayments, Policies.CanArchivePullPayments);
|
||||
PolicyHasChild(policyMap,Policies.CanCreatePullPayments, Policies.CanCreateNonApprovedPullPayments);
|
||||
PolicyHasChild(policyMap, Policies.CanCreateNonApprovedPullPayments, Policies.CanViewPullPayments);
|
||||
PolicyHasChild(policyMap,Policies.CanModifyPaymentRequests, Policies.CanViewPaymentRequests);
|
||||
PolicyHasChild(policyMap,Policies.CanModifyProfile, Policies.CanViewProfile);
|
||||
PolicyHasChild(policyMap,Policies.CanUseLightningNodeInStore, Policies.CanViewLightningInvoiceInStore, Policies.CanCreateLightningInvoiceInStore);
|
||||
@ -267,7 +277,8 @@ namespace BTCPayServer.Client
|
||||
PolicyHasChild(policyMap, Policies.CanUseInternalLightningNode, Policies.CanCreateLightningInvoiceInternalNode, Policies.CanViewLightningInvoiceInternalNode);
|
||||
PolicyHasChild(policyMap, Policies.CanManageCustodianAccounts, Policies.CanViewCustodianAccounts);
|
||||
PolicyHasChild(policyMap, Policies.CanModifyInvoices, Policies.CanViewInvoices, Policies.CanCreateInvoice, Policies.CanCreateLightningInvoiceInStore);
|
||||
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests);
|
||||
PolicyHasChild(policyMap, Policies.CanViewStoreSettings, Policies.CanViewInvoices, Policies.CanViewPaymentRequests, Policies.CanViewReports, Policies.CanViewPullPayments, Policies.CanViewPayouts);
|
||||
PolicyHasChild(policyMap, Policies.CanManagePayouts, Policies.CanViewPayouts);
|
||||
|
||||
var missingPolicies = Policies.AllPolicies.ToHashSet();
|
||||
//recurse through the tree to see which policies are not included in the tree
|
||||
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitAlthash()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("HTML");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Htmlcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.htmlcoin.com/api/tx/{0}" : "https://explorer.htmlcoin.com/api/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"HTML_X = HTML_USD",
|
||||
"HTML_USD = hitbtc(HTML_USD)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/althash.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("172'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitArgoneum()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("AGM");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Argoneum",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://chainz.cryptoid.info/agm/tx.dws?{0}"
|
||||
: "https://chainz.cryptoid.info/agm-test/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"AGM_X = AGM_BTC * BTC_X",
|
||||
"AGM_BTC = argoneum(AGM_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/argoneum.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("421'")
|
||||
: new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBGold()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTG");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BGold",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://btgexplorer.com/tx/{0}" : "https://testnet.btgexplorer.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"BTG_X = BTG_BTC * BTC_X",
|
||||
"BTG_BTC = gate(BTG_BTC)",
|
||||
},
|
||||
CryptoImagePath = "imlegacy/btg.svg",
|
||||
LightningImagePath = "imlegacy/btg-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("156'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBPlus()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("XBC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BPlus",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/xbc/tx.dws?{0}" : "https://chainz.cryptoid.info/xbc/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"XBC_X = XBC_BTC * BTC_X",
|
||||
"XBC_BTC = cryptopia(XBC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/xbc.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("65'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBitcore()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTX");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "BitCore",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.bitcore.cc/tx/{0}" : "https://explorer.bitcore.cc/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"BTX_X = BTX_BTC * BTC_X",
|
||||
"BTX_BTC = graviex(BTX_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/bitcore.svg",
|
||||
LightningImagePath = "imlegacy/bitcore-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("160'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitDash()
|
||||
{
|
||||
//not needed: NBitcoin.Altcoins.Dash.Instance.EnsureRegistered();
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DASH");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Dash",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://insight.dash.org/insight/tx/{0}"
|
||||
: "https://testnet-insight.dashevo.org/insight/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"DASH_X = DASH_BTC * BTC_X",
|
||||
"DASH_BTC = bitfinex(DSH_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/dash.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
//https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("5'")
|
||||
: new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitDogecoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("DOGE");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Dogecoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://dogechain.info/tx/{0}" : "https://dogechain.info/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"DOGE_X = DOGE_BTC * BTC_X",
|
||||
"DOGE_BTC = bittrex(DOGE_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/dogecoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("3'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitFeathercoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("FTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Feathercoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.feathercoin.com/tx/{0}" : "https://explorer.feathercoin.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"FTC_X = FTC_BTC * BTC_X",
|
||||
"FTC_BTC = bittrex(FTC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/feathercoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("8'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitGroestlcoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("GRS");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Groestlcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://chainz.cryptoid.info/grs/tx.dws?{0}.htm"
|
||||
: "https://chainz.cryptoid.info/grs-test/tx.dws?{0}.htm",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"GRS_X = GRS_BTC * BTC_X",
|
||||
"GRS_BTC = bittrex(GRS_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/groestlcoin.png",
|
||||
LightningImagePath = "imlegacy/groestlcoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("17'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportPayJoin = true,
|
||||
VaultSupported = true
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLitecoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Litecoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet
|
||||
? "https://live.blockcypher.com/ltc/tx/{0}/"
|
||||
: "http://explorer.litecointools.com/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LTC_X = LTC_BTC * BTC_X",
|
||||
"LTC_BTC = coingecko(LTC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/litecoin.svg",
|
||||
LightningImagePath = "imlegacy/litecoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("2'") : new KeyPath("1'"),
|
||||
//https://github.com/pooler/electrum-ltc/blob/0d6989a9d2fb2edbea421c116e49d1015c7c5a91/electrum_ltc/constants.py
|
||||
ElectrumMapping = NetworkType == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy },
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH },
|
||||
{0x04b24746U, DerivationType.Segwit },
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy },
|
||||
{0x044a5262U, DerivationType.SegwitP2SH },
|
||||
{0x045f1cf6U, DerivationType.Segwit }
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitMonacoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("MONA");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Monacoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mona.insight.monaco-ex.org/insight/tx/{0}" : "https://testnet-mona.insight.monaco-ex.org/insight/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"MONA_X = MONA_BTC * BTC_X",
|
||||
"MONA_BTC = bittrex(MONA_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/monacoin.png",
|
||||
LightningImagePath = "imlegacy/mona-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("22'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitPolis()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("POLIS");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Polis",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://blockbook.polispay.org/tx/{0}" : "https://blockbook.polispay.org/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"POLIS_X = POLIS_BTC * BTC_X",
|
||||
"POLIS_BTC = polispay(POLIS_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/polis.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1997'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitUfo()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("UFO");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Ufo",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://chainz.cryptoid.info/ufo/tx.dws?{0}" : "https://chainz.cryptoid.info/ufo/tx.dws?{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"UFO_X = UFO_BTC * BTC_X",
|
||||
"UFO_BTC = coinexchange(UFO_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/ufo.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("202'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitViacoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("VIA");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Viacoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://explorer.viacoin.org/tx/{0}" : "https://explorer.viacoin.org/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"VIA_X = VIA_BTC * BTC_X",
|
||||
"VIA_BTC = bittrex(VIA_BTC)"
|
||||
},
|
||||
CryptoImagePath = "imlegacy/viacoin.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("14'") : new KeyPath("1'")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using NBitcoin;
|
||||
using NBitcoin.Altcoins;
|
||||
using NBitcoin.Altcoins.Elements;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLiquid()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
AssetId = NetworkType == ChainName.Mainnet ? ElementsParams<Liquid>.PeggedAssetId : ElementsParams<Liquid.LiquidRegtest>.PeggedAssetId,
|
||||
CryptoCode = "LBTC",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
DisplayName = "Liquid Bitcoin",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LBTC_X = LBTC_BTC * BTC_X",
|
||||
"LBTC_BTC = 1",
|
||||
},
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/liquid.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
@ -1,83 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitLiquidAssets()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("LBTC");
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "USDt",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"USDT_UST = 1",
|
||||
"USDT_X = USDT_BTC * BTC_X",
|
||||
"USDT_BTC = bitfinex(UST_BTC)",
|
||||
},
|
||||
AssetId = new uint256("ce091c998b83c78bb71a632313ba3760f1763d9cfcffae02258ffa9865a37bd2"),
|
||||
DisplayName = "Liquid Tether",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/liquid-tether.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "ETB",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
|
||||
"ETB_X = ETB_BTC * BTC_X",
|
||||
"ETB_BTC = bitpay(ETB_BTC)"
|
||||
},
|
||||
Divisibility = 2,
|
||||
AssetId = new uint256("aa775044c32a7df391902b3659f46dfe004ccb2644ce2ddc7dba31e889391caf"),
|
||||
DisplayName = "Ethiopian Birr",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/etb.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
|
||||
Add(new ElementsBTCPayNetwork()
|
||||
{
|
||||
CryptoCode = "LCAD",
|
||||
NetworkCryptoCode = "LBTC",
|
||||
ShowSyncSummary = false,
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"LCAD_CAD = 1",
|
||||
"LCAD_X = CAD_BTC * BTC_X",
|
||||
"LCAD_BTC = bylls(CAD_BTC)",
|
||||
"CAD_BTC = LCAD_BTC"
|
||||
},
|
||||
AssetId = new uint256("0e99c1a6da379d1f4151fb9df90449d40d0608f6cb33a5bcbfc8c265f42bab0a"),
|
||||
DisplayName = "Liquid CAD",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://liquid.network/tx/{0}" : "https://liquid.network/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/lcad.png",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("1776'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportLightning = false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
@ -1,50 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class ElementsBTCPayNetwork : BTCPayNetwork
|
||||
{
|
||||
public string NetworkCryptoCode { get; set; }
|
||||
public uint256 AssetId { get; set; }
|
||||
public override bool ReadonlyWallet { get; set; } = true;
|
||||
|
||||
public override IEnumerable<(MatchedOutput matchedOutput, OutPoint outPoint)> GetValidOutputs(
|
||||
NewTransactionEvent evtOutputs)
|
||||
{
|
||||
return evtOutputs.Outputs.Where(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId).Select(output =>
|
||||
{
|
||||
var outpoint = new OutPoint(evtOutputs.TransactionData.TransactionHash, output.Index);
|
||||
return (output, outpoint);
|
||||
});
|
||||
}
|
||||
|
||||
public override List<TransactionInformation> FilterValidTransactions(List<TransactionInformation> transactionInformationSet)
|
||||
{
|
||||
return transactionInformationSet.FindAll(information =>
|
||||
information.Outputs.Any(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId) ||
|
||||
information.Inputs.Any(output =>
|
||||
output.Value is AssetMoney assetMoney && assetMoney.AssetId == AssetId));
|
||||
}
|
||||
|
||||
public override PaymentUrlBuilder GenerateBIP21(string cryptoInfoAddress, decimal? cryptoInfoDue)
|
||||
{
|
||||
//precision 0: 10 = 0.00000010
|
||||
//precision 2: 10 = 0.00001000
|
||||
//precision 8: 10 = 10
|
||||
var money = cryptoInfoDue / (decimal)Math.Pow(10, 8 - Divisibility);
|
||||
var builder = base.GenerateBIP21(cryptoInfoAddress, money);
|
||||
builder.QueryParams.Add("assetid", AssetId.ToString());
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,18 +0,0 @@
|
||||
#if ALTCOINS
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public static class LiquidExtensions
|
||||
{
|
||||
public static IEnumerable<string> GetAllElementsSubChains(this BTCPayNetworkProvider networkProvider, BTCPayNetworkProvider unfiltered)
|
||||
{
|
||||
var elementsBased = networkProvider.GetAll().OfType<ElementsBTCPayNetwork>();
|
||||
var parentChains = elementsBased.Select(network => network.NetworkCryptoCode.ToUpperInvariant()).Distinct();
|
||||
return unfiltered.GetAll().OfType<ElementsBTCPayNetwork>()
|
||||
.Where(network => parentChains.Contains(network.NetworkCryptoCode)).Select(network => network.CryptoCode.ToUpperInvariant());
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
@ -1,28 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitMonero()
|
||||
{
|
||||
Add(new MoneroLikeSpecificBtcPayNetwork()
|
||||
{
|
||||
CryptoCode = "XMR",
|
||||
DisplayName = "Monero",
|
||||
Divisibility = 12,
|
||||
BlockExplorerLink =
|
||||
NetworkType == ChainName.Mainnet
|
||||
? "https://www.exploremonero.com/transaction/{0}"
|
||||
: "https://testnet.xmrchain.net/tx/{0}",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"XMR_X = XMR_BTC * BTC_X",
|
||||
"XMR_BTC = kraken(XMR_BTC)"
|
||||
},
|
||||
CryptoImagePath = "/imlegacy/monero.svg",
|
||||
UriScheme = "monero"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class MoneroLikeSpecificBtcPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
public int MaxTrackedConfirmation = 10;
|
||||
public string UriScheme { get; set; }
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
using NBitcoin;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
// Change this if you want another zcash coin
|
||||
public void InitZcash()
|
||||
{
|
||||
Add(new ZcashLikeSpecificBtcPayNetwork()
|
||||
{
|
||||
CryptoCode = "ZEC",
|
||||
DisplayName = "Zcash",
|
||||
Divisibility = 8,
|
||||
BlockExplorerLink =
|
||||
NetworkType == ChainName.Mainnet
|
||||
? "https://www.exploreZcash.com/transaction/{0}"
|
||||
: "https://testnet.xmrchain.net/tx/{0}",
|
||||
DefaultRateRules = new[]
|
||||
{
|
||||
"ZEC_X = ZEC_BTC * BTC_X",
|
||||
"ZEC_BTC = kraken(ZEC_BTC)"
|
||||
},
|
||||
CryptoImagePath = "/imlegacy/zcash.png",
|
||||
UriScheme = "zcash"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class ZcashLikeSpecificBtcPayNetwork : BTCPayNetworkBase
|
||||
{
|
||||
public int MaxTrackedConfirmation = 10;
|
||||
public string UriScheme { get; set; }
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Common;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using NBXplorer.Models;
|
||||
@ -62,6 +64,31 @@ namespace BTCPayServer
|
||||
public KeyPath CoinType { get; set; }
|
||||
|
||||
public Dictionary<uint, DerivationType> ElectrumMapping = new Dictionary<uint, DerivationType>();
|
||||
public BTCPayNetwork SetDefaultElectrumMapping(ChainName chainName)
|
||||
{
|
||||
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
|
||||
ElectrumMapping = chainName == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy }, // xpub
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
|
||||
{0x04b24746U, DerivationType.Segwit }, //zpub
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy}, // tpub
|
||||
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
|
||||
{0x045f1cf6U, DerivationType.Segwit} // vpub
|
||||
};
|
||||
if (!NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
ElectrumMapping =
|
||||
ElectrumMapping
|
||||
.Where(kv => kv.Value == DerivationType.Legacy)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual bool WalletSupported { get; set; } = true;
|
||||
public virtual bool ReadonlyWallet { get; set; } = false;
|
||||
@ -107,25 +134,8 @@ namespace BTCPayServer
|
||||
|
||||
public abstract class BTCPayNetworkBase
|
||||
{
|
||||
private string _blockExplorerLink;
|
||||
public bool ShowSyncSummary { get; set; } = true;
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string BlockExplorerLink
|
||||
{
|
||||
get => _blockExplorerLink;
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty(BlockExplorerLinkDefault))
|
||||
{
|
||||
BlockExplorerLinkDefault = value;
|
||||
}
|
||||
|
||||
_blockExplorerLink = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string BlockExplorerLinkDefault { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public int Divisibility { get; set; } = 8;
|
||||
public bool IsBTC
|
||||
@ -153,5 +163,8 @@ namespace BTCPayServer
|
||||
{
|
||||
return NBitcoin.JsonConverters.Serializer.ToString(obj, null);
|
||||
}
|
||||
|
||||
[Obsolete("Use TransactionLinkProviders service instead")]
|
||||
public string BlockExplorerLink { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public partial class BTCPayNetworkProvider
|
||||
{
|
||||
public void InitBitcoin()
|
||||
{
|
||||
var nbxplorerNetwork = NBXplorerNetworkProvider.GetFromCryptoCode("BTC");
|
||||
Add(new BTCPayNetwork()
|
||||
{
|
||||
CryptoCode = nbxplorerNetwork.CryptoCode,
|
||||
DisplayName = "Bitcoin",
|
||||
BlockExplorerLink = NetworkType == ChainName.Mainnet ? "https://mempool.space/tx/{0}" :
|
||||
NetworkType == Bitcoin.Instance.Signet.ChainName ? "https://mempool.space/signet/tx/{0}"
|
||||
: "https://mempool.space/testnet/tx/{0}",
|
||||
NBXplorerNetwork = nbxplorerNetwork,
|
||||
CryptoImagePath = "imlegacy/bitcoin.svg",
|
||||
LightningImagePath = "imlegacy/bitcoin-lightning.svg",
|
||||
DefaultSettings = BTCPayDefaultSettings.GetDefaultSettings(NetworkType),
|
||||
CoinType = NetworkType == ChainName.Mainnet ? new KeyPath("0'") : new KeyPath("1'"),
|
||||
SupportRBF = true,
|
||||
SupportPayJoin = true,
|
||||
VaultSupported = true,
|
||||
//https://github.com/spesmilo/electrum/blob/11733d6bc271646a00b69ff07657119598874da4/electrum/constants.py
|
||||
ElectrumMapping = NetworkType == ChainName.Mainnet
|
||||
? new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x0488b21eU, DerivationType.Legacy }, // xpub
|
||||
{0x049d7cb2U, DerivationType.SegwitP2SH }, // ypub
|
||||
{0x04b24746U, DerivationType.Segwit }, //zpub
|
||||
}
|
||||
: new Dictionary<uint, DerivationType>()
|
||||
{
|
||||
{0x043587cfU, DerivationType.Legacy}, // tpub
|
||||
{0x044a5262U, DerivationType.SegwitP2SH}, // upub
|
||||
{0x045f1cf6U, DerivationType.Segwit} // vpub
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||
using Microsoft.AspNetCore.HttpOverrides;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using NBitcoin;
|
||||
using NBXplorer;
|
||||
using StandardConfiguration;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
@ -19,92 +26,37 @@ namespace BTCPayServer
|
||||
}
|
||||
}
|
||||
|
||||
BTCPayNetworkProvider(BTCPayNetworkProvider unfiltered, string[] cryptoCodes)
|
||||
{
|
||||
NetworkType = unfiltered.NetworkType;
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(unfiltered.NetworkType);
|
||||
_Networks = new Dictionary<string, BTCPayNetworkBase>();
|
||||
cryptoCodes = cryptoCodes.Select(c => c.ToUpperInvariant()).ToArray();
|
||||
foreach (var network in unfiltered._Networks)
|
||||
{
|
||||
if (cryptoCodes.Contains(network.Key))
|
||||
{
|
||||
_Networks.Add(network.Key, network.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ChainName NetworkType { get; private set; }
|
||||
public BTCPayNetworkProvider(ChainName networkType)
|
||||
public BTCPayNetworkProvider(
|
||||
IEnumerable<BTCPayNetworkBase> networks,
|
||||
SelectedChains selectedChains,
|
||||
NBXplorerNetworkProvider nbxplorerNetworkProvider,
|
||||
Logs logs)
|
||||
{
|
||||
_NBXplorerNetworkProvider = new NBXplorerNetworkProvider(networkType);
|
||||
NetworkType = networkType;
|
||||
InitBitcoin();
|
||||
#if ALTCOINS
|
||||
InitLiquid();
|
||||
InitLiquidAssets();
|
||||
InitLitecoin();
|
||||
InitBitcore();
|
||||
InitDogecoin();
|
||||
InitBGold();
|
||||
InitMonacoin();
|
||||
InitDash();
|
||||
InitFeathercoin();
|
||||
InitAlthash();
|
||||
InitGroestlcoin();
|
||||
InitViacoin();
|
||||
InitMonero();
|
||||
InitZcash();
|
||||
// InitArgoneum();//their rate source is down 9/15/20.
|
||||
// InitMonetaryUnit(); Not supported from Bittrex from 11/23/2022, dead shitcoin
|
||||
|
||||
// Assume that electrum mappings are same as BTC if not specified
|
||||
foreach (var network in _Networks.Values.OfType<BTCPayNetwork>())
|
||||
var networksList = networks.ToList();
|
||||
#if !ALTCOINS
|
||||
var onlyBTC = networksList.Count == 1 && networksList.First().IsBTC;
|
||||
if (!onlyBTC)
|
||||
throw new ConfigException($"This build of BTCPay Server does not support altcoins. Configured networks: {string.Join(',', networksList.Select(n => n.CryptoCode).ToArray())}");
|
||||
#endif
|
||||
_NBXplorerNetworkProvider = nbxplorerNetworkProvider;
|
||||
NetworkType = nbxplorerNetworkProvider.NetworkType;
|
||||
foreach (var network in networksList)
|
||||
{
|
||||
if (network.ElectrumMapping.Count == 0)
|
||||
{
|
||||
network.ElectrumMapping = GetNetwork<BTCPayNetwork>("BTC").ElectrumMapping;
|
||||
if (!network.NBitcoinNetwork.Consensus.SupportSegwit)
|
||||
{
|
||||
network.ElectrumMapping =
|
||||
network.ElectrumMapping
|
||||
.Where(kv => kv.Value == DerivationType.Legacy)
|
||||
.ToDictionary(k => k.Key, k => k.Value);
|
||||
}
|
||||
}
|
||||
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
|
||||
}
|
||||
|
||||
// Disabled because of https://twitter.com/Cryptopia_NZ/status/1085084168852291586
|
||||
//InitBPlus();
|
||||
//InitUfo();
|
||||
#endif
|
||||
}
|
||||
foreach (var chain in selectedChains.ExplicitlySelected)
|
||||
{
|
||||
if (GetNetwork<BTCPayNetworkBase>(chain) == null)
|
||||
throw new ConfigException($"Invalid chains \"{chain}\"");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Keep only the specified crypto
|
||||
/// </summary>
|
||||
/// <param name="cryptoCodes">Crypto to support</param>
|
||||
/// <returns></returns>
|
||||
public BTCPayNetworkProvider Filter(string[] cryptoCodes)
|
||||
{
|
||||
return new BTCPayNetworkProvider(this, cryptoCodes);
|
||||
logs.Configuration.LogInformation("Supported chains: {Chains}", string.Join(',', _Networks.Select(n => n.Key).ToArray()));
|
||||
}
|
||||
|
||||
public BTCPayNetwork BTC => GetNetwork<BTCPayNetwork>("BTC");
|
||||
public BTCPayNetworkBase DefaultNetwork => BTC ?? GetAll().First();
|
||||
|
||||
public void Add(BTCPayNetwork network)
|
||||
{
|
||||
if (network.NBitcoinNetwork == null)
|
||||
return;
|
||||
Add(network as BTCPayNetworkBase);
|
||||
}
|
||||
public void Add(BTCPayNetworkBase network)
|
||||
{
|
||||
_Networks.Add(network.CryptoCode.ToUpperInvariant(), network);
|
||||
}
|
||||
|
||||
public IEnumerable<BTCPayNetworkBase> GetAll()
|
||||
{
|
||||
return _Networks.Values.ToArray();
|
||||
|
@ -4,10 +4,13 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.2.5" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="4.3.0" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Condition="'$(Altcoins)' != 'true'">
|
||||
<Compile Remove="Altcoins\**\*.cs"></Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Altcoins\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -174,7 +174,6 @@ namespace BTCPayServer.Logging
|
||||
logLevelColors = GetLogLevelConsoleColors(logLevel);
|
||||
logLevelString = GetLogLevelString(logLevel);
|
||||
// category and event id
|
||||
var lenBefore = logBuilder.ToString().Length;
|
||||
logBuilder.Append(_loglevelPadding);
|
||||
logBuilder.Append(logName);
|
||||
logBuilder.Append(": ");
|
||||
|
49
BTCPayServer.Common/SelectedChains.cs
Normal file
49
BTCPayServer.Common/SelectedChains.cs
Normal file
@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using BTCPayServer.Logging;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json.Bson;
|
||||
|
||||
namespace BTCPayServer
|
||||
{
|
||||
public class SelectedChains
|
||||
{
|
||||
HashSet<string> chains = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
bool all = false;
|
||||
public SelectedChains(IConfiguration configuration, Logs logs)
|
||||
{
|
||||
foreach (var chain in (configuration["chains"] ?? "btc")
|
||||
.Split(',', StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(t => t.ToUpperInvariant()))
|
||||
{
|
||||
if (new[] { "ETH", "USDT20", "FAU" }.Contains(chain, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
logs.Configuration.LogWarning($"'{chain}' is not anymore supported, please remove it from 'chains'");
|
||||
continue;
|
||||
}
|
||||
if (chain == "*")
|
||||
{
|
||||
all = true;
|
||||
continue;
|
||||
}
|
||||
chains.Add(chain);
|
||||
}
|
||||
if (chains.Count == 0)
|
||||
chains.Add("BTC");
|
||||
if (all)
|
||||
chains.Clear();
|
||||
}
|
||||
|
||||
public bool Contains(string cryptoCode)
|
||||
{
|
||||
return all || chains.Contains(cryptoCode);
|
||||
}
|
||||
public void Add(string cryptoCode)
|
||||
{
|
||||
chains.Add(cryptoCode);
|
||||
}
|
||||
public IEnumerable<string> ExplicitlySelected => chains;
|
||||
}
|
||||
}
|
@ -93,7 +93,7 @@ namespace BTCPayServer.Data
|
||||
ApplicationUser.OnModelCreating(builder, Database);
|
||||
AddressInvoiceData.OnModelCreating(builder);
|
||||
APIKeyData.OnModelCreating(builder, Database);
|
||||
AppData.OnModelCreating(builder);
|
||||
AppData.OnModelCreating(builder, Database);
|
||||
CustodianAccountData.OnModelCreating(builder, Database);
|
||||
//StoredFile.OnModelCreating(builder);
|
||||
InvoiceEventData.OnModelCreating(builder);
|
||||
|
@ -1,13 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.9">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="6.0.9" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BTCPayServer.Abstractions\BTCPayServer.Abstractions.csproj" />
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
@ -16,13 +17,20 @@ namespace BTCPayServer.Data
|
||||
public string Settings { get; set; }
|
||||
public bool Archived { get; set; }
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
builder.Entity<AppData>()
|
||||
.HasOne(o => o.StoreData)
|
||||
.WithMany(i => i.Apps).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<AppData>()
|
||||
.HasOne(a => a.StoreData);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<AppData>()
|
||||
.Property(o => o.Settings)
|
||||
.HasColumnType("JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
// utility methods
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
@ -30,11 +31,10 @@ namespace BTCPayServer.Data
|
||||
public List<PendingInvoiceData> PendingInvoices { get; set; }
|
||||
public List<InvoiceSearchData> InvoiceSearchData { get; set; }
|
||||
public List<RefundData> Refunds { get; set; }
|
||||
public string CurrentRefundId { get; set; }
|
||||
[ForeignKey("Id,CurrentRefundId")]
|
||||
public RefundData CurrentRefund { get; set; }
|
||||
|
||||
|
||||
[Timestamp]
|
||||
// With this, update of InvoiceData will fail if the row was modified by another process
|
||||
public uint XMin { get; set; }
|
||||
internal static void OnModelCreating(ModelBuilder builder, DatabaseFacade databaseFacade)
|
||||
{
|
||||
builder.Entity<InvoiceData>()
|
||||
@ -42,8 +42,6 @@ namespace BTCPayServer.Data
|
||||
.WithMany(a => a.Invoices).OnDelete(DeleteBehavior.Cascade);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.StoreDataId);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.OrderId);
|
||||
builder.Entity<InvoiceData>()
|
||||
.HasOne(o => o.CurrentRefund);
|
||||
builder.Entity<InvoiceData>().HasIndex(o => o.Created);
|
||||
|
||||
if (databaseFacade.IsNpgsql())
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
@ -13,7 +14,6 @@ namespace BTCPayServer.Data
|
||||
public PullPaymentData PullPaymentData { get; set; }
|
||||
public InvoiceData InvoiceData { get; set; }
|
||||
|
||||
|
||||
internal static void OnModelCreating(ModelBuilder builder)
|
||||
{
|
||||
builder.Entity<RefundData>()
|
||||
|
@ -1,14 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
|
||||
namespace BTCPayServer.Data
|
||||
{
|
||||
public class WalletObjectData
|
||||
public class WalletObjectData : IEqualityComparer<WalletObjectData>
|
||||
{
|
||||
public class Types
|
||||
{
|
||||
@ -88,9 +86,30 @@ namespace BTCPayServer.Data
|
||||
if (databaseFacade.IsNpgsql())
|
||||
{
|
||||
builder.Entity<WalletObjectData>()
|
||||
.Property(o => o.Data)
|
||||
.HasColumnType("JSONB");
|
||||
.Property(o => o.Data)
|
||||
.HasColumnType("JSONB");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(WalletObjectData x, WalletObjectData y)
|
||||
{
|
||||
if (ReferenceEquals(x, y)) return true;
|
||||
if (ReferenceEquals(x, null)) return false;
|
||||
if (ReferenceEquals(y, null)) return false;
|
||||
if (x.GetType() != y.GetType()) return false;
|
||||
return string.Equals(x.WalletId, y.WalletId, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
string.Equals(x.Type, y.Type, StringComparison.InvariantCultureIgnoreCase) &&
|
||||
string.Equals(x.Id, y.Id, StringComparison.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
public int GetHashCode(WalletObjectData obj)
|
||||
{
|
||||
HashCode hashCode = new HashCode();
|
||||
hashCode.Add(obj.WalletId, StringComparer.InvariantCultureIgnoreCase);
|
||||
hashCode.Add(obj.Type, StringComparer.InvariantCultureIgnoreCase);
|
||||
hashCode.Add(obj.Id, StringComparer.InvariantCultureIgnoreCase);
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
using System.Security.Permissions;
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata.Internal;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20231020135844_AddBoltcardsTable")]
|
||||
public partial class AddBoltcardsTable : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "boltcards",
|
||||
columns: table => new
|
||||
{
|
||||
id = table.Column<string>(maxLength: 32, nullable: false),
|
||||
counter = table.Column<int>(type: "INT", nullable: false, defaultValue: 0),
|
||||
ppid = table.Column<string>(maxLength: 30, nullable: true),
|
||||
version = table.Column<int>(nullable: false, defaultValue: 0)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_id", x => x.id);
|
||||
table.ForeignKey("FK_boltcards_PullPayments", x => x.ppid, "PullPayments", "Id", onDelete: ReferentialAction.SetNull);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable("boltcards");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20231121031609_removecurrentrefund")]
|
||||
public partial class removecurrentrefund : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Invoices_Refunds_Id_CurrentRefundId",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Invoices_Id_CurrentRefundId",
|
||||
table: "Invoices");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "CurrentRefundId",
|
||||
table: "Invoices");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
using BTCPayServer.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace BTCPayServer.Migrations
|
||||
{
|
||||
[DbContext(typeof(ApplicationDbContext))]
|
||||
[Migration("20231219031609_appssettingstojson")]
|
||||
public partial class appssettingstojson : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
if (migrationBuilder.IsNpgsql())
|
||||
{
|
||||
migrationBuilder.Sql("ALTER TABLE \"Apps\" ALTER COLUMN \"Settings\" TYPE JSONB USING \"Settings\"::JSONB");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -16,25 +16,7 @@ namespace BTCPayServer.Migrations
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "6.0.9");
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InvoiceDataId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Address");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
|
||||
b.ToTable("AddressInvoices");
|
||||
});
|
||||
modelBuilder.HasAnnotation("ProductVersion", "8.0.0");
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
|
||||
{
|
||||
@ -71,6 +53,24 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("ApiKeys");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.Property<string>("Address")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTimeOffset?>("CreatedTime")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("InvoiceDataId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Address");
|
||||
|
||||
b.HasIndex("InvoiceDataId");
|
||||
|
||||
b.ToTable("AddressInvoices");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -287,9 +287,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<DateTimeOffset>("Created")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CurrentRefundId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("CustomerEmail")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
@ -308,6 +305,11 @@ namespace BTCPayServer.Migrations
|
||||
b.Property<string>("StoreDataId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<uint>("XMin")
|
||||
.IsConcurrencyToken()
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Created");
|
||||
@ -316,8 +318,6 @@ namespace BTCPayServer.Migrations
|
||||
|
||||
b.HasIndex("StoreDataId");
|
||||
|
||||
b.HasIndex("Id", "CurrentRefundId");
|
||||
|
||||
b.ToTable("Invoices");
|
||||
});
|
||||
|
||||
@ -786,31 +786,6 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("Stores");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ApplicationUserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StorageFileName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId");
|
||||
|
||||
b.ToTable("Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoreRole", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -868,6 +843,31 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("StoreWebhooks");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("ApplicationUserId")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("FileName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("StorageFileName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("ApplicationUserId");
|
||||
|
||||
b.ToTable("Files");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.U2FDevice", b =>
|
||||
{
|
||||
b.Property<string>("Id")
|
||||
@ -1176,16 +1176,6 @@ namespace BTCPayServer.Migrations
|
||||
b.ToTable("AspNetUserTokens", (string)null);
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("AddressInvoices")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("InvoiceData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.APIKeyData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
@ -1203,6 +1193,16 @@ namespace BTCPayServer.Migrations
|
||||
b.Navigation("User");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AddressInvoiceData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.InvoiceData", "InvoiceData")
|
||||
.WithMany("AddressInvoices")
|
||||
.HasForeignKey("InvoiceDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.Navigation("InvoiceData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.AppData", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
@ -1251,12 +1251,6 @@ namespace BTCPayServer.Migrations
|
||||
.HasForeignKey("StoreDataId")
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
b.HasOne("BTCPayServer.Data.RefundData", "CurrentRefund")
|
||||
.WithMany()
|
||||
.HasForeignKey("Id", "CurrentRefundId");
|
||||
|
||||
b.Navigation("CurrentRefund");
|
||||
|
||||
b.Navigation("StoreData");
|
||||
});
|
||||
|
||||
@ -1419,15 +1413,6 @@ namespace BTCPayServer.Migrations
|
||||
b.Navigation("PullPaymentData");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("StoredFiles")
|
||||
.HasForeignKey("ApplicationUserId");
|
||||
|
||||
b.Navigation("ApplicationUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoreRole", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.StoreData", "StoreData")
|
||||
@ -1468,6 +1453,15 @@ namespace BTCPayServer.Migrations
|
||||
b.Navigation("Webhook");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.StoredFile", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
|
||||
.WithMany("StoredFiles")
|
||||
.HasForeignKey("ApplicationUserId");
|
||||
|
||||
b.Navigation("ApplicationUser");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("BTCPayServer.Data.U2FDevice", b =>
|
||||
{
|
||||
b.HasOne("BTCPayServer.Data.ApplicationUser", "ApplicationUser")
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Version>1.0.0.0</Version>
|
||||
<PackAsTool>true</PackAsTool>
|
||||
<ToolCommandName>btcpay-plugin</ToolCommandName>
|
||||
|
@ -1,37 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace BTCPayServer.Rating
|
||||
{
|
||||
public enum RateSource
|
||||
{
|
||||
Coingecko,
|
||||
Direct
|
||||
}
|
||||
public class AvailableRateProvider
|
||||
{
|
||||
public string Name { get; }
|
||||
public string Url { get; }
|
||||
public string Id { get; }
|
||||
public RateSource Source { get; }
|
||||
|
||||
public AvailableRateProvider(string id, string name, string url) : this(id, name, url, RateSource.Direct)
|
||||
{
|
||||
|
||||
}
|
||||
public AvailableRateProvider(string id, string name, string url, RateSource source)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
Url = url;
|
||||
Source = source;
|
||||
}
|
||||
|
||||
public string DisplayName =>
|
||||
Source switch
|
||||
{
|
||||
RateSource.Direct => Name,
|
||||
RateSource.Coingecko => $"{Name} (via CoinGecko)",
|
||||
_ => throw new NotSupportedException(Source.ToString())
|
||||
};
|
||||
}
|
||||
}
|
@ -4,11 +4,11 @@
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.3.1" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.24" />
|
||||
<PackageReference Include="NBitcoin" Version="7.0.34" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.2" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="1.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -20,13 +20,13 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
public class CurrencyNameTable
|
||||
{
|
||||
public static CurrencyNameTable Instance = new CurrencyNameTable();
|
||||
public static CurrencyNameTable Instance = new();
|
||||
public CurrencyNameTable()
|
||||
{
|
||||
_Currencies = LoadCurrency().ToDictionary(k => k.Code);
|
||||
_Currencies = LoadCurrency().ToDictionary(k => k.Code, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new Dictionary<string, IFormatProvider>();
|
||||
static readonly Dictionary<string, IFormatProvider> _CurrencyProviders = new();
|
||||
|
||||
public NumberFormatInfo GetNumberFormatInfo(string currency, bool useFallback)
|
||||
{
|
||||
@ -64,8 +64,17 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
if (_CurrencyProviders.Count == 0)
|
||||
{
|
||||
foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures).Where(c => !c.IsNeutralCulture))
|
||||
foreach (var culture in CultureInfo.GetCultures(CultureTypes.AllCultures))
|
||||
{
|
||||
// This avoid storms of exception throwing slowing up
|
||||
// startup and debugging sessions
|
||||
if (culture switch
|
||||
{
|
||||
{ LCID: 0x007F or 0x0000 or 0x0c00 or 0x1000 } => true,
|
||||
{ IsNeutralCulture : true } => true,
|
||||
_ => false
|
||||
})
|
||||
continue;
|
||||
try
|
||||
{
|
||||
_CurrencyProviders.TryAdd(new RegionInfo(culture.LCID).ISOCurrencySymbol, culture);
|
||||
|
@ -19,7 +19,7 @@ namespace BTCPayServer.Rating
|
||||
public static CurrencyPair Parse(string str)
|
||||
{
|
||||
if (!TryParse(str, out var result))
|
||||
throw new FormatException("Invalid currency pair");
|
||||
throw new FormatException($"Invalid currency pair ({str})");
|
||||
return result;
|
||||
}
|
||||
public static bool TryParse(string str, out CurrencyPair value)
|
||||
|
@ -1,29 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class ArgoneumRateProvider : IRateProvider
|
||||
{
|
||||
private readonly HttpClient _httpClient;
|
||||
public ArgoneumRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public RateSourceInfo RateSourceInfo => new("argoneum", "Argoneum", "https://rates.argoneum.net/rates");
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
// Example result: AGM to BTC rate: {"agm":5000000.000000}
|
||||
var response = await _httpClient.GetAsync("https://rates.argoneum.net/rates/btc", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var value = jobj["agm"].Value<decimal>();
|
||||
return new[] { new PairRate(new CurrencyPair("BTC", "AGM"), new BidAsk(value)) };
|
||||
}
|
||||
}
|
||||
}
|
@ -29,7 +29,7 @@ namespace BTCPayServer.Rating.Providers
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.btcturk.com/api/v2/ticker", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.btcturk.com/api/v2/ticker", cancellationToken);
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
|
||||
var tickers = jarray.ToObject<Ticker[]>();
|
||||
return tickers
|
||||
|
@ -110,7 +110,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public void LoadState(BackgroundFetcherState state)
|
||||
{
|
||||
if (state.LastRequested is DateTimeOffset lastRequested)
|
||||
if (state.LastRequested is DateTimeOffset)
|
||||
this.LastRequested = state.LastRequested;
|
||||
if (state.LastUpdated is DateTimeOffset updated && state.Rates is List<BackgroundFetcherRate> rates)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://public.bitbank.cc/tickers", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://public.bitbank.cc/tickers", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var data = jobj.ContainsKey("data") ? jobj["data"] : null;
|
||||
if (jobj["success"]?.Value<int>() != 1)
|
||||
|
@ -19,7 +19,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.bitflyer.jp/v1/ticker", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.bitflyer.jp/v1/ticker", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
if (jobj.Property("error_message")?.Value?.Value<string>() is string err)
|
||||
{
|
||||
|
@ -19,7 +19,9 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://bitpay.com/rates", cancellationToken);
|
||||
if (response.Content.Headers.ContentType?.MediaType is not "application/json")
|
||||
throw new HttpRequestException($"Unexpected content type when querying currency rates from Bitpay ({response.Content.Headers.ContentType?.MediaType})");
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
|
@ -18,7 +18,7 @@ public class BudaRateProvider : IRateProvider
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://www.buda.com/api/v2/markets/btc-clp/ticker", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://www.buda.com/api/v2/markets/btc-clp/ticker", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var minAsk = jobj["ticker"]["min_ask"][0].Value<decimal>();
|
||||
var maxBid = jobj["ticker"]["max_bid"][0].Value<decimal>();
|
||||
|
@ -18,7 +18,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://bylls.com/api/price?from_currency=BTC&to_currency=CAD", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://bylls.com/api/price?from_currency=BTC&to_currency=CAD", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var value = jobj["public_price"]["to_price"].Value<decimal>();
|
||||
return new[] { new PairRate(new CurrencyPair("BTC", "CAD"), new BidAsk(value)) };
|
||||
|
File diff suppressed because one or more lines are too long
@ -28,7 +28,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.exchange.cryptomkt.com/api/3/public/ticker/", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.exchange.cryptomkt.com/api/3/public/ticker/", cancellationToken);
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
|
||||
return ((jobj as JObject) ?? new JObject())
|
||||
|
@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Rating;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace BTCPayServer.Services.Rates;
|
||||
|
||||
|
||||
public class ExchangeRateHostRateProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("exchangeratehost", "Yadio", "https://api.exchangerate.host/latest?base=BTC");
|
||||
private readonly HttpClient _httpClient;
|
||||
public ExchangeRateHostRateProvider(HttpClient httpClient)
|
||||
{
|
||||
_httpClient = httpClient ?? new HttpClient();
|
||||
}
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync(RateSourceInfo.Url, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
if(jobj["success"].Value<bool>() is not true || !jobj["base"].Value<string>().Equals("BTC", StringComparison.InvariantCulture))
|
||||
throw new Exception("exchangerate.host returned a non success response or the base currency was not the requested one (BTC)");
|
||||
var results = (JObject) jobj["rates"] ;
|
||||
//key value is currency code to rate value
|
||||
var list = new List<PairRate>();
|
||||
foreach (var item in results)
|
||||
{
|
||||
string name = item.Key;
|
||||
var value = item.Value.Value<decimal>();
|
||||
list.Add(new PairRate(new CurrencyPair("BTC", name), new BidAsk(value)));
|
||||
}
|
||||
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -18,7 +18,7 @@ public class FreeCurrencyRatesRateProvider : IRateProvider
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync(RateSourceInfo.Url, cancellationToken);
|
||||
using var response = await _httpClient.GetAsync(RateSourceInfo.Url, cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var results = (JObject) jobj["btc"] ;
|
||||
|
@ -21,7 +21,7 @@ namespace BTCPayServer.Rating
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.hitbtc.com/api/2/public/ticker", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.hitbtc.com/api/2/public/ticker", cancellationToken);
|
||||
var jarray = await response.Content.ReadAsAsync<JArray>(cancellationToken);
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
|
@ -87,7 +87,6 @@ namespace BTCPayServer.Services.Rates
|
||||
var normalizedPairsList = symbols.Where(s => !notFoundSymbols.ContainsKey(s)).Select(s => helper.NormalizeMarketSymbol(s)).ToList();
|
||||
var csvPairsList = string.Join(",", normalizedPairsList);
|
||||
JToken apiTickers = await MakeJsonRequestAsync<JToken>("/0/public/Ticker", null, new Dictionary<string, object> { { "pair", csvPairsList } }, cancellationToken: cancellationToken);
|
||||
var tickers = new List<KeyValuePair<string, ExchangeTicker>>();
|
||||
foreach (string symbol in symbols)
|
||||
{
|
||||
var ticker = ConvertToExchangeTicker(symbol, apiTickers[symbol]);
|
||||
@ -172,7 +171,7 @@ namespace BTCPayServer.Services.Rates
|
||||
sb.Append(String.Join('&', payload.Select(kv => $"{kv.Key}={kv.Value}").OfType<object>().ToArray()));
|
||||
}
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, sb.ToString());
|
||||
var response = await HttpClient.SendAsync(request, cancellationToken);
|
||||
using var response = await HttpClient.SendAsync(request, cancellationToken);
|
||||
string stringResult = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeObject<T>(stringResult);
|
||||
if (result is JToken json)
|
||||
|
@ -13,7 +13,7 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class RipioExchangeProvider : IRateProvider
|
||||
{
|
||||
public RateSourceInfo RateSourceInfo => new("ripio", "Ripio", "https://api.exchange.ripio.com/api/v1/rate/all/");
|
||||
public RateSourceInfo RateSourceInfo => new("ripio", "Ripio", "https://api.ripiotrade.co/v4/public/tickers");
|
||||
private readonly HttpClient _httpClient;
|
||||
public RipioExchangeProvider(HttpClient httpClient)
|
||||
{
|
||||
@ -21,9 +21,9 @@ namespace BTCPayServer.Services.Rates
|
||||
}
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.exchange.ripio.com/api/v1/rate/all/", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.ripiotrade.co/v4/public/tickers", cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JArray>(cancellationToken));
|
||||
var jarray = (JArray)(await response.Content.ReadAsAsync<JObject>(cancellationToken))["data"];
|
||||
return jarray
|
||||
.Children<JObject>()
|
||||
.Select(jobj => ParsePair(jobj))
|
||||
|
@ -23,7 +23,7 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
public async Task<PairRate[]> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var response = await _httpClient.GetAsync("https://api.yadio.io/exrates/BTC", cancellationToken);
|
||||
using var response = await _httpClient.GetAsync("https://api.yadio.io/exrates/BTC", cancellationToken);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var jobj = await response.Content.ReadAsAsync<JObject>(cancellationToken);
|
||||
var results = jobj["BTC"];
|
||||
|
@ -1,21 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Rating
|
||||
#nullable enable
|
||||
namespace BTCPayServer.Rating;
|
||||
public enum RateSource
|
||||
{
|
||||
public class RateSourceInfo
|
||||
{
|
||||
public RateSourceInfo(string id, string displayName, string url)
|
||||
{
|
||||
Id = id;
|
||||
DisplayName = displayName;
|
||||
Url = url;
|
||||
}
|
||||
public string Id { get; set; }
|
||||
public string DisplayName { get; set; }
|
||||
public string Url { get; set; }
|
||||
}
|
||||
Coingecko,
|
||||
Direct
|
||||
}
|
||||
public record RateSourceInfo(string Id, string DisplayName, string Url, RateSource Source = RateSource.Direct);
|
||||
|
@ -45,7 +45,6 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
var fetchingRates = new Dictionary<CurrencyPair, Task<RateResult>>();
|
||||
var fetchingExchanges = new Dictionary<string, Task<QueryRateResult>>();
|
||||
var consolidatedRates = new ExchangeRates();
|
||||
|
||||
foreach (var i in pairs.Select(p => (Pair: p, RateRule: rules.GetRuleFor(p))))
|
||||
{
|
||||
|
@ -85,14 +85,13 @@ namespace BTCPayServer.Services.Rates
|
||||
bgFetcher.RefreshRate = TimeSpan.FromMinutes(1.0);
|
||||
bgFetcher.ValidatyTime = TimeSpan.FromMinutes(5.0);
|
||||
Providers.Add(supportedExchange.Id, bgFetcher);
|
||||
var rsi = coingecko.RateSourceInfo;
|
||||
AvailableRateProviders.Add(new(rsi.Id, rsi.DisplayName, rsi.Url, RateSource.Coingecko));
|
||||
AvailableRateProviders.Add(coingecko.RateSourceInfo);
|
||||
}
|
||||
}
|
||||
AvailableRateProviders.Sort((a, b) => StringComparer.Ordinal.Compare(a.DisplayName, b.DisplayName));
|
||||
}
|
||||
|
||||
public List<AvailableRateProvider> AvailableRateProviders { get; } = new List<AvailableRateProvider>();
|
||||
public List<RateSourceInfo> AvailableRateProviders { get; } = new List<RateSourceInfo>();
|
||||
|
||||
public async Task<QueryRateResult> QueryRates(string exchangeName, CancellationToken cancellationToken)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Data;
|
||||
using BTCPayServer.Events;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Lightning;
|
||||
@ -23,6 +24,7 @@ using Newtonsoft.Json.Linq;
|
||||
using OpenQA.Selenium;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
using WalletSettingsViewModel = BTCPayServer.Models.StoreViewModels.WalletSettingsViewModel;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
@ -174,7 +176,7 @@ namespace BTCPayServer.Tests
|
||||
#pragma warning disable CS0618 // Type or member is obsolete
|
||||
.OfType<DerivationSchemeSettings>().First(o => o.PaymentId.IsBTCOnChain);
|
||||
#pragma warning restore CS0618 // Type or member is obsolete
|
||||
DerivationSchemeSettings.TryParseFromWalletFile(content, onchainBTC.Network, out var expected, out var error);
|
||||
FastTests.GetParsers().TryParseWalletFile(content, onchainBTC.Network, out var expected, out var error);
|
||||
Assert.Equal(expected.ToJson(), onchainBTC.ToJson());
|
||||
Assert.Null(error);
|
||||
|
||||
@ -756,24 +758,26 @@ noninventoryitem:
|
||||
vmpos.Template = AppService.SerializeTemplate(MigrationStartupTask.ParsePOSYML(vmpos.Template));
|
||||
Assert.IsType<RedirectToActionResult>(pos.UpdatePointOfSale(app.Id, vmpos).Result);
|
||||
|
||||
//inventoryitem has 1 item available
|
||||
await tester.WaitForEvent<AppInventoryUpdaterHostedService.UpdateAppInventory>(() =>
|
||||
async Task AssertCanBuy(string choiceKey, bool expected)
|
||||
{
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, choiceKey: "inventoryitem").Result);
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
var redirect = Assert.IsType<RedirectToActionResult>(await publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, choiceKey: choiceKey));
|
||||
if (expected)
|
||||
Assert.Equal("UIInvoice", redirect.ControllerName);
|
||||
else
|
||||
Assert.NotEqual("UIInvoice", redirect.ControllerName);
|
||||
}
|
||||
|
||||
//inventoryitem has 1 item available
|
||||
await AssertCanBuy("inventoryitem", true);
|
||||
|
||||
//we already bought all available stock so this should fail
|
||||
await Task.Delay(100);
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, choiceKey: "inventoryitem").Result);
|
||||
await AssertCanBuy("inventoryitem", false);
|
||||
|
||||
//inventoryitem has unlimited items available
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, choiceKey: "noninventoryitem").Result);
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
.ViewPointOfSale(app.Id, PosViewType.Cart, 1, choiceKey: "noninventoryitem").Result);
|
||||
await AssertCanBuy("noninventoryitem", true);
|
||||
await AssertCanBuy("noninventoryitem", true);
|
||||
|
||||
//verify invoices where created
|
||||
invoices = user.BitPay.GetInvoices();
|
||||
@ -805,7 +809,6 @@ btconly:
|
||||
- BTC
|
||||
normal:
|
||||
price: 1.0";
|
||||
|
||||
vmpos.Template = AppService.SerializeTemplate(MigrationStartupTask.ParsePOSYML(vmpos.Template));
|
||||
Assert.IsType<RedirectToActionResult>(pos.UpdatePointOfSale(app.Id, vmpos).Result);
|
||||
Assert.IsType<RedirectToActionResult>(publicApps
|
||||
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
||||
using BTCPayServer.Configuration;
|
||||
using BTCPayServer.Controllers;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using BTCPayServer.Plugins.Altcoins;
|
||||
using BTCPayServer.Services.Wallets;
|
||||
using BTCPayServer.Tests.Logging;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
@ -52,11 +53,12 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
tester.ActivateLBTC();
|
||||
await tester.StartAsync();
|
||||
|
||||
//https://github.com/ElementsProject/elements/issues/956
|
||||
await tester.LBTCExplorerNode.SendCommandAsync("rescanblockchain");
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
user.RegisterDerivationScheme("LBTC");
|
||||
user.RegisterDerivationScheme("USDT");
|
||||
user.RegisterDerivationScheme("ETB");
|
||||
await user.GrantAccessAsync();
|
||||
|
||||
await tester.LBTCExplorerNode.GenerateAsync(4);
|
||||
//no tether on our regtest, lets create it and set it
|
||||
var tether = tester.NetworkProvider.GetNetwork<ElementsBTCPayNetwork>("USDT");
|
||||
@ -75,6 +77,10 @@ namespace BTCPayServer.Tests
|
||||
.AssetId = etb.AssetId;
|
||||
|
||||
|
||||
user.RegisterDerivationScheme("LBTC");
|
||||
user.RegisterDerivationScheme("USDT");
|
||||
user.RegisterDerivationScheme("ETB");
|
||||
|
||||
//test: register 2 assets on the same elements network and make sure paying an invoice on one does not affect the other in any way
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice(0.1m, "BTC"));
|
||||
Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count);
|
||||
@ -82,7 +88,7 @@ namespace BTCPayServer.Tests
|
||||
//1 lbtc = 1 btc
|
||||
Assert.Equal(1, ci.Rate);
|
||||
var star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true,
|
||||
1, "UNSET", lbtc.AssetId);
|
||||
1, "UNSET",false, lbtc.AssetId.ToString());
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
@ -95,8 +101,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
ci = invoice.CryptoInfo.Single(info => info.CryptoCode.Equals("USDT"));
|
||||
Assert.Equal(3, invoice.SupportedTransactionCurrencies.Count);
|
||||
star = await tester.LBTCExplorerNode.SendCommandAsync("sendtoaddress", ci.Address, ci.Due, "", "", false, true,
|
||||
1, "UNSET", tether.AssetId);
|
||||
star = tester.LBTCExplorerNode.SendCommand("sendtoaddress", ci.Address, decimal.Parse(ci.Due), "x", "z", false, true, 1, "unset", false, tether.AssetId.ToString());
|
||||
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Import Project="../Build/Common.csproj" />
|
||||
<PropertyGroup>
|
||||
<NoWarn>$(NoWarn),xUnit1031</NoWarn>
|
||||
<IsPackable>false</IsPackable>
|
||||
<UserSecretsId>AB0AC1DD-9D26-485B-9416-56A33F268117</UserSecretsId>
|
||||
<!--https://devblogs.microsoft.com/aspnet/testing-asp-net-core-mvc-web-apps-in-memory/-->
|
||||
@ -19,13 +20,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.14" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Newtonsoft.Json.Schema" Version="3.0.15" />
|
||||
<PackageReference Include="Selenium.Support" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.1" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="116.0.5845.9600" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="119.0.6045.10500" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -232,17 +232,13 @@ namespace BTCPayServer.Tests
|
||||
ndax.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("BTC_CAD"), new BidAsk(6000m)));
|
||||
rateProvider.Providers.Add("ndax", ndax);
|
||||
|
||||
var bittrex = new MockRateProvider();
|
||||
bittrex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("DOGE_BTC"), new BidAsk(0.004m)));
|
||||
rateProvider.Providers.Add("bittrex", bittrex);
|
||||
|
||||
|
||||
var bitfinex = new MockRateProvider();
|
||||
bitfinex.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("UST_BTC"), new BidAsk(0.000136m)));
|
||||
rateProvider.Providers.Add("bitfinex", bitfinex);
|
||||
|
||||
var bitpay = new MockRateProvider();
|
||||
bitpay.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ETB_BTC"), new BidAsk(0.1m)));
|
||||
bitpay.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("DOGE_BTC"), new BidAsk(0.004m)));
|
||||
rateProvider.Providers.Add("bitpay", bitpay);
|
||||
var kraken = new MockRateProvider();
|
||||
kraken.ExchangeRates.Add(new PairRate(CurrencyPair.Parse("ETH_BTC"), new BidAsk(0.1m)));
|
||||
|
@ -12,7 +12,8 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
this._Parent = serverTester;
|
||||
var url = serverTester.GetEnvironment(environmentName, defaultValue);
|
||||
Client = (ChargeClient)LightningClientFactory.CreateClient(url, network);
|
||||
|
||||
Client = (ChargeClient)new LightningClientFactory(network).Create(url);
|
||||
P2PHost = _Parent.GetEnvironment(environmentName + "_HOST", defaultHost);
|
||||
}
|
||||
public ChargeClient Client { get; set; }
|
||||
|
@ -60,13 +60,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("Bitcoin", s.Driver.FindElement(By.CssSelector(".payment-method.active")).Text);
|
||||
Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text);
|
||||
var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||
var address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
var clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||
var copyAddress = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||
Assert.Equal($"bitcoin:{address}", payUrl);
|
||||
var address = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||
Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text);
|
||||
Assert.DoesNotContain("lightning=", payUrl);
|
||||
Assert.Equal(address, copyAddress);
|
||||
Assert.Equal($"bitcoin:{address}", payUrl);
|
||||
Assert.Equal($"bitcoin:{address}", clipboard);
|
||||
Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue);
|
||||
s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC"));
|
||||
|
||||
@ -97,11 +97,11 @@ namespace BTCPayServer.Tests
|
||||
Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text);
|
||||
Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text);
|
||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||
copyAddress = s.Driver.FindElement(By.CssSelector("#Lightning_BTC_LightningLike .truncate-center-start")).Text;
|
||||
address = s.Driver.FindElement(By.CssSelector("#Lightning_BTC_LightningLike .truncate-center-start")).Text;
|
||||
Assert.Equal($"lightning:{address}", payUrl);
|
||||
Assert.Equal(address, copyAddress);
|
||||
Assert.Equal($"lightning:{address}", clipboard);
|
||||
Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue);
|
||||
s.Driver.ElementDoesNotExist(By.Id("Address_BTC"));
|
||||
|
||||
@ -153,7 +153,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
|
||||
await Task.Delay(200);
|
||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
address = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||
var amountFraction = "0.00001";
|
||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
|
||||
Money.Parse(amountFraction));
|
||||
@ -202,15 +202,14 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// Pay partial amount
|
||||
await Task.Delay(200);
|
||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
amountFraction = "0.00001";
|
||||
await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest),
|
||||
Money.Parse(amountFraction));
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
|
||||
s.Driver.FindElement(By.Id("test-payment-amount")).Clear();
|
||||
s.Driver.FindElement(By.Id("test-payment-amount")).SendKeys("0.00001");
|
||||
|
||||
// Fake Pay
|
||||
TestUtils.Eventually(() =>
|
||||
{
|
||||
s.Driver.FindElement(By.Id("FakePayment")).Click();
|
||||
s.Driver.FindElement(By.Id("mine-block")).Click();
|
||||
paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo"));
|
||||
Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text);
|
||||
Assert.Contains("Please send", paymentInfo.Text);
|
||||
@ -265,18 +264,19 @@ namespace BTCPayServer.Tests
|
||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||
Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text);
|
||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||
var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||
var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||
Assert.StartsWith($"bitcoin:{address}?amount=", payUrl);
|
||||
Assert.StartsWith($"bitcoin:{copyAddressOnchain}?amount=", payUrl);
|
||||
Assert.Contains("?amount=", payUrl);
|
||||
Assert.Contains("&lightning=", payUrl);
|
||||
Assert.StartsWith("bcrt", copyAddressOnchain);
|
||||
Assert.Equal(address, copyAddressOnchain);
|
||||
Assert.StartsWith("lnbcrt", copyAddressLightning);
|
||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?amount=", qrValue);
|
||||
Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?amount=", qrValue);
|
||||
Assert.Contains("&lightning=LNBCRT", qrValue);
|
||||
Assert.Contains("&lightning=lnbcrt", clipboard);
|
||||
Assert.Equal(clipboard, payUrl);
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
@ -333,17 +333,18 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.WaitUntilAvailable(By.Id("Checkout-v2"));
|
||||
Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method")));
|
||||
qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value");
|
||||
address = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard");
|
||||
payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href");
|
||||
copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC .truncate-center-start")).Text;
|
||||
copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC .truncate-center-start")).Text;
|
||||
Assert.StartsWith($"bitcoin:{address}", payUrl);
|
||||
Assert.StartsWith($"bitcoin:{copyAddressOnchain}", payUrl);
|
||||
Assert.Contains("?lightning=lnurl", payUrl);
|
||||
Assert.DoesNotContain("amount=", payUrl);
|
||||
Assert.StartsWith("bcrt", copyAddressOnchain);
|
||||
Assert.Equal(address, copyAddressOnchain);
|
||||
Assert.StartsWith("lnurl", copyAddressLightning);
|
||||
Assert.StartsWith($"bitcoin:{address.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||
Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?lightning=LNURL", qrValue);
|
||||
Assert.Contains($"bitcoin:{copyAddressOnchain}?lightning=lnurl", clipboard);
|
||||
Assert.Equal(clipboard, payUrl);
|
||||
|
||||
// Check details
|
||||
s.Driver.ToggleCollapse("PaymentDetails");
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0.401-bullseye-slim AS builder
|
||||
FROM mcr.microsoft.com/dotnet/sdk:8.0.101-bookworm-slim AS builder
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends chromium-driver \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@ -98,7 +98,7 @@ retry:
|
||||
}
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
Assert.False(true, "Elements was found");
|
||||
Assert.Fail("Elements was found");
|
||||
}
|
||||
|
||||
public static void UntilJsIsReady(this WebDriverWait wait)
|
||||
@ -197,10 +197,11 @@ retry:
|
||||
driver.FindElement(selector).Click();
|
||||
}
|
||||
|
||||
[DebuggerHidden]
|
||||
public static bool ElementDoesNotExist(this IWebDriver driver, By selector)
|
||||
{
|
||||
Assert.Throws<NoSuchElementException>(() =>
|
||||
Assert.Throws<NoSuchElementException>(
|
||||
[DebuggerStepThrough]
|
||||
() =>
|
||||
{
|
||||
driver.FindElement(selector);
|
||||
});
|
||||
|
@ -10,6 +10,7 @@ using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Extensions;
|
||||
using BTCPayServer.Abstractions.Models;
|
||||
using BTCPayServer.Client;
|
||||
using BTCPayServer.Client.Models;
|
||||
using BTCPayServer.Configuration;
|
||||
@ -18,12 +19,16 @@ using BTCPayServer.Data;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.JsonConverters;
|
||||
using BTCPayServer.Logging;
|
||||
using BTCPayServer.Payments;
|
||||
using BTCPayServer.Payments.Bitcoin;
|
||||
using BTCPayServer.Payments.Lightning;
|
||||
using BTCPayServer.Plugins;
|
||||
using BTCPayServer.Plugins.Bitcoin;
|
||||
using BTCPayServer.Rating;
|
||||
using BTCPayServer.Services;
|
||||
using BTCPayServer.Services.Apps;
|
||||
using BTCPayServer.Services.Fees;
|
||||
using BTCPayServer.Services.Invoices;
|
||||
using BTCPayServer.Services.Labels;
|
||||
using BTCPayServer.Services.Rates;
|
||||
@ -131,7 +136,7 @@ namespace BTCPayServer.Tests
|
||||
var tags = new HashSet<String>(g.Select(o => o.Tag));
|
||||
if (tags.Count != 1)
|
||||
{
|
||||
Assert.False(true, $"All docker images '{g.Key}' in docker-compose.yml and docker-compose.altcoins.yml should have the same tags. (Found {string.Join(',', tags)})");
|
||||
Assert.Fail($"All docker images '{g.Key}' in docker-compose.yml and docker-compose.altcoins.yml should have the same tags. (Found {string.Join(',', tags)})");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -155,6 +160,43 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("Test", data.FromAsset);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanInterpolateOrBound()
|
||||
{
|
||||
var testData = new ((int Blocks, decimal Fee)[] Data, int Target, decimal Expected) []
|
||||
{
|
||||
([(0, 0m), (10, 100m)], 5, 50m),
|
||||
([(50, 0m), (100, 100m)], 5, 0.0m),
|
||||
([(50, 0m), (100, 100m)], 101, 100.0m),
|
||||
([(50, 100m), (50, 100m)], 101, 100.0m),
|
||||
([(50, 0m), (100, 100m)], 75, 50m),
|
||||
([(0, 0m), (50, 50m), (100, 100m)], 75, 75m),
|
||||
([(0, 0m), (500, 50m), (1000, 100m)], 750, 75m),
|
||||
([(0, 0m), (500, 50m), (1000, 100m)], 100, 10m),
|
||||
([(0, 0m), (100, 100m)], 80, 80m),
|
||||
([(0, 0m), (100, 100m)], 25, 25m),
|
||||
([(0, 0m), (25, 25m), (50, 50m), (100, 100m), (110, 120m)], 75, 75m),
|
||||
([(0, 0m), (25, 0m), (50, 50m), (100, 100m), (110, 0m)], 75, 75m),
|
||||
([(0, 0m), (25, 0m), (50, 50m), (100, 100m), (110, 0m)], 50, 50m),
|
||||
([(0, 0m), (25, 0m), (50, 50m), (100, 100m), (110, 0m)], 100, 100m),
|
||||
([(0, 0m), (25, 0m), (50, 50m), (100, 100m), (110, 0m)], 102, 80m),
|
||||
};
|
||||
foreach (var t in testData)
|
||||
{
|
||||
var actual = MempoolSpaceFeeProvider.InterpolateOrBound(t.Data.Select(t => new MempoolSpaceFeeProvider.BlockFeeRate(t.Blocks, new FeeRate(t.Fee))).ToArray(), t.Target);
|
||||
Assert.Equal(new FeeRate(t.Expected), actual);
|
||||
}
|
||||
}
|
||||
[Fact]
|
||||
public void CanRandomizeByPercentage()
|
||||
{
|
||||
var generated = Enumerable.Range(0, 1000).Select(_ => MempoolSpaceFeeProvider.RandomizeByPercentage(100.0m, 10.0m)).ToArray();
|
||||
Assert.Empty(generated.Where(g => g < 90m));
|
||||
Assert.Empty(generated.Where(g => g > 110m));
|
||||
Assert.NotEmpty(generated.Where(g => g < 91m));
|
||||
Assert.NotEmpty(generated.Where(g => g > 109m));
|
||||
}
|
||||
|
||||
private void CanParseDecimalsCore(string str, decimal expected)
|
||||
{
|
||||
var d = JsonConvert.DeserializeObject<LedgerEntryData>(str);
|
||||
@ -410,7 +452,7 @@ namespace BTCPayServer.Tests
|
||||
public void CanCalculateDust()
|
||||
{
|
||||
var entity = new InvoiceEntity() { Currency = "USD" };
|
||||
entity.Networks = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
entity.Networks = CreateNetworkProvider(ChainName.Regtest);
|
||||
#pragma warning disable CS0618
|
||||
entity.Payments = new System.Collections.Generic.List<PaymentEntity>();
|
||||
entity.SetPaymentMethod(new PaymentMethod()
|
||||
@ -456,7 +498,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanCalculateCryptoDue()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var entity = new InvoiceEntity() { Currency = "USD" };
|
||||
entity.Networks = networkProvider;
|
||||
#pragma warning disable CS0618
|
||||
@ -644,12 +686,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanAcceptInvoiceWithTolerance()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var paymentMethodHandlerDictionary = new PaymentMethodHandlerDictionary(new IPaymentMethodHandler[]
|
||||
{
|
||||
new BitcoinLikePaymentHandler(null, networkProvider, null, null, null, null),
|
||||
new LightningLikePaymentHandler(null, null, networkProvider, null, null, null),
|
||||
});
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var entity = new InvoiceEntity();
|
||||
entity.Networks = networkProvider;
|
||||
#pragma warning disable CS0618
|
||||
@ -764,7 +801,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public async Task CanEnumerateTorServices()
|
||||
{
|
||||
var tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest),
|
||||
var tor = new TorServices(CreateNetworkProvider(ChainName.Regtest),
|
||||
new OptionsWrapper<BTCPayServerOptions>(new BTCPayServerOptions()
|
||||
{
|
||||
TorrcFile = TestUtils.GetTestDataFullPath("Tor/torrc")
|
||||
@ -776,7 +813,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Single(tor.Services.Where(t => t.ServiceType == TorServiceType.RPC));
|
||||
Assert.True(tor.Services.Count(t => t.ServiceType == TorServiceType.Other) > 1);
|
||||
|
||||
tor = new TorServices(new BTCPayNetworkProvider(ChainName.Regtest),
|
||||
tor = new TorServices(CreateNetworkProvider(ChainName.Regtest),
|
||||
new OptionsWrapper<BTCPayServerOptions>(new BTCPayServerOptions()
|
||||
{
|
||||
TorrcFile = null,
|
||||
@ -813,12 +850,13 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void CanParseDerivationSchemes()
|
||||
{
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||
|
||||
// xpub
|
||||
var xpub = "xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw";
|
||||
DerivationStrategyBase strategyBase = parser.Parse(xpub);
|
||||
Assert.Throws<FormatException>(() => parser.Parse(xpub, false, false, true));
|
||||
DerivationStrategyBase strategyBase = parser.Parse(xpub, false, false, false);
|
||||
Assert.IsType<DirectDerivationStrategy>(strategyBase);
|
||||
Assert.True(((DirectDerivationStrategy)strategyBase).Segwit);
|
||||
Assert.Equal("tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS", strategyBase.ToString());
|
||||
@ -839,7 +877,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(expected, inner.ToString());
|
||||
|
||||
// Output Descriptor
|
||||
networkProvider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
networkProvider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
parser = new DerivationSchemeParser(networkProvider.BTC);
|
||||
var od = "wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48";
|
||||
(strategyBase, rootedKeyPath) = parser.ParseOutputDescriptor(od);
|
||||
@ -878,18 +916,26 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(TradeQuantity.Parse(qty2.ToString()), TradeQuantity.Parse(" 1.3 "));
|
||||
}
|
||||
|
||||
|
||||
public static WalletFileParsers GetParsers()
|
||||
{
|
||||
var service = new ServiceCollection();
|
||||
BTCPayServerServices.AddOnchainWalletParsers(service);
|
||||
return service.BuildServiceProvider().GetRequiredService<WalletFileParsers>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ParseDerivationSchemeSettings()
|
||||
{
|
||||
var testnet = new BTCPayNetworkProvider(ChainName.Testnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var mainnet = new BTCPayNetworkProvider(ChainName.Mainnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var testnet = CreateNetworkProvider(ChainName.Testnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var mainnet = CreateNetworkProvider(ChainName.Mainnet).GetNetwork<BTCPayNetwork>("BTC");
|
||||
var root = new Mnemonic(
|
||||
"usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage")
|
||||
.DeriveExtKey();
|
||||
|
||||
var parsers = GetParsers();
|
||||
// xpub
|
||||
var tpub = "tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS";
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(tpub, testnet, out var settings, out var error));
|
||||
Assert.True(parsers.TryParseWalletFile(tpub, testnet, out var settings, out var error));
|
||||
Assert.Null(error);
|
||||
Assert.True(settings.AccountDerivation is DirectDerivationStrategy { Segwit: false });
|
||||
Assert.Equal($"{tpub}-[legacy]", ((DirectDerivationStrategy)settings.AccountDerivation).ToString());
|
||||
@ -900,7 +946,7 @@ namespace BTCPayServer.Tests
|
||||
var fingerprint = "e5746fd9";
|
||||
var account = "84'/1'/0'";
|
||||
var str = $"[{fingerprint}/{account}]{vpub}";
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(str, testnet, out settings, out error));
|
||||
Assert.True(parsers.TryParseWalletFile(str, testnet, out settings, out error));
|
||||
Assert.Null(error);
|
||||
Assert.True(settings.AccountDerivation is DirectDerivationStrategy { Segwit: true });
|
||||
Assert.Equal(vpub, settings.AccountOriginal);
|
||||
@ -909,7 +955,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(account, settings.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
|
||||
// ColdCard
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.True(parsers.TryParseWalletFile(
|
||||
"{\"keystore\": {\"ckcc_xpub\": \"xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw\", \"xpub\": \"ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/0'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}",
|
||||
mainnet, out settings, out error));
|
||||
Assert.Null(error);
|
||||
@ -925,28 +971,28 @@ namespace BTCPayServer.Tests
|
||||
settings.AccountDerivation.GetDerivation().ScriptPubKey);
|
||||
|
||||
// Should be legacy
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.True(parsers.TryParseWalletFile(
|
||||
"{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"tpubDDWYqT3P24znfsaGX7kZcQhNc5LAjnQiKQvUCHF2jS6dsgJBRtymopEU5uGpMaR5YChjuiExZG1X2aTbqXkp82KqH5qnqwWHp6EWis9ZvKr\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/44'/1'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}",
|
||||
testnet, out settings, out error));
|
||||
Assert.True(settings.AccountDerivation is DirectDerivationStrategy { Segwit: false });
|
||||
Assert.Null(error);
|
||||
|
||||
// Should be segwit p2sh
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.True(parsers.TryParseWalletFile(
|
||||
"{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"upub5DSddA9NoRUyJrQ4p86nsCiTSY7kLHrSxx3joEJXjHd4HPARhdXUATuk585FdWPVC2GdjsMePHb6BMDmf7c6KG4K4RPX6LVqBLtDcWpQJmh\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/49'/1'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}",
|
||||
testnet, out settings, out error));
|
||||
Assert.True(settings.AccountDerivation is P2SHDerivationStrategy { Inner: DirectDerivationStrategy { Segwit: true } });
|
||||
Assert.Null(error);
|
||||
|
||||
// Should be segwit
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.True(parsers.TryParseWalletFile(
|
||||
"{\"keystore\": {\"ckcc_xpub\": \"tpubD6NzVbkrYhZ4YHNiuTdTmHRmbcPRLfqgyneZFCL1mkzkUBjXriQShxTh9HL34FK2mhieasJVk9EzJrUfkFqRNQBjiXgx3n5BhPkxKBoFmaS\", \"xpub\": \"vpub5YjYxTemJ39tFRnuAhwduyxG2tKGjoEpmvqVQRPqdYrqa6YGoeSzBtHXaJUYB19zDbXs3JjbEcVWERjQBPf9bEfUUMZNMv1QnMyHV8JPqyf\", \"label\": \"Coldcard Import 0x60d1af8b\", \"ckcc_xfp\": 1624354699, \"type\": \"hardware\", \"hw_type\": \"coldcard\", \"derivation\": \"m/84'/1'/0'\"}, \"wallet_type\": \"standard\", \"use_encryption\": false, \"seed_version\": 17}",
|
||||
testnet, out settings, out error));
|
||||
Assert.True(settings.AccountDerivation is DirectDerivationStrategy { Segwit: true });
|
||||
Assert.Null(error);
|
||||
|
||||
// Specter
|
||||
Assert.True(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.True(parsers.TryParseWalletFile(
|
||||
"{\"label\": \"Specter\", \"blockheight\": 123456, \"descriptor\": \"wpkh([8bafd160/49h/0h/0h]xpub661MyMwAqRbcGVBsTGeNZN6QGVHmMHLdSA4FteGsRrEriu4pnVZMZWnruFFFXkMnyoBjyHndD3Qwcfz4MPzBUxjSevweNFQx7SAYZATtcDw/0/*)#9x4vkw48\"}",
|
||||
mainnet, out var specter, out error));
|
||||
Assert.Equal(root.GetPublicKey().GetHDFingerPrint(), specter.AccountKeySettings[0].RootFingerprint);
|
||||
@ -955,19 +1001,63 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal("Specter", specter.Label);
|
||||
Assert.Null(error);
|
||||
|
||||
//BSMS BIP129, Nunchuk
|
||||
|
||||
var bsms = @"BSMS 1.0
|
||||
wsh(sortedmulti(1,[5c9e228d/48'/0'/0'/2']xpub6EgGHjcvovyN3nK921zAGPfuB41cJXkYRdt3tLGmiMyvbgHpss4X1eRZwShbEBb1znz2e2bCkCED87QZpin3sSYKbmCzQ9Sc7LaV98ngdeX/**,[2b0e251e/48'/0'/0'/2']xpub6DrimHB8KUSkPvmJ8Pk8RE769EdDm2VEoZ8MBz76w9QupP8Py4wexs4Pa3aRB1LUEhc9GyY6ypDWEFFRCgqeDQePcyWQfjtmintrehq3JCL/**))
|
||||
/0/*,/1/*
|
||||
bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku
|
||||
";
|
||||
|
||||
Assert.True(parsers.TryParseWalletFile(bsms,
|
||||
mainnet, out var nunchuk, out error));
|
||||
|
||||
Assert.Equal(2, nunchuk.AccountKeySettings.Length);
|
||||
//check that the account key settings match those in bsms string
|
||||
Assert.Equal("5c9e228d", nunchuk.AccountKeySettings[0].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
Assert.Equal("2b0e251e", nunchuk.AccountKeySettings[1].RootFingerprint.ToString());
|
||||
Assert.Equal("48'/0'/0'/2'", nunchuk.AccountKeySettings[1].AccountKeyPath.ToString());
|
||||
|
||||
var multsig = Assert.IsType<MultisigDerivationStrategy>
|
||||
(Assert.IsType<P2WSHDerivationStrategy>(nunchuk.AccountDerivation).Inner);
|
||||
|
||||
Assert.True(multsig.LexicographicOrder);
|
||||
Assert.Equal(1, multsig.RequiredSignatures);
|
||||
|
||||
var deposit = new NBXplorer.KeyPathTemplates(null).GetKeyPathTemplate(DerivationFeature.Deposit);
|
||||
var line = nunchuk.AccountDerivation.GetLineFor(deposit).Derive(0);
|
||||
|
||||
Assert.Equal(BitcoinAddress.Create("bc1qfzu57kgu5jthl934f9xrdzzx8mmemx7gn07tf0grnvz504j6kzusu2v0ku", Network.Main).ScriptPubKey,
|
||||
line.ScriptPubKey);
|
||||
|
||||
Assert.Equal("BSMS", nunchuk.Source);
|
||||
Assert.Null(error);
|
||||
|
||||
|
||||
// Failure case
|
||||
Assert.False(DerivationSchemeSettings.TryParseFromWalletFile(
|
||||
Assert.False(parsers.TryParseWalletFile(
|
||||
"{\"keystore\": {\"ckcc_xpub\": \"tpubFailure\", \"xpub\": \"tpubFailure\", \"label\": \"Failure\"}, \"wallet_type\": \"standard\"}",
|
||||
testnet, out settings, out error));
|
||||
Assert.Null(settings);
|
||||
Assert.NotNull(error);
|
||||
|
||||
|
||||
//passport
|
||||
var passportText =
|
||||
"{\"Source\": \"Passport\", \"Descriptor\": \"tr([5c9e228d/86'/0'/0']xpub6EgGHjcvovyN3nK921zAGPfuB41cJXkYRdt3tLGmiMyvbgHpss4X1eRZwShbEBb1znz2e2bCkCED87QZpin3sSYKbmCzQ9Sc7LaV98ngdeX/0/*)\", \"FirmwareVersion\": \"v1.0.0\"}";
|
||||
Assert.True(parsers.TryParseWalletFile(passportText, mainnet, out var passport, out error));
|
||||
Assert.Equal("Passport", passport.Source);
|
||||
Assert.True(passport.AccountDerivation is TaprootDerivationStrategy);
|
||||
Assert.Equal("5c9e228d", passport.AccountKeySettings[0].RootFingerprint.ToString());
|
||||
Assert.Equal("86'/0'/0'", passport.AccountKeySettings[0].AccountKeyPath.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckRatesProvider()
|
||||
public async Task CheckRatesProvider()
|
||||
{
|
||||
var spy = new SpyRateProvider();
|
||||
RateRules.TryParse("X_X = bittrex(X_X);", out var rateRules);
|
||||
RateRules.TryParse("X_X = bitpay(X_X);", out var rateRules);
|
||||
|
||||
var factory = CreateBTCPayRateFactory();
|
||||
factory.Providers.Clear();
|
||||
@ -975,24 +1065,23 @@ namespace BTCPayServer.Tests
|
||||
factory.Providers.Clear();
|
||||
var fetch = new BackgroundFetcherRateProvider(spy);
|
||||
fetch.DoNotAutoFetchIfExpired = true;
|
||||
factory.Providers.Add("bittrex", fetch);
|
||||
var fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter()
|
||||
.GetResult();
|
||||
factory.Providers.Add("bitpay", fetch);
|
||||
var fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertHit();
|
||||
fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter().GetResult();
|
||||
fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertNotHit();
|
||||
fetch.UpdateIfNecessary(default).GetAwaiter().GetResult();
|
||||
await fetch.UpdateIfNecessary(default);
|
||||
spy.AssertNotHit();
|
||||
fetch.RefreshRate = TimeSpan.FromSeconds(1.0);
|
||||
Thread.Sleep(1020);
|
||||
fetchedRate = fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default).GetAwaiter().GetResult();
|
||||
fetchedRate = await fetcher.FetchRate(CurrencyPair.Parse("BTC_USD"), rateRules, default);
|
||||
spy.AssertNotHit();
|
||||
fetch.ValidatyTime = TimeSpan.FromSeconds(1.0);
|
||||
fetch.UpdateIfNecessary(default).GetAwaiter().GetResult();
|
||||
await fetch.UpdateIfNecessary(default);
|
||||
spy.AssertHit();
|
||||
fetch.GetRatesAsync(default).GetAwaiter().GetResult();
|
||||
await fetch.GetRatesAsync(default);
|
||||
Thread.Sleep(1000);
|
||||
Assert.Throws<InvalidOperationException>(() => fetch.GetRatesAsync(default).GetAwaiter().GetResult());
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() => fetch.GetRatesAsync(default));
|
||||
}
|
||||
|
||||
public static RateProviderFactory CreateBTCPayRateFactory()
|
||||
@ -1246,7 +1335,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void HasCurrencyDataForNetworks()
|
||||
{
|
||||
var btcPayNetworkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var btcPayNetworkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
foreach (var network in btcPayNetworkProvider.GetAll())
|
||||
{
|
||||
var cd = CurrencyNameTable.Instance.GetCurrencyData(network.CryptoCode, false);
|
||||
@ -1564,7 +1653,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
var b = JsonConvert.DeserializeObject<PullPaymentBlob>("{}");
|
||||
Assert.Equal(TimeSpan.FromDays(30.0), b.BOLT11Expiration);
|
||||
var aaa = JsonConvert.SerializeObject(b);
|
||||
JsonConvert.SerializeObject(b);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -1577,7 +1666,7 @@ namespace BTCPayServer.Tests
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.AppendLine("// Some cool comments");
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("// Some other cool comments");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X);");
|
||||
@ -1588,7 +1677,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(
|
||||
"// Some cool comments\n" +
|
||||
"DOGE_X = DOGE_BTC * BTC_X * 1.1;\n" +
|
||||
"DOGE_BTC = bittrex(DOGE_BTC);\n" +
|
||||
"DOGE_BTC = bitpay(DOGE_BTC);\n" +
|
||||
"// Some other cool comments\n" +
|
||||
"BTC_USD = kraken(BTC_USD);\n" +
|
||||
"BTC_X = coinbase(BTC_X);\n" +
|
||||
@ -1596,10 +1685,10 @@ namespace BTCPayServer.Tests
|
||||
rules.ToString());
|
||||
var tests = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "DOGE_USD", Expected: "bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1"),
|
||||
(Pair: "DOGE_CAD", Expected: "bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02"),
|
||||
(Pair: "SATS_CAD", Expected: "0.00000001 * coinbase(BTC_CAD)"),
|
||||
(Pair: "Sats_USD", Expected: "0.00000001 * kraken(BTC_USD)")
|
||||
@ -1609,13 +1698,13 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(test.Expected, rules.GetRuleFor(CurrencyPair.Parse(test.Pair)).ToString());
|
||||
}
|
||||
rules.Spread = 0.2m;
|
||||
Assert.Equal("(bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
Assert.Equal("(bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1) * (0.8, 1.2)", rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")).ToString());
|
||||
////////////////
|
||||
|
||||
// Check errors conditions
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = LTC_CAD * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("LTC_CHF = LTC_CHF * 1.01");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
@ -1636,7 +1725,7 @@ namespace BTCPayServer.Tests
|
||||
// Check if we can resolve exchange rates
|
||||
builder = new StringBuilder();
|
||||
builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
|
||||
builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
|
||||
builder.AppendLine("DOGE_BTC = bitpay(DOGE_BTC)");
|
||||
builder.AppendLine("BTC_usd = kraken(BTC_USD)");
|
||||
builder.AppendLine("BTC_X = Coinbase(BTC_X)");
|
||||
builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");
|
||||
@ -1644,10 +1733,10 @@ namespace BTCPayServer.Tests
|
||||
|
||||
var tests2 = new[]
|
||||
{
|
||||
(Pair: "DOGE_USD", Expected: "bittrex(DOGE_BTC) * kraken(BTC_USD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),kraken(BTC_USD)"),
|
||||
(Pair: "DOGE_USD", Expected: "bitpay(DOGE_BTC) * kraken(BTC_USD) * 1.1", ExpectedExchangeRates: "bitpay(DOGE_BTC),kraken(BTC_USD)"),
|
||||
(Pair: "BTC_USD", Expected: "kraken(BTC_USD)", ExpectedExchangeRates: "kraken(BTC_USD)"),
|
||||
(Pair: "BTC_CAD", Expected: "coinbase(BTC_CAD)", ExpectedExchangeRates: "coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", ExpectedExchangeRates: "bittrex(DOGE_BTC),coinbase(BTC_CAD)"),
|
||||
(Pair: "DOGE_CAD", Expected: "bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", ExpectedExchangeRates: "bitpay(DOGE_BTC),coinbase(BTC_CAD)"),
|
||||
(Pair: "LTC_CAD", Expected: "coinaverage(LTC_CAD) * 1.02", ExpectedExchangeRates: "coinaverage(LTC_CAD)"),
|
||||
(Pair: "SATS_USD", Expected: "0.00000001 * kraken(BTC_USD)", ExpectedExchangeRates: "kraken(BTC_USD)"),
|
||||
(Pair: "SATS_EUR", Expected: "0.00000001 * coinbase(BTC_EUR)", ExpectedExchangeRates: "coinbase(BTC_EUR)")
|
||||
@ -1659,11 +1748,11 @@ namespace BTCPayServer.Tests
|
||||
Assert.Equal(test.ExpectedExchangeRates, string.Join(',', rule.ExchangeRates.OfType<object>().ToArray()));
|
||||
}
|
||||
var rule2 = rules.GetRuleFor(CurrencyPair.Parse("DOGE_CAD"));
|
||||
rule2.ExchangeRates.SetRate("bittrex", CurrencyPair.Parse("DOGE_BTC"), new BidAsk(5000m));
|
||||
rule2.ExchangeRates.SetRate("bitpay", CurrencyPair.Parse("DOGE_BTC"), new BidAsk(5000m));
|
||||
rule2.Reevaluate();
|
||||
Assert.True(rule2.HasError);
|
||||
Assert.Equal("5000 * ERR_RATE_UNAVAILABLE(coinbase, BTC_CAD) * 1.1", rule2.ToString(true));
|
||||
Assert.Equal("bittrex(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", rule2.ToString(false));
|
||||
Assert.Equal("bitpay(DOGE_BTC) * coinbase(BTC_CAD) * 1.1", rule2.ToString(false));
|
||||
rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_CAD"), new BidAsk(2000.4m));
|
||||
rule2.Reevaluate();
|
||||
Assert.False(rule2.HasError);
|
||||
@ -1825,8 +1914,7 @@ namespace BTCPayServer.Tests
|
||||
new KeyValuePair<string, string>("chains", "usdt")}
|
||||
})
|
||||
});
|
||||
|
||||
var networkProvider = config.ConfigureNetworkProvider(BTCPayLogs);
|
||||
var networkProvider = CreateNetworkProvider(config);
|
||||
Assert.NotNull(networkProvider.GetNetwork("LBTC"));
|
||||
Assert.NotNull(networkProvider.GetNetwork("USDT"));
|
||||
}
|
||||
@ -1834,16 +1922,16 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Altcoins", "Altcoins")]
|
||||
public void CanParseDerivationScheme()
|
||||
{
|
||||
var testnetNetworkProvider = new BTCPayNetworkProvider(ChainName.Testnet);
|
||||
var regtestNetworkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var mainnetNetworkProvider = new BTCPayNetworkProvider(ChainName.Mainnet);
|
||||
var testnetNetworkProvider = CreateNetworkProvider(ChainName.Testnet);
|
||||
var regtestNetworkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var mainnetNetworkProvider = CreateNetworkProvider(ChainName.Mainnet);
|
||||
var testnetParser = new DerivationSchemeParser(testnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var mainnetParser = new DerivationSchemeParser(mainnetNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
NBXplorer.DerivationStrategy.DerivationStrategyBase result;
|
||||
// Passing electrum stuff
|
||||
// Passing a native segwit from mainnet to a testnet parser, means the testnet parser will try to convert it into segwit
|
||||
result = testnetParser.Parse(
|
||||
"zpub6nL6PUGurpU3DfPDSZaRS6WshpbNc9ctCFFzrCn54cssnheM31SZJZUcFHKtjJJNhAueMbh6ptFMfy1aeiMQJr3RJ4DDt1hAPx7sMTKV48t");
|
||||
"zpub6nL6PUGurpU3DfPDSZaRS6WshpbNc9ctCFFzrCn54cssnheM31SZJZUcFHKtjJJNhAueMbh6ptFMfy1aeiMQJr3RJ4DDt1hAPx7sMTKV48t", false, false, false);
|
||||
Assert.Equal(
|
||||
"tpubD93CJNkmGjLXnsBqE2zGDqfEh1Q8iJ8wueordy3SeWt1RngbbuxXCsqASuVWFywmfoCwUE1rSfNJbaH4cBNcbp8WcyZgPiiRSTazLGL8U9w",
|
||||
result.ToString());
|
||||
@ -1867,7 +1955,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
// if prefix not recognize, assume it is segwit
|
||||
result = testnetParser.Parse(
|
||||
"xpub661MyMwAqRbcGeVGU5e5KBcau1HHEUGf9Wr7k4FyLa8yRPNQrrVa7Ndrgg8Afbe2UYXMSL6tJBFd2JewwWASsePPLjkcJFL1tTVEs3UQ23X");
|
||||
"xpub661MyMwAqRbcGeVGU5e5KBcau1HHEUGf9Wr7k4FyLa8yRPNQrrVa7Ndrgg8Afbe2UYXMSL6tJBFd2JewwWASsePPLjkcJFL1tTVEs3UQ23X", false, false, false);
|
||||
Assert.Equal(
|
||||
"tpubD6NzVbkrYhZ4YSg7vGdAX6wxE8NwDrmih9SR6cK7gUtsAg37w5LfFpJgviCxC6bGGT4G3uckqH5fiV9ZLN1gm5qgQLVuymzFUR5ed7U7ksu",
|
||||
result.ToString());
|
||||
@ -1876,13 +1964,13 @@ namespace BTCPayServer.Tests
|
||||
var tpub =
|
||||
"tpubD6NzVbkrYhZ4Wc65tjhmcKdWFauAo7bGLRTxvggygkNyp6SMGutJp7iociwsinU33jyNBp1J9j2hJH5yQsayfiS3LEU2ZqXodAcnaygra8o";
|
||||
|
||||
result = testnetParser.Parse(tpub);
|
||||
result = testnetParser.Parse(tpub, false, true);
|
||||
Assert.Equal(tpub, result.ToString());
|
||||
|
||||
var regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("BTC"));
|
||||
var parsed =
|
||||
regtestParser.Parse(
|
||||
"xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
|
||||
"xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]", false, false, false);
|
||||
Assert.Equal(
|
||||
"tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]",
|
||||
parsed.ToString());
|
||||
@ -1890,14 +1978,14 @@ namespace BTCPayServer.Tests
|
||||
// Let's make sure we can't generate segwit with dogecoin
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("DOGE"));
|
||||
parsed = regtestParser.Parse(
|
||||
"xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]");
|
||||
"xpub6DG1rMYXiQtCc6CfdLFD9CtxqhzzRh7j6Sq6EdE9abgYy3cfDRrniLLv2AdwqHL1exiLnnKR5XXcaoiiexf3Y9R6J6rxkJtqJHzNzMW9QMZ-[p2sh]", false, false, false);
|
||||
Assert.Equal(
|
||||
"tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]",
|
||||
parsed.ToString());
|
||||
|
||||
regtestParser = new DerivationSchemeParser(regtestNetworkProvider.GetNetwork<BTCPayNetwork>("DOGE"));
|
||||
parsed = regtestParser.Parse(
|
||||
"tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]");
|
||||
"tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[p2sh]", false, false, false);
|
||||
Assert.Equal(
|
||||
"tpubDDdeNbNDRgqestPX5XEJM8ELAq6eR5cne5RPbBHHvWSSiLHNHehsrn1kGCijMnHFSsFFQMqHcdMfGzDL3pWHRasPMhcGRqZ4tFankQ3i4ok-[legacy]",
|
||||
parsed.ToString());
|
||||
@ -2002,7 +2090,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
#pragma warning disable CS0618
|
||||
var dummy = new Key().PubKey.GetAddress(ScriptPubKeyType.Legacy, Network.RegTest).ToString();
|
||||
var networkProvider = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
var networkProvider = CreateNetworkProvider(ChainName.Regtest);
|
||||
var networkBTC = networkProvider.GetNetwork("BTC");
|
||||
var networkLTC = networkProvider.GetNetwork("LTC");
|
||||
InvoiceEntity invoiceEntity = new InvoiceEntity();
|
||||
@ -2074,6 +2162,7 @@ namespace BTCPayServer.Tests
|
||||
[Fact]
|
||||
public void AllPoliciesShowInUI()
|
||||
{
|
||||
new BitpayRateProvider(new System.Net.Http.HttpClient()).GetRatesAsync(default).GetAwaiter().GetResult();
|
||||
foreach (var policy in Policies.AllPolicies)
|
||||
{
|
||||
Assert.True(UIManageController.AddApiKeyViewModel.PermissionValueItem.PermissionDescriptions.ContainsKey(policy));
|
||||
@ -2119,7 +2208,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
["derivationStrategy"] = "tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf"
|
||||
};
|
||||
var scheme = DerivationSchemeSettings.Parse("tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf", new BTCPayNetworkProvider(ChainName.Regtest).BTC);
|
||||
var scheme = DerivationSchemeSettings.Parse("tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf", CreateNetworkProvider(ChainName.Regtest).BTC);
|
||||
|
||||
scheme.Source = "ManualDerivationScheme";
|
||||
scheme.AccountOriginal = "tpubDDLQZ1WMdy5YJAJWmRNoTJ3uQkavEPXCXnmD4eAuo9BKbzFUBbJmVHys5M3ku4Qw1C165wGpVWH55gZpHjdsCyntwNzhmCAzGejSL6rzbyf";
|
||||
@ -2139,7 +2228,7 @@ namespace BTCPayServer.Tests
|
||||
.Select(o =>
|
||||
{
|
||||
var entity = JsonConvert.DeserializeObject<InvoiceEntity>(o.ToString());
|
||||
entity.Networks = new BTCPayNetworkProvider(ChainName.Regtest);
|
||||
entity.Networks = CreateNetworkProvider(ChainName.Regtest);
|
||||
return entity.DerivationStrategies.ToString();
|
||||
})
|
||||
.ToHashSet();
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Form;
|
||||
using BTCPayServer.Forms;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
@ -10,16 +11,25 @@ using Xunit.Abstractions;
|
||||
|
||||
namespace BTCPayServer.Tests;
|
||||
|
||||
[Trait("Fast", "Fast")]
|
||||
[Collection(nameof(NonParallelizableCollectionDefinition))]
|
||||
[Trait("Integration", "Integration")]
|
||||
public class FormTests : UnitTestBase
|
||||
{
|
||||
public FormTests(ITestOutputHelper helper) : base(helper)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanParseForm()
|
||||
|
||||
[Fact(Timeout = TestUtils.TestTimeout)]
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanParseForm()
|
||||
{
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var user = tester.NewAccount();
|
||||
user.GrantAccess();
|
||||
var service = tester.PayTester.GetService<FormDataService>();
|
||||
|
||||
var form = new Form()
|
||||
{
|
||||
Fields = new List<Field>
|
||||
@ -40,8 +50,6 @@ public class FormTests : UnitTestBase
|
||||
}
|
||||
}
|
||||
};
|
||||
var providers = new FormComponentProviders(new List<IFormComponentProvider>());
|
||||
var service = new FormDataService(null, providers);
|
||||
Assert.False(service.IsFormSchemaValid(form.ToString(), out _, out _));
|
||||
form = new Form
|
||||
{
|
||||
@ -164,7 +172,7 @@ public class FormTests : UnitTestBase
|
||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||
Clear(form);
|
||||
form.SetValues(obj);
|
||||
service.SetValues(form, obj);
|
||||
obj = service.GetValues(form);
|
||||
Assert.Equal("original", obj["invoice"]["test"].Value<string>());
|
||||
Assert.Equal("updated", obj["invoice_item3"].Value<string>());
|
||||
@ -182,12 +190,73 @@ public class FormTests : UnitTestBase
|
||||
}
|
||||
}
|
||||
};
|
||||
form.SetValues(obj);
|
||||
|
||||
service.SetValues(form, obj);
|
||||
obj = service.GetValues(form);
|
||||
Assert.Null(obj["test"].Value<string>());
|
||||
form.SetValues(new JObject { ["test"] = "hello" });
|
||||
|
||||
service.SetValues(form, new JObject { ["test"] = "hello" });
|
||||
obj = service.GetValues(form);
|
||||
Assert.Equal("hello", obj["test"].Value<string>());
|
||||
|
||||
var req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Null(req.Amount);
|
||||
Assert.Null(req.Currency);
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount",
|
||||
Type = "number",
|
||||
Value = "1"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(1, req.Amount);
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_adjustment",
|
||||
Type = "number",
|
||||
Value = "1"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(2, req.Amount);
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_adjustment2",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}currency",
|
||||
Type = "text",
|
||||
Value = "eur"
|
||||
});
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal("eur", req.Currency);
|
||||
Assert.Equal(4, req.Amount);
|
||||
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_multiply_adjustment",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(8, req.Amount);
|
||||
|
||||
|
||||
form.Fields.Add(new Field
|
||||
{
|
||||
Name = $"{FormDataService.InvoiceParameterPrefix}amount_multiply_adjustment1",
|
||||
Type = "number",
|
||||
Value = "2"
|
||||
});
|
||||
|
||||
req = service.GenerateInvoiceParametersFromForm(form);
|
||||
Assert.Equal(16, req.Amount);
|
||||
}
|
||||
|
||||
private void Clear(Form form)
|
||||
|
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Abstractions.Contracts;
|
||||
@ -28,6 +29,7 @@ using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
using CreateApplicationUserRequest = BTCPayServer.Client.Models.CreateApplicationUserRequest;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
@ -56,8 +58,8 @@ namespace BTCPayServer.Tests
|
||||
var factory = tester.PayTester.GetService<IBTCPayServerClientFactory>();
|
||||
Assert.NotNull(factory);
|
||||
var client = await factory.Create(user.UserId, user.StoreId);
|
||||
var u = await client.GetCurrentUser();
|
||||
var s = await client.GetStores();
|
||||
await client.GetCurrentUser();
|
||||
await client.GetStores();
|
||||
var store = await client.GetStore(user.StoreId);
|
||||
Assert.NotNull(store);
|
||||
var addr = await client.GetLightningDepositAddress(user.StoreId, "BTC");
|
||||
@ -1084,7 +1086,39 @@ namespace BTCPayServer.Tests
|
||||
Assert.IsType<string>(lnrURLs.LNURLUri);
|
||||
Assert.Equal(12.303228134m, test4.Amount);
|
||||
Assert.Equal("BTC", test4.Currency);
|
||||
|
||||
|
||||
// Check we can register Boltcard
|
||||
var uid = new byte[7];
|
||||
RandomNumberGenerator.Fill(uid);
|
||||
var card = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid
|
||||
});
|
||||
Assert.Equal(0, card.Version);
|
||||
var card1keys = new[] { card.K0, card.K1, card.K2, card.K3, card.K4 };
|
||||
Assert.DoesNotContain(null, card1keys);
|
||||
var card2 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid
|
||||
});
|
||||
Assert.Equal(1, card2.Version);
|
||||
Assert.StartsWith("lnurlw://", card2.LNURLW);
|
||||
Assert.EndsWith("/boltcard", card2.LNURLW);
|
||||
var card2keys = new[] { card2.K0, card2.K1, card2.K2, card2.K3, card2.K4 };
|
||||
Assert.DoesNotContain(null, card2keys);
|
||||
for (int i = 0; i < card1keys.Length; i++)
|
||||
{
|
||||
if (i == 1)
|
||||
Assert.Contains(card1keys[i], card2keys);
|
||||
else
|
||||
Assert.DoesNotContain(card1keys[i], card2keys);
|
||||
}
|
||||
var card3 = await client.RegisterBoltcard(test4.Id, new RegisterBoltcardRequest()
|
||||
{
|
||||
UID = uid,
|
||||
OnExisting = OnExistingBehavior.KeepVersion
|
||||
});
|
||||
Assert.Equal(card2.Version, card3.Version);
|
||||
// Test with SATS denomination values
|
||||
var testSats = await client.CreatePullPayment(storeId, new Client.Models.CreatePullPaymentRequest()
|
||||
{
|
||||
@ -1104,7 +1138,7 @@ namespace BTCPayServer.Tests
|
||||
var approved = await acc.CreateClient(Policies.CanCreatePullPayments);
|
||||
await AssertPermissionError(Policies.CanCreatePullPayments, async () =>
|
||||
{
|
||||
var pullPayment = await nonApproved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
||||
await nonApproved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
||||
{
|
||||
Amount = 100,
|
||||
Currency = "USD",
|
||||
@ -1115,7 +1149,7 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
await AssertPermissionError(Policies.CanCreatePullPayments, async () =>
|
||||
{
|
||||
var pullPayment = await nonApproved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
await nonApproved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
{
|
||||
Amount = 100,
|
||||
PaymentMethod = "BTC",
|
||||
@ -1124,7 +1158,7 @@ namespace BTCPayServer.Tests
|
||||
});
|
||||
});
|
||||
|
||||
var pullPayment = await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
||||
await approved.CreatePullPayment(acc.StoreId, new CreatePullPaymentRequest()
|
||||
{
|
||||
Amount = 100,
|
||||
Currency = "USD",
|
||||
@ -1133,7 +1167,7 @@ namespace BTCPayServer.Tests
|
||||
AutoApproveClaims = true
|
||||
});
|
||||
|
||||
var p = await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
await approved.CreatePayout(acc.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
{
|
||||
Amount = 100,
|
||||
PaymentMethod = "BTC",
|
||||
@ -1284,15 +1318,18 @@ namespace BTCPayServer.Tests
|
||||
var client = await user.CreateClient(Policies.Unrestricted);
|
||||
|
||||
//create store
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest() { Name = "A" });
|
||||
var newStore = await client.CreateStore(new CreateStoreRequest { Name = "A" });
|
||||
Assert.Equal("A", newStore.Name);
|
||||
Assert.Equal(CheckoutType.V2, newStore.CheckoutType);
|
||||
|
||||
//update store
|
||||
Assert.Empty(newStore.PaymentMethodCriteria);
|
||||
await client.GenerateOnChainWallet(newStore.Id, "BTC", new GenerateOnChainWalletRequest());
|
||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest()
|
||||
var updatedStore = await client.UpdateStore(newStore.Id, new UpdateStoreRequest
|
||||
{
|
||||
Name = "B",
|
||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>()
|
||||
CheckoutType = CheckoutType.V1,
|
||||
PaymentMethodCriteria = new List<PaymentMethodCriteriaData>
|
||||
{
|
||||
new()
|
||||
{
|
||||
@ -1304,6 +1341,7 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
});
|
||||
Assert.Equal("B", updatedStore.Name);
|
||||
Assert.Equal(CheckoutType.V1, updatedStore.CheckoutType);
|
||||
var s = (await client.GetStore(newStore.Id));
|
||||
Assert.Equal("B", s.Name);
|
||||
var pmc = Assert.Single(s.PaymentMethodCriteria);
|
||||
@ -1457,7 +1495,7 @@ namespace BTCPayServer.Tests
|
||||
[Trait("Integration", "Integration")]
|
||||
public async Task CanUseWebhooks()
|
||||
{
|
||||
void AssertHook(FakeServer fakeServer, Client.Models.StoreWebhookData hook)
|
||||
void AssertHook(FakeServer fakeServer, StoreWebhookData hook)
|
||||
{
|
||||
Assert.True(hook.Enabled);
|
||||
Assert.True(hook.AuthorizedEvents.Everything);
|
||||
@ -2095,18 +2133,18 @@ namespace BTCPayServer.Tests
|
||||
//validation errors
|
||||
await AssertValidationError(new[] { nameof(CreateInvoiceRequest.Amount), $"{nameof(CreateInvoiceRequest.Checkout)}.{nameof(CreateInvoiceRequest.Checkout.PaymentTolerance)}", $"{nameof(CreateInvoiceRequest.Checkout)}.{nameof(CreateInvoiceRequest.Checkout.PaymentMethods)}[0]" }, async () =>
|
||||
{
|
||||
await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest() { Amount = -1, Checkout = new CreateInvoiceRequest.CheckoutOptions() { PaymentTolerance = -2, PaymentMethods = new[] { "jasaas_sdsad" } } });
|
||||
await client.CreateInvoice(user.StoreId, new CreateInvoiceRequest { Amount = -1, Checkout = new CreateInvoiceRequest.CheckoutOptions { PaymentTolerance = -2, PaymentMethods = new[] { "jasaas_sdsad" } } });
|
||||
});
|
||||
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "helloinvalid", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "helloinvalid", Amount = 1 });
|
||||
});
|
||||
await user.RegisterDerivationSchemeAsync("BTC");
|
||||
string origOrderId = "testOrder";
|
||||
var newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest()
|
||||
new CreateInvoiceRequest
|
||||
{
|
||||
Currency = "USD",
|
||||
Amount = 1,
|
||||
@ -2193,7 +2231,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//list NonExisting Status
|
||||
var invoicesNonExistingStatus = await viewOnly.GetInvoices(user.StoreId,
|
||||
status: new[] { BTCPayServer.Client.Models.InvoiceStatus.Invalid });
|
||||
status: new[] { InvoiceStatus.Invalid });
|
||||
Assert.NotNull(invoicesNonExistingStatus);
|
||||
Assert.Empty(invoicesNonExistingStatus);
|
||||
|
||||
@ -2211,7 +2249,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
//update
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 1 });
|
||||
Assert.Contains(InvoiceStatus.Settled, newInvoice.AvailableStatusesForManualMarking);
|
||||
Assert.Contains(InvoiceStatus.Invalid, newInvoice.AvailableStatusesForManualMarking);
|
||||
await client.MarkInvoiceStatus(user.StoreId, newInvoice.Id, new MarkInvoiceStatusRequest()
|
||||
@ -2223,7 +2261,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.DoesNotContain(InvoiceStatus.Settled, newInvoice.AvailableStatusesForManualMarking);
|
||||
Assert.Contains(InvoiceStatus.Invalid, newInvoice.AvailableStatusesForManualMarking);
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 1 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 1 });
|
||||
await client.MarkInvoiceStatus(user.StoreId, newInvoice.Id, new MarkInvoiceStatusRequest()
|
||||
{
|
||||
Status = InvoiceStatus.Invalid
|
||||
@ -2238,13 +2276,13 @@ namespace BTCPayServer.Tests
|
||||
await AssertHttpError(403, async () =>
|
||||
{
|
||||
await viewOnly.UpdateInvoice(user.StoreId, invoice.Id,
|
||||
new UpdateInvoiceRequest()
|
||||
new UpdateInvoiceRequest
|
||||
{
|
||||
Metadata = metadataForUpdate
|
||||
});
|
||||
});
|
||||
invoice = await client.UpdateInvoice(user.StoreId, invoice.Id,
|
||||
new UpdateInvoiceRequest()
|
||||
new UpdateInvoiceRequest
|
||||
{
|
||||
Metadata = metadataForUpdate
|
||||
});
|
||||
@ -2284,13 +2322,12 @@ namespace BTCPayServer.Tests
|
||||
await client.UnarchiveInvoice(user.StoreId, invoice.Id);
|
||||
Assert.NotNull(await client.GetInvoice(user.StoreId, invoice.Id));
|
||||
|
||||
|
||||
foreach (var marked in new[] { InvoiceStatus.Settled, InvoiceStatus.Invalid })
|
||||
{
|
||||
var inv = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest() { Currency = "USD", Amount = 100 });
|
||||
new CreateInvoiceRequest { Currency = "USD", Amount = 100 });
|
||||
await user.PayInvoice(inv.Id);
|
||||
await client.MarkInvoiceStatus(user.StoreId, inv.Id, new MarkInvoiceStatusRequest()
|
||||
await client.MarkInvoiceStatus(user.StoreId, inv.Id, new MarkInvoiceStatusRequest
|
||||
{
|
||||
Status = marked
|
||||
});
|
||||
@ -2318,13 +2355,12 @@ namespace BTCPayServer.Tests
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
newInvoice = await client.CreateInvoice(user.StoreId,
|
||||
new CreateInvoiceRequest()
|
||||
new CreateInvoiceRequest
|
||||
{
|
||||
Currency = "USD",
|
||||
Amount = 1,
|
||||
Checkout = new CreateInvoiceRequest.CheckoutOptions()
|
||||
Checkout = new CreateInvoiceRequest.CheckoutOptions
|
||||
{
|
||||
DefaultLanguage = "it-it ",
|
||||
RedirectURL = "http://toto.com/lol"
|
||||
@ -2501,7 +2537,6 @@ namespace BTCPayServer.Tests
|
||||
Expiry = TimeSpan.FromSeconds(400),
|
||||
PrivateRouteHints = false
|
||||
});
|
||||
var chargeInvoice = invoiceData;
|
||||
Assert.NotNull(await client.GetLightningInvoice("BTC", invoiceData.Id));
|
||||
|
||||
// check list for internal node
|
||||
@ -3643,7 +3678,7 @@ namespace BTCPayServer.Tests
|
||||
SavePrivateKeys = true
|
||||
});
|
||||
|
||||
var preApprovedPayoutWithoutPullPayment = await adminClient.CreatePayout(admin.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
await adminClient.CreatePayout(admin.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
{
|
||||
Amount = 0.0001m,
|
||||
Approved = true,
|
||||
@ -3781,9 +3816,19 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
case "before-automated-payout-processing":
|
||||
beforeHookTcs.TrySetResult();
|
||||
var bd = (BeforePayoutActionData)tuple.args;
|
||||
foreach (var p in bd.Payouts)
|
||||
{
|
||||
TestLogs.LogInformation("Before Processed: " + p.Id);
|
||||
}
|
||||
break;
|
||||
case "after-automated-payout-processing":
|
||||
afterHookTcs.TrySetResult();
|
||||
var ad = (AfterPayoutActionData)tuple.args;
|
||||
foreach (var p in ad.Payouts)
|
||||
{
|
||||
TestLogs.LogInformation("After Processed: " + p.Id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
@ -3798,7 +3843,21 @@ namespace BTCPayServer.Tests
|
||||
await beforeHookTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
|
||||
await afterHookTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
|
||||
payouts = await adminClient.GetStorePayouts(admin.StoreId);
|
||||
Assert.Single(payouts.Where(data => data.State == PayoutState.InProgress && data.Id == payoutThatShouldBeProcessedStraightAway.Id));
|
||||
try
|
||||
{
|
||||
Assert.Single(payouts.Where(data => data.State == PayoutState.InProgress && data.Id == payoutThatShouldBeProcessedStraightAway.Id));
|
||||
}
|
||||
catch (SingleException)
|
||||
{
|
||||
TestLogs.LogInformation("Debugging flaky test...");
|
||||
TestLogs.LogInformation("payoutThatShouldBeProcessedStraightAway: " + payoutThatShouldBeProcessedStraightAway.Id);
|
||||
foreach (var p in payouts)
|
||||
{
|
||||
TestLogs.LogInformation("Payout Id: " + p.Id);
|
||||
TestLogs.LogInformation("Payout State: " + p.State);
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
beforeHookTcs = new TaskCompletionSource();
|
||||
afterHookTcs = new TaskCompletionSource();
|
||||
@ -3851,7 +3910,7 @@ namespace BTCPayServer.Tests
|
||||
|
||||
beforeHookTcs = new TaskCompletionSource();
|
||||
afterHookTcs = new TaskCompletionSource();
|
||||
var payoutThatShouldNotBeProcessedStraightAway3 = await adminClient.CreatePayout(admin.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
await adminClient.CreatePayout(admin.StoreId, new CreatePayoutThroughStoreRequest()
|
||||
{
|
||||
Amount = 0.3m,
|
||||
Approved = true,
|
||||
@ -4271,7 +4330,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
||||
await admin.GrantAccessAsync(true);
|
||||
|
||||
var unauthClient = new BTCPayServerClient(tester.PayTester.ServerUri);
|
||||
var authClientNoPermissions = await admin.CreateClient(Policies.CanViewInvoices);
|
||||
await admin.CreateClient(Policies.CanViewInvoices);
|
||||
var adminClient = await admin.CreateClient(Policies.Unrestricted);
|
||||
var managerClient = await admin.CreateClient(Policies.CanManageCustodianAccounts);
|
||||
var withdrawalClient = await admin.CreateClient(Policies.CanWithdrawFromCustodianAccounts);
|
||||
@ -4471,7 +4530,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
||||
await AssertHttpError(404, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", simulateWithdrawalRequest));
|
||||
|
||||
// Test: SimulateWithdrawal, wrong store ID
|
||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
// TODO it is weird that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
await AssertHttpError(403, async () => await withdrawalClient.SimulateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, simulateWithdrawalRequest));
|
||||
|
||||
// Test: SimulateWithdrawal, correct payment method, wrong amount
|
||||
@ -4502,7 +4561,7 @@ clientBasic.PreviewUpdateStoreRateConfiguration(user.StoreId, new StoreRateConfi
|
||||
await AssertHttpError(404, async () => await withdrawalClient.CreateCustodianAccountWithdrawal(storeId, "WRONG-ACCOUNT-ID", createWithdrawalRequest));
|
||||
|
||||
// Test: CreateWithdrawal, wrong store ID
|
||||
// TODO it is wierd that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
// TODO it is weird that 403 is considered normal, but it is like this for all calls where the store is wrong... I'd have preferred a 404 error, because the store cannot be found.
|
||||
await AssertHttpError(403, async () => await withdrawalClient.CreateCustodianAccountWithdrawal("WRONG-STORE-ID", accountId, createWithdrawalRequest));
|
||||
|
||||
// Test: CreateWithdrawal, correct payment method, wrong amount
|
||||
|
@ -29,7 +29,7 @@ namespace BTCPayServer.Tests
|
||||
using var s = CreateSeleniumTester(newDb: true);
|
||||
await s.StartAsync();
|
||||
|
||||
var u1 = s.RegisterNewUser(true);
|
||||
s.RegisterNewUser(true);
|
||||
var hot = s.CreateNewStore();
|
||||
var seed = s.GenerateWallet(isHotWallet: true);
|
||||
var cold = s.CreateNewStore();
|
||||
|
@ -68,7 +68,7 @@ namespace BTCPayServer.Tests
|
||||
{
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var network = tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
|
||||
tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
|
||||
var repo = tester.PayTester.GetService<UTXOLocker>();
|
||||
var outpoint = RandomOutpoint();
|
||||
|
||||
@ -189,10 +189,10 @@ namespace BTCPayServer.Tests
|
||||
using var tester = CreateServerTester();
|
||||
await tester.StartAsync();
|
||||
var broadcaster = tester.PayTester.GetService<DelayedTransactionBroadcaster>();
|
||||
var payjoinRepository = tester.PayTester.GetService<UTXOLocker>();
|
||||
tester.PayTester.GetService<UTXOLocker>();
|
||||
broadcaster.Disable();
|
||||
var network = tester.NetworkProvider.GetNetwork<BTCPayNetwork>("BTC");
|
||||
var btcPayWallet = tester.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
|
||||
tester.PayTester.GetService<BTCPayWalletProvider>().GetWallet(network);
|
||||
var cashCow = tester.ExplorerNode;
|
||||
cashCow.Generate(2); // get some money in case
|
||||
|
||||
@ -218,7 +218,7 @@ namespace BTCPayServer.Tests
|
||||
receiverUser.GrantAccess(true);
|
||||
receiverUser.RegisterDerivationScheme("BTC", receiverAddressType, true);
|
||||
await receiverUser.ModifyOnchainPaymentSettings(p => p.PayJoinEnabled = true);
|
||||
var receiverCoin = await receiverUser.ReceiveUTXO(Money.Satoshis(810), network);
|
||||
await receiverUser.ReceiveUTXO(Money.Satoshis(810), network);
|
||||
|
||||
string errorCode = receiverAddressType == senderAddressType ? null : "unavailable|any UTXO available";
|
||||
var invoice = receiverUser.BitPay.CreateInvoice(new Invoice() { Price = 50000, Currency = "SATS", FullNotifications = true });
|
||||
@ -236,7 +236,7 @@ namespace BTCPayServer.Tests
|
||||
txBuilder.SendEstimatedFees(new FeeRate(50m));
|
||||
var psbt = txBuilder.BuildPSBT(false);
|
||||
psbt = await senderUser.Sign(psbt);
|
||||
var pj = await senderUser.SubmitPayjoin(invoice, psbt, errorCode, false);
|
||||
await senderUser.SubmitPayjoin(invoice, psbt, errorCode, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -250,11 +250,11 @@ namespace BTCPayServer.Tests
|
||||
s.RegisterNewUser(true);
|
||||
var receiver = s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
var receiverSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, "BTC");
|
||||
|
||||
var sender = s.CreateNewStore();
|
||||
var senderSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit);
|
||||
var senderWalletId = new WalletId(sender.storeId, "BTC");
|
||||
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
@ -305,7 +305,7 @@ namespace BTCPayServer.Tests
|
||||
var cryptoCode = "BTC";
|
||||
var receiver = s.CreateNewStore();
|
||||
s.EnableCheckout(CheckoutType.V1);
|
||||
var receiverSeed = s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
var receiverWalletId = new WalletId(receiver.storeId, cryptoCode);
|
||||
|
||||
//payjoin is enabled by default.
|
||||
@ -320,7 +320,7 @@ namespace BTCPayServer.Tests
|
||||
Assert.True(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected);
|
||||
|
||||
var sender = s.CreateNewStore();
|
||||
var senderSeed = s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
s.GenerateWallet(cryptoCode, "", true, true, format);
|
||||
var senderWalletId = new WalletId(sender.storeId, cryptoCode);
|
||||
await s.Server.ExplorerNode.GenerateAsync(1);
|
||||
await s.FundStoreWallet(senderWalletId);
|
||||
@ -374,7 +374,7 @@ namespace BTCPayServer.Tests
|
||||
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).Clear();
|
||||
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).SendKeys("2");
|
||||
s.Driver.FindElement(By.Id("SignTransaction")).Click();
|
||||
var txId = await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
|
||||
await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
|
||||
{
|
||||
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
|
||||
return Task.CompletedTask;
|
||||
@ -406,7 +406,6 @@ namespace BTCPayServer.Tests
|
||||
await TestUtils.EventuallyAsync(async () =>
|
||||
{
|
||||
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
|
||||
var dto = invoice.EntityToDTO();
|
||||
Assert.Equal(InvoiceStatusLegacy.Paid, invoice.Status);
|
||||
});
|
||||
s.GoToInvoices(receiver.storeId);
|
||||
@ -875,7 +874,6 @@ retry:
|
||||
new Invoice() { Price = 0.02m, Currency = "BTC", FullNotifications = true });
|
||||
cashCow.SendToAddress(BitcoinAddress.Create(invoice.BitcoinAddress, cashCow.Network),
|
||||
Money.Coins(0.06m));
|
||||
var receiverWalletId = new WalletId(receiverUser.StoreId, "BTC");
|
||||
|
||||
//give the cow some cash
|
||||
await cashCow.GenerateAsync(1);
|
||||
@ -963,8 +961,6 @@ retry:
|
||||
senderUser.GenerateWalletResponseV.MasterHDKey.Derive(signingKeySettings.GetRootedKeyPath()
|
||||
.KeyPath);
|
||||
|
||||
|
||||
var n = tester.ExplorerClient.Network.NBitcoinNetwork;
|
||||
var Invoice1Coin1 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
|
||||
.SetChange(senderChange)
|
||||
.Send(parsedBip21.Address, parsedBip21.Amount)
|
||||
@ -973,7 +969,7 @@ retry:
|
||||
.SendEstimatedFees(new FeeRate(100m))
|
||||
.BuildTransaction(true);
|
||||
|
||||
var Invoice1Coin2 = tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
|
||||
tester.ExplorerClient.Network.NBitcoinNetwork.CreateTransactionBuilder()
|
||||
.SetChange(senderChange)
|
||||
.Send(parsedBip21.Address, parsedBip21.Amount)
|
||||
.AddCoins(coin2.Coin)
|
||||
@ -1133,8 +1129,7 @@ retry:
|
||||
|
||||
var invoice7Coin6Response1Tx = await senderUser.SubmitPayjoin(invoice7, invoice7Coin6Tx, btcPayNetwork);
|
||||
var Invoice7Coin6Response1TxSigned = invoice7Coin6TxBuilder.SignTransaction(invoice7Coin6Response1Tx);
|
||||
var contributedInputsInvoice7Coin6Response1TxSigned =
|
||||
Invoice7Coin6Response1TxSigned.Inputs.Single(txin => coin6.OutPoint != txin.PrevOut);
|
||||
Invoice7Coin6Response1TxSigned.Inputs.Single(txin => coin6.OutPoint != txin.PrevOut);
|
||||
|
||||
|
||||
////var receiverWalletPayJoinState = payJoinStateProvider.Get(receiverWalletId);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user