public async Task <ActionResult <Order> > UserLimitOrder([FromBody] DevApiUserLimitOrder req)
        {
            var user = await _userManager.FindByEmailAsync(req.Email);

            if (user == null)
            {
                return(BadRequest());
            }

            var side = OrderSide.Any;

            if (req.Side == "buy")
            {
                side = OrderSide.Bid;
            }
            else if (req.Side == "sell")
            {
                side = OrderSide.Ask;
            }
            else
            {
                return(BadRequest());
            }

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via   = new ViaJsonRpc(_settings.AccessHttpUrl);
            var order = via.OrderLimitQuery(user.Exchange.Id, req.Market, side, req.Amount.ToString(), req.Price.ToString(), _settings.TakerFeeRate, _settings.MakerFeeRate, "DEVAPI");

            return(order);
        }
        public ActionResult <DevApiClearAllOrders> ClearAllOrders([FromBody] DevApiClearAllOrders req)
        {
            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via = new ViaJsonRpc(_settings.AccessHttpUrl);

            while (true)
            {
                var count  = 0;
                var orders = via.OrderBookQuery(req.Market, OrderSide.Ask, 0, 100);
                if (orders.orders.Count() == 0)
                {
                    count++;
                }
                foreach (var order in orders.orders)
                {
                    via.OrderCancelQuery(order.user, order.market, order.id);
                }
                orders = via.OrderBookQuery(req.Market, OrderSide.Bid, 0, 100);
                if (orders.orders.Count() == 0)
                {
                    count++;
                }
                foreach (var order in orders.orders)
                {
                    via.OrderCancelQuery(order.user, order.market, order.id);
                }
                if (count >= 2)
                {
                    break;
                }
            }
            return(req);
        }
Exemple #3
0
        public IActionResult KLines(string market, long start, long end, long interval)
        {
            Debug.Assert(market != null);
            var now = DateTimeOffset.Now.ToUnixTimeSeconds();

            if (start == 0)
            {
                start = now - 24 * 3600;
            }
            if (end == 0)
            {
                end = now;
            }
            if (interval == 0)
            {
                interval = 3600;
            }
            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via = new ViaJsonRpc(_settings.AccessHttpUrl);

            try
            {
                var klines = via.KlineQuery(market, start, end, interval);
                return(Json(klines));
            }
            catch (ViaJsonException ex)
            {
                _logger.LogError(ex, "exception getting klines");
                return(BadRequest());
            }
        }
Exemple #4
0
        bool DepositAndCreateTrade(ApplicationUser brokerUser, BrokerOrder order)
        {
            // check broker exchange id
            if (brokerUser.Exchange == null)
            {
                _logger.LogError("Failed to get broker exchange id");
                return(false);
            }
            // create and test backend connection
            var via = new ViaJsonRpc(_settings.AccessHttpUrl); //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)

            via.BalanceQuery(1);
            // register new deposit with the exchange backend
            var amount = order.AmountSend.ToString();
            var source = new Dictionary <string, object>();

            source["BrokerOrderToken"] = order.Token;
            var businessId = order.Id;

            try
            {
                via.BalanceUpdateQuery(brokerUser.Exchange.Id, order.AssetSend, "deposit", businessId, amount, source);
            }
            catch (ViaJsonException ex)
            {
                if (ex.Err == ViaError.BALANCE_UPDATE__REPEAT_UPDATE)
                {
                    _logger.LogError(ex, $"broker already made this exchange update - exch id: {brokerUser.Exchange.Id}, business id: {businessId}");
                }
                else
                {
                    throw;
                }
            }
            // make trade
            string tradeAmount;

            if (order.Side == OrderSide.Bid)
            {
                tradeAmount = order.AmountReceive.ToString();
            }
            else if (order.Side == OrderSide.Ask)
            {
                tradeAmount = order.AmountSend.ToString();
            }
            else
            {
                throw new Exception("invalid order side");
            }
            if (!_settings.MarketOrderBidAmountMoney)
            {
                via.OrderMarketQuery(brokerUser.Exchange.Id, order.Market, order.Side, tradeAmount, "0", _apiSettings.Broker.BrokerTag, _settings.MarketOrderBidAmountMoney);
            }
            else
            {
                via.OrderMarketQuery(brokerUser.Exchange.Id, order.Market, order.Side, tradeAmount, "0", _apiSettings.Broker.BrokerTag);
            }
            return(true);
        }
