public void CanParseRateRules()
        {
            // Check happy path
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("// Some cool comments");
            builder.AppendLine("DOGE_X = DOGE_BTC * BTC_X * 1.1");
            builder.AppendLine("DOGE_BTC = Bittrex(DOGE_BTC)");
            builder.AppendLine("// Some other cool comments");
            builder.AppendLine("BTC_usd = kraken(BTC_USD)");
            builder.AppendLine("BTC_X = Coinbase(BTC_X);");
            builder.AppendLine("X_X = CoinAverage(X_X) * 1.02");

            Assert.False(RateRules.TryParse("DPW*&W&#hdi&#&3JJD", out var rules));
            Assert.True(RateRules.TryParse(builder.ToString(), out rules));
            Assert.Equal(
                "// Some cool comments\n" +
                "DOGE_X = DOGE_BTC * BTC_X * 1.1;\n" +
                "DOGE_BTC = bittrex(DOGE_BTC);\n" +
                "// Some other cool comments\n" +
                "BTC_USD = kraken(BTC_USD);\n" +
                "BTC_X = coinbase(BTC_X);\n" +
                "X_X = coinaverage(X_X) * 1.02;",
                rules.ToString());
            var tests = new[]
Esempio n. 2
0
            public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
            {
                var exchangeName = node.Expression.ToString();

                if (exchangeName.StartsWith("ERR_", StringComparison.OrdinalIgnoreCase))
                {
                    Errors.Add(RateRulesErrors.PreprocessError);
                    return(base.VisitInvocationExpression(node));
                }

                var currencyPair = node.ArgumentList.ChildNodes().FirstOrDefault()?.ToString();

                if (currencyPair == null || !CurrencyPair.TryParse(currencyPair, out var pair))
                {
                    Errors.Add(RateRulesErrors.InvalidCurrencyIdentifier);
                    return(RateRules.CreateExpression($"ERR_INVALID_CURRENCY_PAIR({node.ToString()})"));
                }
                else
                {
                    var rate = Rates.GetRate(exchangeName, pair);
                    if (rate == null)
                    {
                        Errors.Add(RateRulesErrors.RateUnavailable);
                        return(RateRules.CreateExpression($"ERR_RATE_UNAVAILABLE({exchangeName}, {pair.ToString()})"));
                    }
                    else
                    {
                        return(RateRules.CreateExpression(rate.ToString()));
                    }
                }
            }
Esempio n. 3
0
        public static RateRule CreateFromExpression(string expression, CurrencyPair currencyPair)
        {
            var ex = RateRules.CreateExpression(expression);

            RateRules.TryParse("", out var rules);
            return(new RateRule(rules, currencyPair, ex));
        }
        public void SecondDuplicatedRuleIsIgnored()
        {
            StringBuilder builder = new StringBuilder();

            builder.AppendLine("DOGE_X = 1.1");
            builder.AppendLine("DOGE_X = 1.2");
            Assert.True(RateRules.TryParse(builder.ToString(), out var rules));
            var rule = rules.GetRuleFor(new CurrencyPair("DOGE", "BTC"));

            rule.Reevaluate();
            Assert.True(!rule.HasError);
            Assert.Equal(1.1m, rule.BidAsk.Ask);
        }
Esempio n. 5
0
            public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
            {
                if (IsInvocation)
                {
                    Errors.Add(RateRulesErrors.InvalidCurrencyIdentifier);
                    return(RateRules.CreateExpression($"ERR_INVALID_CURRENCY_PAIR({node.ToString()})"));
                }
                IsInvocation  = true;
                _ExchangeName = node.Expression.ToString();
                var result = base.VisitInvocationExpression(node);

                IsInvocation = false;
                return(result);
            }
Esempio n. 6
0
 public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
 {
     if (
         (!IsInvocation || IsArgumentList) &&
         CurrencyPair.TryParse(node.Identifier.ValueText, out var currentPair))
     {
         var replacedPair = new CurrencyPair(left: currentPair.Left == "X" ? pair.Left : currentPair.Left,
                                             right: currentPair.Right == "X" ? pair.Right : currentPair.Right);
         if (IsInvocation) // eg. replace bittrex(BTC_X) to bittrex(BTC_USD)
         {
             ExchangeRates.Add(new ExchangeRate()
             {
                 CurrencyPair = replacedPair, Exchange = _ExchangeName
             });
             return(SyntaxFactory.IdentifierName(replacedPair.ToString()));
         }
         else // eg. replace BTC_X to BTC_USD, then replace by the expression for BTC_USD
         {
             var bestCandidate = parent.FindBestCandidate(replacedPair);
             if (nested > MaxNestedCount)
             {
                 Errors.Add(RateRulesErrors.TooMuchNestedCalls);
                 return(RateRules.CreateExpression($"ERR_TOO_MUCH_NESTED_CALLS({replacedPair})"));
             }
             var innerFlatten = CreateNewContext(replacedPair);
             var replaced     = innerFlatten.Visit(bestCandidate);
             if (replaced is ExpressionSyntax expression)
             {
                 var hasBinaryOps = new HasBinaryOperations();
                 hasBinaryOps.Visit(expression);
                 if (hasBinaryOps.Result)
                 {
                     replaced = SyntaxFactory.ParenthesizedExpression(expression);
                 }
             }
             if (Errors.Contains(RateRulesErrors.TooMuchNestedCalls))
             {
                 return(RateRules.CreateExpression($"ERR_TOO_MUCH_NESTED_CALLS({replacedPair})"));
             }
             return(replaced);
         }
     }
     return(base.VisitIdentifierName(node));
 }
Esempio n. 7
0
        public async Task <IActionResult> GetRates2(string cryptoCode = null, string storeId = null)
        {
            cryptoCode = cryptoCode ?? "BTC";
            var network = _NetworkProvider.GetNetwork(cryptoCode);

            if (network == null)
            {
                return(NotFound());
            }

            RateRules rules = null;

            if (storeId != null)
            {
                var store = await _StoreRepo.FindStore(storeId);

                if (store == null)
                {
                    return(NotFound());
                }
                rules = store.GetStoreBlob().GetRateRules();
            }

            var rateProvider = _RateProviderFactory.GetRateProvider(network, rules);

            if (rateProvider == null)
            {
                return(NotFound());
            }

            var allRates = (await rateProvider.GetRatesAsync());

            return(Json(allRates.Select(r =>
                                        new NBitpayClient.Rate()
            {
                Code = r.Currency,
                Name = _CurrencyNameTable.GetCurrencyData(r.Currency)?.Name,
                Value = r.Value
            }).Where(n => n.Name != null).ToArray()));
        }
Esempio n. 8
0
 public IRateProvider ApplyRateRules(BTCPayNetwork network, IRateProvider rateProvider)
 {
     if (!PreferredExchange.IsCoinAverage())
     {
         // If the original rateProvider is a cache, use the same inner provider as fallback, and same memory cache to wrap it all
         if (rateProvider is CachedRateProvider cachedRateProvider)
         {
             rateProvider = new FallbackRateProvider(new IRateProvider[] {
                 new CoinAverageRateProvider(network.CryptoCode)
                 {
                     Exchange = PreferredExchange
                 },
                 cachedRateProvider.Inner
             });
             rateProvider = new CachedRateProvider(network.CryptoCode, rateProvider, cachedRateProvider.MemoryCache)
             {
                 AdditionalScope = PreferredExchange
             };
         }
         else
         {
             rateProvider = new FallbackRateProvider(new IRateProvider[] {
                 new CoinAverageRateProvider(network.CryptoCode)
                 {
                     Exchange = PreferredExchange
                 },
                 rateProvider
             });
         }
     }
     if (RateRules == null || RateRules.Count == 0)
     {
         return(rateProvider);
     }
     return(new TweakRateProvider(network, rateProvider, RateRules.ToList()));
 }
        public async Task <IActionResult> Rates(RatesViewModel model, string command = null, string storeId = null, CancellationToken cancellationToken = default)
        {
            model.SetExchangeRates(GetSupportedExchanges(), model.PreferredExchange);
            model.StoreId = storeId ?? model.StoreId;
            CurrencyPair[] currencyPairs = null;
            try
            {
                currencyPairs = model.DefaultCurrencyPairs?
                                .Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
                                .Select(p => CurrencyPair.Parse(p))
                                .ToArray();
            }
            catch
            {
                ModelState.AddModelError(nameof(model.DefaultCurrencyPairs), "Invalid currency pairs (should be for example: BTC_USD,BTC_CAD,BTC_JPY)");
            }
            if (!ModelState.IsValid)
            {
                return(View(model));
            }
            if (model.PreferredExchange != null)
            {
                model.PreferredExchange = model.PreferredExchange.Trim().ToLowerInvariant();
            }

            var blob = StoreData.GetStoreBlob();

            model.DefaultScript      = blob.GetDefaultRateRules(_NetworkProvider).ToString();
            model.AvailableExchanges = GetSupportedExchanges();

            blob.PreferredExchange    = model.PreferredExchange;
            blob.Spread               = (decimal)model.Spread / 100.0m;
            blob.DefaultCurrencyPairs = currencyPairs;
            if (!model.ShowScripting)
            {
                if (!GetSupportedExchanges().Select(c => c.Name).Contains(blob.PreferredExchange, StringComparer.OrdinalIgnoreCase))
                {
                    ModelState.AddModelError(nameof(model.PreferredExchange), $"Unsupported exchange ({model.RateSource})");
                    return(View(model));
                }
            }
            RateRules rules = null;

            if (model.ShowScripting)
            {
                if (!RateRules.TryParse(model.Script, out rules, out var errors))
                {
                    errors = errors ?? new List <RateRulesErrors>();
                    var errorString = String.Join(", ", errors.ToArray());
                    ModelState.AddModelError(nameof(model.Script), $"Parsing error ({errorString})");
                    return(View(model));
                }
                else
                {
                    blob.RateScript = rules.ToString();
                    ModelState.Remove(nameof(model.Script));
                    model.Script = blob.RateScript;
                }
            }
            rules = blob.GetRateRules(_NetworkProvider);

            if (command == "Test")
            {
                if (string.IsNullOrWhiteSpace(model.ScriptTest))
                {
                    ModelState.AddModelError(nameof(model.ScriptTest), "Fill out currency pair to test for (like BTC_USD,BTC_CAD)");
                    return(View(model));
                }
                var splitted = model.ScriptTest.Split(',', StringSplitOptions.RemoveEmptyEntries);

                var pairs = new List <CurrencyPair>();
                foreach (var pair in splitted)
                {
                    if (!CurrencyPair.TryParse(pair, out var currencyPair))
                    {
                        ModelState.AddModelError(nameof(model.ScriptTest), $"Invalid currency pair '{pair}' (it should be formatted like BTC_USD,BTC_CAD)");
                        return(View(model));
                    }
                    pairs.Add(currencyPair);
                }

                var fetchs      = _RateFactory.FetchRates(pairs.ToHashSet(), rules, cancellationToken);
                var testResults = new List <RatesViewModel.TestResultViewModel>();
                foreach (var fetch in fetchs)
                {
                    var testResult = await(fetch.Value);
                    testResults.Add(new RatesViewModel.TestResultViewModel()
                    {
                        CurrencyPair = fetch.Key.ToString(),
                        Error        = testResult.Errors.Count != 0,
                        Rule         = testResult.Errors.Count == 0 ? testResult.Rule + " = " + testResult.BidAsk.Bid.ToString(CultureInfo.InvariantCulture)
                                                            : testResult.EvaluatedRule
                    });
                }
                model.TestRateRules = testResults;
                return(View(model));
            }
            else // command == Save
            {
                if (StoreData.SetStoreBlob(blob))
                {
                    await _Repo.UpdateStore(StoreData);

                    StatusMessage = "Rate settings updated";
                }
                return(RedirectToAction(nameof(Rates), new
                {
                    storeId = StoreData.Id
                }));
            }
        }
Esempio n. 10
0
 public RateRule(RateRules parent, CurrencyPair currencyPair, SyntaxNode candidate)
 {
     _CurrencyPair   = currencyPair;
     flatten         = new FlattenExpressionRewriter(parent, currencyPair);
     this.expression = flatten.Visit(candidate);
 }
Esempio n. 11
0
 public FlattenExpressionRewriter(RateRules parent, CurrencyPair pair)
 {
     this.pair   = pair;
     this.parent = parent;
 }
Esempio n. 12
0
        public async Task <IActionResult> NewAtomicSwap(
            [ModelBinder(typeof(WalletIdModelBinder))]
            WalletId walletId,
            NewViewModel newVM)
        {
            var fromWallet = await GetDerivationStrategy(walletId);

            var statusAsync = ExplorerClientProvider.GetExplorerClient(fromWallet.Network).GetStatusAsync();

            if (fromWallet == null)
            {
                return(NotFound());
            }
            var wallets = await GetNamedWallets(walletId.CryptoCode);

            newVM.SetWalletList(wallets, newVM.SelectedWallet);
            newVM.CryptoCode = fromWallet.Network.CryptoCode;
            if (!WalletId.TryParse(newVM.SelectedWallet, out var selectedWalletId))
            {
                ModelState.AddModelError(nameof(newVM.SelectedWallet), "Invalid wallet id");
                return(View(newVM));
            }
            var toWallet = await GetDerivationStrategy(selectedWalletId);

            if (toWallet == null)
            {
                ModelState.AddModelError(nameof(newVM.SelectedWallet), "Invalid wallet id");
                return(View(newVM));
            }

            var             id    = Encoders.Base58.EncodeData(RandomUtils.GetBytes(20));
            AtomicSwapOffer offer = new AtomicSwapOffer();

            offer.MarketMakerUri = new Uri($"{this.Request.GetAbsoluteRoot().WithTrailingSlash()}api/xswap/{id}", UriKind.Absolute);
            offer.Offer          = new AtomicSwapOfferAsset()
            {
                Amount     = Money.Coins((decimal)newVM.Amount),
                CryptoCode = walletId.CryptoCode,
            };

            var minRelayFee   = (await statusAsync).BitcoinStatus.MinRelayTxFee;
            var minimumAmount = minRelayFee.GetFee(200); // Arbitrary but should cover the dust of any output

            if (offer.Offer.Amount <= minimumAmount)
            {
                ModelState.AddModelError(nameof(newVM.Amount), $"Amount must be above {minimumAmount}");
                return(View(newVM));
            }
            offer.Price = new AtomicSwapOfferAsset()
            {
                CryptoCode = toWallet.PaymentId.CryptoCode
            };
            var lockTimespan = TimeSpan.FromDays(2);

            offer.CreatedAt = DateTimeOffset.UtcNow;

            var storeData = await Repository.FindStore(walletId.StoreId, GetUserId());

            if (ModelState.IsValid)
            {
                var pair = new CurrencyPair("AAA", "BBB");
                newVM.RateRule = $"{pair} = {newVM.RateRule}";

                if (RateRules.TryParse(newVM.RateRule, out var rules, out var rateRulesErrors))
                {
                    rules.Spread = (decimal)newVM.Spread / 100.0m;
                    var rateResult = await RateFetcher.FetchRate(pair, rules, CancellationToken.None);

                    if (rateResult.BidAsk == null)
                    {
                        string errorMessage = "Error when fetching rate";
                        if (rateResult.EvaluatedRule != null)
                        {
                            errorMessage += $" ({rateResult.EvaluatedRule})";
                        }
                        ModelState.AddModelError(nameof(newVM.RateRule), errorMessage);
                    }
                    else
                    {
                        offer.Price.Amount = Money.Coins(offer.Offer.Amount.ToDecimal(MoneyUnit.BTC) * rateResult.BidAsk.Ask);

                        rules.Spread = 0;
                        offer.Rule   = rules.GetRuleFor(pair).ToString();
                    }
                }
                else
                {
                    string errorDetails = "";
                    if (rateRulesErrors.Count > 0)
                    {
                        errorDetails = $" ({rateRulesErrors[0]})";
                    }
                    ModelState.AddModelError(nameof(newVM.RateRule), $"Impossible to parse rate rules{errorDetails}");
                }
            }
Esempio n. 13
0
        public async Task <decimal> GetCurrentContributionAmount(Dictionary <string, decimal> stats, string primaryCurrency, RateRules rateRules)
        {
            var result = new List <decimal>();

            var ratesTask = _RateFetcher.FetchRates(
                stats.Keys
                .Select((x) => new CurrencyPair(primaryCurrency, PaymentMethodId.Parse(x).CryptoCode))
                .Distinct()
                .ToHashSet(),
                rateRules).Select(async rateTask =>
            {
                var(key, value) = rateTask;
                var tResult     = await value;
                var rate        = tResult.BidAsk?.Bid;
                if (rate == null)
                {
                    return;
                }

                foreach (var stat in stats)
                {
                    if (string.Equals(PaymentMethodId.Parse(stat.Key).CryptoCode, key.Right,
                                      StringComparison.InvariantCultureIgnoreCase))
                    {
                        result.Add((1m / rate.Value) * stat.Value);
                    }
                }
            });

            await Task.WhenAll(ratesTask);

            return(result.Sum());
        }