Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
1d7081d8b8 | |||
c0174c0c2c | |||
fa8324c1f9 | |||
4b0951caec | |||
0d51c99717 | |||
24623c59d7 | |||
88044f6b76 | |||
38edbf8362 | |||
bc0acf5701 | |||
a82f181126 | |||
be0139a46f | |||
4db5b4f2b1 | |||
93cefced80 | |||
85f586f623 | |||
2be1f97419 | |||
63014231ab | |||
3ac37497ab | |||
d0cafb020f | |||
d3b3198b68 | |||
c1f17ff63b |
@ -10,8 +10,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.0" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0">
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using BTCPayServer.Configuration;
|
||||
using System.Linq;
|
||||
using BTCPayServer.HostedServices;
|
||||
using BTCPayServer.Hosting;
|
||||
using BTCPayServer.Payments;
|
||||
@ -139,6 +140,11 @@ namespace BTCPayServer.Tests
|
||||
_Host.Start();
|
||||
InvoiceRepository = (InvoiceRepository)_Host.Services.GetService(typeof(InvoiceRepository));
|
||||
StoreRepository = (StoreRepository)_Host.Services.GetService(typeof(StoreRepository));
|
||||
var dashBoard = (NBXplorerDashboard)_Host.Services.GetService(typeof(NBXplorerDashboard));
|
||||
while(!dashBoard.IsFullySynched())
|
||||
{
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
if (MockRates)
|
||||
{
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM microsoft/dotnet:2.1.300-sdk-alpine3.7
|
||||
FROM microsoft/dotnet:2.1.403-sdk-alpine3.7
|
||||
WORKDIR /app
|
||||
# caches restore result by copying csproj file separately
|
||||
COPY BTCPayServer.Tests/BTCPayServer.Tests.csproj BTCPayServer.Tests/BTCPayServer.Tests.csproj
|
||||
|
@ -29,12 +29,6 @@ If you want to stop, and remove all existing data
|
||||
docker-compose down --v
|
||||
```
|
||||
|
||||
You can run the tests inside a container by running
|
||||
|
||||
```
|
||||
docker-compose run --rm tests
|
||||
```
|
||||
|
||||
You can run tests on `MySql` database instead of `Postgres` by setting environnement variable `TESTS_DB` equals to `MySql`.
|
||||
|
||||
## How to manually test payments
|
||||
|
@ -44,6 +44,7 @@ using BTCPayServer.Lightning;
|
||||
using BTCPayServer.Models.WalletViewModels;
|
||||
using System.Security.Claims;
|
||||
using BTCPayServer.Security;
|
||||
using NBXplorer.Models;
|
||||
|
||||
namespace BTCPayServer.Tests
|
||||
{
|
||||
@ -480,10 +481,6 @@ namespace BTCPayServer.Tests
|
||||
|
||||
async Task CanSendLightningPaymentCore(ServerTester tester, TestAccount user)
|
||||
{
|
||||
// TODO: If this parameter is less than 1 second we start having concurrency problems
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(1000));
|
||||
//
|
||||
|
||||
var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice()
|
||||
{
|
||||
Price = 0.01m,
|
||||
@ -492,6 +489,7 @@ namespace BTCPayServer.Tests
|
||||
OrderId = "orderId",
|
||||
ItemDesc = "Some description"
|
||||
});
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(1000)); // Give time to listen the new invoices
|
||||
await tester.SendLightningPaymentAsync(invoice);
|
||||
await EventuallyAsync(async () =>
|
||||
{
|
||||
@ -760,6 +758,7 @@ namespace BTCPayServer.Tests
|
||||
}, Facade.Merchant);
|
||||
var payment1 = invoice.BtcDue + Money.Coins(0.0001m);
|
||||
var payment2 = invoice.BtcDue;
|
||||
|
||||
var tx1 = new uint256(tester.ExplorerNode.SendCommand("sendtoaddress", new object[]
|
||||
{
|
||||
invoice.BitcoinAddress,
|
||||
@ -769,8 +768,10 @@ namespace BTCPayServer.Tests
|
||||
false, //subtractfeefromamount
|
||||
true, //replaceable
|
||||
}).ResultString);
|
||||
Logs.Tester.LogInformation($"Let's send a first payment of {payment1} for the {invoice.BtcDue} invoice ({tx1})");
|
||||
var invoiceAddress = BitcoinAddress.Create(invoice.BitcoinAddress, user.SupportedNetwork.NBitcoinNetwork);
|
||||
|
||||
Logs.Tester.LogInformation($"The invoice should be paidOver");
|
||||
Eventually(() =>
|
||||
{
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||
@ -788,9 +789,18 @@ namespace BTCPayServer.Tests
|
||||
var output = tx.Outputs.First(o => o.Value == payment1);
|
||||
output.Value = payment2;
|
||||
output.ScriptPubKey = invoiceAddress.ScriptPubKey;
|
||||
var replaced = tester.ExplorerNode.SignRawTransaction(tx);
|
||||
tester.ExplorerNode.SendRawTransaction(replaced);
|
||||
var test = tester.ExplorerClient.GetUTXOs(user.DerivationScheme, null);
|
||||
|
||||
using(var cts = new CancellationTokenSource(10000))
|
||||
using (var listener = tester.ExplorerClient.CreateNotificationSession())
|
||||
{
|
||||
listener.ListenAllDerivationSchemes();
|
||||
var replaced = tester.ExplorerNode.SignRawTransaction(tx);
|
||||
Thread.Sleep(1000); // Make sure the replacement has a different timestamp
|
||||
var tx2 = tester.ExplorerNode.SendRawTransaction(replaced);
|
||||
Logs.Tester.LogInformation($"Let's RBF with a payment of {payment2} ({tx2}), waiting for NBXplorer to pick it up");
|
||||
Assert.Equal(tx2, ((NewTransactionEvent)listener.NextEvent(cts.Token)).TransactionData.TransactionHash);
|
||||
}
|
||||
Logs.Tester.LogInformation($"The invoice should now not be paidOver anymore");
|
||||
Eventually(() =>
|
||||
{
|
||||
invoice = user.BitPay.GetInvoice(invoice.Id);
|
||||
@ -1699,7 +1709,8 @@ namespace BTCPayServer.Tests
|
||||
foreach (var value in result)
|
||||
{
|
||||
var rateResult = value.Value.GetAwaiter().GetResult();
|
||||
Assert.NotNull(rateResult.BidAsk);
|
||||
Logs.Tester.LogInformation($"Testing {value.Key.ToString()}");
|
||||
Assert.True(rateResult.BidAsk != null, $"Impossible to get the rate {rateResult.EvaluatedRule}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ services:
|
||||
|
||||
|
||||
nbxplorer:
|
||||
image: nicolasdorier/nbxplorer:1.1.0.4
|
||||
image: nicolasdorier/nbxplorer:1.1.0.9
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "32838:32838"
|
||||
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<Version>1.0.3.2</Version>
|
||||
<Version>1.0.3.5</Version>
|
||||
<NoWarn>NU1701,CA1816,CA1308,CA1810,CA2208</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
@ -36,34 +36,39 @@
|
||||
<PackageReference Include="BTCPayServer.Lightning.All" Version="1.1.0.1" />
|
||||
<PackageReference Include="BuildBundlerMinifier" Version="2.7.385" />
|
||||
<PackageReference Include="DigitalRuby.ExchangeSharp" Version="0.5.3" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.19" />
|
||||
<PackageReference Include="Hangfire" Version="1.6.20" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage" Version="1.5.2" />
|
||||
<PackageReference Include="Hangfire.PostgreSql" Version="1.4.8.2" />
|
||||
<PackageReference Include="LedgerWallet" Version="2.0.0.2" />
|
||||
<PackageReference Include="Meziantou.AspNetCore.BundleTagHelpers" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Filter" Version="1.1.2" />
|
||||
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.6.0" />
|
||||
<PackageReference Include="Microsoft.NetCore.Analyzers" Version="2.6.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NBitcoin" Version="4.1.1.66" />
|
||||
<PackageReference Include="NBitpayClient" Version="1.0.0.30" />
|
||||
<PackageReference Include="DBreeze" Version="1.87.0" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.3.3" />
|
||||
<PackageReference Include="DBreeze" Version="1.92.0" />
|
||||
<PackageReference Include="NBXplorer.Client" Version="1.0.3.4" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
|
||||
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.RateLimits" Version="1.0.0.3" />
|
||||
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.0" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="2.1.2" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="2.1.2" />
|
||||
<PackageReference Include="Serilog" Version="2.7.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="4.0.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2016.1.0" />
|
||||
<PackageReference Include="System.Xml.XmlSerializer" Version="4.3.0" />
|
||||
<PackageReference Include="Text.Analyzers" Version="2.6.0" />
|
||||
<PackageReference Include="Text.Analyzers" Version="2.6.2">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.5" />
|
||||
<PackageReference Include="YamlDotNet" Version="5.2.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -134,6 +139,9 @@
|
||||
<Content Update="Views\Server\SSHService.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Stores\ShowToken.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Stores\PayButtonEnable.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
@ -158,6 +166,9 @@
|
||||
<Content Update="Views\Wallets\WalletRescan.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Wallets\WalletSendLedger.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
<Content Update="Views\Wallets\WalletTransactions.cshtml">
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
@ -171,10 +182,4 @@
|
||||
<Pack>$(IncludeRazorContentInPack)</Pack>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="devtest.pfx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -37,6 +37,8 @@ namespace BTCPayServer.Configuration
|
||||
}
|
||||
else if (typeof(T) == typeof(string))
|
||||
return (T)(object)str;
|
||||
else if (typeof(T) == typeof(IPAddress))
|
||||
return (T)(object)IPAddress.Parse(str);
|
||||
else if (typeof(T) == typeof(IPEndPoint))
|
||||
{
|
||||
var separator = str.LastIndexOf(":", StringComparison.InvariantCulture);
|
||||
|
@ -106,6 +106,8 @@ namespace BTCPayServer.Configuration
|
||||
builder.AppendLine("### Server settings ###");
|
||||
builder.AppendLine("#port=" + defaultSettings.DefaultPort);
|
||||
builder.AppendLine("#bind=127.0.0.1");
|
||||
builder.AppendLine("#httpscertificatefilepath=devtest.pfx");
|
||||
builder.AppendLine("#httpscertificatefilepassword=toto");
|
||||
builder.AppendLine();
|
||||
builder.AppendLine("### Database ###");
|
||||
builder.AppendLine("#postgres=User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=myDataBase;");
|
||||
|
@ -34,13 +34,66 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
DerivationSchemeViewModel vm = new DerivationSchemeViewModel();
|
||||
vm.ServerUrl = WalletsController.GetLedgerWebsocketUrl(this.HttpContext, cryptoCode, null);
|
||||
vm.CryptoCode = cryptoCode;
|
||||
vm.RootKeyPath = network.GetRootKeyPath();
|
||||
SetExistingValues(store, vm);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/derivations/{cryptoCode}/ledger/ws")]
|
||||
public async Task<IActionResult> AddDerivationSchemeLedger(
|
||||
string storeId,
|
||||
string cryptoCode,
|
||||
string command,
|
||||
int account = 0)
|
||||
{
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return NotFound();
|
||||
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
var hw = new HardwareWalletService(webSocket);
|
||||
object result = null;
|
||||
var network = _NetworkProvider.GetNetwork(cryptoCode);
|
||||
|
||||
using (var normalOperationTimeout = new CancellationTokenSource())
|
||||
{
|
||||
normalOperationTimeout.CancelAfter(TimeSpan.FromMinutes(30));
|
||||
try
|
||||
{
|
||||
if (command == "test")
|
||||
{
|
||||
result = await hw.Test(normalOperationTimeout.Token);
|
||||
}
|
||||
if (command == "getxpub")
|
||||
{
|
||||
var getxpubResult = await hw.GetExtPubKey(network, account, normalOperationTimeout.Token);
|
||||
result = getxpubResult;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{ result = new LedgerTestResult() { Success = false, Error = "Timeout" }; }
|
||||
catch (Exception ex)
|
||||
{ result = new LedgerTestResult() { Success = false, Error = ex.Message }; }
|
||||
finally { hw.Dispose(); }
|
||||
try
|
||||
{
|
||||
if (result != null)
|
||||
{
|
||||
UTF8Encoding UTF8NOBOM = new UTF8Encoding(false);
|
||||
var bytes = UTF8NOBOM.GetBytes(JsonConvert.SerializeObject(result, MvcJsonOptions.Value.SerializerSettings));
|
||||
await webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, new CancellationTokenSource(2000).Token);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
finally
|
||||
{
|
||||
await webSocket.CloseSocket();
|
||||
}
|
||||
}
|
||||
return new EmptyResult();
|
||||
}
|
||||
|
||||
private void SetExistingValues(StoreData store, DerivationSchemeViewModel vm)
|
||||
{
|
||||
vm.DerivationScheme = GetExistingDerivationStrategy(vm.CryptoCode, store)?.DerivationStrategyBase.ToString();
|
||||
@ -60,7 +113,6 @@ namespace BTCPayServer.Controllers
|
||||
[Route("{storeId}/derivations/{cryptoCode}")]
|
||||
public async Task<IActionResult> AddDerivationScheme(string storeId, DerivationSchemeViewModel vm, string cryptoCode)
|
||||
{
|
||||
vm.ServerUrl = WalletsController.GetLedgerWebsocketUrl(this.HttpContext, cryptoCode, null);
|
||||
vm.CryptoCode = cryptoCode;
|
||||
var store = HttpContext.GetStoreData();
|
||||
if (store == null)
|
||||
@ -109,7 +161,7 @@ namespace BTCPayServer.Controllers
|
||||
// - The user is setting a new derivation scheme
|
||||
(!vm.Confirmation && strategy != null && exisingStrategy != strategy.DerivationStrategyBase.ToString()) ||
|
||||
// - The user is clicking on continue without changing anything
|
||||
(!vm.Confirmation && willBeExcluded == wasExcluded);
|
||||
(!vm.Confirmation && willBeExcluded == wasExcluded);
|
||||
|
||||
if (!showAddress)
|
||||
{
|
||||
|
@ -23,6 +23,7 @@ using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.Extensions.Options;
|
||||
using NBitcoin;
|
||||
using NBitcoin.DataEncoders;
|
||||
|
||||
@ -51,6 +52,7 @@ namespace BTCPayServer.Controllers
|
||||
IFeeProviderFactory feeRateProvider,
|
||||
LanguageService langService,
|
||||
ChangellyClientProvider changellyClientProvider,
|
||||
IOptions<MvcJsonOptions> mvcJsonOptions,
|
||||
IHostingEnvironment env, IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_RateFactory = rateFactory;
|
||||
@ -59,6 +61,7 @@ namespace BTCPayServer.Controllers
|
||||
_UserManager = userManager;
|
||||
_LangService = langService;
|
||||
_changellyClientProvider = changellyClientProvider;
|
||||
MvcJsonOptions = mvcJsonOptions;
|
||||
_TokenController = tokenController;
|
||||
_WalletProvider = walletProvider;
|
||||
_Env = env;
|
||||
@ -570,6 +573,45 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/tokens/{tokenId}/revoke")]
|
||||
public async Task<IActionResult> RevokeToken(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null || token.StoreId != StoreData.Id)
|
||||
return NotFound();
|
||||
return View("Confirm", new ConfirmModel()
|
||||
{
|
||||
Action = "Revoke the token",
|
||||
Title = "Revoke the token",
|
||||
Description = $"The access token with the label \"{token.Label}\" will be revoked, do you wish to continue?",
|
||||
ButtonClass = "btn-danger"
|
||||
});
|
||||
}
|
||||
[HttpPost]
|
||||
[Route("{storeId}/tokens/{tokenId}/revoke")]
|
||||
public async Task<IActionResult> RevokeTokenConfirm(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null ||
|
||||
token.StoreId != StoreData.Id ||
|
||||
!await _TokenRepository.DeleteToken(tokenId))
|
||||
StatusMessage = "Failure to revoke this token";
|
||||
else
|
||||
StatusMessage = "Token revoked";
|
||||
return RedirectToAction(nameof(ListTokens));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{storeId}/tokens/{tokenId}")]
|
||||
public async Task<IActionResult> ShowToken(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null || token.StoreId != StoreData.Id)
|
||||
return NotFound();
|
||||
return View(token);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("/api-tokens")]
|
||||
[Route("{storeId}/Tokens/Create")]
|
||||
@ -634,6 +676,7 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
|
||||
public string GeneratedPairingCode { get; set; }
|
||||
public IOptions<MvcJsonOptions> MvcJsonOptions { get; }
|
||||
|
||||
[HttpGet]
|
||||
[Route("/api-tokens")]
|
||||
@ -671,21 +714,6 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/Tokens/Delete")]
|
||||
public async Task<IActionResult> DeleteToken(string tokenId)
|
||||
{
|
||||
var token = await _TokenRepository.GetToken(tokenId);
|
||||
if (token == null ||
|
||||
token.StoreId != StoreData.Id ||
|
||||
!await _TokenRepository.DeleteToken(tokenId))
|
||||
StatusMessage = "Failure to revoke this token";
|
||||
else
|
||||
StatusMessage = "Token revoked";
|
||||
return RedirectToAction(nameof(ListTokens));
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{storeId}/tokens/apikey")]
|
||||
public async Task<IActionResult> GenerateAPIKey()
|
||||
|
@ -46,6 +46,9 @@ namespace BTCPayServer.Controllers
|
||||
private readonly IFeeProviderFactory _feeRateProvider;
|
||||
private readonly BTCPayWalletProvider _walletProvider;
|
||||
public RateFetcher RateFetcher { get; }
|
||||
[TempData]
|
||||
public string StatusMessage { get; set; }
|
||||
|
||||
CurrencyNameTable _currencyTable;
|
||||
public WalletsController(StoreRepository repo,
|
||||
CurrencyNameTable currencyTable,
|
||||
@ -150,18 +153,27 @@ namespace BTCPayServer.Controllers
|
||||
DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
|
||||
var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
var storeData = store.GetStoreBlob();
|
||||
var rateRules = store.GetStoreBlob().GetRateRules(NetworkProvider);
|
||||
rateRules.Spread = 0.0m;
|
||||
var currencyPair = new Rating.CurrencyPair(paymentMethod.PaymentId.CryptoCode, GetCurrencyCode(storeData.DefaultLang) ?? "USD");
|
||||
WalletModel model = new WalletModel()
|
||||
WalletSendModel model = new WalletSendModel()
|
||||
{
|
||||
DefaultAddress = defaultDestination,
|
||||
DefaultAmount = defaultAmount,
|
||||
ServerUrl = GetLedgerWebsocketUrl(this.HttpContext, walletId.CryptoCode, paymentMethod.DerivationStrategyBase),
|
||||
CryptoCurrency = walletId.CryptoCode
|
||||
Destination = defaultDestination,
|
||||
CryptoCode = walletId.CryptoCode
|
||||
};
|
||||
if (double.TryParse(defaultAmount, out var amount))
|
||||
model.Amount = (decimal)amount;
|
||||
|
||||
var feeProvider = _feeRateProvider.CreateFeeProvider(network);
|
||||
var recommendedFees = feeProvider.GetFeeRateAsync();
|
||||
var balance = _walletProvider.GetWallet(network).GetBalance(paymentMethod.DerivationStrategyBase);
|
||||
model.CurrentBalance = (await balance).ToDecimal(MoneyUnit.BTC);
|
||||
model.RecommendedSatoshiPerByte = (int)(await recommendedFees).GetFee(1).Satoshi;
|
||||
model.FeeSatoshiPerByte = model.RecommendedSatoshiPerByte;
|
||||
|
||||
using (CancellationTokenSource cts = new CancellationTokenSource())
|
||||
{
|
||||
@ -185,6 +197,74 @@ namespace BTCPayServer.Controllers
|
||||
return View(model);
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[Route("{walletId}/send")]
|
||||
public async Task<IActionResult> WalletSend(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletSendModel vm)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
var store = await Repository.FindStore(walletId.StoreId, GetUserId());
|
||||
if (store == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
var destination = ParseDestination(vm.Destination, network.NBitcoinNetwork);
|
||||
if (destination == null)
|
||||
ModelState.AddModelError(nameof(vm.Destination), "Invalid address");
|
||||
|
||||
if (vm.Amount.HasValue)
|
||||
{
|
||||
if (vm.CurrentBalance == vm.Amount.Value && !vm.SubstractFees)
|
||||
ModelState.AddModelError(nameof(vm.Amount), "You are sending all your balance to the same destination, you should substract the fees");
|
||||
if (vm.CurrentBalance < vm.Amount.Value)
|
||||
ModelState.AddModelError(nameof(vm.Amount), "You are sending more than what you own");
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
return View(vm);
|
||||
|
||||
return RedirectToAction(nameof(WalletSendLedger), new WalletSendLedgerModel()
|
||||
{
|
||||
Destination = vm.Destination,
|
||||
Amount = vm.Amount.Value,
|
||||
SubstractFees = vm.SubstractFees,
|
||||
FeeSatoshiPerByte = vm.FeeSatoshiPerByte
|
||||
});
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{walletId}/send/ledger")]
|
||||
public async Task<IActionResult> WalletSendLedger(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId, WalletSendLedgerModel vm)
|
||||
{
|
||||
if (walletId?.StoreId == null)
|
||||
return NotFound();
|
||||
var store = await Repository.FindStore(walletId.StoreId, GetUserId());
|
||||
DerivationStrategy paymentMethod = GetPaymentMethod(walletId, store);
|
||||
if (paymentMethod == null)
|
||||
return NotFound();
|
||||
var network = this.NetworkProvider.GetNetwork(walletId?.CryptoCode);
|
||||
if (network == null)
|
||||
return NotFound();
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
private IDestination[] ParseDestination(string destination, Network network)
|
||||
{
|
||||
try
|
||||
{
|
||||
destination = destination?.Trim();
|
||||
return new IDestination[] { BitcoinAddress.Create(destination, network) };
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("{walletId}/rescan")]
|
||||
public async Task<IActionResult> WalletRescan(
|
||||
@ -204,7 +284,7 @@ namespace BTCPayServer.Controllers
|
||||
vm.IsSupportedByCurrency = _dashboard.Get(walletId.CryptoCode)?.Status?.BitcoinStatus?.Capabilities?.CanScanTxoutSet == true;
|
||||
var explorer = ExplorerClientProvider.GetExplorerClient(walletId.CryptoCode);
|
||||
var scanProgress = await explorer.GetScanUTXOSetInformationAsync(paymentMethod.DerivationStrategyBase);
|
||||
if(scanProgress != null)
|
||||
if (scanProgress != null)
|
||||
{
|
||||
vm.PreviousError = scanProgress.Error;
|
||||
if (scanProgress.Status == ScanUTXOStatus.Queued || scanProgress.Status == ScanUTXOStatus.Pending)
|
||||
@ -298,20 +378,25 @@ namespace BTCPayServer.Controllers
|
||||
return _userManager.GetUserId(User);
|
||||
}
|
||||
|
||||
public static string GetLedgerWebsocketUrl(HttpContext httpContext, string cryptoCode, DerivationStrategyBase derivationStrategy)
|
||||
[HttpGet]
|
||||
[Route("{walletId}/send/ledger/success")]
|
||||
public IActionResult WalletSendLedgerSuccess(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId,
|
||||
string txid)
|
||||
{
|
||||
return $"{httpContext.Request.GetAbsoluteRoot().WithTrailingSlash()}ws/ledger/{cryptoCode}/{derivationStrategy?.ToString() ?? string.Empty}";
|
||||
StatusMessage = $"Transaction broadcasted ({txid})";
|
||||
return RedirectToAction(nameof(this.WalletTransactions), new { walletId = walletId.ToString() });
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Route("/ws/ledger/{cryptoCode}/{derivationScheme?}")]
|
||||
[Route("{walletId}/send/ledger/ws")]
|
||||
public async Task<IActionResult> LedgerConnection(
|
||||
[ModelBinder(typeof(WalletIdModelBinder))]
|
||||
WalletId walletId,
|
||||
string command,
|
||||
// getinfo
|
||||
string cryptoCode = null,
|
||||
// getxpub
|
||||
[ModelBinder(typeof(ModelBinders.DerivationSchemeModelBinder))]
|
||||
DerivationStrategyBase derivationScheme = null,
|
||||
int account = 0,
|
||||
// sendtoaddress
|
||||
string destination = null, string amount = null, string feeRate = null, string substractFees = null
|
||||
@ -319,6 +404,11 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
if (!HttpContext.WebSockets.IsWebSocketRequest)
|
||||
return NotFound();
|
||||
|
||||
var cryptoCode = walletId.CryptoCode;
|
||||
var storeBlob = (await Repository.FindStore(walletId.StoreId, GetUserId()));
|
||||
var derivationScheme = GetPaymentMethod(walletId, storeBlob).DerivationStrategyBase;
|
||||
|
||||
var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
|
||||
using (var normalOperationTimeout = new CancellationTokenSource())
|
||||
@ -382,15 +472,6 @@ namespace BTCPayServer.Controllers
|
||||
}
|
||||
catch { throw new FormatException("Invalid value for subtract fees"); }
|
||||
}
|
||||
if (command == "test")
|
||||
{
|
||||
result = await hw.Test(normalOperationTimeout.Token);
|
||||
}
|
||||
if (command == "getxpub")
|
||||
{
|
||||
var getxpubResult = await hw.GetExtPubKey(network, account, normalOperationTimeout.Token);
|
||||
result = getxpubResult;
|
||||
}
|
||||
if (command == "getinfo")
|
||||
{
|
||||
var strategy = GetDirectDerivationStrategy(derivationScheme);
|
||||
@ -398,13 +479,12 @@ namespace BTCPayServer.Controllers
|
||||
{
|
||||
throw new Exception($"This store is not configured to use this ledger");
|
||||
}
|
||||
|
||||
var feeProvider = _feeRateProvider.CreateFeeProvider(network);
|
||||
var recommendedFees = feeProvider.GetFeeRateAsync();
|
||||
var balance = _walletProvider.GetWallet(network).GetBalance(derivationScheme);
|
||||
result = new GetInfoResult() { Balance = (double)(await balance).ToDecimal(MoneyUnit.BTC), RecommendedSatoshiPerByte = (int)(await recommendedFees).GetFee(1).Satoshi };
|
||||
result = new GetInfoResult();
|
||||
}
|
||||
if (command == "test")
|
||||
{
|
||||
result = await hw.Test(normalOperationTimeout.Token);
|
||||
}
|
||||
|
||||
if (command == "sendtoaddress")
|
||||
{
|
||||
if (!_dashboard.IsFullySynched(network.CryptoCode, out var summary))
|
||||
@ -549,8 +629,6 @@ namespace BTCPayServer.Controllers
|
||||
|
||||
public class GetInfoResult
|
||||
{
|
||||
public int RecommendedSatoshiPerByte { get; set; }
|
||||
public double Balance { get; set; }
|
||||
}
|
||||
|
||||
public class SendToAddressResult
|
||||
|
@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Hangfire;
|
||||
using Hangfire.MemoryStorage;
|
||||
using Hangfire.PostgreSql;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Migrations;
|
||||
using JetBrains.Annotations;
|
||||
|
@ -57,19 +57,22 @@ namespace BTCPayServer.Hosting
|
||||
return context.GetHttpContext().User.IsInRole(_Role);
|
||||
}
|
||||
}
|
||||
public Startup(IConfiguration conf, IHostingEnvironment env)
|
||||
public Startup(IConfiguration conf, IHostingEnvironment env, ILoggerFactory loggerFactory)
|
||||
{
|
||||
Configuration = conf;
|
||||
_Env = env;
|
||||
LoggerFactory = loggerFactory;
|
||||
}
|
||||
IHostingEnvironment _Env;
|
||||
public IConfiguration Configuration
|
||||
{
|
||||
get; set;
|
||||
}
|
||||
public ILoggerFactory LoggerFactory { get; }
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
Logs.Configure(LoggerFactory);
|
||||
services.ConfigureBTCPayServer(Configuration);
|
||||
services.AddMemoryCache();
|
||||
services.AddIdentity<ApplicationUser, IdentityRole>()
|
||||
@ -119,14 +122,42 @@ namespace BTCPayServer.Hosting
|
||||
});
|
||||
});
|
||||
|
||||
// Needed to debug U2F for ledger support
|
||||
//services.Configure<KestrelServerOptions>(kestrel =>
|
||||
//{
|
||||
// kestrel.Listen(IPAddress.Loopback, 5012, l =>
|
||||
// {
|
||||
// l.UseHttps("devtest.pfx", "toto");
|
||||
// });
|
||||
//});
|
||||
// If the HTTPS certificate path is not set this logic will NOT be used and the default Kestrel binding logic will be.
|
||||
string httpsCertificateFilePath = Configuration.GetOrDefault<string>("HttpsCertificateFilePath", null);
|
||||
bool useDefaultCertificate = Configuration.GetOrDefault<bool>("HttpsUseDefaultCertificate", false);
|
||||
bool hasCertPath = !String.IsNullOrEmpty(httpsCertificateFilePath);
|
||||
if (hasCertPath || useDefaultCertificate)
|
||||
{
|
||||
var bindAddress = Configuration.GetOrDefault<IPAddress>("bind", IPAddress.Any);
|
||||
int bindPort = Configuration.GetOrDefault<int>("port", 443);
|
||||
|
||||
services.Configure<KestrelServerOptions>(kestrel =>
|
||||
{
|
||||
if (hasCertPath && !File.Exists(httpsCertificateFilePath))
|
||||
{
|
||||
// Note that by design this is a fatal error condition that will cause the process to exit.
|
||||
throw new ConfigException($"The https certificate file could not be found at {httpsCertificateFilePath}.");
|
||||
}
|
||||
if(hasCertPath && useDefaultCertificate)
|
||||
{
|
||||
throw new ConfigException($"Conflicting settings: if HttpsUseDefaultCertificate is true, HttpsCertificateFilePath should not be used");
|
||||
}
|
||||
|
||||
kestrel.Listen(bindAddress, bindPort, l =>
|
||||
{
|
||||
if (hasCertPath)
|
||||
{
|
||||
Logs.Configuration.LogInformation($"Using HTTPS with the certificate located in {httpsCertificateFilePath}.");
|
||||
l.UseHttps(httpsCertificateFilePath, Configuration.GetOrDefault<string>("HttpsCertificateFilePassword", null));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logs.Configuration.LogInformation($"Using HTTPS with the default certificate");
|
||||
l.UseHttps();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void Configure(
|
||||
@ -136,7 +167,6 @@ namespace BTCPayServer.Hosting
|
||||
BTCPayServerOptions options,
|
||||
ILoggerFactory loggerFactory)
|
||||
{
|
||||
Logs.Configure(loggerFactory);
|
||||
Logs.Configuration.LogInformation($"Root Path: {options.RootPath}");
|
||||
if (options.RootPath.Equals("/", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
|
@ -29,7 +29,6 @@ namespace BTCPayServer.Models.StoreViewModels
|
||||
public bool Confirmation { get; set; }
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
public string ServerUrl { get; set; }
|
||||
public string StatusMessage { get; internal set; }
|
||||
public KeyPath RootKeyPath { get; set; }
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Models.WalletViewModels
|
||||
{
|
||||
public class WalletSendLedgerModel
|
||||
{
|
||||
public int FeeSatoshiPerByte { get; set; }
|
||||
public bool SubstractFees { get; set; }
|
||||
public decimal Amount { get; set; }
|
||||
public string Destination { get; set; }
|
||||
}
|
||||
}
|
36
BTCPayServer/Models/WalletViewModels/WalletSendModel.cs
Normal file
36
BTCPayServer/Models/WalletViewModels/WalletSendModel.cs
Normal file
@ -0,0 +1,36 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Models.WalletViewModels
|
||||
{
|
||||
public class WalletSendModel
|
||||
{
|
||||
[Required]
|
||||
public string Destination { get; set; }
|
||||
|
||||
[Range(0.0, double.MaxValue)]
|
||||
[Required]
|
||||
public decimal? Amount { get; set; }
|
||||
|
||||
public decimal CurrentBalance { get; set; }
|
||||
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public int RecommendedSatoshiPerByte { get; set; }
|
||||
|
||||
[Display(Name = "Subtract fees from amount")]
|
||||
public bool SubstractFees { get; set; }
|
||||
|
||||
[Range(1, int.MaxValue)]
|
||||
[Display(Name = "Fee rate (satoshi per byte)")]
|
||||
[Required]
|
||||
public int FeeSatoshiPerByte { get; set; }
|
||||
public decimal? Rate { get; set; }
|
||||
public int Divisibility { get; set; }
|
||||
public string Fiat { get; set; }
|
||||
public string RateError { get; set; }
|
||||
}
|
||||
}
|
@ -149,8 +149,7 @@ namespace BTCPayServer.Payments.Bitcoin
|
||||
foreach (var output in evt.Outputs)
|
||||
{
|
||||
foreach (var txCoin in evt.TransactionData.Transaction.Outputs.AsCoins()
|
||||
.Where(o => o.ScriptPubKey == output.ScriptPubKey)
|
||||
.Select(o => output.Redeem == null ? o : o.ToScriptCoin(output.Redeem)))
|
||||
.Where(o => o.ScriptPubKey == output.ScriptPubKey))
|
||||
{
|
||||
var invoice = await _InvoiceRepository.GetInvoiceFromScriptPubKey(output.ScriptPubKey, network.CryptoCode);
|
||||
if (invoice != null)
|
||||
|
@ -1,22 +1,40 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Docker-Regtest": {
|
||||
"commandName": "Project",
|
||||
"commandLineArgs": "--debuglog debug.log",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"BTCPAY_NETWORK": "regtest",
|
||||
"BTCPAY_BUNDLEJSCSS": "true",
|
||||
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
|
||||
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
|
||||
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true;macaroonfilepath=D:\\admin.macaroon",
|
||||
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"BTCPAY_CHAINS": "btc,ltc",
|
||||
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
|
||||
"profiles": {
|
||||
"Docker-Regtest": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"BTCPAY_NETWORK": "regtest",
|
||||
"BTCPAY_BUNDLEJSCSS": "true",
|
||||
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
|
||||
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
|
||||
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true;macaroonfilepath=D:\\admin.macaroon",
|
||||
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"BTCPAY_CHAINS": "btc,ltc",
|
||||
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
|
||||
},
|
||||
"applicationUrl": "http://127.0.0.1:14142/"
|
||||
},
|
||||
"applicationUrl": "http://127.0.0.1:14142/"
|
||||
"Docker-Regtest-https": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"BTCPAY_NETWORK": "regtest",
|
||||
"BTCPAY_PORT": "14142",
|
||||
"BTCPAY_HttpsUseDefaultCertificate": "true",
|
||||
"BTCPAY_BUNDLEJSCSS": "true",
|
||||
"BTCPAY_LTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"BTCPAY_BTCLIGHTNING": "type=charge;server=http://127.0.0.1:54938/;api-token=foiewnccewuify",
|
||||
"BTCPAY_BTCEXTERNALLNDGRPC": "type=lnd-grpc;server=https://lnd:lnd@127.0.0.1:53280/;allowinsecure=true",
|
||||
"BTCPAY_BTCEXTERNALLNDREST": "type=lnd-rest;server=https://lnd:lnd@127.0.0.1:53280/lnd-rest/btc/;allowinsecure=true;macaroonfilepath=D:\\admin.macaroon",
|
||||
"BTCPAY_BTCEXPLORERURL": "http://127.0.0.1:32838/",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"BTCPAY_CHAINS": "btc,ltc",
|
||||
"BTCPAY_POSTGRES": "User ID=postgres;Host=127.0.0.1;Port=39372;Database=btcpayserver"
|
||||
},
|
||||
"applicationUrl": "https://localhost:14142/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,17 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
try
|
||||
{
|
||||
var global = _Helper.ExchangeSymbolToGlobalSymbol(symbol);
|
||||
string global = null;
|
||||
if(symbol.StartsWith("DASH", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var p2 = symbol.Substring(4);
|
||||
p2 = p2 == "XBT" ? "BTC" : p2;
|
||||
global = $"{p2}_{symbol.Substring(0, 4)}";
|
||||
}
|
||||
else
|
||||
{
|
||||
global = _Helper.ExchangeSymbolToGlobalSymbol(symbol);
|
||||
}
|
||||
if (CurrencyPair.TryParse(global, out var pair))
|
||||
result.Add(new ExchangeRate("kraken", pair.Inverse(), new BidAsk(ticker.Bid, ticker.Ask)));
|
||||
else
|
||||
|
@ -102,6 +102,8 @@ namespace BTCPayServer.Services.Rates
|
||||
Providers.Add("bittrex", new ExchangeSharpRateProvider("bittrex", new ExchangeBittrexAPI(), true));
|
||||
Providers.Add("poloniex", new ExchangeSharpRateProvider("poloniex", new ExchangePoloniexAPI(), true));
|
||||
Providers.Add("hitbtc", new ExchangeSharpRateProvider("hitbtc", new ExchangeHitbtcAPI(), false));
|
||||
|
||||
// Cryptopia is often not available
|
||||
Providers.Add("cryptopia", new ExchangeSharpRateProvider("cryptopia", new ExchangeCryptopiaAPI(), false));
|
||||
|
||||
// Handmade providers
|
||||
@ -118,6 +120,8 @@ namespace BTCPayServer.Services.Rates
|
||||
|
||||
foreach (var provider in Providers.ToArray())
|
||||
{
|
||||
if (provider.Key == "cryptopia") // Shitty exchange, rate often unavailable, it spams the logs
|
||||
continue;
|
||||
var prov = new BackgroundFetcherRateProvider(Providers[provider.Key]);
|
||||
if(provider.Key == CoinAverageRateProvider.CoinAverageName)
|
||||
{
|
||||
|
@ -128,9 +128,6 @@
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
<script type="text/javascript">
|
||||
var srvModel = @Html.Raw(Json.Serialize(Model.ServerUrl));
|
||||
</script>
|
||||
<script src="~/js/ledgerwebsocket.js" type="text/javascript" defer="defer"></script>
|
||||
<script src="~/js/StoreAddDerivationScheme.js" type="text/javascript" defer="defer"></script>
|
||||
}
|
||||
|
@ -18,9 +18,8 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<th>SIN</th>
|
||||
<th>Facade</th>
|
||||
<th>Actions</th>
|
||||
<th class="text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -28,13 +27,9 @@
|
||||
{
|
||||
<tr>
|
||||
<td>@token.Label</td>
|
||||
<td>@token.SIN</td>
|
||||
<td>@token.Facade</td>
|
||||
<td>
|
||||
<form asp-action="DeleteToken" method="post">
|
||||
<input type="hidden" name="tokenId" value="@token.Id">
|
||||
<button type="submit" class="btn btn-danger" role="button">Revoke</button>
|
||||
</form>
|
||||
<td class="text-right">
|
||||
<a asp-action="ShowToken" asp-route-tokenId="@token.Id">See information</a> - <a asp-action="RevokeToken" asp-route-tokenId="@token.Id">Revoke</a>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
|
29
BTCPayServer/Views/Stores/ShowToken.cshtml
Normal file
29
BTCPayServer/Views/Stores/ShowToken.cshtml
Normal file
@ -0,0 +1,29 @@
|
||||
@model BTCPayServer.Authentication.BitTokenEntity
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData.SetActivePageAndTitle(StoreNavPages.Tokens, "Access Tokens");
|
||||
}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<h5>Token information</h5>
|
||||
<table class="table table-sm">
|
||||
<tr>
|
||||
<th>Label</th>
|
||||
<td>@Model.Label</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>SIN</th>
|
||||
<td>@Model.SIN</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Token</th>
|
||||
<td>@Model.Value</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Facade</th>
|
||||
<td>@Model.Facade</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
@ -42,6 +42,7 @@
|
||||
}
|
||||
</td>
|
||||
<td style="text-align:right">
|
||||
<a asp-action="ListInvoices" asp-controller="Invoice" asp-route-searchTerm="storeid:@store.Id">Invoices</a><span> - </span>
|
||||
@if (store.IsOwner)
|
||||
{
|
||||
<a asp-action="UpdateStore" asp-controller="Stores" asp-route-storeId="@store.Id">Settings</a><span> - </span>
|
||||
|
@ -92,7 +92,7 @@ else
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<p>Scanning in progress, refresh the page to see the progress...</p>
|
||||
<p>Scanning in progress, refresh the page to see the progress... (Estimated time: @Model.RemainingTime)</p>
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="@(Model.Progress.Value)"
|
||||
aria-valuemin="0" aria-valuemax="100" style="white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width:@(Model.Progress.Value)%;">
|
||||
|
@ -1,4 +1,4 @@
|
||||
@model WalletModel
|
||||
@model WalletSendModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["Title"] = "Manage wallet";
|
||||
@ -6,75 +6,60 @@
|
||||
}
|
||||
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<div id="walletAlert" class="alert alert-danger alert-dismissible" style="display:none;" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<span id="alertMessage"></span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<p>
|
||||
You can send money received by this store to an address with the help of your Ledger Wallet. <br />
|
||||
If you don't have a Ledger Wallet, use Electrum with your favorite hardware wallet to transfer crypto. <br />
|
||||
If your Ledger wallet is not detected:
|
||||
Send funds to a destination address.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Make sure you are running the Ledger app with version superior or equal to 1.2.4</li>
|
||||
<li>Use Google Chrome browser and open the coin app on your Ledger</li>
|
||||
</ul>
|
||||
<p id="hw-loading"><span class="fa fa-question-circle" style="color:orange"></span> <span>Detecting hardware wallet...</span></p>
|
||||
<p id="hw-error" style="display:none;"><span class="fa fa-times-circle" style="color:red;"></span> <span class="hw-label">An error happened</span></p>
|
||||
<p id="hw-success" style="display:none;"><span class="fa fa-check-circle" style="color:green;"></span> <span class="hw-label">Detecting hardware wallet...</span></p>
|
||||
|
||||
<p id="check-loading" style="display:none;"><span class="fa fa-question-circle" style="color:orange"></span> <span class="check-label">Detecting hardware wallet...</span></p>
|
||||
<p id="check-error" style="display:none;"><span class="fa fa-times-circle" style="color:red;"></span> <span class="check-label">An error happened</span></p>
|
||||
<p id="check-success" style="display:none;"><span class="fa fa-check-circle" style="color:green;"></span> <span class="check-label">Detecting hardware wallet...</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form id="sendform" style="display:none;">
|
||||
<input type="hidden" id="cryptoCode" asp-for="CryptoCurrency" />
|
||||
<form method="post">
|
||||
<input type="hidden" asp-for="Divisibility" />
|
||||
<input type="hidden" asp-for="Fiat" />
|
||||
<input type="hidden" asp-for="Rate" />
|
||||
<input type="hidden" asp-for="CurrentBalance" />
|
||||
<input type="hidden" asp-for="RecommendedSatoshiPerByte" />
|
||||
<input type="hidden" asp-for="CryptoCode" />
|
||||
|
||||
<div class="form-group">
|
||||
<label>Destination</label>
|
||||
<input id="destination-textbox" name="Destination" class="form-control" type="text" />
|
||||
<span id="Destination-Error" class="text-danger"></span>
|
||||
<label asp-for="Destination"></label>
|
||||
<input asp-for="Destination" class="form-control" />
|
||||
<span asp-validation-for="Destination" class="text-danger"></span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Amount</label>
|
||||
<label asp-for="Amount"></label>
|
||||
<div class="input-group">
|
||||
<input id="amount-textbox" name="Amount" class="form-control" type="text" onkeyup='updateFiatValue();' />
|
||||
<input asp-for="Amount" class="form-control" onkeyup='updateFiatValue();' />
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text text-muted" style="display:none;" id="fiatValue"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@*<span class="text-muted" id="fiatValue"></span>*@
|
||||
<span id="Amount-Error" class="text-danger"></span>
|
||||
<p class="form-text text-muted crypto-info" style="display: none;">
|
||||
Your current balance is <a id="crypto-balance-link" href="#"><span id="crypto-balance"></span></a> <span id="crypto-code"></span>.
|
||||
<span asp-validation-for="Amount" class="text-danger"></span>
|
||||
<p class="form-text text-muted crypto-info">
|
||||
Your current balance is <a id="crypto-balance-link" href="#"><span>@Model.CurrentBalance</span></a> <span>@Model.CryptoCode</span>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Fee rate (satoshi per byte)</label>
|
||||
<input id="fee-textbox" name="FeeRate" class="form-control" type="text" />
|
||||
<label asp-for="FeeSatoshiPerByte"></label>
|
||||
<input asp-for="FeeSatoshiPerByte" class="form-control" />
|
||||
<span id="FeeRate-Error" class="text-danger"></span>
|
||||
<p class="form-text text-muted crypto-info" style="display: none;">
|
||||
The recommended value is <a id="crypto-fee-link" href="#"><span id="crypto-fee"></span></a> satoshi per byte.
|
||||
<p class="form-text text-muted crypto-info">
|
||||
The recommended value is <a id="crypto-fee-link" href="#"><span>@Model.RecommendedSatoshiPerByte</span></a> satoshi per byte.
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Subtract fees from amount</label>
|
||||
<input id="substract-checkbox" name="SubstractFees" class="form-check" type="checkbox" />
|
||||
<label asp-for="SubstractFees"></label>
|
||||
<input asp-for="SubstractFees" class="form-check" />
|
||||
</div>
|
||||
<button id="confirm-button" name="command" type="submit" class="btn btn-primary">Confirm</button>
|
||||
<button type="submit" class="btn btn-primary">Confirm</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@section Scripts
|
||||
{
|
||||
<script type="text/javascript">
|
||||
var srvModel = @Html.Raw(Json.Serialize(Model));
|
||||
</script>
|
||||
<script src="~/js/ledgerwebsocket.js" type="text/javascript" defer="defer"></script>
|
||||
<script src="~/js/StoreWallet.js" type="text/javascript" defer="defer"></script>
|
||||
{
|
||||
<script src="~/js/WalletSend.js" type="text/javascript" defer="defer"></script>
|
||||
}
|
||||
|
43
BTCPayServer/Views/Wallets/WalletSendLedger.cshtml
Normal file
43
BTCPayServer/Views/Wallets/WalletSendLedger.cshtml
Normal file
@ -0,0 +1,43 @@
|
||||
@model WalletSendLedgerModel
|
||||
@{
|
||||
Layout = "../Shared/_NavLayout.cshtml";
|
||||
ViewData["Title"] = "Manage wallet";
|
||||
ViewData.SetActivePageAndTitle(WalletsNavPages.Send);
|
||||
}
|
||||
|
||||
<h4>Sign the transaction with Ledger</h4>
|
||||
<div id="walletAlert" class="alert alert-danger alert-dismissible" style="display:none;" role="alert">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<span id="alertMessage"></span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
<input type="hidden" asp-for="Destination" />
|
||||
<input type="hidden" asp-for="Amount" />
|
||||
<input type="hidden" asp-for="FeeSatoshiPerByte" />
|
||||
<input type="hidden" asp-for="SubstractFees" />
|
||||
<p>
|
||||
You can send money received by this store to an address with the help of your Ledger Wallet. <br />
|
||||
If you don't have a Ledger Wallet, use Electrum with your favorite hardware wallet to transfer crypto. <br />
|
||||
If your Ledger wallet is not detected:
|
||||
</p>
|
||||
<ul>
|
||||
<li>Make sure you are running the Ledger app with version superior or equal to 1.2.4</li>
|
||||
<li>Use Google Chrome browser and open the coin app on your Ledger</li>
|
||||
</ul>
|
||||
<p id="hw-loading"><span class="fa fa-question-circle" style="color:orange"></span> <span>Detecting hardware wallet...</span></p>
|
||||
<p id="hw-error" style="display:none;"><span class="fa fa-times-circle" style="color:red;"></span> <span class="hw-label">An error happened</span></p>
|
||||
<p id="hw-success" style="display:none;"><span class="fa fa-check-circle" style="color:green;"></span> <span class="hw-label">Detecting hardware wallet...</span></p>
|
||||
|
||||
<p id="check-loading" style="display:none;"><span class="fa fa-question-circle" style="color:orange"></span> <span class="check-label">Detecting hardware wallet...</span></p>
|
||||
<p id="check-error" style="display:none;"><span class="fa fa-times-circle" style="color:red;"></span> <span class="check-label">An error happened</span></p>
|
||||
<p id="check-success" style="display:none;"><span class="fa fa-check-circle" style="color:green;"></span> <span class="check-label">Detecting hardware wallet...</span></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@section Scripts
|
||||
{
|
||||
<script src="~/js/ledgerwebsocket.js" type="text/javascript" defer="defer"></script>
|
||||
<script src="~/js/WalletSendLedger.js" type="text/javascript" defer="defer"></script>
|
||||
}
|
@ -17,6 +17,11 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-10 text-center">
|
||||
<partial name="_StatusMessage" for="@TempData["TempDataProperty-StatusMessage"]" />
|
||||
</div>
|
||||
</div>
|
||||
<h4>@ViewData["Title"]</h4>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
|
@ -1,6 +1,16 @@
|
||||
$(function () {
|
||||
var ledgerDetected = false;
|
||||
var bridge = new ledgerwebsocket.LedgerWebSocketBridge(srvModel);
|
||||
|
||||
var loc = window.location, new_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
new_uri = "wss:";
|
||||
} else {
|
||||
new_uri = "ws:";
|
||||
}
|
||||
new_uri += "//" + loc.host;
|
||||
new_uri += loc.pathname + "/ledger/ws";
|
||||
|
||||
var bridge = new ledgerwebsocket.LedgerWebSocketBridge(new_uri);
|
||||
|
||||
var cryptoSelector = $("#CryptoCurrency");
|
||||
function GetSelectedCryptoCode() {
|
||||
|
@ -1,160 +0,0 @@
|
||||
|
||||
function updateFiatValue() {
|
||||
if (srvModel.rate !== null) {
|
||||
var fiatValue = $("#fiatValue");
|
||||
fiatValue.css("display", "inline");
|
||||
var amountValue = parseFloat($("#amount-textbox").val());
|
||||
if (!isNaN(amountValue)) {
|
||||
fiatValue.text("= " + (srvModel.rate * amountValue).toFixed(srvModel.divisibility) + " " + srvModel.fiat);
|
||||
}
|
||||
}
|
||||
}
|
||||
$(function () {
|
||||
var ledgerDetected = false;
|
||||
var bridge = new ledgerwebsocket.LedgerWebSocketBridge(srvModel.serverUrl);
|
||||
var recommendedFees = "";
|
||||
var recommendedBalance = "";
|
||||
var cryptoCode = $("#cryptoCode").val();
|
||||
if (srvModel.defaultAddress !== null) {
|
||||
$("#destination-textbox").val(srvModel.defaultAddress);
|
||||
}
|
||||
if (srvModel.defaultAmount !== null) {
|
||||
$("#amount-textbox").val(srvModel.defaultAmount);
|
||||
}
|
||||
function WriteAlert(type, message) {
|
||||
$("#walletAlert").removeClass("alert-danger");
|
||||
$("#walletAlert").removeClass("alert-warning");
|
||||
$("#walletAlert").removeClass("alert-success");
|
||||
$("#walletAlert").addClass("alert-" + type);
|
||||
$("#walletAlert").css("display", "block");
|
||||
$("#alertMessage").text(message);
|
||||
}
|
||||
|
||||
function Write(prefix, type, message) {
|
||||
|
||||
$("#" + prefix + "-loading").css("display", "none");
|
||||
$("#" + prefix + "-error").css("display", "none");
|
||||
$("#" + prefix + "-success").css("display", "none");
|
||||
|
||||
$("#" + prefix + "-" + type).css("display", "block");
|
||||
|
||||
$("." + prefix + "-label").text(message);
|
||||
}
|
||||
|
||||
$("#sendform").on("submit", function (elem) {
|
||||
elem.preventDefault();
|
||||
|
||||
if ($("#amount-textbox").val() === "") {
|
||||
$("#amount-textbox").val(recommendedBalance);
|
||||
$("#substract-checkbox").prop("checked", true);
|
||||
}
|
||||
|
||||
if ($("#fee-textbox").val() === "") {
|
||||
$("#fee-textbox").val(recommendedFees);
|
||||
}
|
||||
|
||||
var args = "";
|
||||
args += "cryptoCode=" + cryptoCode;
|
||||
args += "&destination=" + $("#destination-textbox").val();
|
||||
args += "&amount=" + $("#amount-textbox").val();
|
||||
args += "&feeRate=" + $("#fee-textbox").val();
|
||||
args += "&substractFees=" + $("#substract-checkbox").prop("checked");
|
||||
|
||||
WriteAlert("warning", 'Please validate the transaction on your ledger');
|
||||
|
||||
var confirmButton = $("#confirm-button");
|
||||
confirmButton.prop("disabled", true);
|
||||
confirmButton.addClass("disabled");
|
||||
|
||||
bridge.sendCommand('sendtoaddress', args, 60 * 10 /* timeout */)
|
||||
.catch(function (reason) {
|
||||
WriteAlert("danger", reason);
|
||||
confirmButton.prop("disabled", false);
|
||||
confirmButton.removeClass("disabled");
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
confirmButton.prop("disabled", false);
|
||||
confirmButton.removeClass("disabled");
|
||||
if (result.error) {
|
||||
WriteAlert("danger", result.error);
|
||||
} else {
|
||||
WriteAlert("success", 'Transaction broadcasted (' + result.transactionId + ')');
|
||||
$("#fee-textbox").val("");
|
||||
$("#amount-textbox").val("");
|
||||
$("#destination-textbox").val("");
|
||||
$("#substract-checkbox").prop("checked", false);
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#crypto-balance-link").on("click", function (elem) {
|
||||
elem.preventDefault();
|
||||
var val = $("#crypto-balance-link").text();
|
||||
$("#amount-textbox").val(val);
|
||||
$("#substract-checkbox").prop('checked', true);
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#crypto-fee-link").on("click", function (elem) {
|
||||
elem.preventDefault();
|
||||
var val = $("#crypto-fee-link").text();
|
||||
$("#fee-textbox").val(val);
|
||||
return false;
|
||||
});
|
||||
|
||||
var updateInfo = function () {
|
||||
if (!ledgerDetected)
|
||||
return false;
|
||||
$(".crypto-info").css("display", "none");
|
||||
bridge.sendCommand("getinfo", "cryptoCode=" + cryptoCode)
|
||||
.catch(function (reason) { Write('check', 'error', reason); })
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
if (result.error) {
|
||||
Write('check', 'error', result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Write('check', 'success', 'This store is configured to use your ledger');
|
||||
$(".crypto-info").css("display", "block");
|
||||
recommendedFees = result.recommendedSatoshiPerByte;
|
||||
recommendedBalance = result.balance;
|
||||
$("#crypto-fee").text(result.recommendedSatoshiPerByte);
|
||||
$("#crypto-balance").text(result.balance);
|
||||
$("#crypto-code").text(cryptoCode);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
bridge.isSupported()
|
||||
.then(function (supported) {
|
||||
if (!supported) {
|
||||
Write('hw', 'error', 'U2F or Websocket are not supported by this browser');
|
||||
}
|
||||
else {
|
||||
bridge.sendCommand('test', null, 5)
|
||||
.catch(function (reason) {
|
||||
if (reason.name === "TransportError")
|
||||
reason = "Are you running the ledger app with version equals or above 1.2.4?";
|
||||
Write('hw', 'error', reason);
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
if (result.error) {
|
||||
Write('hw', 'error', result.error);
|
||||
} else {
|
||||
Write('hw', 'success', 'Ledger detected');
|
||||
$("#sendform").css("display", "block");
|
||||
ledgerDetected = true;
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
32
BTCPayServer/wwwroot/js/WalletSend.js
Normal file
32
BTCPayServer/wwwroot/js/WalletSend.js
Normal file
@ -0,0 +1,32 @@
|
||||
function updateFiatValue() {
|
||||
var rateStr = $("#Rate").val();
|
||||
var divisibilityStr = $("#Divisibility").val();
|
||||
var fiat = $("#Fiat").val();
|
||||
var rate = parseFloat(rateStr);
|
||||
var divisibility = parseInt(divisibilityStr);
|
||||
if (!isNaN(rate) && !isNaN(divisibility)) {
|
||||
var fiatValue = $("#fiatValue");
|
||||
var amountValue = parseFloat($("#Amount").val());
|
||||
if (!isNaN(amountValue)) {
|
||||
fiatValue.css("display", "inline");
|
||||
fiatValue.text("= " + (rate * amountValue).toFixed(divisibility) + " " + fiat);
|
||||
}
|
||||
}
|
||||
}
|
||||
$(function () {
|
||||
updateFiatValue();
|
||||
$("#crypto-fee-link").on("click", function (elem) {
|
||||
elem.preventDefault();
|
||||
var val = $("#crypto-fee-link").text();
|
||||
$("#FeeSatoshiPerByte").val(val);
|
||||
return false;
|
||||
});
|
||||
|
||||
$("#crypto-balance-link").on("click", function (elem) {
|
||||
elem.preventDefault();
|
||||
var val = $("#crypto-balance-link").text();
|
||||
$("#Amount").val(val);
|
||||
$("#SubstractFees").prop('checked', true);
|
||||
return false;
|
||||
});
|
||||
});
|
120
BTCPayServer/wwwroot/js/WalletSendLedger.js
Normal file
120
BTCPayServer/wwwroot/js/WalletSendLedger.js
Normal file
@ -0,0 +1,120 @@
|
||||
$(function () {
|
||||
var destination = $("#Destination").val();
|
||||
var amount = $("#Amount").val();
|
||||
var fee = $("#FeeSatoshiPerByte").val();
|
||||
var substractFee = $("#SubstractFees").val();
|
||||
|
||||
var loc = window.location, ws_uri;
|
||||
if (loc.protocol === "https:") {
|
||||
ws_uri = "wss:";
|
||||
} else {
|
||||
ws_uri = "ws:";
|
||||
}
|
||||
ws_uri += "//" + loc.host;
|
||||
ws_uri += loc.pathname + "/ws";
|
||||
|
||||
var successCallback = loc.protocol + "//" + loc.host + loc.pathname + "/success";
|
||||
|
||||
var ledgerDetected = false;
|
||||
var bridge = new ledgerwebsocket.LedgerWebSocketBridge(ws_uri);
|
||||
var cryptoCode = $("#cryptoCode").val();
|
||||
function WriteAlert(type, message) {
|
||||
$("#walletAlert").removeClass("alert-danger");
|
||||
$("#walletAlert").removeClass("alert-warning");
|
||||
$("#walletAlert").removeClass("alert-success");
|
||||
$("#walletAlert").addClass("alert-" + type);
|
||||
$("#walletAlert").css("display", "block");
|
||||
$("#alertMessage").text(message);
|
||||
}
|
||||
|
||||
function Write(prefix, type, message) {
|
||||
|
||||
$("#" + prefix + "-loading").css("display", "none");
|
||||
$("#" + prefix + "-error").css("display", "none");
|
||||
$("#" + prefix + "-success").css("display", "none");
|
||||
|
||||
$("#" + prefix + "-" + type).css("display", "block");
|
||||
|
||||
$("." + prefix + "-label").text(message);
|
||||
}
|
||||
|
||||
var updateInfo = function () {
|
||||
if (!ledgerDetected)
|
||||
return false;
|
||||
$(".crypto-info").css("display", "none");
|
||||
bridge.sendCommand("getinfo", "cryptoCode=" + cryptoCode)
|
||||
.catch(function (reason) { Write('check', 'error', reason); })
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
if (result.error) {
|
||||
Write('check', 'error', result.error);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
Write('check', 'success', 'This store is configured to use your ledger');
|
||||
$(".crypto-info").css("display", "block");
|
||||
|
||||
|
||||
var args = "";
|
||||
args += "cryptoCode=" + cryptoCode;
|
||||
args += "&destination=" + destination;
|
||||
args += "&amount=" + amount;
|
||||
args += "&feeRate=" + fee;
|
||||
args += "&substractFees=" + substractFee;
|
||||
|
||||
WriteAlert("warning", 'Please validate the transaction on your ledger');
|
||||
|
||||
var confirmButton = $("#confirm-button");
|
||||
confirmButton.prop("disabled", true);
|
||||
confirmButton.addClass("disabled");
|
||||
|
||||
bridge.sendCommand('sendtoaddress', args, 60 * 10 /* timeout */)
|
||||
.catch(function (reason) {
|
||||
WriteAlert("danger", reason);
|
||||
confirmButton.prop("disabled", false);
|
||||
confirmButton.removeClass("disabled");
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
confirmButton.prop("disabled", false);
|
||||
confirmButton.removeClass("disabled");
|
||||
if (result.error) {
|
||||
WriteAlert("danger", result.error);
|
||||
} else {
|
||||
WriteAlert("success", 'Transaction broadcasted (' + result.transactionId + ')');
|
||||
window.location.replace(successCallback + "?txid=" + result.transactionId);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
bridge.isSupported()
|
||||
.then(function (supported) {
|
||||
if (!supported) {
|
||||
Write('hw', 'error', 'U2F or Websocket are not supported by this browser');
|
||||
}
|
||||
else {
|
||||
bridge.sendCommand('test', null, 5)
|
||||
.catch(function (reason) {
|
||||
if (reason.name === "TransportError")
|
||||
reason = "Are you running the ledger app with version equals or above 1.2.4?";
|
||||
Write('hw', 'error', reason);
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result)
|
||||
return;
|
||||
if (result.error) {
|
||||
Write('hw', 'error', result.error);
|
||||
} else {
|
||||
Write('hw', 'success', 'Ledger detected');
|
||||
$("#sendform").css("display", "block");
|
||||
ledgerDetected = true;
|
||||
updateInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
FROM microsoft/dotnet:2.1.402-sdk-alpine3.7 AS builder
|
||||
FROM microsoft/dotnet:2.1.403-sdk-alpine3.7 AS builder
|
||||
WORKDIR /source
|
||||
COPY BTCPayServer/BTCPayServer.csproj BTCPayServer.csproj
|
||||
# Cache some dependencies
|
||||
@ -6,7 +6,7 @@ RUN dotnet restore
|
||||
COPY BTCPayServer/. .
|
||||
RUN dotnet publish --output /app/ --configuration Release
|
||||
|
||||
FROM microsoft/dotnet:2.1.4-aspnetcore-runtime-alpine3.7
|
||||
FROM microsoft/dotnet:2.1.5-aspnetcore-runtime-alpine3.7
|
||||
|
||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT false
|
||||
RUN apk add --no-cache icu-libs
|
||||
|
21
README.md
21
README.md
@ -63,7 +63,7 @@ You can also read the [BTCPay Merchants Guide](https://www.reddit.com/r/Bitcoin/
|
||||
|
||||
While the documentation advises to use docker-compose, you may want to build BTCPay yourself.
|
||||
|
||||
First install .NET Core SDK v2.1.4 (with patch version >= 402) as specified by [Microsoft website](https://www.microsoft.com/net/download/dotnet-core/2.1).
|
||||
First install .NET Core SDK v2.1.4 (with patch version >= 403) as specified by [Microsoft website](https://www.microsoft.com/net/download/dotnet-core/2.1).
|
||||
|
||||
On Powershell:
|
||||
```
|
||||
@ -89,6 +89,25 @@ On linux:
|
||||
./run.sh --help
|
||||
```
|
||||
|
||||
## How to debug
|
||||
|
||||
If you want to debug, use Visual Studio Code or Visual studio 2017.
|
||||
|
||||
You need to run the development time docker-compose as described [in the test guide](BTCPayServer.Tests/README.md).
|
||||
|
||||
You can then run the debugger by using the Launch Profile `Docker-Regtest` on either Visual Studio Code or Visual studio 2017.
|
||||
|
||||
If you need to debug ledger wallet interaction, install the development time certificate with:
|
||||
|
||||
```bash
|
||||
# Install development time certificate in the trust store
|
||||
dotnet dev-certs https --trust
|
||||
```
|
||||
|
||||
Then use the `Docker-Regtest-https` debug profile.
|
||||
|
||||
|
||||
|
||||
## Other dependencies
|
||||
|
||||
For more information, see the documentation: [How to deploy a BTCPay server instance](https://github.com/btcpayserver/btcpayserver-doc/#deployment).
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "2.1.402"
|
||||
"version": "2.1.403"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user