Exemple #5
0
        public async Task <IActionResult> MarketOrder(TradeViewModel model)
        {
            // if tripwire tripped cancel
            if (!_tripwire.TradingEnabled())
            {
                _logger.LogError("Tripwire tripped, exiting MarketOrder()");
                this.FlashError($"Trading not enabled");
                return(RedirectToAction("Trade", new { market = model.Market }));
            }
            (var success, var error) = Utils.ValidateOrderParams(_settings, model.Order, null, marketOrder: true);
            if (!success)
            {
                return(FlashErrorAndRedirect("Trade", model.Market, error));
            }

            var user = await GetUser(required : true);

            // lock process of performing trade
            lock (_userLocks.GetLock(user.Id))
            {
                try
                {
                    //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
                    var via = new ViaJsonRpc(_settings.AccessHttpUrl);
                    (var side, var error2) = Utils.GetOrderSide(model.Order.Side);
                    if (error2 != null)
                    {
                        return(BadRequest(error2));
                    }
                    Order order;
                    if (!_settings.MarketOrderBidAmountMoney)
                    {
                        order = via.OrderMarketQuery(user.Exchange.Id, model.Market, side, model.Order.Amount, _settings.TakerFeeRate, "viafront", _settings.MarketOrderBidAmountMoney);
                    }
                    else
                    {
                        order = via.OrderMarketQuery(user.Exchange.Id, model.Market, side, model.Order.Amount, _settings.TakerFeeRate, "viafront");
                    }
                    // send email: order created
                    var amountUnit = _settings.Markets[model.Market].AmountUnit;
                    this.FlashSuccess($"Market Order Created ({order.market} - {order.side}, Amount: {order.amount} {amountUnit})");
                    return(RedirectToAction("Trade", new { market = model.Market }));
                }
                catch (ViaJsonException ex)
                {
                    if (ex.Err == ViaError.PUT_MARKET__BALANCE_NOT_ENOUGH)
                    {
                        this.FlashError($"Market Order Failed (balance too small)");
                        return(RedirectToAction("Trade", new { market = model.Market, side = model.Order.Side, amount = model.Order.Amount }));
                    }
                    if (ex.Err == ViaError.PUT_MARKET__NO_ENOUGH_TRADER)
                    {
                        this.FlashError($"Market Order Failed (insufficient market depth)");
                        return(RedirectToAction("Trade", new { market = model.Market, side = model.Order.Side, amount = model.Order.Amount }));
                    }
                    throw;
                }
            }
        }
        public async Task <ActionResult <DevApiUserFundGive> > UserFundGive([FromBody] DevApiUserFundGive req)
        {
            var user = await _userManager.FindByEmailAsync(req.Email);

            if (user == null)
            {
                return(BadRequest());
            }

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via = new ViaJsonRpc(_settings.AccessHttpUrl);

            via.BalanceUpdateQuery(user.Exchange.Id, req.Asset, "DEVAPI", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), req.Amount.ToString());
            return(req);
        }
Exemple #7
0
        public async Task <IActionResult> CancelOrder(OrdersPendingPartialViewModel model)
        {
            var user = await GetUser(required : true);

            // lock process of cancelling trade
            lock (_userLocks.GetLock(user.Id))
            {
                //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
                var via   = new ViaJsonRpc(_settings.AccessHttpUrl);
                var order = via.OrderCancelQuery(user.Exchange.Id, model.Market, model.OrderId);
            }

            this.FlashSuccess("Order Cancelled");
            return(RedirectToAction("Trade", new { market = model.Market }));
        }
Exemple #8
0
        public async Task <IActionResult> Index()
        {
            var user = await GetUser(required : true);

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via      = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balances = Utils.GetUsedBalances(_settings, via, user.Exchange);

            var model = new BalanceViewModel
            {
                User          = user,
                AssetSettings = _settings.Assets,
                Balances      = balances
            };

            return(View(model));
        }
        public async Task <ActionResult <DevApiUserFundGetResult> > UserFundGet([FromBody] DevApiUserFundGet req)
        {
            var user = await _userManager.FindByEmailAsync(req.Email);

            if (user == null)
            {
                return(BadRequest());
            }

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(user.Exchange.Id, req.Asset);

            return(new DevApiUserFundGetResult {
                Amount = decimal.Parse(balance.Available)
            });
        }
Exemple #10
0
        static public OrdersCompletedViewModel Construct(ApplicationUser loggedInUser, ApplicationUser tradeUser, string market, OrderSide side, ExchangeSettings settings, int offset, int limit)
        {
            var via = new ViaJsonRpc(settings.AccessHttpUrl);
            var now = DateTimeOffset.Now.ToUnixTimeSeconds();
            var bidOrdersCompleted = via.OrdersCompletedQuery(tradeUser.Exchange.Id, market, 1, now, offset, limit, side);

            var model = new OrdersCompletedViewModel
            {
                User            = loggedInUser,
                Market          = market,
                MarketNice      = string.Format("{0}/{1}", settings.Markets[market].AmountUnit, settings.Markets[market].PriceUnit),
                AssetSettings   = settings.Assets,
                Settings        = settings.Markets[market],
                OrdersCompleted = bidOrdersCompleted,
            };

            return(model);
        }
Exemple #11
0
        public async Task <IActionResult> WithdrawFiat(string asset)
        {
            var user = await GetUser(required : true);

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(user.Exchange.Id, asset);

            var model = new WithdrawFiatViewModel
            {
                User              = user,
                AssetSettings     = _settings.Assets,
                Asset             = asset,
                BalanceAvailable  = balance.Available,
                TwoFactorRequired = user.TwoFactorEnabled,
            };

            return(View(model));
        }
