mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2026-02-22 14:44:23 +01:00
Remove CoinAverage integration
This commit is contained in:
@@ -1,192 +0,0 @@
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using System.ComponentModel;
|
||||
using BTCPayServer.Rating;
|
||||
using System.Threading;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class CoinAverageException : Exception
|
||||
{
|
||||
public CoinAverageException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class RatesSetting
|
||||
{
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
}
|
||||
|
||||
public interface ICoinAverageAuthenticator
|
||||
{
|
||||
Task AddHeader(HttpRequestMessage message);
|
||||
}
|
||||
|
||||
public class CoinAverageRateProvider : IRateProvider, IHasExchangeName
|
||||
{
|
||||
public const string CoinAverageName = "coinaverage";
|
||||
public CoinAverageRateProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public HttpClient HttpClient
|
||||
{
|
||||
get
|
||||
{
|
||||
return _LocalClient ?? _Client;
|
||||
}
|
||||
set
|
||||
{
|
||||
_LocalClient = value;
|
||||
}
|
||||
}
|
||||
HttpClient _LocalClient;
|
||||
static HttpClient _Client = new HttpClient();
|
||||
|
||||
public string Exchange { get; set; } = CoinAverageName;
|
||||
|
||||
public string CryptoCode { get; set; }
|
||||
|
||||
public string Market
|
||||
{
|
||||
get; set;
|
||||
} = "global";
|
||||
|
||||
public ICoinAverageAuthenticator Authenticator { get; set; }
|
||||
|
||||
public string ExchangeName => Exchange ?? CoinAverageName;
|
||||
|
||||
private bool TryToBidAsk(JProperty p, out BidAsk bidAsk)
|
||||
{
|
||||
bidAsk = null;
|
||||
if (Exchange == CoinAverageName)
|
||||
{
|
||||
JToken last = p.Value["last"];
|
||||
if (!decimal.TryParse(last.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v) ||
|
||||
v <= 0)
|
||||
return false;
|
||||
bidAsk = new BidAsk(v);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
JToken bid = p.Value["bid"];
|
||||
JToken ask = p.Value["ask"];
|
||||
if (bid == null || ask == null ||
|
||||
!decimal.TryParse(bid.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v1) ||
|
||||
!decimal.TryParse(ask.Value<string>(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v2) ||
|
||||
v1 > v2 ||
|
||||
v1 <= 0 || v2 <= 0)
|
||||
return false;
|
||||
bidAsk = new BidAsk(v1, v2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<ExchangeRates> GetRatesAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
string url = Exchange == CoinAverageName ? $"https://apiv2.bitcoinaverage.com/indices/{Market}/ticker/short"
|
||||
: $"https://apiv2.bitcoinaverage.com/exchanges/{Exchange}";
|
||||
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, url);
|
||||
var auth = Authenticator;
|
||||
if (auth != null)
|
||||
{
|
||||
await auth.AddHeader(request);
|
||||
}
|
||||
var resp = await HttpClient.SendAsync(request, cancellationToken);
|
||||
using (resp)
|
||||
{
|
||||
|
||||
if ((int)resp.StatusCode == 401)
|
||||
throw new CoinAverageException("Unauthorized access to the API");
|
||||
if ((int)resp.StatusCode == 429)
|
||||
throw new CoinAverageException("Exceed API limits");
|
||||
if ((int)resp.StatusCode == 403)
|
||||
throw new CoinAverageException("Unauthorized access to the API, premium plan needed");
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var rates = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
||||
if (Exchange != CoinAverageName)
|
||||
{
|
||||
rates = (JObject)rates["symbols"];
|
||||
}
|
||||
|
||||
var exchangeRates = new ExchangeRates();
|
||||
foreach (var prop in rates.Properties())
|
||||
{
|
||||
ExchangeRate exchangeRate = new ExchangeRate();
|
||||
exchangeRate.Exchange = Exchange;
|
||||
if (!TryToBidAsk(prop, out var value))
|
||||
continue;
|
||||
exchangeRate.BidAsk = value;
|
||||
if (CurrencyPair.TryParse(prop.Name, out var pair))
|
||||
{
|
||||
exchangeRate.CurrencyPair = pair;
|
||||
exchangeRates.Add(exchangeRate);
|
||||
}
|
||||
}
|
||||
return exchangeRates;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task TestAuthAsync()
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/blockchain/tx_price/BTCUSD/8a3b4394ba811a9e2b0bbf3cc56888d053ea21909299b2703cdc35e156c860ff");
|
||||
var auth = Authenticator;
|
||||
if (auth != null)
|
||||
{
|
||||
await auth.AddHeader(request);
|
||||
}
|
||||
var resp = await HttpClient.SendAsync(request);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<GetRateLimitsResponse> GetRateLimitsAsync()
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Get, "https://apiv2.bitcoinaverage.com/info/ratelimits");
|
||||
var auth = Authenticator;
|
||||
if (auth != null)
|
||||
{
|
||||
await auth.AddHeader(request);
|
||||
}
|
||||
var resp = await HttpClient.SendAsync(request);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var jobj = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
||||
var response = new GetRateLimitsResponse();
|
||||
response.CounterReset = TimeSpan.FromSeconds(jobj["counter_reset"].Value<int>());
|
||||
var totalPeriod = jobj["total_period"].Value<string>();
|
||||
if (totalPeriod == "24h")
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromHours(24);
|
||||
}
|
||||
else if (totalPeriod == "30d")
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromDays(30);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.TotalPeriod = TimeSpan.FromSeconds(jobj["total_period"].Value<int>());
|
||||
}
|
||||
response.RequestsLeft = jobj["requests_left"].Value<int>();
|
||||
response.RequestsPerPeriod = jobj["requests_per_period"].Value<int>();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
public class GetRateLimitsResponse
|
||||
{
|
||||
public TimeSpan CounterReset { get; set; }
|
||||
public int RequestsLeft { get; set; }
|
||||
public int RequestsPerPeriod { get; set; }
|
||||
public TimeSpan TotalPeriod { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
public class CoinAverageSettingsAuthenticator : ICoinAverageAuthenticator
|
||||
{
|
||||
CoinAverageSettings _Settings;
|
||||
public CoinAverageSettingsAuthenticator(CoinAverageSettings settings)
|
||||
{
|
||||
_Settings = settings;
|
||||
}
|
||||
public Task AddHeader(HttpRequestMessage message)
|
||||
{
|
||||
return _Settings.AddHeader(message);
|
||||
}
|
||||
}
|
||||
public class CoinAverageSettings : ICoinAverageAuthenticator
|
||||
{
|
||||
private static readonly DateTime _epochUtc = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
||||
|
||||
public (String PublicKey, String PrivateKey)? KeyPair { get; set; }
|
||||
|
||||
public Task AddHeader(HttpRequestMessage message)
|
||||
{
|
||||
var signature = GetCoinAverageSignature();
|
||||
if (signature != null)
|
||||
{
|
||||
message.Headers.Add("X-signature", signature);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public string GetCoinAverageSignature()
|
||||
{
|
||||
var keyPair = KeyPair;
|
||||
if (!keyPair.HasValue)
|
||||
return null;
|
||||
if (string.IsNullOrEmpty(keyPair.Value.PublicKey) || string.IsNullOrEmpty(keyPair.Value.PrivateKey))
|
||||
return null;
|
||||
var timestamp = (int)((DateTime.UtcNow - _epochUtc).TotalSeconds);
|
||||
var payload = timestamp + "." + keyPair.Value.PublicKey;
|
||||
var digestValueBytes = new HMACSHA256(Encoding.ASCII.GetBytes(keyPair.Value.PrivateKey)).ComputeHash(Encoding.ASCII.GetBytes(payload));
|
||||
var digestValueHex = NBitcoin.DataEncoders.Encoders.Hex.EncodeData(digestValueBytes);
|
||||
return payload + "." + digestValueHex;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,24 +158,6 @@ namespace BTCPayServer.Services.Rates
|
||||
Providers.Add(supportedExchange.Id, bgFetcher);
|
||||
}
|
||||
}
|
||||
|
||||
var cache = new MemoryCache(_CacheOptions);
|
||||
foreach (var supportedExchange in GetCoinAverageSupportedExchanges())
|
||||
{
|
||||
if (!Providers.ContainsKey(supportedExchange.Id))
|
||||
{
|
||||
var coinAverage = new CoinAverageRateProvider()
|
||||
{
|
||||
HttpClient = _httpClientFactory.CreateClient("RATEPROVIDER_COINAVERAGE"),
|
||||
Exchange = supportedExchange.Id
|
||||
};
|
||||
var cached = new CachedRateProvider(supportedExchange.Id, coinAverage, cache)
|
||||
{
|
||||
CacheSpan = CacheSpan
|
||||
};
|
||||
Providers.Add(supportedExchange.Id, cached);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerable<AvailableRateProvider> _AvailableRateProviders = null;
|
||||
@@ -192,85 +174,11 @@ namespace BTCPayServer.Services.Rates
|
||||
{
|
||||
availableProviders.TryAdd(exchange.Id, exchange);
|
||||
}
|
||||
foreach (var exchange in GetCoinAverageSupportedExchanges())
|
||||
{
|
||||
availableProviders.TryAdd(exchange.Id, exchange);
|
||||
}
|
||||
_AvailableRateProviders = availableProviders.Values.OrderBy(o => o.Name).ToArray();
|
||||
}
|
||||
return _AvailableRateProviders;
|
||||
}
|
||||
|
||||
internal IEnumerable<AvailableRateProvider> GetCoinAverageSupportedExchanges()
|
||||
{
|
||||
foreach (var item in
|
||||
new[] {
|
||||
(DisplayName: "Idex", Name: "idex"),
|
||||
(DisplayName: "Coinfloor", Name: "coinfloor"),
|
||||
(DisplayName: "Okex", Name: "okex"),
|
||||
(DisplayName: "Bitfinex", Name: "bitfinex"),
|
||||
(DisplayName: "Bittylicious", Name: "bittylicious"),
|
||||
(DisplayName: "BTC Markets", Name: "btcmarkets"),
|
||||
(DisplayName: "Kucoin", Name: "kucoin"),
|
||||
(DisplayName: "IDAX", Name: "idax"),
|
||||
(DisplayName: "Kraken", Name: "kraken"),
|
||||
(DisplayName: "Bit2C", Name: "bit2c"),
|
||||
(DisplayName: "Mercado Bitcoin", Name: "mercado"),
|
||||
(DisplayName: "CEX.IO", Name: "cex"),
|
||||
(DisplayName: "Bitex.la", Name: "bitex"),
|
||||
(DisplayName: "Quoine", Name: "quoine"),
|
||||
(DisplayName: "Stex", Name: "stex"),
|
||||
(DisplayName: "CoinTiger", Name: "cointiger"),
|
||||
(DisplayName: "Poloniex", Name: "poloniex"),
|
||||
(DisplayName: "Zaif", Name: "zaif"),
|
||||
(DisplayName: "Huobi", Name: "huobi"),
|
||||
(DisplayName: "QuickBitcoin", Name: "quickbitcoin"),
|
||||
(DisplayName: "Tidex", Name: "tidex"),
|
||||
(DisplayName: "Tokenomy", Name: "tokenomy"),
|
||||
(DisplayName: "Bitcoin.co.id", Name: "bitcoin_co_id"),
|
||||
(DisplayName: "Kryptono", Name: "kryptono"),
|
||||
(DisplayName: "Bitso", Name: "bitso"),
|
||||
(DisplayName: "Korbit", Name: "korbit"),
|
||||
(DisplayName: "Yobit", Name: "yobit"),
|
||||
(DisplayName: "BitBargain", Name: "bitbargain"),
|
||||
(DisplayName: "Livecoin", Name: "livecoin"),
|
||||
(DisplayName: "Hotbit", Name: "hotbit"),
|
||||
(DisplayName: "Coincheck", Name: "coincheck"),
|
||||
(DisplayName: "Binance", Name: "binance"),
|
||||
(DisplayName: "Bit-Z", Name: "bitz"),
|
||||
(DisplayName: "Coinbase Pro", Name: "coinbasepro"),
|
||||
(DisplayName: "Rock Trading", Name: "rocktrading"),
|
||||
(DisplayName: "Bittrex", Name: "bittrex"),
|
||||
(DisplayName: "BitBay", Name: "bitbay"),
|
||||
(DisplayName: "Tokenize", Name: "tokenize"),
|
||||
(DisplayName: "Hitbtc", Name: "hitbtc"),
|
||||
(DisplayName: "Upbit", Name: "upbit"),
|
||||
(DisplayName: "Bitstamp", Name: "bitstamp"),
|
||||
(DisplayName: "Luno", Name: "luno"),
|
||||
(DisplayName: "Trade.io", Name: "tradeio"),
|
||||
(DisplayName: "LocalBitcoins", Name: "localbitcoins"),
|
||||
(DisplayName: "Independent Reserve", Name: "independentreserve"),
|
||||
(DisplayName: "Coinsquare", Name: "coinsquare"),
|
||||
(DisplayName: "Exmoney", Name: "exmoney"),
|
||||
(DisplayName: "Coinegg", Name: "coinegg"),
|
||||
(DisplayName: "FYB-SG", Name: "fybsg"),
|
||||
(DisplayName: "Cryptonit", Name: "cryptonit"),
|
||||
(DisplayName: "BTCTurk", Name: "btcturk"),
|
||||
(DisplayName: "bitFlyer", Name: "bitflyer"),
|
||||
(DisplayName: "Negocie Coins", Name: "negociecoins"),
|
||||
(DisplayName: "OasisDEX", Name: "oasisdex"),
|
||||
(DisplayName: "CoinMate", Name: "coinmate"),
|
||||
(DisplayName: "BitForex", Name: "bitforex"),
|
||||
(DisplayName: "Bitsquare", Name: "bitsquare"),
|
||||
(DisplayName: "FYB-SE", Name: "fybse"),
|
||||
(DisplayName: "itBit", Name: "itbit"),
|
||||
})
|
||||
{
|
||||
yield return new AvailableRateProvider(item.Name, item.Name, item.DisplayName, $"https://apiv2.bitcoinaverage.com/exchanges/{item.Name}", RateSource.CoinAverage);
|
||||
}
|
||||
yield return new AvailableRateProvider("gdax", "coinbasepro", string.Empty, $"https://apiv2.bitcoinaverage.com/exchanges/coinbasepro", RateSource.CoinAverage);
|
||||
}
|
||||
|
||||
internal IEnumerable<AvailableRateProvider> GetCoinGeckoSupportedExchanges()
|
||||
{
|
||||
return JArray.Parse(CoinGeckoRateProvider.SupportedExchanges).Select(token =>
|
||||
|
||||
@@ -84,74 +84,6 @@ namespace BTCPayServer.Controllers
|
||||
_sshState = sshState;
|
||||
}
|
||||
|
||||
[Route("server/rates")]
|
||||
public async Task<IActionResult> Rates()
|
||||
{
|
||||
var rates = (await _SettingsRepository.GetSettingAsync<RatesSetting>()) ?? new RatesSetting();
|
||||
|
||||
var vm = new RatesViewModel()
|
||||
{
|
||||
PrivateKey = rates.PrivateKey,
|
||||
PublicKey = rates.PublicKey
|
||||
};
|
||||
await FetchRateLimits(vm);
|
||||
return View(vm);
|
||||
}
|
||||
|
||||
private static async Task FetchRateLimits(RatesViewModel vm)
|
||||
{
|
||||
var coinAverage = GetCoinaverageService(vm, false);
|
||||
if (coinAverage != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
vm.RateLimits = await coinAverage.GetRateLimitsAsync();
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
[Route("server/rates")]
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> Rates(RatesViewModel vm)
|
||||
{
|
||||
var rates = (await _SettingsRepository.GetSettingAsync<RatesSetting>()) ?? new RatesSetting();
|
||||
rates.PrivateKey = vm.PrivateKey;
|
||||
rates.PublicKey = vm.PublicKey;
|
||||
try
|
||||
{
|
||||
var service = GetCoinaverageService(vm, true);
|
||||
if (service != null)
|
||||
await service.TestAuthAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
ModelState.AddModelError(nameof(vm.PrivateKey), "Invalid API key pair");
|
||||
}
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
await FetchRateLimits(vm);
|
||||
return View(vm);
|
||||
}
|
||||
await _SettingsRepository.UpdateSetting(rates);
|
||||
TempData[WellKnownTempData.SuccessMessage] = "Rate settings successfully updated";
|
||||
return RedirectToAction(nameof(Rates));
|
||||
}
|
||||
|
||||
private static CoinAverageRateProvider GetCoinaverageService(RatesViewModel vm, bool withAuth)
|
||||
{
|
||||
var settings = new CoinAverageSettings()
|
||||
{
|
||||
KeyPair = (vm.PublicKey, vm.PrivateKey)
|
||||
};
|
||||
if (!withAuth || settings.GetCoinAverageSignature() != null)
|
||||
{
|
||||
return new CoinAverageRateProvider()
|
||||
{ Authenticator = settings };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
[Route("server/users")]
|
||||
public IActionResult ListUsers(int skip = 0, int count = 50)
|
||||
{
|
||||
|
||||
@@ -29,14 +29,11 @@ namespace BTCPayServer.HostedServices
|
||||
}
|
||||
}
|
||||
private SettingsRepository _SettingsRepository;
|
||||
private CoinAverageSettings _coinAverageSettings;
|
||||
RateProviderFactory _RateProviderFactory;
|
||||
public RatesHostedService(SettingsRepository repo,
|
||||
RateProviderFactory rateProviderFactory,
|
||||
CoinAverageSettings coinAverageSettings)
|
||||
RateProviderFactory rateProviderFactory)
|
||||
{
|
||||
this._SettingsRepository = repo;
|
||||
_coinAverageSettings = coinAverageSettings;
|
||||
_RateProviderFactory = rateProviderFactory;
|
||||
}
|
||||
|
||||
@@ -44,7 +41,6 @@ namespace BTCPayServer.HostedServices
|
||||
{
|
||||
return new Task[]
|
||||
{
|
||||
CreateLoopTask(RefreshCoinAverageSettings),
|
||||
CreateLoopTask(RefreshRates)
|
||||
};
|
||||
}
|
||||
@@ -142,19 +138,5 @@ namespace BTCPayServer.HostedServices
|
||||
.ToList();
|
||||
await _SettingsRepository.UpdateSetting(cache);
|
||||
}
|
||||
|
||||
async Task RefreshCoinAverageSettings()
|
||||
{
|
||||
var rates = (await _SettingsRepository.GetSettingAsync<RatesSetting>()) ?? new RatesSetting();
|
||||
if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey))
|
||||
{
|
||||
_coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
_coinAverageSettings.KeyPair = null;
|
||||
}
|
||||
await _SettingsRepository.WaitSettingsChanged<RatesSetting>(Cancellation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using BTCPayServer.Services.Rates;
|
||||
|
||||
namespace BTCPayServer.Models.ServerViewModels
|
||||
{
|
||||
public class RatesViewModel
|
||||
{
|
||||
[Display(Name = "Bitcoin average api keys")]
|
||||
public string PublicKey { get; set; }
|
||||
public string PrivateKey { get; set; }
|
||||
public GetRateLimitsResponse RateLimits { get; internal set; }
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
@model BTCPayServer.Models.ServerViewModels.RatesViewModel
|
||||
@{
|
||||
ViewData.SetActivePageAndTitle(ServerNavPages.Rates);
|
||||
}
|
||||
|
||||
|
||||
<partial name="_StatusMessage" />
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div asp-validation-summary="All" class="text-danger"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="form-group">
|
||||
<h5>Bitcoin Average</h5>
|
||||
<p>BTCPay relies on Bitcoin Average for getting crypto-currency to fiat rates</p>
|
||||
<p>If you want BTCPay's rate cache to be smaller than 15min, you should register to BitcoinAverage and get a paid API Key.</p>
|
||||
<p>If your BTCPay server instance supports many merchants or is used a lot, BitcoinAverage will rate limit your server and invoices will be created using less accurate rates. (From Bitpay)<br /></p>
|
||||
</div>
|
||||
<form method="post">
|
||||
<label asp-for="PublicKey"></label>
|
||||
<div class="form-inline">
|
||||
<input asp-for="PublicKey" style="width:50%;" class="form-control" placeholder="Public key" />
|
||||
<label class="sr-only" asp-for="PrivateKey"></label>
|
||||
<input asp-for="PrivateKey" style="width:50%;" class="form-control" placeholder="Private key" />
|
||||
<p class="form-text text-muted">You can find the information on the <a target="_blank" href="https://bitcoinaverage.com/en/apikeys">bitcoinaverage api key page</a></p>
|
||||
</div>
|
||||
@if(Model.RateLimits != null)
|
||||
{
|
||||
<h5>Current Bitcoin Average Quotas:</h5>
|
||||
<table class="table table-sm removetopborder">
|
||||
<tr>
|
||||
<th>Quota period</th>
|
||||
<td>@Model.RateLimits.TotalPeriod.TimeString()</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Requests quota</th>
|
||||
<td>@Model.RateLimits.RequestsLeft/@Model.RateLimits.RequestsPerPeriod</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Quota reset in</th>
|
||||
<td>@Model.RateLimits.CounterReset.TimeString()</td>
|
||||
</tr>
|
||||
</table>
|
||||
}
|
||||
<button type="submit" class="btn btn-primary" name="command" value="Save">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@section Scripts {
|
||||
@await Html.PartialAsync("_ValidationScriptsPartial")
|
||||
}
|
||||
@@ -7,6 +7,6 @@ namespace BTCPayServer.Views.Server
|
||||
{
|
||||
public enum ServerNavPages
|
||||
{
|
||||
Index, Users, Rates, Emails, Policies, Theme, Services, Maintenance, Logs, Files
|
||||
Index, Users, Emails, Policies, Theme, Services, Maintenance, Logs, Files
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user