mirror of
https://github.com/aljazceru/btcpayserver.git
synced 2025-12-17 22:14:26 +01:00
Calculate rate properly per crypto
This commit is contained in:
@@ -459,11 +459,11 @@ namespace BTCPayServer.Tests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void CheckRatesProvider()
|
public void CheckRatesProvider()
|
||||||
{
|
{
|
||||||
var coinAverage = new CoinAverageRateProvider();
|
var coinAverage = new CoinAverageRateProvider("BTC");
|
||||||
var jpy = coinAverage.GetRateAsync("JPY").GetAwaiter().GetResult();
|
var jpy = coinAverage.GetRateAsync("JPY").GetAwaiter().GetResult();
|
||||||
var jpy2 = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/"))).GetRateAsync("JPY").GetAwaiter().GetResult();
|
var jpy2 = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/"))).GetRateAsync("JPY").GetAwaiter().GetResult();
|
||||||
|
|
||||||
var cached = new CachedRateProvider(coinAverage, new MemoryCache(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }));
|
var cached = new CachedRateProvider("BTC", coinAverage, new MemoryCache(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }));
|
||||||
cached.CacheSpan = TimeSpan.FromSeconds(10);
|
cached.CacheSpan = TimeSpan.FromSeconds(10);
|
||||||
var a = cached.GetRateAsync("JPY").GetAwaiter().GetResult();
|
var a = cached.GetRateAsync("JPY").GetAwaiter().GetResult();
|
||||||
var b = cached.GetRateAsync("JPY").GetAwaiter().GetResult();
|
var b = cached.GetRateAsync("JPY").GetAwaiter().GetResult();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Services.Rates;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
|
||||||
namespace BTCPayServer
|
namespace BTCPayServer
|
||||||
@@ -12,7 +13,7 @@ namespace BTCPayServer
|
|||||||
public string CryptoCode { get; internal set; }
|
public string CryptoCode { get; internal set; }
|
||||||
public string BlockExplorerLink { get; internal set; }
|
public string BlockExplorerLink { get; internal set; }
|
||||||
public string UriScheme { get; internal set; }
|
public string UriScheme { get; internal set; }
|
||||||
|
public IRateProvider DefaultRateProvider { get; set; }
|
||||||
|
|
||||||
[Obsolete("Should not be needed")]
|
[Obsolete("Should not be needed")]
|
||||||
public bool IsBTC
|
public bool IsBTC
|
||||||
|
|||||||
@@ -2,7 +2,10 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BTCPayServer.Services.Rates;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using NBitcoin;
|
using NBitcoin;
|
||||||
|
using NBitpayClient;
|
||||||
|
|
||||||
namespace BTCPayServer
|
namespace BTCPayServer
|
||||||
{
|
{
|
||||||
@@ -15,6 +18,11 @@ namespace BTCPayServer
|
|||||||
Dictionary<string, BTCPayNetwork> _Networks = new Dictionary<string, BTCPayNetwork>();
|
Dictionary<string, BTCPayNetwork> _Networks = new Dictionary<string, BTCPayNetwork>();
|
||||||
public BTCPayNetworkProvider(Network network)
|
public BTCPayNetworkProvider(Network network)
|
||||||
{
|
{
|
||||||
|
var coinaverage = new CoinAverageRateProvider("BTC");
|
||||||
|
var bitpay = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/")));
|
||||||
|
var btcRate = new FallbackRateProvider(new IRateProvider[] { coinaverage, bitpay });
|
||||||
|
|
||||||
|
var ltcRate = new CoinAverageRateProvider("LTC");
|
||||||
if (network == Network.Main)
|
if (network == Network.Main)
|
||||||
{
|
{
|
||||||
Add(new BTCPayNetwork()
|
Add(new BTCPayNetwork()
|
||||||
@@ -23,6 +31,7 @@ namespace BTCPayServer
|
|||||||
BlockExplorerLink = "https://www.smartbit.com.au/tx/{0}",
|
BlockExplorerLink = "https://www.smartbit.com.au/tx/{0}",
|
||||||
NBitcoinNetwork = Network.Main,
|
NBitcoinNetwork = Network.Main,
|
||||||
UriScheme = "bitcoin",
|
UriScheme = "bitcoin",
|
||||||
|
DefaultRateProvider = btcRate
|
||||||
});
|
});
|
||||||
Add(new BTCPayNetwork()
|
Add(new BTCPayNetwork()
|
||||||
{
|
{
|
||||||
@@ -30,6 +39,7 @@ namespace BTCPayServer
|
|||||||
BlockExplorerLink = "https://live.blockcypher.com/ltc/tx/{0}/",
|
BlockExplorerLink = "https://live.blockcypher.com/ltc/tx/{0}/",
|
||||||
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Mainnet,
|
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Mainnet,
|
||||||
UriScheme = "litecoin",
|
UriScheme = "litecoin",
|
||||||
|
DefaultRateProvider = ltcRate
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,6 +51,7 @@ namespace BTCPayServer
|
|||||||
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
|
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
|
||||||
NBitcoinNetwork = Network.TestNet,
|
NBitcoinNetwork = Network.TestNet,
|
||||||
UriScheme = "bitcoin",
|
UriScheme = "bitcoin",
|
||||||
|
DefaultRateProvider = btcRate
|
||||||
});
|
});
|
||||||
Add(new BTCPayNetwork()
|
Add(new BTCPayNetwork()
|
||||||
{
|
{
|
||||||
@@ -48,6 +59,7 @@ namespace BTCPayServer
|
|||||||
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
|
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
|
||||||
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Testnet,
|
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Testnet,
|
||||||
UriScheme = "litecoin",
|
UriScheme = "litecoin",
|
||||||
|
DefaultRateProvider = ltcRate
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +70,8 @@ namespace BTCPayServer
|
|||||||
CryptoCode = "BTC",
|
CryptoCode = "BTC",
|
||||||
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
|
BlockExplorerLink = "https://testnet.smartbit.com.au/tx/{0}",
|
||||||
NBitcoinNetwork = Network.RegTest,
|
NBitcoinNetwork = Network.RegTest,
|
||||||
UriScheme = "bitcoin"
|
UriScheme = "bitcoin",
|
||||||
|
DefaultRateProvider = btcRate
|
||||||
});
|
});
|
||||||
Add(new BTCPayNetwork()
|
Add(new BTCPayNetwork()
|
||||||
{
|
{
|
||||||
@@ -66,6 +79,7 @@ namespace BTCPayServer
|
|||||||
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
|
BlockExplorerLink = "http://explorer.litecointools.com/tx/{0}",
|
||||||
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Regtest,
|
NBitcoinNetwork = NBXplorer.Altcoins.Litecoin.Networks.Regtest,
|
||||||
UriScheme = "litecoin",
|
UriScheme = "litecoin",
|
||||||
|
DefaultRateProvider = ltcRate
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
InvoiceRepository _InvoiceRepository;
|
InvoiceRepository _InvoiceRepository;
|
||||||
BTCPayWallet _Wallet;
|
BTCPayWallet _Wallet;
|
||||||
IRateProvider _RateProvider;
|
IRateProviderFactory _RateProviders;
|
||||||
StoreRepository _StoreRepository;
|
StoreRepository _StoreRepository;
|
||||||
UserManager<ApplicationUser> _UserManager;
|
UserManager<ApplicationUser> _UserManager;
|
||||||
IFeeProviderFactory _FeeProviderFactory;
|
IFeeProviderFactory _FeeProviderFactory;
|
||||||
@@ -58,7 +58,7 @@ namespace BTCPayServer.Controllers
|
|||||||
CurrencyNameTable currencyNameTable,
|
CurrencyNameTable currencyNameTable,
|
||||||
UserManager<ApplicationUser> userManager,
|
UserManager<ApplicationUser> userManager,
|
||||||
BTCPayWallet wallet,
|
BTCPayWallet wallet,
|
||||||
IRateProvider rateProvider,
|
IRateProviderFactory rateProviders,
|
||||||
StoreRepository storeRepository,
|
StoreRepository storeRepository,
|
||||||
EventAggregator eventAggregator,
|
EventAggregator eventAggregator,
|
||||||
BTCPayNetworkProvider networkProvider,
|
BTCPayNetworkProvider networkProvider,
|
||||||
@@ -70,7 +70,7 @@ namespace BTCPayServer.Controllers
|
|||||||
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
_StoreRepository = storeRepository ?? throw new ArgumentNullException(nameof(storeRepository));
|
||||||
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
_InvoiceRepository = invoiceRepository ?? throw new ArgumentNullException(nameof(invoiceRepository));
|
||||||
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
_Wallet = wallet ?? throw new ArgumentNullException(nameof(wallet));
|
||||||
_RateProvider = rateProvider ?? throw new ArgumentNullException(nameof(rateProvider));
|
_RateProviders = rateProviders ?? throw new ArgumentNullException(nameof(rateProviders));
|
||||||
_UserManager = userManager;
|
_UserManager = userManager;
|
||||||
_FeeProviderFactory = feeProviderFactory ?? throw new ArgumentNullException(nameof(feeProviderFactory));
|
_FeeProviderFactory = feeProviderFactory ?? throw new ArgumentNullException(nameof(feeProviderFactory));
|
||||||
_EventAggregator = eventAggregator;
|
_EventAggregator = eventAggregator;
|
||||||
@@ -122,7 +122,7 @@ namespace BTCPayServer.Controllers
|
|||||||
{
|
{
|
||||||
network = derivationStrategy.Network,
|
network = derivationStrategy.Network,
|
||||||
getFeeRate = _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network).GetFeeRateAsync(),
|
getFeeRate = _FeeProviderFactory.CreateFeeProvider(derivationStrategy.Network).GetFeeRateAsync(),
|
||||||
getRate = _RateProvider.GetRateAsync(invoice.Currency),
|
getRate = _RateProviders.GetRateProvider(derivationStrategy.Network).GetRateAsync(invoice.Currency),
|
||||||
getAddress = _Wallet.ReserveAddressAsync(derivationStrategy)
|
getAddress = _Wallet.ReserveAddressAsync(derivationStrategy)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ namespace BTCPayServer.HostedServices
|
|||||||
var alreadyAccounted = new HashSet<OutPoint>(invoice.Payments.Select(p => p.Outpoint));
|
var alreadyAccounted = new HashSet<OutPoint>(invoice.Payments.Select(p => p.Outpoint));
|
||||||
foreach (var coin in coins.Coins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
|
foreach (var coin in coins.Coins.Where(c => !alreadyAccounted.Contains(c.Outpoint)))
|
||||||
{
|
{
|
||||||
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin).ConfigureAwait(false);
|
var payment = await _InvoiceRepository.AddPayment(invoice.Id, coin, coins.Strategy.Network.CryptoCode).ConfigureAwait(false);
|
||||||
invoice.Payments.Add(payment);
|
invoice.Payments.Add(payment);
|
||||||
context.Events.Add(new InvoicePaymentEvent(invoice.Id));
|
context.Events.Add(new InvoicePaymentEvent(invoice.Id));
|
||||||
dirtyAddress = true;
|
dirtyAddress = true;
|
||||||
|
|||||||
@@ -155,12 +155,7 @@ namespace BTCPayServer.Hosting
|
|||||||
else
|
else
|
||||||
return new Bitpay(new Key(), new Uri("https://test.bitpay.com/"));
|
return new Bitpay(new Key(), new Uri("https://test.bitpay.com/"));
|
||||||
});
|
});
|
||||||
services.TryAddSingleton<IRateProvider>(o =>
|
services.TryAddSingleton<IRateProviderFactory, CachedDefaultRateProviderFactory>();
|
||||||
{
|
|
||||||
var coinaverage = new CoinAverageRateProvider();
|
|
||||||
var bitpay = new BitpayRateProvider(new Bitpay(new Key(), new Uri("https://bitpay.com/")));
|
|
||||||
return new CachedRateProvider(new FallbackRateProvider(new IRateProvider[] { coinaverage, bitpay }), o.GetRequiredService<IMemoryCache>()) { CacheSpan = TimeSpan.FromMinutes(1.0) };
|
|
||||||
});
|
|
||||||
|
|
||||||
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
|
services.TryAddScoped<IHttpContextAccessor, HttpContextAccessor>();
|
||||||
services.TryAddSingleton<IAuthorizationHandler, OwnStoreHandler>();
|
services.TryAddSingleton<IAuthorizationHandler, OwnStoreHandler>();
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
AddToTextSearch(invoiceId, addresses.Select(a => a.ToString()).ToArray());
|
AddToTextSearch(invoiceId, addresses.Select(a => a.ToString()).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PaymentEntity> AddPayment(string invoiceId, Coin receivedCoin)
|
public async Task<PaymentEntity> AddPayment(string invoiceId, Coin receivedCoin, string cryptoCode)
|
||||||
{
|
{
|
||||||
using (var context = _ContextFactory.CreateContext())
|
using (var context = _ContextFactory.CreateContext())
|
||||||
{
|
{
|
||||||
@@ -428,6 +428,7 @@ namespace BTCPayServer.Services.Invoices
|
|||||||
Outpoint = receivedCoin.Outpoint,
|
Outpoint = receivedCoin.Outpoint,
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
Output = receivedCoin.TxOut,
|
Output = receivedCoin.TxOut,
|
||||||
|
CryptoCode = cryptoCode,
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
ReceivedTime = DateTime.UtcNow
|
ReceivedTime = DateTime.UtcNow
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services.Rates
|
||||||
|
{
|
||||||
|
public class CachedDefaultRateProviderFactory : IRateProviderFactory
|
||||||
|
{
|
||||||
|
IMemoryCache _Cache;
|
||||||
|
ConcurrentDictionary<string, IRateProvider> _Providers = new ConcurrentDictionary<string, IRateProvider>();
|
||||||
|
public CachedDefaultRateProviderFactory(IMemoryCache cache)
|
||||||
|
{
|
||||||
|
if (cache == null)
|
||||||
|
throw new ArgumentNullException(nameof(cache));
|
||||||
|
_Cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TimeSpan CacheSpan { get; set; } = TimeSpan.FromMinutes(1.0);
|
||||||
|
public IRateProvider GetRateProvider(BTCPayNetwork network)
|
||||||
|
{
|
||||||
|
return _Providers.GetOrAdd(network.CryptoCode, new CachedRateProvider(network.CryptoCode, network.DefaultRateProvider, _Cache) { CacheSpan = CacheSpan });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,8 +10,9 @@ namespace BTCPayServer.Services.Rates
|
|||||||
{
|
{
|
||||||
private IRateProvider _Inner;
|
private IRateProvider _Inner;
|
||||||
private IMemoryCache _MemoryCache;
|
private IMemoryCache _MemoryCache;
|
||||||
|
private string _CryptoCode;
|
||||||
|
|
||||||
public CachedRateProvider(IRateProvider inner, IMemoryCache memoryCache)
|
public CachedRateProvider(string cryptoCode, IRateProvider inner, IMemoryCache memoryCache)
|
||||||
{
|
{
|
||||||
if (inner == null)
|
if (inner == null)
|
||||||
throw new ArgumentNullException(nameof(inner));
|
throw new ArgumentNullException(nameof(inner));
|
||||||
@@ -19,6 +20,7 @@ namespace BTCPayServer.Services.Rates
|
|||||||
throw new ArgumentNullException(nameof(memoryCache));
|
throw new ArgumentNullException(nameof(memoryCache));
|
||||||
this._Inner = inner;
|
this._Inner = inner;
|
||||||
this._MemoryCache = memoryCache;
|
this._MemoryCache = memoryCache;
|
||||||
|
this._CryptoCode = cryptoCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TimeSpan CacheSpan
|
public TimeSpan CacheSpan
|
||||||
@@ -29,7 +31,7 @@ namespace BTCPayServer.Services.Rates
|
|||||||
|
|
||||||
public Task<decimal> GetRateAsync(string currency)
|
public Task<decimal> GetRateAsync(string currency)
|
||||||
{
|
{
|
||||||
return _MemoryCache.GetOrCreateAsync("CURR_" + currency, (ICacheEntry entry) =>
|
return _MemoryCache.GetOrCreateAsync("CURR_" + currency + "_" + _CryptoCode, (ICacheEntry entry) =>
|
||||||
{
|
{
|
||||||
entry.AbsoluteExpiration = DateTimeOffset.UtcNow + CacheSpan;
|
entry.AbsoluteExpiration = DateTimeOffset.UtcNow + CacheSpan;
|
||||||
return _Inner.GetRateAsync(currency);
|
return _Inner.GetRateAsync(currency);
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ namespace BTCPayServer.Services.Rates
|
|||||||
{
|
{
|
||||||
static HttpClient _Client = new HttpClient();
|
static HttpClient _Client = new HttpClient();
|
||||||
|
|
||||||
|
public CoinAverageRateProvider(string cryptoCode)
|
||||||
|
{
|
||||||
|
CryptoCode = cryptoCode ?? "BTC";
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CryptoCode { get; set; }
|
||||||
|
|
||||||
public string Market
|
public string Market
|
||||||
{
|
{
|
||||||
get; set;
|
get; set;
|
||||||
@@ -51,8 +58,8 @@ namespace BTCPayServer.Services.Rates
|
|||||||
resp.EnsureSuccessStatusCode();
|
resp.EnsureSuccessStatusCode();
|
||||||
var rates = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
var rates = JObject.Parse(await resp.Content.ReadAsStringAsync());
|
||||||
return rates.Properties()
|
return rates.Properties()
|
||||||
.Where(p => p.Name.StartsWith("BTC", StringComparison.OrdinalIgnoreCase))
|
.Where(p => p.Name.StartsWith(CryptoCode, StringComparison.OrdinalIgnoreCase))
|
||||||
.ToDictionary(p => p.Name.Substring(3, 3), p => ToDecimal(p.Value["last"]));
|
.ToDictionary(p => p.Name.Substring(CryptoCode.Length, p.Name.Length - CryptoCode.Length), p => ToDecimal(p.Value["last"]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
12
BTCPayServer/Services/Rates/IRateProviderFactory.cs
Normal file
12
BTCPayServer/Services/Rates/IRateProviderFactory.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace BTCPayServer.Services.Rates
|
||||||
|
{
|
||||||
|
public interface IRateProviderFactory
|
||||||
|
{
|
||||||
|
IRateProvider GetRateProvider(BTCPayNetwork network);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
{
|
{
|
||||||
<tr>
|
<tr>
|
||||||
<td>@address.GetCryptoCode()</td>
|
<td>@address.GetCryptoCode()</td>
|
||||||
<td>@address.Address</td>
|
<td>@address.GetAddress()</td>
|
||||||
<td>@(!address.UnAssigned.HasValue)</td>
|
<td>@(!address.UnAssigned.HasValue)</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user