Exemple #12
0
        static public OrdersPendingViewModel Construct(ApplicationUser loggedInUser, ApplicationUser tradeUser, string market, ExchangeSettings settings, int offset, int limit)
        {
            var via           = new ViaJsonRpc(settings.AccessHttpUrl);
            var now           = DateTimeOffset.Now.ToUnixTimeSeconds();
            var ordersPending = via.OrdersPendingQuery(tradeUser.Exchange.Id, market, offset, limit);

            var model = new OrdersPendingViewModel
            {
                User          = loggedInUser,
                Market        = market,
                MarketNice    = string.Format("{0}/{1}", settings.Markets[market].AmountUnit, settings.Markets[market].PriceUnit),
                AssetSettings = settings.Assets,
                Settings      = settings.Markets[market],
                OrdersPending = new OrdersPendingPartialViewModel {
                    OrdersPending = ordersPending
                },
            };

            return(model);
        }
        public IActionResult UserInspectExchangeBalanceHistory(string id, string asset, int offset = 0, int limit = 20)
        {
            var user        = GetUser(required: true).Result;
            var userInspect = _userManager.FindByIdAsync(id).Result;

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var history = via.BalanceHistoryQuery(userInspect.Exchange.Id, asset, "", 0, 0, offset, limit);

            ViewData["userid"] = id;
            var model = new UserBalanceHistoryViewModel
            {
                User           = user,
                Asset          = asset,
                AssetSettings  = _settings.Assets,
                BalanceHistory = history
            };

            return(View(model));
        }
Exemple #14
0
        static public TradeViewModel Construct(ApplicationUser loggedInUser, ApplicationUser tradeUser, string market, string side, string amount, string price, ExchangeSettings settings)
        {
            var via                = new ViaJsonRpc(settings.AccessHttpUrl);
            var balances           = Utils.GetUsedBalances(settings, via, tradeUser.Exchange);
            var ordersPending      = via.OrdersPendingQuery(tradeUser.Exchange.Id, market, 0, 10);
            var now                = DateTimeOffset.Now.ToUnixTimeSeconds();
            var bidOrdersCompleted = via.OrdersCompletedQuery(tradeUser.Exchange.Id, market, 1, now, 0, 10, OrderSide.Bid);
            var askOrdersCompleted = via.OrdersCompletedQuery(tradeUser.Exchange.Id, market, 1, now, 0, 10, OrderSide.Ask);

            var orderDepth = via.OrderDepthQuery(market, settings.OrderBookLimit, settings.Markets[market].PriceInterval);
            var ob         = new OrderbookPartialViewModel
            {
                AssetSettings = settings.Assets,
                AmountUnit    = settings.Markets[market].AmountUnit,
                PriceUnit     = settings.Markets[market].PriceUnit,
                OrderDepth    = orderDepth
            };

            var model = new TradeViewModel
            {
                User          = loggedInUser,
                Market        = market,
                MarketNice    = string.Format("{0}/{1}", settings.Markets[market].AmountUnit, settings.Markets[market].PriceUnit),
                AssetSettings = settings.Assets,
                Settings      = settings.Markets[market],
                OrderBook     = ob,
                Balances      = new BalancesPartialViewModel {
                    Balances = balances
                },
                OrdersPending = new OrdersPendingPartialViewModel {
                    OrdersPending = ordersPending
                },
                BidOrdersCompleted = bidOrdersCompleted,
                AskOrdersCompleted = askOrdersCompleted,
                Order = new ApiOrderCreateLimit {
                    Market = market, Side = side, Amount = amount, Price = price
                }
            };

            return(model);
        }
Exemple #15
0
        public async Task <ActionResult <DevApiUserFundSet> > UserFundSet([FromBody] DevApiUserFundSet req)
        {
            var user = await _userManager.FindByEmailAsync(req.Email);

            if (user == null)
            {
                return(BadRequest());
            }

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via       = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance   = via.BalanceQuery(user.Exchange.Id, req.Asset);
            var available = decimal.Parse(balance.Available);
            var change    = req.Amount - available;

            if (change != 0)
            {
                via.BalanceUpdateQuery(user.Exchange.Id, req.Asset, "DEVAPI", DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(), change.ToString());
            }
            return(req);
        }
        public IActionResult UserInspect(string id)
        {
            var user        = GetUser(required: true).Result;
            var userInspect = _userManager.FindByIdAsync(id).Result;
            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via      = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balances = Utils.GetUsedBalances(_settings, via, userInspect.Exchange);

            // get kyc level
            var      level    = userInspect.Kyc != null ? userInspect.Kyc.Level : 0;
            KycLevel kycLevel = null;

            if (level < _kycSettings.Levels.Count())
            {
                kycLevel = _kycSettings.Levels[level];
            }
            // get user kyc request
            string kycRequestUrl = null;
            var    kycRequest    = _context.KycRequests.Where(r => r.ApplicationUserId == userInspect.Id).OrderByDescending(r => r.Date).FirstOrDefault();

            if (kycRequest != null)
            {
                kycRequestUrl = $"{_kycSettings.KycServerUrl}/request/{kycRequest.Token}";
            }

            var model = new UserViewModel
            {
                User        = user,
                UserInspect = userInspect,
                Balances    = new BalancesPartialViewModel {
                    Balances = balances
                },
                KycLevel      = kycLevel,
                KycRequestUrl = kycRequestUrl,
                AssetSettings = _settings.Assets,
            };

            return(View(model));
        }
