コード例 #1
0
        public async Task <IActionResult> LimitOrder(TradeViewModel model)
        {
            // if tripwire tripped cancel
            if (!_tripwire.TradingEnabled())
            {
                _logger.LogError("Tripwire tripped, exiting LimitOrder()");
                this.FlashError($"Trading not enabled");
                return(RedirectToAction("Trade", new { market = model.Market }));
            }
            (var success, var error) = Utils.ValidateOrderParams(_settings, model.Order, model.Order.Price);
            if (!success)
            {
                this.FlashError(error);
                return(RedirectToAction("Trade", new { market = model.Market, side = model.Order.Side, amount = model.Order.Amount, price = model.Order.Price }));
            }

            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));
                    }
                    var order = via.OrderLimitQuery(user.Exchange.Id, model.Market, side, model.Order.Amount, model.Order.Price, _settings.TakerFeeRate, _settings.MakerFeeRate, "viafront");
                    // send email: order created
                    var amountUnit = _settings.Markets[model.Market].AmountUnit;
                    var priceUnit  = _settings.Markets[model.Market].PriceUnit;
                    this.FlashSuccess($"Limit Order Created ({order.market} - {order.side}, Amount: {order.amount} {amountUnit}, Price: {order.price} {priceUnit})");
                    return(RedirectToAction("Trade", new { market = model.Market }));
                }
                catch (ViaJsonException ex)
                {
                    if (ex.Err == ViaError.PUT_LIMIT__BALANCE_NOT_ENOUGH)
                    {
                        this.FlashError($"Limit Order Failed (balance too small)");
                        return(RedirectToAction("Trade", new { market = model.Market, side = model.Order.Side, amount = model.Order.Amount, price = model.Order.Price }));
                    }
                    throw;
                }
            }
        }
コード例 #2
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));
        }