private List <string> TransferPaytopiaTotalToSystemUser(IExchangeDataContext context, Dictionary <int, decimal> totals)
        {
            Log.Message(LogLevel.Info, "TransferPaytopiaTotalToSystemUser entered.");

            List <string> transactionIds = new List <string>();

            foreach (var kvp in totals)
            {
                Guid txId = Guid.NewGuid();
                context.Transfer.Add(new TransferHistory()
                {
                    CurrencyId   = kvp.Key,
                    Amount       = kvp.Value,
                    UserId       = Constant.SYSTEM_USER_PAYTOPIA,
                    TransferType = TransferType.Paytopia,
                    ToUserId     = Constant.SYSTEM_USER_STAGING,
                    Timestamp    = DateTime.UtcNow
                });

                transactionIds.Add(txId.ToString());
            }

            Log.Message(LogLevel.Info, "TransferPaytopiaTotalToSystemUser exited.");

            return(transactionIds);
        }
        private List <string> DepositTradeHistoryTotalToSystemUser(IExchangeDataContext context, Dictionary <int, decimal> totals)
        {
            Log.Message(LogLevel.Info, "DepositTradeHistoryTotalToSystemUser entered.");

            List <string> transactionIds = new List <string>();

            foreach (var kvp in totals)
            {
                Guid txId = Guid.NewGuid();
                context.Deposit.Add(new Deposit()
                {
                    CurrencyId = kvp.Key,
                    Amount     = kvp.Value,
                    UserId     = Constant.SYSTEM_USER_STAGING,
                    Status     = DepositStatus.Confirmed,
                    Txid       = txId.ToString(),
                    TimeStamp  = DateTime.UtcNow
                });

                transactionIds.Add(txId.ToString());
            }

            Log.Message(LogLevel.Info, "DepositTradeHistoryTotalToSystemUser exited.");

            return(transactionIds);
        }
 private async Task <AddressModel> GetAddressModel(IExchangeDataContext context, int currency, Guid userId)
 {
     return(await context.Address
            .Where(x => x.CurrencyId == currency && x.UserId == userId)
            .Select(x => new AddressModel
     {
         AddressData = x.AddressHash,
         AddressData2 = x.Currency.BaseAddress,
         CurrencyId = x.CurrencyId,
         CurrencyType = x.Currency.Type,
         Name = x.Currency.Name,
         Symbol = x.Currency.Symbol,
         QrFormat = x.Currency.Settings.QrFormat,
         AddressType = x.Currency.Settings.AddressType
     }).FirstOrDefaultNoLockAsync().ConfigureAwait(false));
 }
        private async Task PerformWalletTransactionsAndUpdateDepositsWithIds(List <string> transactionIds, IExchangeDataContext context)
        {
            Log.Message(LogLevel.Info, "PerformWalletTransactionsAndUpdateDepositsWithIds entered.");

            // send transactions to the wallets for System_User cefs payments and update the deposits with the real txId.
            var cefsDeposits = await context.Deposit.Where(x => transactionIds.Contains(x.Txid)).ToListAsync().ConfigureAwait(false);

            foreach (var txId in transactionIds)
            {
                try
                {
                    var deposit  = cefsDeposits.FirstOrDefault(x => x.Txid == txId);
                    var currency = await context.Currency.FirstOrDefaultAsync(c => c.Id == deposit.CurrencyId).ConfigureAwait(false);

                    var currencyAddress = await context.Address.FirstOrDefaultAsync(a => a.UserId == Constant.SYSTEM_USER_STAGING && a.CurrencyId == deposit.CurrencyId).ConfigureAwait(false);

                    if (currencyAddress == null)
                    {
                        Log.Message(LogLevel.Error, $"No address exists for currency {currency.Symbol}. Unable to process CEF deposit.");
                        continue;
                    }

                    var connector = new WalletConnector(currency.WalletHost, currency.WalletPort, currency.WalletUser, currency.WalletPass, WALLET_TIMEOUT);
                    var result    = await connector.SendToAddressAsync(currencyAddress.AddressHash, deposit.Amount);

                    if (result != null)
                    {
                        deposit.Txid = result.Txid;
                        await context.SaveChangesAsync();
                    }
                }
                catch (Exception ex)
                {
                    Log.Message(LogLevel.Error, $"Wallet Transaction blew up.\r\n{ex.Message}");
                }
            }

            Log.Message(LogLevel.Info, "PerformWalletTransactionsAndUpdateDepositsWithIds exited.");
        }
        private async Task SaveTransfersAndAuditAllUserBalances(Dictionary <Guid, CEFSBalance> userIds, List <TransferHistory> transfers, List <int> Currencies, IExchangeDataContext context)
        {
            Log.Message(LogLevel.Info, "SaveTransfersAndSuditUserBalances entered.");

            await context.Database.BulkInsertAsync(transfers);

            await context.SaveChangesAsync();

            // audit all users for all currencies involved.
            foreach (var currencyId in Currencies)
            {
                foreach (var id in userIds.Keys)
                {
                    await context.AuditUserBalance(id, currencyId);
                }

                await context.AuditUserBalance(Constant.SYSTEM_USER_PAYTOPIA, currencyId);

                await context.AuditUserBalance(Constant.SYSTEM_USER_STAGING, currencyId);

                await context.AuditUserBalance(Constant.SYSTEM_USER_CEFS, currencyId);
            }

            await context.SaveChangesAsync();

            Log.Message(LogLevel.Info, "SaveTransfersAndSuditUserBalances exited.");
        }
        private void TransferSumsToSystemStagingUser(List <string> transactionIds, Dictionary <int, decimal> tradeHistoryFeeByCurrencyTotals, Dictionary <int, decimal> paytopiaPaymentFeeByCurrencyTotals, IExchangeDataContext context)
        {
            Log.Message(LogLevel.Info, "TransferSumsToSystemStagingUser entered.");

            // deposit totals into system staging user wallets in preparation for transfer to all users....
            // both calls update the context
            transactionIds.AddRange(DepositTradeHistoryTotalToSystemUser(context, tradeHistoryFeeByCurrencyTotals));
            transactionIds.AddRange(TransferPaytopiaTotalToSystemUser(context, paytopiaPaymentFeeByCurrencyTotals));

            Log.Message(LogLevel.Info, "TransferSumsToSystemStagingUser exited.");
        }
        private async Task <Dictionary <Guid, CEFSBalance> > GetUsersAndTheirBalanacesForCEFPayments(DateTime startOfMonthToProcess, IExchangeDataContext context)
        {
            Log.Message(LogLevel.Info, "GetUsersForCEFPayments entered.");

            List <Guid> userIds;
            Dictionary <Guid, CEFSBalance> results = new Dictionary <Guid, CEFSBalance>();

            var tradePairs = await context.TradePair.Where(x => x.CurrencyId1 == Constant.CEFS_ID).Select(x => x.Id).ToListAsync();

            var startOfFollowingMonth = startOfMonthToProcess.AddMonths(1);

            // Get all CEFS data from the begining of time until the end of the last month;
            var allTransfers = await context.Transfer.Where(x => x.CurrencyId == Constant.CEFS_ID && x.Timestamp < startOfFollowingMonth).ToListAsync();

            var allDeposits = await context.Deposit.Where(x => x.CurrencyId == Constant.CEFS_ID && x.TimeStamp < startOfFollowingMonth).ToListAsync();

            var allWithdraw = await context.Withdraw.Where(x => x.CurrencyId == Constant.CEFS_ID && x.TimeStamp < startOfFollowingMonth).ToListAsync();

            var allTrades = await context.Trade.Where(x => (x.Status == TradeStatus.Pending || x.Status == TradeStatus.Partial) && tradePairs.Contains(x.TradePairId) && x.Timestamp < startOfFollowingMonth).ToListAsync();

            var allTradeHistory = await context.TradeHistory.Where(x => tradePairs.Contains(x.TradePairId) && x.Timestamp < startOfFollowingMonth).ToListAsync();

            // Get users who have been involved with CEFS
            userIds = new List <Guid>()
                      .Concat(allTransfers.Select(x => x.UserId))
                      .Concat(allTransfers.Select(x => x.ToUserId))
                      .Concat(allDeposits.Select(x => x.UserId))
                      .Concat(allWithdraw.Select(x => x.UserId))
                      .Concat(allTrades.Select(x => x.UserId))
                      .Concat(allTradeHistory.Select(x => x.UserId))
                      .Concat(allTradeHistory.Select(x => x.ToUserId))
                      .Distinct()
                      .ToList();

            foreach (var id in userIds)
            {
                CEFSBalance balance = new CEFSBalance();

                decimal totalDepositsConfirmed   = allDeposits.Where(x => x.UserId == id && x.Status == DepositStatus.Confirmed).Sum(x => x.Amount);
                decimal totalDepositsUnconfirmed = allDeposits.Where(x => x.UserId == id && x.Status == DepositStatus.UnConfirmed).Sum(x => x.Amount);
                decimal totalWithdraw            = allWithdraw.Where(x => x.UserId == id && x.Status == WithdrawStatus.Complete).Sum(x => x.Amount);
                decimal totalPendingWithdraw     = allWithdraw.Where(x => x.UserId == id && (x.Status != WithdrawStatus.Complete && x.Status != WithdrawStatus.Canceled)).Sum(x => x.Amount);
                decimal totalBuy  = allTradeHistory.Where(x => x.UserId == id).Sum(x => x.Amount);
                decimal totalSell = allTradeHistory.Where(x => x.ToUserId == id).Sum(x => x.Amount);

                decimal totalBuyFee   = allTradeHistory.Where(x => x.UserId == id).Sum(x => x.Fee);
                decimal totalSellFee  = allTradeHistory.Where(x => x.ToUserId == id).Sum(x => x.Fee);
                decimal totalBuyBase  = allTradeHistory.Where(x => x.UserId == id).Sum(x => x.Rate);
                decimal totalSellBase = allTradeHistory.Where(x => x.ToUserId == id).Sum(x => x.Rate);

                decimal heldForOrders     = allTrades.Where(x => x.UserId == id && x.Type == TradeHistoryType.Sell).Sum(x => x.Remaining);
                decimal heldForOrdersBase = allTrades.Where(x => x.UserId == id && x.Type == TradeHistoryType.Buy).Sum(x => (x.Remaining * x.Rate) + x.Fee);

                decimal totalTransferIn  = allTransfers.Where(x => x.ToUserId == id).Sum(x => x.Amount);
                decimal totalTransferOut = allTransfers.Where(x => x.UserId == id).Sum(x => x.Amount);

                /*
                 *      The following section has commented out variables because these are only applicable
                 *      to currencies that have a base currency.
                 *      If you are copying this code to use elsewhere, be advised that these variables may be needed
                 *      to create a robust and correct calculation.
                 */

                decimal totalTradeIn       = (totalBuy + 0 /*totalBuyBase*/);
                decimal totalTradeOut      = (totalSell + 0 /*totalSellBase*/ + 0 /*totalBuyFee */ + 0 /*totalSellFee*/);
                decimal totalIn            = (totalDepositsConfirmed + totalDepositsUnconfirmed + totalTradeIn + totalTransferIn);
                decimal totalOut           = (totalWithdraw + totalTradeOut + totalTransferOut);
                decimal totalBalance       = (totalIn - totalOut);
                decimal totalHeldForOrders = (heldForOrders + 0 /*heldForOrdersBase*/);

                balance.UserId            = id;
                balance.Total             = totalBalance;
                balance.Unconfirmed       = totalDepositsUnconfirmed;
                balance.HeldForOrders     = totalHeldForOrders;
                balance.PendingWithdrawal = totalPendingWithdraw;

                if (balance.Total > 0)
                {
                    results.Add(id, balance);
                }
            }

            Log.Message(LogLevel.Info, "GetUsersForCEFPayments exited.");

            return(results);
        }