Exemple #17
0
        protected decimal CalculateWithdrawalAssetEquivalent(ILogger logger, KycSettings kyc, string asset, decimal amount)
        {
            if (asset == kyc.WithdrawalAsset)
            {
                return(amount);
            }

            foreach (var market in _settings.Markets.Keys)
            {
                if (market.StartsWith(asset) && market.EndsWith(kyc.WithdrawalAsset))
                {
                    try
                    {
                        //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
                        var via      = new ViaJsonRpc(_settings.AccessHttpUrl);
                        var price    = via.MarketPriceQuery(market);
                        var priceDec = decimal.Parse(price);
                        if (priceDec <= 0)
                        {
                            continue;
                        }
                        return(amount * priceDec);
                    }
                    catch (ViaJsonException ex)
                    {
                        _logger.LogError(ex, $"Error getting market price for asset '{market}'");
                    }
                }
            }
            ;

            if (kyc.WithdrawalAssetBaseRates.ContainsKey(asset))
            {
                return(amount * kyc.WithdrawalAssetBaseRates[asset]);
            }

            logger.LogError($"no price found for asset {asset}");
            throw new Exception($"no price found for asset {asset}");
        }
Exemple #18
0
        public IActionResult Orderbook(string market)
        {
            Debug.Assert(market != null);
            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via        = new ViaJsonRpc(_settings.AccessHttpUrl);
            var orderDepth = via.OrderDepthQuery(market, _settings.OrderBookLimit, _settings.Markets[market].PriceInterval);

            var ob = new OrderbookPartialViewModel
            {
                AssetSettings = _settings.Assets,
                AmountUnit    = _settings.Markets[market].AmountUnit,
                PriceUnit     = _settings.Markets[market].PriceUnit,
                OrderDepth    = orderDepth
            };
            var model = new OrderbookViewModel
            {
                User       = GetUser().Result,
                Market     = market,
                MarketNice = string.Format("{0}/{1}", _settings.Markets[market].AmountUnit, _settings.Markets[market].PriceUnit),
                OrderBook  = ob
            };

            return(View(model));
        }
Exemple #19
0
        static void Main(string[] args)
        {
            var    userId     = 1;
            var    asset      = "BTC";
            var    business   = "deposit";
            int    start_time = 0;
            int    end_time   = 0;
            int    offset     = 0;
            int    limit      = 50;
            string market     = "BTCUSD";

            var via     = new ViaJsonRpc("http://10.50.1.2:8080");
            var balance = via.BalanceQuery(userId, asset);

            Console.WriteLine("Your {0} balance is {1} ({2} frozen)", asset, balance.Available, balance.Freeze);
            var balances = via.BalanceQuery(userId);

            foreach (var item in balances)
            {
                Console.WriteLine("Your {0} balance is {1} ({2} frozen)", item.Key, item.Value.Available, item.Value.Freeze);
            }
            // // var status = via.BalanceUpdateQuery(userId, asset, "deposit", 15, "5.5");
            // // Console.WriteLine("Your balance update state is: {0}", status);
            // var balanceHistroy = via.BalanceHistoryQuery(userId, asset, business, start_time, end_time, offset, limit);
            // Console.WriteLine("There is {0} records:", balanceHistroy.limit);
            // foreach(var item in balanceHistroy.records)
            // {
            //     Console.WriteLine(item.asset);
            // }

            //var limitOrder = via.OrderLimitQuery(userId, market, OrderSide.Ask, "1", "9200", "0.002", "0.001", "");

            //var transactionOrder = via.OrderTransactionQuery(14,0,50);
            //Console.WriteLine(transactionOrder.records);

            var orderBook = via.OrderBookQuery(market, OrderSide.Ask, 0, 50);

            orderBook = via.OrderBookQuery(market, OrderSide.Bid, 0, 50);

            var ordersPending = via.OrdersPendingQuery(userId, market, 0, 10);

            Console.WriteLine("{0}: You have {1} pending orders", market, ordersPending.total);
            var assetA = market.Substring(0, 3);
            var assetB = market.Substring(3, 3);

            foreach (var item in ordersPending.records)
            {
                var feeAsset = item.side == OrderSide.Ask ? assetB : assetA;
                Console.WriteLine($"{item.side} - {item.type} - price: {item.price} {assetB}, amount: {item.amount} {assetA}, remaining: {item.left} {assetA}, traded: {item.deal_stock} {assetA} / {item.deal_money} {assetB}, fee: {item.deal_fee} {feeAsset}");
            }

            var ordersCompleted = via.OrdersCompletedQuery(userId, market, 1, 99999999999, 0, 10, OrderSide.Bid);

            Console.WriteLine("{0}: completed bid orders", market);
            foreach (var item in ordersCompleted.records)
            {
                var feeAsset = item.side == OrderSide.Ask ? assetB : assetA;
                Console.WriteLine($"{item.side} - {item.type} - price: {item.price} {assetB}, amount: {item.amount} {assetA}, traded: {item.deal_stock} {assetA} / {item.deal_money} {assetB}, fee: {item.deal_fee} {feeAsset}");
            }

            ordersCompleted = via.OrdersCompletedQuery(userId, market, 1, 99999999999, 0, 10, OrderSide.Ask);
            Console.WriteLine("{0}: completed ask orders", market);
            foreach (var item in ordersCompleted.records)
            {
                var feeAsset = item.side == OrderSide.Ask ? assetB : assetA;
                Console.WriteLine($"{item.side} - {item.type} - price: {item.price} {assetB}, amount: {item.amount} {assetA}, traded: {item.deal_stock} {assetA} / {item.deal_money} {assetB}, fee: {item.deal_fee} {feeAsset}");
            }

            // Console.WriteLine(transactionOrder.records);
            //var status = via.TodayMarketStatus(market);
            //Console.WriteLine("Todays Market Status is {0}", status);

            // var orderDepth = via.OrderDepthQuery(market, 50, "1");
            // var transactionhistory = via.MarketHistoryQuery(market, 50, 0);
            // foreach(var item in transactionhistory)
            // {
            // Console.Write(item.id);
            // }

            /*
             * Console.WriteLine("Klines...");
             * var klines = via.KlineQuery(market, 1, 12000000000, 3600);
             * foreach (var kline in klines)
             *  Console.WriteLine(KlineResponse.ParseKlineList(kline));
             */
        }
