diff --git a/BTCPayServer.Rating/Providers/CoinAverageRateProvider.cs b/BTCPayServer.Rating/Providers/CoinAverageRateProvider.cs deleted file mode 100644 index 960d5d290..000000000 --- a/BTCPayServer.Rating/Providers/CoinAverageRateProvider.cs +++ /dev/null @@ -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(), 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(), System.Globalization.NumberStyles.AllowExponent | System.Globalization.NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var v1) || - !decimal.TryParse(ask.Value(), 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 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 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()); - var totalPeriod = jobj["total_period"].Value(); - 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()); - } - response.RequestsLeft = jobj["requests_left"].Value(); - response.RequestsPerPeriod = jobj["requests_per_period"].Value(); - 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; } - } -} diff --git a/BTCPayServer.Rating/Providers/CoinAverageSettings.cs b/BTCPayServer.Rating/Providers/CoinAverageSettings.cs deleted file mode 100644 index 29d19004f..000000000 --- a/BTCPayServer.Rating/Providers/CoinAverageSettings.cs +++ /dev/null @@ -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; - } - } -} diff --git a/BTCPayServer.Rating/Services/RateProviderFactory.cs b/BTCPayServer.Rating/Services/RateProviderFactory.cs index 55208fe83..bf2d3ad81 100644 --- a/BTCPayServer.Rating/Services/RateProviderFactory.cs +++ b/BTCPayServer.Rating/Services/RateProviderFactory.cs @@ -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 _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 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 GetCoinGeckoSupportedExchanges() { return JArray.Parse(CoinGeckoRateProvider.SupportedExchanges).Select(token => diff --git a/BTCPayServer/Controllers/ServerController.cs b/BTCPayServer/Controllers/ServerController.cs index 066b50a81..7455f0d80 100644 --- a/BTCPayServer/Controllers/ServerController.cs +++ b/BTCPayServer/Controllers/ServerController.cs @@ -84,74 +84,6 @@ namespace BTCPayServer.Controllers _sshState = sshState; } - [Route("server/rates")] - public async Task Rates() - { - var rates = (await _SettingsRepository.GetSettingAsync()) ?? 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 Rates(RatesViewModel vm) - { - var rates = (await _SettingsRepository.GetSettingAsync()) ?? 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) { diff --git a/BTCPayServer/HostedServices/RatesHostedService.cs b/BTCPayServer/HostedServices/RatesHostedService.cs index 1b091bb88..801ce9a09 100644 --- a/BTCPayServer/HostedServices/RatesHostedService.cs +++ b/BTCPayServer/HostedServices/RatesHostedService.cs @@ -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()) ?? new RatesSetting(); - if (!string.IsNullOrWhiteSpace(rates.PrivateKey) && !string.IsNullOrWhiteSpace(rates.PublicKey)) - { - _coinAverageSettings.KeyPair = (rates.PublicKey, rates.PrivateKey); - } - else - { - _coinAverageSettings.KeyPair = null; - } - await _SettingsRepository.WaitSettingsChanged(Cancellation); - } } } diff --git a/BTCPayServer/Models/ServerViewModels/RatesViewModel.cs b/BTCPayServer/Models/ServerViewModels/RatesViewModel.cs deleted file mode 100644 index 903468cce..000000000 --- a/BTCPayServer/Models/ServerViewModels/RatesViewModel.cs +++ /dev/null @@ -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; } - } -} diff --git a/BTCPayServer/Views/Server/Rates.cshtml b/BTCPayServer/Views/Server/Rates.cshtml deleted file mode 100644 index 14095a9b4..000000000 --- a/BTCPayServer/Views/Server/Rates.cshtml +++ /dev/null @@ -1,56 +0,0 @@ -@model BTCPayServer.Models.ServerViewModels.RatesViewModel -@{ - ViewData.SetActivePageAndTitle(ServerNavPages.Rates); -} - - - - - -
-
-
-
-
-
-
-
-
Bitcoin Average
-

BTCPay relies on Bitcoin Average for getting crypto-currency to fiat rates

-

If you want BTCPay's rate cache to be smaller than 15min, you should register to BitcoinAverage and get a paid API Key.

-

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)

-
-
- -
- - - -

You can find the information on the bitcoinaverage api key page

-
- @if(Model.RateLimits != null) - { -
Current Bitcoin Average Quotas:
- - - - - - - - - - - - - -
Quota period@Model.RateLimits.TotalPeriod.TimeString()
Requests quota@Model.RateLimits.RequestsLeft/@Model.RateLimits.RequestsPerPeriod
Quota reset in@Model.RateLimits.CounterReset.TimeString()
- } - -
-
-
- -@section Scripts { - @await Html.PartialAsync("_ValidationScriptsPartial") -} diff --git a/BTCPayServer/Views/Server/ServerNavPages.cs b/BTCPayServer/Views/Server/ServerNavPages.cs index bf0d098ee..40cb46546 100644 --- a/BTCPayServer/Views/Server/ServerNavPages.cs +++ b/BTCPayServer/Views/Server/ServerNavPages.cs @@ -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 } }