diff --git a/BTCPayServer.Tests/RateRulesTest.cs b/BTCPayServer.Tests/RateRulesTest.cs index d82b287d1..f12736855 100644 --- a/BTCPayServer.Tests/RateRulesTest.cs +++ b/BTCPayServer.Tests/RateRulesTest.cs @@ -113,12 +113,21 @@ namespace BTCPayServer.Tests builder.AppendLine("DOGE_BTC = 2000"); Assert.True(RateRules.TryParse(builder.ToString(), out rules)); rules.GlobalMultiplier = 1.1m; + rule2 = rules.GetRuleFor(CurrencyPair.Parse("DOGE_USD")); Assert.Equal("(2000 * (-3 + coinbase(BTC_CAD) + 50 - 5)) * 1.1", rule2.ToString()); rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_CAD"), 1000m); Assert.True(rule2.Reevaluate()); Assert.Equal("(2000 * (-3 + 1000 + 50 - 5)) * 1.1", rule2.ToString(true)); Assert.Equal((2000m * (-3m + 1000m + 50m - 5m)) * 1.1m, rule2.Value.Value); + + // Test inverse + rule2 = rules.GetRuleFor(CurrencyPair.Parse("USD_DOGE")); + Assert.Equal("(1 / (2000 * (-3 + coinbase(BTC_CAD) + 50 - 5))) * 1.1", rule2.ToString()); + rule2.ExchangeRates.SetRate("coinbase", CurrencyPair.Parse("BTC_CAD"), 1000m); + Assert.True(rule2.Reevaluate()); + Assert.Equal("(1 / (2000 * (-3 + 1000 + 50 - 5))) * 1.1", rule2.ToString(true)); + Assert.Equal(( 1.0m / (2000m * (-3m + 1000m + 50m - 5m))) * 1.1m, rule2.Value.Value); //////// } } diff --git a/BTCPayServer/BTCPayServer.csproj b/BTCPayServer/BTCPayServer.csproj index cd4e8d50f..2b36f82d5 100644 --- a/BTCPayServer/BTCPayServer.csproj +++ b/BTCPayServer/BTCPayServer.csproj @@ -2,7 +2,7 @@ Exe netcoreapp2.0 - 1.0.2.2 + 1.0.2.3 NU1701,CA1816,CA1308,CA1810,CA2208 diff --git a/BTCPayServer/Rating/CurrencyPair.cs b/BTCPayServer/Rating/CurrencyPair.cs index 7ba9cfe5a..c9a6c6e9e 100644 --- a/BTCPayServer/Rating/CurrencyPair.cs +++ b/BTCPayServer/Rating/CurrencyPair.cs @@ -90,5 +90,10 @@ namespace BTCPayServer.Rating { return $"{Left}_{Right}"; } + + public CurrencyPair Inverse() + { + return new CurrencyPair(Right, Left); + } } } diff --git a/BTCPayServer/Rating/RateRules.cs b/BTCPayServer/Rating/RateRules.cs index 61772a14f..b7c7f20bc 100644 --- a/BTCPayServer/Rating/RateRules.cs +++ b/BTCPayServer/Rating/RateRules.cs @@ -133,28 +133,30 @@ namespace BTCPayServer.Rating if (currencyPair.Left == "X" || currencyPair.Right == "X") throw new ArgumentException(paramName: nameof(currencyPair), message: "Invalid X currency"); var candidate = FindBestCandidate(currencyPair); - if (GlobalMultiplier != decimal.One) { candidate = CreateExpression($"({candidate}) * {GlobalMultiplier.ToString(CultureInfo.InvariantCulture)}"); } return new RateRule(this, currencyPair, candidate); } - + public ExpressionSyntax FindBestCandidate(CurrencyPair p) { - var candidates = new List<(CurrencyPair Pair, int Prioriy, ExpressionSyntax Expression)>(); + var invP = p.Inverse(); + var candidates = new List<(CurrencyPair Pair, int Prioriy, ExpressionSyntax Expression, bool Inverse)>(); foreach (var pair in new[] { - (Pair: p, Priority: 0), - (Pair: new CurrencyPair(p.Left, "X"), Priority: 1), - (Pair: new CurrencyPair("X", p.Right), Priority: 1), - (Pair: new CurrencyPair("X", "X"), Priority: 2) + (Pair: p, Priority: 0, Inverse: false), + (Pair: new CurrencyPair(p.Left, "X"), Priority: 1, Inverse: false), + (Pair: new CurrencyPair("X", p.Right), Priority: 1, Inverse: false), + (Pair: new CurrencyPair(invP.Left, "X"), Priority: 2, Inverse: true), + (Pair: new CurrencyPair("X", invP.Right), Priority: 2, Inverse: true), + (Pair: new CurrencyPair("X", "X"), Priority: 3, Inverse: false) }) { if (ruleList.ExpressionsByPair.TryGetValue(pair.Pair, out var expression)) { - candidates.Add((pair.Pair, pair.Priority, expression.Expression)); + candidates.Add((pair.Pair, pair.Priority, expression.Expression, pair.Inverse)); } } if (candidates.Count == 0) @@ -163,8 +165,9 @@ namespace BTCPayServer.Rating .OrderBy(c => c.Prioriy) .ThenBy(c => c.Expression.Span.Start) .First(); - - return best.Expression; + return best.Inverse + ? CreateExpression($"1 / {invP}") + : best.Expression; } internal static ExpressionSyntax CreateExpression(string str) @@ -364,7 +367,7 @@ namespace BTCPayServer.Rating string _ExchangeName = null; public List Errors = new List(); - const int MaxNestedCount = 6; + const int MaxNestedCount = 8; public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node) { if (CurrencyPair.TryParse(node.Identifier.ValueText, out var currentPair)) diff --git a/BTCPayServer/Services/Invoices/InvoiceEntity.cs b/BTCPayServer/Services/Invoices/InvoiceEntity.cs index e30181706..960aca473 100644 --- a/BTCPayServer/Services/Invoices/InvoiceEntity.cs +++ b/BTCPayServer/Services/Invoices/InvoiceEntity.cs @@ -365,14 +365,14 @@ namespace BTCPayServer.Services.Invoices var scheme = info.Network.UriScheme; cryptoInfo.Url = ServerUrl.WithTrailingSlash() + $"i/{paymentId}/{Id}"; - if (paymentId.PaymentType == PaymentTypes.BTCLike) { + var cryptoSuffix = cryptoInfo.CryptoCode == "BTC" ? "" : "/" + cryptoInfo.CryptoCode; cryptoInfo.PaymentUrls = new NBitpayClient.InvoicePaymentUrls() { - BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={cryptoInfo.Url}", - BIP72b = $"{scheme}:?r={cryptoInfo.Url}", - BIP73 = cryptoInfo.Url, + BIP72 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}&r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}", + BIP72b = $"{scheme}:?r={ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}")}", + BIP73 = ServerUrl.WithTrailingSlash() + ($"i/{Id}{cryptoSuffix}"), BIP21 = $"{scheme}:{cryptoInfo.Address}?amount={cryptoInfo.Due}", }; }