Exemple #20
0
        public static Dictionary <string, Balance> GetUsedBalances(ExchangeSettings settings, ViaJsonRpc via, Exchange xch)
        {
            var balances = via.BalanceQuery(xch.Id);

            foreach (var key in balances.Keys.ToList())
            {
                if (!settings.Assets.ContainsKey(key))
                {
                    balances.Remove(key);
                }
            }
            return(balances);
        }
Exemple #21
0
        public static async Task <AddressIncommingTxs> CheckAddressIncommingTxsAndUpdateWalletAndExchangeBalance(IEmailSender emailSender, ExchangeSettings settings, string asset, IWallet wallet, ChainAssetSettings chainAssetSettings, ApplicationUser user, WalletAddr addr)
        {
            // create and test backend connection
            var via = new ViaJsonRpc(settings.AccessHttpUrl); //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)

            via.BalanceQuery(1);

            // get wallet transactions
            var newlySeenTxs = new List <WalletTx>();
            var incommingTxs = wallet.GetAddrTransactions(addr.Address);

            if (incommingTxs != null)
            {
                incommingTxs = incommingTxs.Where(t => t.Direction == WalletDirection.Incomming);
            }
            else
            {
                incommingTxs = new List <WalletTx>();
            }
            foreach (var tx in incommingTxs)
            {
                if (tx.State == WalletTxState.None)
                {
                    // send email: deposit detected
                    wallet.SeenTransaction(tx);
                    newlySeenTxs.Add(tx);
                    if (!string.IsNullOrEmpty(user.Email))
                    {
                        await emailSender.SendEmailChainDepositDetectedAsync(user.Email, asset, wallet.AmountToString(tx.AmountOutputs()), tx.ChainTx.TxId);
                    }
                }
            }
            var unackedTxs = wallet.GetAddrUnacknowledgedTransactions(addr.Address);

            if (unackedTxs != null)
            {
                unackedTxs = unackedTxs.Where(t => t.Direction == WalletDirection.Incomming && t.ChainTx.Confirmations >= chainAssetSettings.MinConf);
            }
            else
            {
                unackedTxs = new List <WalletTx>();
            }
            BigInteger newDeposits = 0;

            foreach (var tx in unackedTxs)
            {
                newDeposits += tx.AmountOutputs();
                // send email: deposit confirmed
                await emailSender.SendEmailChainDepositConfirmedAsync(user.Email, asset, wallet.AmountToString(tx.AmountOutputs()), tx.ChainTx.TxId);
            }

            // ack txs and save wallet
            IEnumerable <WalletTx> justAckedTxs = unackedTxs;

            if (unackedTxs.Any())
            {
                justAckedTxs = new List <WalletTx>(unackedTxs); // wallet.Save will kill unackedTxs because they are no longer unacked
                wallet.AcknowledgeTransactions(unackedTxs);
                wallet.Save();
            }
            else if (newlySeenTxs.Any())
            {
                wallet.Save();
            }

            // register new deposits with the exchange backend
            foreach (var tx in justAckedTxs)
            {
                var amount = wallet.AmountToString(tx.AmountOutputs());
                var source = new Dictionary <string, object>();
                source["txid"] = tx.ChainTx.TxId;
                var businessId = tx.Id;
                via.BalanceUpdateQuery(user.Exchange.Id, asset, "deposit", businessId, amount, source);
            }

            return(new AddressIncommingTxs {
                IncommingTxs = incommingTxs, NewlySeenTxs = newlySeenTxs, JustAckedTxs = justAckedTxs, NewDeposits = newDeposits
            });
        }
