private async Task FinalizeSwap(IBitcoinTransaction transaction, IOffchainTransfer offchainTransfer)
        {
            var transactionsContextData = new Dictionary <string, SwapOffchainContextData>();

            var allTransfers = new HashSet <string>(offchainTransfer.GetAdditionalData().ChildTransfers)
            {
                offchainTransfer.Id
            };

            foreach (var transferId in allTransfers)
            {
                try
                {
                    var transfer = await _offchainTransferRepository.GetTransfer(transferId);

                    if (!transactionsContextData.ContainsKey(transfer.OrderId))
                    {
                        var ctx = await _bitcoinTransactionService.GetTransactionContext <SwapOffchainContextData>(transaction.TransactionId);

                        if (ctx == null)
                        {
                            continue;
                        }

                        transactionsContextData.Add(transfer.OrderId, ctx);
                    }

                    var contextData = transactionsContextData[transfer.OrderId];

                    var operation = contextData.Operations.FirstOrDefault(x => x.TransactionId == transferId);

                    if (operation == null)
                    {
                        continue;
                    }

                    if (string.IsNullOrWhiteSpace(operation?.ClientTradeId) || string.IsNullOrWhiteSpace(operation?.ClientId))
                    {
                        await _log.WriteWarningAsync(nameof(OffchainTransactionFinalizeFunction),
                                                     nameof(FinalizeSwap), operation?.ToJson(),
                                                     $"Missing fields. Client trade id {operation?.ClientTradeId}, client {operation?.ClientId}, transfer: {transferId}");

                        continue;
                    }

                    await Task.WhenAll(
                        _offchainTransferRepository.CompleteTransfer(transferId),
                        _clientTradesRepository.SetIsSettledAsync(operation.ClientId, operation.ClientTradeId, true)
                        );
                }
                catch (Exception e)
                {
                    await _log.WriteErrorAsync(nameof(OffchainTransactionFinalizeFunction), nameof(FinalizeSwap), $"Transfer: {transferId}", e);
                }
            }
        }
        private async Task <bool> ProcessIssue(IBitcoinTransaction transaction, CashInOutQueueMessage msg)
        {
            var isOffchain = await _clientSettingsRepository.IsOffchainClient(msg.ClientId);

            //Get client wallet
            var walletCredentials = await _walletCredentialsRepository
                                    .GetAsync(msg.ClientId);

            var amount  = msg.Amount.ParseAnyDouble();
            var context = await _bitcoinTransactionService.GetTransactionContext <IssueContextData>(transaction.TransactionId);

            //Register cash operation
            var cashOperationId = await _cashOperationsRepository
                                  .RegisterAsync(new CashInOutOperation
            {
                Id            = Guid.NewGuid().ToString("N"),
                ClientId      = msg.ClientId,
                Multisig      = walletCredentials.MultiSig,
                AssetId       = msg.AssetId,
                Amount        = Math.Abs(amount),
                DateTime      = DateTime.UtcNow,
                AddressTo     = walletCredentials.MultiSig,
                TransactionId = transaction.TransactionId,
                State         = isOffchain ? TransactionStates.InProcessOffchain : TransactionStates.InProcessOnchain
            });

            context.CashOperationId = cashOperationId;
            var contextJson = context.ToJson();
            var cmd         = new IssueCommand
            {
                Amount        = amount,
                AssetId       = msg.AssetId,
                Multisig      = walletCredentials.MultiSig,
                Context       = contextJson,
                TransactionId = Guid.Parse(transaction.TransactionId)
            };

            await _bitcoinTransactionsRepository.UpdateAsync(transaction.TransactionId, cmd.ToJson(), null, "");

            await _bitcoinTransactionService.SetTransactionContext(transaction.TransactionId, context);

            if (isOffchain)
            {
                await _offchainRequestService.CreateOffchainRequestAndNotify(transaction.TransactionId, msg.ClientId, msg.AssetId, (decimal)amount, null, OffchainTransferType.CashinToClient);
            }
            else
            {
                await _bitcoinCommandSender.SendCommand(cmd);
            }

            return(true);
        }
        private async Task <bool> ProcessManualOperation(IBitcoinTransaction transaction, CashInOutQueueMessage msg)
        {
            var asset = await _assetsService.TryGetAssetAsync(msg.AssetId);

            var walletCredentials = await _walletCredentialsRepository.GetAsync(msg.ClientId);

            var context          = transaction.GetContextData <CashOutContextData>();
            var isOffchainClient = await _clientSettingsRepository.IsOffchainClient(msg.ClientId);

            var isBtcOffchainClient = isOffchainClient && asset.Blockchain == Blockchain.Bitcoin;

            var operation = new CashInOutOperation
            {
                Id             = Guid.NewGuid().ToString(),
                ClientId       = msg.ClientId,
                Multisig       = walletCredentials.MultiSig,
                AssetId        = msg.AssetId,
                Amount         = msg.Amount.ParseAnyDouble(),
                DateTime       = DateTime.UtcNow,
                AddressFrom    = walletCredentials.MultiSig,
                AddressTo      = context.Address,
                TransactionId  = msg.Id,
                Type           = CashOperationType.None,
                BlockChainHash = asset.IssueAllowed && isBtcOffchainClient ? string.Empty : transaction.BlockchainHash,
                State          = GetState(transaction, isBtcOffchainClient)
            };

            var newHistoryEntry = new HistoryEntry
            {
                ClientId   = operation.ClientId,
                Amount     = operation.Amount,
                Currency   = asset.Name,
                DateTime   = msg.Date,
                OpType     = "CashInOut",
                CustomData = JsonConvert.SerializeObject(operation)
            };

            try
            {
                await _cashOperationsRepository.RegisterAsync(operation)
                .ContinueWith(t => _historyWriter.Push(newHistoryEntry));
            }
            catch (Exception e)
            {
                await _log.WriteErrorAsync(nameof(CashInOutQueue), nameof(ProcessManualOperation), null, e);

                return(false);
            }

            return(true);
        }
        private async Task FinalizeCashOut(IBitcoinTransaction transaction, IOffchainTransfer offchainTransfer)
        {
            var amount = Math.Abs((double)offchainTransfer.Amount);

            var data = await _exchangeOperationsService.FinishCashOutAsync(transaction.TransactionId, offchainTransfer.ClientId, (double)offchainTransfer.Amount, offchainTransfer.AssetId);

            await CreateHubCashoutIfNeed(offchainTransfer);

            if (!data.IsOk())
            {
                await _log.WriteWarningAsync("CashOutController", "CashOut", data.ToJson(), "ME operation failed");

                await _srvSlackNotifications.SendNotification(ChannelTypes.Errors, $"Cashout failed in ME, client: {offchainTransfer.ClientId}, transfer: {transaction.TransactionId}, ME code result: {data.Code}");
            }

            var contextData = await _bitcoinTransactionService.GetTransactionContext <CashOutContextData>(transaction.TransactionId);

            var swiftData = contextData.AddData?.SwiftData;

            if (swiftData != null)
            {
                await _cashOutAttemptRepository.SetIsSettledOffchain(contextData.ClientId, swiftData.CashOutRequestId);
            }
            else
            {
                if (offchainTransfer.AssetId == LykkeConstants.SolarAssetId)
                {
                    await PostSolarCashOut(offchainTransfer.ClientId, contextData.Address, amount, transaction.TransactionId);
                }
                else if (offchainTransfer.AssetId == LykkeConstants.ChronoBankAssetId)
                {
                    await PostChronoBankCashOut(contextData.Address, amount, transaction.TransactionId);
                }
                else if (offchainTransfer.AssetId == LykkeConstants.QuantaAssetId)
                {
                    await PostQuantaCashOut(contextData.Address, amount, transaction.TransactionId);
                }
                else
                {
                    var clientData = await _personalDataService.GetAsync(contextData.ClientId);

                    await _srvEmailsFacade.SendNoRefundOCashOutMail(clientData.Email, contextData.Amount, contextData.AssetId, transaction.BlockchainHash);
                }
            }
        }
        private async Task <bool> ProcessDestroy(IBitcoinTransaction transaction, CashInOutQueueMessage msg)
        {
            var amount = msg.Amount.ParseAnyDouble();
            //Get uncolor context data
            var context = await _bitcoinTransactionService.GetTransactionContext <UncolorContextData>(transaction.TransactionId);

            //Register cash operation
            var cashOperationId = await _cashOperationsRepository
                                  .RegisterAsync(new CashInOutOperation
            {
                Id            = Guid.NewGuid().ToString("N"),
                ClientId      = msg.ClientId,
                Multisig      = context.AddressFrom,
                AssetId       = msg.AssetId,
                Amount        = -Math.Abs(amount),
                DateTime      = DateTime.UtcNow,
                AddressFrom   = context.AddressFrom,
                AddressTo     = context.AddressTo,
                TransactionId = msg.Id
            });

            //Update context data
            context.CashOperationId = cashOperationId;
            var contextJson = context.ToJson();
            var cmd         = new DestroyCommand
            {
                Context       = contextJson,
                Amount        = Math.Abs(amount),
                AssetId       = msg.AssetId,
                Address       = context.AddressFrom,
                TransactionId = Guid.Parse(msg.Id)
            };

            await _bitcoinTransactionsRepository.UpdateAsync(transaction.TransactionId, cmd.ToJson(), null, "");

            await _bitcoinTransactionService.SetTransactionContext(transaction.TransactionId, context);

            //Send to bitcoin
            await _bitcoinCommandSender.SendCommand(cmd);

            return(true);
        }
        private async Task FinalizeTransfer(IBitcoinTransaction transaction, IOffchainTransfer transfer)
        {
            var contextData = await _bitcoinTransactionService.GetTransactionContext <TransferContextData>(transaction.TransactionId);

            switch (contextData.TransferType)
            {
            case TransferType.ToMarginAccount:
                await FinalizeTransferToMargin(contextData, transfer);

                return;

            case TransferType.Common:
                await FinalizeCommonTransfer(transaction, contextData);

                return;

            default:
                throw new ArgumentOutOfRangeException();
            }
        }
        private async Task FinalizeCommonTransfer(IBitcoinTransaction transaction, TransferContextData contextData)
        {
            foreach (var transfer in contextData.Transfers)
            {
                await _transferEventsRepository.SetIsSettledIfExistsAsync(transfer.ClientId, transfer.OperationId, true);

                var clientData = await _personalDataService.GetAsync(transfer.ClientId);

                if (transfer.Actions?.CashInConvertedOkEmail != null)
                {
                    await
                    _srvEmailsFacade.SendTransferCompletedEmail(clientData.Email, clientData.FullName,
                                                                transfer.Actions.CashInConvertedOkEmail.AssetFromId, transfer.Actions.CashInConvertedOkEmail.AmountFrom,
                                                                transfer.Actions.CashInConvertedOkEmail.AmountLkk, transfer.Actions.CashInConvertedOkEmail.Price, transaction.BlockchainHash);
                }

                if (transfer.Actions?.SendTransferEmail != null)
                {
                    await
                    _srvEmailsFacade.SendDirectTransferCompletedEmail(clientData.Email, clientData.FullName,
                                                                      transfer.Actions.SendTransferEmail.AssetId, transfer.Actions.SendTransferEmail.Amount,
                                                                      transaction.BlockchainHash);
                }

                if (transfer.Actions?.PushNotification != null)
                {
                    var clientAcc = await _clientAccountsRepository.GetByIdAsync(transfer.ClientId);

                    var asset = await _assetsService.TryGetAssetAsync(transfer.Actions.PushNotification.AssetId);

                    await _appNotifications.SendAssetsCreditedNotification(new[] { clientAcc.NotificationsId },
                                                                           transfer.Actions.PushNotification.Amount, transfer.Actions.PushNotification.AssetId,
                                                                           string.Format(TextResources.CreditedPushText, transfer.Actions.PushNotification.Amount.GetFixedAsString(asset.Accuracy),
                                                                                         transfer.Actions.PushNotification.AssetId));
                }
            }

            await _paymentTransactionsRepository.SetStatus(transaction.TransactionId, PaymentStatus.NotifyProcessed);
        }
 private static TransactionStates GetState(IBitcoinTransaction transaction, bool isBtcOffchainClient)
 {
     return(isBtcOffchainClient ?
            (string.IsNullOrWhiteSpace(transaction.BlockchainHash) ? TransactionStates.SettledOffchain : TransactionStates.SettledOnchain) :
            (string.IsNullOrWhiteSpace(transaction.BlockchainHash) ? TransactionStates.InProcessOnchain : TransactionStates.SettledOnchain));
 }
        private async Task <bool> ProcessCashOut(IBitcoinTransaction transaction, CashInOutQueueMessage msg)
        {
            //Get client wallet
            var walletCredentials = await _walletCredentialsRepository
                                    .GetAsync(msg.ClientId);

            var amount  = msg.Amount.ParseAnyDouble();
            var context = await _bitcoinTransactionService.GetTransactionContext <CashOutContextData>(transaction.TransactionId);

            var asset = await _assetsService.TryGetAssetAsync(msg.AssetId);

            var isOffchainClient = await _clientSettingsRepository.IsOffchainClient(msg.ClientId);

            var isBtcOffchainClient = isOffchainClient && asset.Blockchain == Blockchain.Bitcoin;

            bool isForwardWithdawal = context.AddData?.ForwardWithdrawal != null;

            if (isForwardWithdawal)
            {
                var baseAsset = await _assetsService.TryGetAssetAsync(asset.ForwardBaseAsset);

                var forwardCashInId = await _cashOperationsRepository
                                      .RegisterAsync(new CashInOutOperation
                {
                    Id            = Guid.NewGuid().ToString(),
                    ClientId      = msg.ClientId,
                    Multisig      = walletCredentials.MultiSig,
                    AssetId       = baseAsset.Id,
                    Amount        = Math.Abs(amount),
                    DateTime      = DateTime.UtcNow.AddDays(asset.ForwardFrozenDays),
                    AddressFrom   = walletCredentials.MultiSig,
                    AddressTo     = context.Address,
                    TransactionId = msg.Id,
                    Type          = CashOperationType.ForwardCashIn,
                    State         = isBtcOffchainClient ?
                                    TransactionStates.InProcessOffchain : TransactionStates.InProcessOnchain
                });

                await _forwardWithdrawalRepository.SetLinkedCashInOperationId(msg.ClientId, context.AddData.ForwardWithdrawal.Id,
                                                                              forwardCashInId);
            }

            //Register cash operation
            var cashOperationId = await _cashOperationsRepository
                                  .RegisterAsync(new CashInOutOperation
            {
                Id             = Guid.NewGuid().ToString(),
                ClientId       = msg.ClientId,
                Multisig       = walletCredentials.MultiSig,
                AssetId        = msg.AssetId,
                Amount         = -Math.Abs(amount),
                DateTime       = DateTime.UtcNow,
                AddressFrom    = walletCredentials.MultiSig,
                AddressTo      = context.Address,
                TransactionId  = msg.Id,
                Type           = isForwardWithdawal ? CashOperationType.ForwardCashOut : CashOperationType.None,
                BlockChainHash = asset.IssueAllowed && isBtcOffchainClient ? string.Empty : transaction.BlockchainHash,
                State          = GetState(transaction, isBtcOffchainClient)
            });

            //Update context data
            context.CashOperationId = cashOperationId;
            var contextJson = context.ToJson();
            var cmd         = new CashOutCommand
            {
                Amount             = Math.Abs(amount),
                AssetId            = msg.AssetId,
                Context            = contextJson,
                SourceAddress      = walletCredentials.MultiSig,
                DestinationAddress = context.Address,
                TransactionId      = Guid.Parse(transaction.TransactionId)
            };

            await _bitcoinTransactionsRepository.UpdateAsync(transaction.TransactionId, cmd.ToJson(), null, "");

            await _bitcoinTransactionService.SetTransactionContext(transaction.TransactionId, context);

            if (!isOffchainClient && asset.Blockchain == Blockchain.Bitcoin)
            {
                await _bitcoinCommandSender.SendCommand(cmd);
            }

            if (asset.Blockchain == Blockchain.Ethereum)
            {
                string errMsg = string.Empty;

                try
                {
                    var address = await _bcnClientCredentialsRepository.GetClientAddress(msg.ClientId);

                    var txRequest =
                        await _ethereumTransactionRequestRepository.GetAsync(Guid.Parse(transaction.TransactionId));

                    txRequest.OperationIds = new[] { cashOperationId };
                    await _ethereumTransactionRequestRepository.UpdateAsync(txRequest);

                    var response = await _srvEthereumHelper.SendCashOutAsync(txRequest.Id,
                                                                             txRequest.SignedTransfer.Sign,
                                                                             asset, address, txRequest.AddressTo,
                                                                             txRequest.Volume);

                    if (response.HasError)
                    {
                        errMsg = response.Error.ToJson();
                    }
                }
                catch (Exception e)
                {
                    errMsg = $"{e.GetType()}\n{e.Message}";
                }

                if (!string.IsNullOrEmpty(errMsg))
                {
                    await _ethClientEventLogs.WriteEvent(msg.ClientId, Event.Error,
                                                         new { Request = transaction.TransactionId, Error = errMsg }.ToJson());
                }
            }

            return(true);
        }
Exemple #10
0
 public static BaseContextData GetBaseContextData(this IBitcoinTransaction src)
 {
     return(Newtonsoft.Json.JsonConvert.DeserializeObject <BaseContextData>(src.ContextData));
 }
Exemple #11
0
 public static T GetContextData <T>(this IBitcoinTransaction src)
 {
     return(Newtonsoft.Json.JsonConvert.DeserializeObject <T>(src.ContextData));
 }
        private async Task FinalizeIssue(IBitcoinTransaction transaction)
        {
            var contextData = await _bitcoinTransactionService.GetTransactionContext <IssueContextData>(transaction.TransactionId);

            await _cashOperationsRepository.SetIsSettledAsync(contextData.ClientId, contextData.CashOperationId, true);
        }