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); }
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()); } }
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); }
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); }
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 })); }
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) }); }
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); }
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)); }
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)); }
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); }
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)); }
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}"); }
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)); }
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)); */ }
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); }
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 }); }
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); }
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); }
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}"); }
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)); }
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)); }