Exemple #22
0
        bool ChainWithdrawToCustomer(ApplicationUser brokerUser, BrokerOrder order)
        {
            var asset  = order.AssetReceive;
            var amount = order.AmountReceive;

            var wallet = _walletProvider.GetChain(asset);

            if (wallet == null)
            {
                _logger.LogError($"No chain wallet for {asset}");
                return(false);
            }
            var brokerWalletTag = wallet.GetTag(brokerUser.Id);

            if (brokerWalletTag == null)
            {
                _logger.LogError($"No tag for broker {brokerUser.Id}");
                return(false);
            }

            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(brokerUser.Exchange.Id, asset);

            // validate amount
            var amountInt    = wallet.StringToAmount(amount.ToString());
            var availableInt = wallet.StringToAmount(balance.Available);

            if (amountInt > availableInt)
            {
                _logger.LogError("broker available balance is too small");
                return(false);
            }
            if (amountInt <= 0)
            {
                _logger.LogError("amount must be greather then or equal to 0");
                return(false);
            }

            var consolidatedFundsTag = _walletProvider.ConsolidatedFundsTag();

            using (var dbtx = wallet.BeginDbTransaction())
            {
                // ensure tag exists
                if (!wallet.HasTag(consolidatedFundsTag))
                {
                    wallet.NewTag(consolidatedFundsTag);
                    wallet.Save();
                }

                // register withdrawal with wallet
                var tag = wallet.GetTag(brokerUser.Id);
                if (tag == null)
                {
                    tag = wallet.NewTag(brokerUser.Id);
                }
                var spend = wallet.RegisterPendingSpend(consolidatedFundsTag, consolidatedFundsTag,
                                                        order.Recipient, amountInt, tag);
                wallet.Save();
                var businessId = spend.Id;

                try
                {
                    // link pending withdrawal to broker order
                    var bow = new BrokerOrderChainWithdrawal {
                        BrokerOrderId = order.Id, SpendCode = spend.SpendCode
                    };
                    _context.BrokerOrderChainWithdrawals.Add(bow);
                    // we save changes here so that we a broker order cannot be processed twice(BrokerOrderChainWithdrawal.BrokerOrderId is unique)
                    _context.SaveChanges();
                }
                catch
                {
                    _logger.LogError($"unable to create BrokerOrderChainWithdrawal object ({order.Id}, {spend.SpendCode}");
                    throw;
                }

                // register withdrawal with the exchange backend
                var negativeAmount = -amount;
                try
                {
                    via.BalanceUpdateQuery(brokerUser.Exchange.Id, asset, "withdraw", businessId, negativeAmount.ToString(), null);
                }
                catch (ViaJsonException ex)
                {
                    _logger.LogError(ex, "Failed to update (withdraw) user balance (xch id: {0}, asset: {1}, businessId: {2}, amount {3}",
                                     brokerUser.Exchange.Id, asset, businessId, negativeAmount);
                    if (ex.Err == ViaError.BALANCE_UPDATE__BALANCE_NOT_ENOUGH)
                    {
                        dbtx.Rollback();
                        _logger.LogError("balance not enough");
                        return(false);
                    }
                    throw;
                }

                dbtx.Commit();
            }

            return(true);
        }
Exemple #23
0
        public bool FiatWithdrawToCustomer(ApplicationUser brokerUser, BrokerOrder order)
        {
            var asset  = order.AssetReceive;
            var amount = order.AmountReceive;

            var wallet = _walletProvider.GetFiat(asset);

            if (wallet == null)
            {
                _logger.LogError($"No fiat wallet for {asset}");
                return(false);
            }

            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(brokerUser.Exchange.Id, asset);

            // validate amount
            var amountInt    = wallet.AmountToLong(amount);
            var availableInt = wallet.StringToAmount(balance.Available);

            if (amountInt > availableInt)
            {
                _logger.LogError("broker available balance is too small");
                return(false);
            }
            if (amountInt <= 0)
            {
                _logger.LogError("amount must be greather then or equal to 0");
                return(false);
            }

            using (var dbtx = wallet.BeginDbTransaction())
            {
                // register withdrawal with wallet
                var acct = new BankAccount {
                    AccountNumber = order.Recipient
                };
                var tx = wallet.RegisterPendingWithdrawal(brokerUser.Id, amountInt, acct);
                if (tx == null)
                {
                    _logger.LogError($"Failed to create fiat withdrawal ('{order.Token}')");
                    return(false);
                }
                wallet.Save();
                var businessId = tx.Id;

                try
                {
                    // link pending withdrawal to broker order
                    var bow = new BrokerOrderFiatWithdrawal {
                        BrokerOrderId = order.Id, DepositCode = tx.DepositCode
                    };
                    _context.BrokerOrderFiatWithdrawals.Add(bow);
                    // we save changes here so that we a broker order cannot be processed twice(BrokerOrderChainWithdrawal.BrokerOrderId is unique)
                    _context.SaveChanges();
                }
                catch
                {
                    _logger.LogError($"unable to create BrokerOrderChainWithdrawal object ({order.Id}, {tx.DepositCode}");
                    throw;
                }

                // register withdrawal with the exchange backend
                var negativeAmount = -amount;
                try
                {
                    via.BalanceUpdateQuery(brokerUser.Exchange.Id, asset, "withdraw", businessId, negativeAmount.ToString(), null);
                }
                catch (ViaJsonException ex)
                {
                    _logger.LogError(ex, "Failed to update (withdraw) user balance (xch id: {0}, asset: {1}, businessId: {2}, amount {3}",
                                     brokerUser.Exchange.Id, asset, businessId, negativeAmount);
                    if (ex.Err == ViaError.BALANCE_UPDATE__BALANCE_NOT_ENOUGH)
                    {
                        dbtx.Rollback();
                        _logger.LogError("balance not enough");
                        return(false);
                    }
                    throw;
                }

                dbtx.Commit();
            }

            return(true);
        }
