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; } } }
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)); }