Do not roundup rates

This commit is contained in:
nicolas.dorier
2018-05-13 15:09:17 +09:00
parent 449738414b
commit f7fe855274
3 changed files with 47 additions and 23 deletions

View File

@@ -265,7 +265,7 @@ namespace BTCPayServer.Tests
var user = tester.NewAccount(); var user = tester.NewAccount();
user.GrantAccess(); user.GrantAccess();
user.RegisterDerivationScheme("BTC"); user.RegisterDerivationScheme("BTC");
// Set tolerance to 50% // Set tolerance to 50%
var stores = user.GetController<StoresController>(); var stores = user.GetController<StoresController>();
var vm = Assert.IsType<StoreViewModel>(Assert.IsType<ViewResult>(stores.UpdateStore()).Model); var vm = Assert.IsType<StoreViewModel>(Assert.IsType<ViewResult>(stores.UpdateStore()).Model);
@@ -296,6 +296,22 @@ namespace BTCPayServer.Tests
} }
} }
[Fact]
public void RoundupCurrenciesCorrectly()
{
foreach(var test in new[]
{
(0.0005m, "$0.0005 (USD)"),
(0.001m, "$0.001 (USD)"),
(0.01m, "$0.01 (USD)"),
(0.1m, "$0.10 (USD)"),
})
{
var actual = InvoiceController.FormatCurrency(test.Item1, "USD", new CurrencyNameTable());
Assert.Equal(test.Item2, actual);
}
}
[Fact] [Fact]
public void CanPayUsingBIP70() public void CanPayUsingBIP70()
{ {
@@ -617,7 +633,7 @@ namespace BTCPayServer.Tests
ItemDesc = "Some description", ItemDesc = "Some description",
FullNotifications = true FullNotifications = true
}, Facade.Merchant); }, Facade.Merchant);
var cashCow = tester.ExplorerNode; var cashCow = tester.ExplorerNode;
var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network); var invoiceAddress = BitcoinAddress.Create(invoice.CryptoInfo[0].Address, cashCow.Network);
var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Satoshis(10); var firstPayment = invoice.CryptoInfo[0].TotalDue - Money.Satoshis(10);
@@ -1411,7 +1427,7 @@ namespace BTCPayServer.Tests
{ {
var provider = new BTCPayNetworkProvider(NetworkType.Mainnet); var provider = new BTCPayNetworkProvider(NetworkType.Mainnet);
var factory = CreateBTCPayRateFactory(provider); var factory = CreateBTCPayRateFactory(provider);
foreach (var result in factory foreach (var result in factory
.DirectProviders .DirectProviders
.Select(p => (ExpectedName: p.Key, ResultAsync: p.Value.GetRatesAsync())) .Select(p => (ExpectedName: p.Key, ResultAsync: p.Value.GetRatesAsync()))
@@ -1423,8 +1439,8 @@ namespace BTCPayServer.Tests
Assert.NotEmpty(exchangeRates.ByExchange[result.ExpectedName]); Assert.NotEmpty(exchangeRates.ByExchange[result.ExpectedName]);
// This check if the currency pair is using right currency pair // This check if the currency pair is using right currency pair
Assert.Contains(exchangeRates.ByExchange[result.ExpectedName], Assert.Contains(exchangeRates.ByExchange[result.ExpectedName],
e => ( e.CurrencyPair == new CurrencyPair("BTC", "USD") || e => (e.CurrencyPair == new CurrencyPair("BTC", "USD") ||
e.CurrencyPair == new CurrencyPair("BTC", "EUR") || e.CurrencyPair == new CurrencyPair("BTC", "EUR") ||
e.CurrencyPair == new CurrencyPair("BTC", "USDT")) e.CurrencyPair == new CurrencyPair("BTC", "USDT"))
&& e.Value > 1.0m // 1BTC will always be more than 1USD && e.Value > 1.0m // 1BTC will always be more than 1USD
@@ -1454,7 +1470,7 @@ namespace BTCPayServer.Tests
private static BTCPayRateProviderFactory CreateBTCPayRateFactory(BTCPayNetworkProvider provider) private static BTCPayRateProviderFactory CreateBTCPayRateFactory(BTCPayNetworkProvider provider)
{ {
return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, null, new CoinAverageSettings()); return new BTCPayRateProviderFactory(new MemoryCacheOptions() { ExpirationScanFrequency = TimeSpan.FromSeconds(1.0) }, provider, new CoinAverageSettings());
} }
[Fact] [Fact]

View File

@@ -51,7 +51,7 @@ namespace BTCPayServer.Controllers
StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }), StoreLink = Url.Action(nameof(StoresController.UpdateStore), "Stores", new { storeId = store.Id }),
Id = invoice.Id, Id = invoice.Id,
Status = invoice.Status, Status = invoice.Status,
TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" : TransactionSpeed = invoice.SpeedPolicy == SpeedPolicy.HighSpeed ? "high" :
invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" : invoice.SpeedPolicy == SpeedPolicy.MediumSpeed ? "medium" :
invoice.SpeedPolicy == SpeedPolicy.LowMediumSpeed ? "low-medium" : invoice.SpeedPolicy == SpeedPolicy.LowMediumSpeed ? "low-medium" :
"low", "low",
@@ -61,7 +61,7 @@ namespace BTCPayServer.Controllers
MonitoringDate = invoice.MonitoringExpiration, MonitoringDate = invoice.MonitoringExpiration,
OrderId = invoice.OrderId, OrderId = invoice.OrderId,
BuyerInformation = invoice.BuyerInformation, BuyerInformation = invoice.BuyerInformation,
Fiat = FormatCurrency((decimal)dto.Price, dto.Currency), Fiat = FormatCurrency((decimal)dto.Price, dto.Currency, _CurrencyNameTable),
NotificationUrl = invoice.NotificationURL, NotificationUrl = invoice.NotificationURL,
RedirectUrl = invoice.RedirectURL, RedirectUrl = invoice.RedirectURL,
ProductInformation = invoice.ProductInformation, ProductInformation = invoice.ProductInformation,
@@ -291,11 +291,29 @@ namespace BTCPayServer.Controllers
private string FormatCurrency(PaymentMethod paymentMethod) private string FormatCurrency(PaymentMethod paymentMethod)
{ {
string currency = paymentMethod.ParentEntity.ProductInformation.Currency; string currency = paymentMethod.ParentEntity.ProductInformation.Currency;
return FormatCurrency(paymentMethod.Rate, currency); return FormatCurrency(paymentMethod.Rate, currency, _CurrencyNameTable);
} }
public string FormatCurrency(decimal price, string currency) public static string FormatCurrency(decimal price, string currency, CurrencyNameTable currencies)
{ {
return price.ToString("C", _CurrencyNameTable.GetCurrencyProvider(currency)) + $" ({currency})"; var provider = ((CultureInfo)currencies.GetCurrencyProvider(currency)).NumberFormat;
var currencyData = currencies.GetCurrencyData(currency);
var divisibility = currencyData.Divisibility;
while (true)
{
var rounded = decimal.Round(price, divisibility, MidpointRounding.AwayFromZero);
if ((Math.Abs(rounded - price) / price) < 0.001m)
{
price = rounded;
break;
}
divisibility++;
}
if(divisibility != provider.CurrencyDecimalDigits)
{
provider = (NumberFormatInfo)provider.Clone();
provider.CurrencyDecimalDigits = divisibility;
}
return price.ToString("C", provider) + $" ({currency})";
} }
[HttpGet] [HttpGet]
@@ -430,7 +448,7 @@ namespace BTCPayServer.Controllers
var stores = await _StoreRepository.GetStoresByUserId(GetUserId()); var stores = await _StoreRepository.GetStoresByUserId(GetUserId());
model.Stores = new SelectList(stores, nameof(StoreData.Id), nameof(StoreData.StoreName), model.StoreId); model.Stores = new SelectList(stores, nameof(StoreData.Id), nameof(StoreData.StoreName), model.StoreId);
var store = stores.FirstOrDefault(s => s.Id == model.StoreId); var store = stores.FirstOrDefault(s => s.Id == model.StoreId);
if(store == null) if (store == null)
{ {
ModelState.AddModelError(nameof(model.StoreId), "Store not found"); ModelState.AddModelError(nameof(model.StoreId), "Store not found");
} }

View File

@@ -36,7 +36,6 @@ namespace BTCPayServer.Services.Rates
} }
IMemoryCache _Cache; IMemoryCache _Cache;
private IOptions<MemoryCacheOptions> _CacheOptions; private IOptions<MemoryCacheOptions> _CacheOptions;
CurrencyNameTable _CurrencyTable;
public IMemoryCache Cache public IMemoryCache Cache
{ {
get get
@@ -47,12 +46,10 @@ namespace BTCPayServer.Services.Rates
CoinAverageSettings _CoinAverageSettings; CoinAverageSettings _CoinAverageSettings;
public BTCPayRateProviderFactory(IOptions<MemoryCacheOptions> cacheOptions, public BTCPayRateProviderFactory(IOptions<MemoryCacheOptions> cacheOptions,
BTCPayNetworkProvider btcpayNetworkProvider, BTCPayNetworkProvider btcpayNetworkProvider,
CurrencyNameTable currencyTable,
CoinAverageSettings coinAverageSettings) CoinAverageSettings coinAverageSettings)
{ {
if (cacheOptions == null) if (cacheOptions == null)
throw new ArgumentNullException(nameof(cacheOptions)); throw new ArgumentNullException(nameof(cacheOptions));
_CurrencyTable = currencyTable;
_CoinAverageSettings = coinAverageSettings; _CoinAverageSettings = coinAverageSettings;
_Cache = new MemoryCache(cacheOptions); _Cache = new MemoryCache(cacheOptions);
_CacheOptions = cacheOptions; _CacheOptions = cacheOptions;
@@ -90,7 +87,7 @@ namespace BTCPayServer.Services.Rates
public CoinAverageExchanges GetSupportedExchanges() public CoinAverageExchanges GetSupportedExchanges()
{ {
CoinAverageExchanges exchanges = new CoinAverageExchanges(); CoinAverageExchanges exchanges = new CoinAverageExchanges();
foreach(var exchange in _CoinAverageSettings.AvailableExchanges) foreach (var exchange in _CoinAverageSettings.AvailableExchanges)
{ {
exchanges.Add(exchange.Value); exchanges.Add(exchange.Value);
} }
@@ -180,13 +177,6 @@ namespace BTCPayServer.Services.Rates
} }
rateRule.Reevaluate(); rateRule.Reevaluate();
result.Value = rateRule.Value; result.Value = rateRule.Value;
var currencyData = _CurrencyTable?.GetCurrencyData(rateRule.CurrencyPair.Right);
if(currencyData != null && result.Value.HasValue)
{
result.Value = decimal.Round(result.Value.Value, currencyData.Divisibility, MidpointRounding.AwayFromZero);
}
result.Errors = rateRule.Errors; result.Errors = rateRule.Errors;
result.EvaluatedRule = rateRule.ToString(true); result.EvaluatedRule = rateRule.ToString(true);
result.Rule = rateRule.ToString(false); result.Rule = rateRule.ToString(false);