Exemple #24
0
        static async Task ProcessFiat(IServiceProvider serviceProvider, bool isDeposit, string asset, string depositCode, long date, decimal amount, string bankMetadata)
        {
            // deposit code to int
            var depositCodeInt = long.Parse(depositCode);

            // convert amount to int
            asset = asset.ToUpper();
            var walletProvider = serviceProvider.GetRequiredService <IWalletProvider>();
            var wallet         = walletProvider.GetFiat(asset);
            var amountInt      = wallet.StringToAmount(amount.ToString());

            // process deposit
            FiatWalletTx tx;

            if (isDeposit)
            {
                tx = wallet.UpdateDeposit(depositCode, date, amountInt, bankMetadata);
            }
            else
            {
                tx = wallet.UpdateWithdrawal(depositCode, date, amountInt, bankMetadata);
            }
            System.Diagnostics.Debug.Assert(tx != null);

            // get user
            var userManager = serviceProvider.GetRequiredService <UserManager <ApplicationUser> >();
            var user        = await userManager.FindByIdAsync(tx.Tag.Tag);

            var context = serviceProvider.GetRequiredService <ApplicationDbContext>();

            // test backend connection (get user balance)
            var settings = serviceProvider.GetRequiredService <IOptions <ExchangeSettings> >();
            var via      = new ViaJsonRpc(settings.Value.AccessHttpUrl); //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var balance  = via.BalanceQuery(user.Exchange.Id, asset);

            Console.WriteLine($"Before - available {asset} balance: {balance.Available}");

            // save wallet
            wallet.Save();
            Console.WriteLine($"Saved {asset} wallet");

            // register new deposits with the exchange backend
            var source = new Dictionary <string, object>();

            source["bankMetadata"] = bankMetadata;
            var amountStr = amount.ToString();

            if (!isDeposit)
            {
                amountStr = (-amount).ToString();
            }
            via.BalanceUpdateQuery(user.Exchange.Id, asset, "deposit", depositCodeInt, amountStr, source);
            Console.WriteLine($"Updated exchange backend");

            balance = via.BalanceQuery(user.Exchange.Id, asset);
            Console.WriteLine($"After - available {asset} balance: {balance.Available}");

            // send email
            var emailSender = serviceProvider.GetRequiredService <IEmailSender>();

            if (isDeposit)
            {
                await emailSender.SendEmailFiatDepositConfirmedAsync(user.Email, asset, wallet.AmountToString(tx.Amount), tx.DepositCode);
            }
            else
            {
                await emailSender.SendEmailFiatWithdrawalConfirmedAsync(user.Email, asset, wallet.AmountToString(tx.Amount), tx.DepositCode);
            }
            Console.WriteLine($"Sent email to {user.Email}");
        }
Exemple #25
0
        public async Task <IActionResult> WithdrawFiat(WithdrawFiatViewModel model)
        {
            var user = await GetUser(required : true);

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(user.Exchange.Id, model.Asset);

            // fill in model in case we need to error out early
            model.User             = user;
            model.AssetSettings    = _settings.Assets;
            model.BalanceAvailable = balance.Available;

            // if tripwire tripped cancel
            if (!_tripwire.WithdrawalsEnabled())
            {
                _logger.LogError("Tripwire tripped, exiting WithdrawFiat()");
                this.FlashError($"Withdrawals not enabled");
                return(View(model));
            }
            await _tripwire.RegisterEvent(TripwireEventType.WithdrawalAttempt);

            // check 2fa authentication
            if (user.TwoFactorEnabled)
            {
                if (model.TwoFactorCode == null)
                {
                    model.TwoFactorCode = "";
                }
                var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
                if (!await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authenticatorCode))
                {
                    this.FlashError($"Invalid authenticator code");
                    return(View(model));
                }
            }

            var wallet    = _walletProvider.GetFiat(model.Asset);
            var amountInt = wallet.StringToAmount(model.Amount.ToString());

            if (amountInt <= 0)
            {
                this.FlashError("Amount must be greater then 0");
                return(View(model));
            }
            var decimals = _settings.Assets[model.Asset].Decimals;

            if (Utils.GetDecimalPlaces(model.Amount) > decimals)
            {
                this.FlashError($"Amount must have a maximum of {decimals} digits after the decimal place");
                return(View(model));
            }

            if (!Utils.ValidateBankAccount(model.WithdrawalAccount))
            {
                this.FlashError($"Bank account invalid");
                return(View(model));
            }

            // lock process of checking balance and performing withdrawal
            lock (_userLocks.GetLock(user.Id))
            {
                //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
                balance = via.BalanceQuery(user.Exchange.Id, model.Asset);
                // validate amount
                var availableInt = wallet.StringToAmount(balance.Available);
                if (amountInt > availableInt)
                {
                    this.FlashError("Amount must be less then or equal to available balance");
                    return(View(model));
                }

                // validate kyc level
                (var success, var withdrawalAssetAmount, var error) = ValidateWithdrawlLimit(user, model.Asset, model.Amount);
                if (!success)
                {
                    this.FlashError(error);
                    return(View(model));
                }

                // create pending withdrawal
                var account = new BankAccount {
                    AccountNumber = model.WithdrawalAccount
                };
                model.PendingTx = wallet.RegisterPendingWithdrawal(user.Id, amountInt, account);

                // register new withdrawal with the exchange backend
                var source         = new Dictionary <string, object>();
                var amountStr      = (-model.Amount).ToString();
                var depositCodeInt = long.Parse(model.PendingTx.DepositCode);
                via.BalanceUpdateQuery(user.Exchange.Id, model.Asset, "withdraw", depositCodeInt, amountStr, source);
                Console.WriteLine($"Updated exchange backend");

                // save wallet (after we have posted the withdrawal to the backend)
                wallet.Save();

                // register withdrawal with kyc limits
                user.AddWithdrawal(_context, model.Asset, model.Amount, withdrawalAssetAmount);
                _context.SaveChanges();
            }

            // register withdrawal with tripwire
            await _tripwire.RegisterEvent(TripwireEventType.Withdrawal);

            // send email: withdrawal created
            await _emailSender.SendEmailFiatWithdrawalCreatedAsync(user.Email, model.Asset, model.Amount.ToString(), model.PendingTx.DepositCode);

            return(View("WithdrawalFiatCreated", model));
        }
Exemple #26
0
        public async Task <IActionResult> Withdraw(WithdrawViewModel model)
        {
            var user = await GetUser(required : true);

            //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
            var via     = new ViaJsonRpc(_settings.AccessHttpUrl);
            var balance = via.BalanceQuery(user.Exchange.Id, model.Asset);

            // fill in model in case we need to error out early
            model.User             = user;
            model.AssetSettings    = _settings.Assets;
            model.BalanceAvailable = balance.Available;

            // if tripwire tripped cancel
            if (!_tripwire.WithdrawalsEnabled())
            {
                _logger.LogError("Tripwire tripped, exiting Withdraw()");
                this.FlashError($"Withdrawals not enabled");
                return(View(model));
            }
            await _tripwire.RegisterEvent(TripwireEventType.WithdrawalAttempt);

            if (!ModelState.IsValid)
            {
                // redisplay form
                return(View(model));
            }

            // check 2fa authentication
            if (user.TwoFactorEnabled)
            {
                if (model.TwoFactorCode == null)
                {
                    model.TwoFactorCode = "";
                }
                var authenticatorCode = model.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);
                if (!await _userManager.VerifyTwoFactorTokenAsync(user, _userManager.Options.Tokens.AuthenticatorTokenProvider, authenticatorCode))
                {
                    this.FlashError($"Invalid authenticator code");
                    return(View(model));
                }
            }

            // lock process of checking balance and performing withdrawal
            lock (_userLocks.GetLock(user.Id))
            {
                //TODO: move this to a ViaRpcProvider in /Services (like IWalletProvider)
                balance = via.BalanceQuery(user.Exchange.Id, model.Asset);
                model.BalanceAvailable = balance.Available;

                var wallet = _walletProvider.GetChain(model.Asset);

                // validate amount
                var amountInt    = wallet.StringToAmount(model.Amount.ToString());
                var availableInt = wallet.StringToAmount(balance.Available);
                if (amountInt > availableInt)
                {
                    this.FlashError("Amount must be less then or equal to available balance");
                    return(View(model));
                }
                if (amountInt <= 0)
                {
                    this.FlashError("Amount must be greather then or equal to 0");
                    return(View(model));
                }

                // validate address
                if (!wallet.ValidateAddress(model.WithdrawalAddress))
                {
                    this.FlashError("Withdrawal address is not valid");
                    return(View(model));
                }

                // validate kyc level
                (var success, var withdrawalAssetAmount, var error) = ValidateWithdrawlLimit(user, model.Asset, model.Amount);
                if (!success)
                {
                    this.FlashError(error);
                    return(View(model));
                }

                var consolidatedFundsTag = _walletProvider.ConsolidatedFundsTag();

                using (var dbtx = wallet.BeginDbTransaction())
                {
                    // ensure tag exists
                    if (!wallet.HasTag(consolidatedFundsTag))
                    {
                        wallet.NewTag(consolidatedFundsTag);
                        wallet.Save();
                    }

                    // register withdrawal with wallet
                    var tag = wallet.GetTag(user.Id);
                    if (tag == null)
                    {
                        tag = wallet.NewTag(user.Id);
                    }
                    var spend = wallet.RegisterPendingSpend(consolidatedFundsTag, consolidatedFundsTag,
                                                            model.WithdrawalAddress, amountInt, tag);
                    wallet.Save();
                    var businessId = spend.Id;

                    // register withdrawal with the exchange backend
                    var negativeAmount = -model.Amount;
                    try
                    {
                        via.BalanceUpdateQuery(user.Exchange.Id, model.Asset, "withdraw", businessId, negativeAmount.ToString(), null);
                    }
                    catch (ViaJsonException ex)
                    {
                        _logger.LogError(ex, "Failed to update (withdraw) user balance (xch id: {0}, asset: {1}, businessId: {2}, amount {3}",
                                         user.Exchange.Id, model.Asset, businessId, negativeAmount);
                        if (ex.Err == ViaError.BALANCE_UPDATE__BALANCE_NOT_ENOUGH)
                        {
                            dbtx.Rollback();
                            this.FlashError("Balance not enough");
                            return(View(model));
                        }
                        throw;
                    }

                    dbtx.Commit();
                }

                // register withdrawal with kyc limits
                user.AddWithdrawal(_context, model.Asset, model.Amount, withdrawalAssetAmount);
                _context.SaveChanges();
            }

            // register withdrawal with tripwire
            await _tripwire.RegisterEvent(TripwireEventType.Withdrawal);

            this.FlashSuccess(string.Format("Created withdrawal: {0} {1} to {2}", model.Amount, model.Asset, model.WithdrawalAddress));
            // send email: withdrawal created
            await _emailSender.SendEmailChainWithdrawalCreatedAsync(user.Email, model.Asset, model.Amount.ToString());

            return(View(model));
        }