예제 #1
0
        public async Task EnqueueCashoutAsync(IHotWalletOperation hotWalletCashout)
        {
            if (hotWalletCashout == null)
            {
                return;
            }

            var existingCashout = await _hotWalletCashoutRepository.GetAsync(hotWalletCashout.OperationId);

            if (existingCashout != null)
            {
                throw new ClientSideException(ExceptionType.EntityAlreadyExists, "Operation with Id was enqueued before");
            }

            hotWalletCashout.OperationType = HotWalletOperationType.Cashout;
            await RetryCashoutAsync(hotWalletCashout);
        }
예제 #2
0
        private async Task <IHotWalletOperation> GetOperationAsync(string trHash, string operationId)
        {
            operationId = operationId ?? "";
            var cashoutTransaction = await _hotWalletCashoutTransactionRepository.GetByTransactionHashAsync(trHash) ??
                                     await _hotWalletCashoutTransactionRepository.GetByOperationIdAsync(operationId);

            var cashout = await _hotWalletCashoutRepository.GetAsync(cashoutTransaction?.OperationId);

            return(cashout);
        }
예제 #3
0
        /// <param name="depositContractAddress"></param>
        /// <param name="erc20TokenAddress"></param>
        /// <param name="destinationAddress"></param>
        /// <returns>TransactionHash</returns>
        public async Task <string> RecievePaymentFromDepositContract(string depositContractAddress,
                                                                     string erc20TokenAddress, string destinationAddress)
        {
            var depositContract = await _contractRepository.GetByContractAddress(depositContractAddress);

            if (depositContract == null)
            {
                throw new ClientSideException(ExceptionType.WrongParams, $"DepositContractAddress {depositContractAddress} does not exist");
            }

            var userWallet = await TransferWalletSharedService.GetUserTransferWalletAsync(_userTransferWalletRepository,
                                                                                          depositContractAddress, erc20TokenAddress, depositContract.UserAddress);

            if (userWallet != null && !string.IsNullOrEmpty(userWallet.LastBalance))
            {
                throw new ClientSideException(ExceptionType.TransferInProcessing, $"Transfer from {depositContractAddress} was started before wait for it to complete");
            }

            var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(depositContractAddress, erc20TokenAddress);

            if (balance == 0)
            {
                throw new ClientSideException(ExceptionType.NotEnoughFunds, $"No tokens detected at deposit address {depositContractAddress}");
            }

            var guidStr = Guid.NewGuid().ToString();

            var message = new Lykke.Service.EthereumCore.Core.Messages.LykkePay.LykkePayErc20TransferMessage()
            {
                OperationId = guidStr
            };

            var existingOperation = await _operationsRepository.GetAsync(guidStr);

            if (existingOperation != null)
            {
                throw new ClientSideException(ExceptionType.EntityAlreadyExists, "Try again later");
            }
            var transactionSenderAddress = _appSettings.LykkePay.LykkePayAddress;
            var estimationResult         = await Erc20SharedService.EstimateDepositTransferAsync(_web3,
                                                                                                 _settings.Erc20DepositContract.Abi,
                                                                                                 transactionSenderAddress,
                                                                                                 depositContractAddress,
                                                                                                 erc20TokenAddress,
                                                                                                 destinationAddress);

            if (!estimationResult)
            {
                throw new ClientSideException(ExceptionType.WrongDestination, $"Can't estimate transfer {depositContractAddress}, {erc20TokenAddress}, {destinationAddress}");
            }

            await _operationsRepository.SaveAsync(new HotWalletOperation()
            {
                Amount        = balance,
                FromAddress   = depositContractAddress,
                OperationId   = guidStr,
                OperationType = HotWalletOperationType.Cashin,
                ToAddress     = destinationAddress,
                TokenAddress  = erc20TokenAddress
            });

            await _transferQueue.PutRawMessageAsync(Newtonsoft.Json.JsonConvert.SerializeObject(message));

            await TransferWalletSharedService.UpdateUserTransferWalletAsync(_userTransferWalletRepository,
                                                                            depositContractAddress, erc20TokenAddress, depositContract.UserAddress, balance.ToString());

            return(guidStr);
        }
예제 #4
0
        public async Task Execute(LykkePayErc20TransferNotificationMessage message, QueueTriggeringContext context)
        {
            if (string.IsNullOrEmpty(message?.OperationId))
            {
                await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                "Execute", "", "Empty message skipped");

                return;
            }

            try
            {
                var operation = await _operationsRepository.GetAsync(message.OperationId);

                if (operation == null)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", message.ToJson(), $"No operation for id {message?.OperationId} message skipped");

                    return;
                }

                Transaction transaction = await _web3.Eth.Transactions.GetTransactionByHash.SendRequestAsync(message.TransactionHash);

                if (transaction == null)
                {
                    message.LastError = "Not yet indexed";
                    message.DequeueCount++;
                    context.MoveMessageToEnd(message.ToJson());
                    context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 30000);
                    return;
                }

                TransferEvent @event = new TransferEvent(operation.OperationId,
                                                         message.TransactionHash,
                                                         message.Balance,
                                                         operation.TokenAddress,
                                                         operation.FromAddress,
                                                         operation.ToAddress,
                                                         transaction?.BlockHash,
                                                         (ulong)transaction?.BlockNumber.Value,
                                                         SenderType.EthereumCore,
                                                         EventType.Started,
                                                         WorkflowType.LykkePay,
                                                         DateTime.UtcNow);

                await _rabbitQueuePublisher.PublshEvent(@event);
            }
            catch (Exception ex)
            {
                if (message == null)
                {
                    return;
                }

                if (ex.Message != message.LastError)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayTransferNotificationJob),
                                                    "Execute", message.ToJson(), "transaction.OperationId");
                }

                message.LastError = ex.Message;
                message.DequeueCount++;
                context.MoveMessageToEnd(message.ToJson());
                context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 200);

                await _logger.WriteErrorAsync(nameof(LykkePayTransferNotificationJob), "Execute", message.ToJson(), ex);
            }
        }
예제 #5
0
        public async Task Execute(LykkePayErc20TransferMessage transaction, QueueTriggeringContext context)
        {
            IHotWalletOperation operation = null;

            if (string.IsNullOrEmpty(transaction?.OperationId))
            {
                await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                "Execute", "", "Empty message skipped");

                return;
            }

            try
            {
                operation = await _operationsRepository.GetAsync(transaction.OperationId);

                if (operation == null)
                {
                    await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"No operation for id {transaction?.OperationId} message skipped");

                    return;
                }

                var transactionSenderAddress = _settings.Airlines.AirlinesAddress;
                var balance =
                    await _ercInterfaceService.GetPendingBalanceForExternalTokenAsync(operation.FromAddress,
                                                                                      operation.TokenAddress);

                if (balance < operation.Amount)
                {
                    await _logger.WriteWarningAsync(nameof(Erc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"Sendig Failed Event: DepositAddress: {operation.FromAddress}, " +
                                                    $"TokenAddress: {operation.TokenAddress}, " +
                                                    $"DesiredAmount: {operation.Amount} " +
                                                    $"CurrentBalance {balance}");

                    TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                             "",
                                                             operation.Amount.ToString(),
                                                             operation.TokenAddress,
                                                             operation.FromAddress,
                                                             operation.ToAddress,
                                                             "",
                                                             0,
                                                             SenderType.EthereumCore,
                                                             EventType.NotEnoughFunds,
                                                             WorkflowType.Airlines,
                                                             DateTime.UtcNow);

                    await _rabbitQueuePublisher.PublshEvent(@event);

                    return;
                }

                var trHash = await Erc223SharedService.StartDepositTransferAsync(_web3,
                                                                                 _settings.EthereumCore.Erc223DepositContract.Abi,
                                                                                 transactionSenderAddress,
                                                                                 operation.FromAddress,
                                                                                 operation.TokenAddress,
                                                                                 operation.ToAddress,
                                                                                 operation.Amount);

                await _hotWalletTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                });

                var message = new CoinTransactionMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                };

                //Observe transaction
                await _transactionMonitoringQueue.PutRawMessageAsync(message.ToJson());

                var notificationMessage = new LykkePayErc20TransferNotificationMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash,
                    Balance         = operation.Amount.ToString()
                };

                await _transactionStartedNotificationQueue.PutRawMessageAsync(notificationMessage.ToJson());
            }
            catch (ClientSideException ex)
            {
                if (operation == null)
                {
                    return;
                }

                TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                         "",
                                                         operation.Amount.ToString(),
                                                         operation.TokenAddress,
                                                         operation.FromAddress,
                                                         operation.ToAddress,
                                                         "",
                                                         0,
                                                         SenderType.EthereumCore,
                                                         EventType.Failed,
                                                         WorkflowType.Airlines,
                                                         DateTime.UtcNow);

                _logger.WriteWarning("Execute", operation.ToJson(), "ClientSideException", ex);

                await _rabbitQueuePublisher.PublshEvent(@event);
            }
            catch (Exception ex)
            {
                if (transaction == null)
                {
                    return;
                }

                if (ex.Message != transaction.LastError)
                {
                    _logger.WriteWarning("Execute", transaction.ToJson(), $"{transaction.OperationId}");
                }

                transaction.LastError = ex.Message;
                transaction.DequeueCount++;
                context.MoveMessageToEnd(transaction.ToJson());
                context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 200);

                _logger.WriteError("Execute", transaction.ToJson(), ex);
            }
        }
예제 #6
0
        public async Task Execute(LykkePayErc20TransferMessage transaction, QueueTriggeringContext context)
        {
            IHotWalletOperation operation = null;

            if (string.IsNullOrEmpty(transaction?.OperationId))
            {
                await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                "Execute", "", "Empty message skipped");

                return;
            }

            try
            {
                operation = await _operationsRepository.GetAsync(transaction.OperationId);

                if (operation == null)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"No operation for id {transaction?.OperationId} message skipped");

                    return;
                }

                var transactionSenderAddress = _settings.LykkePay.LykkePayAddress;
                var balance =
                    await _ercInterfaceService.GetPendingBalanceForExternalTokenAsync(operation.FromAddress,
                                                                                      operation.TokenAddress);

                if (balance == 0)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(),
                                                    $"DepositAddress: {operation.FromAddress}, TokenAddress: {operation.TokenAddress}");

                    //TODO: Transaction Failed

                    return;
                }

                var trHash = await Erc20SharedService.StartDepositTransferAsync(_web3, _settings.EthereumCore,
                                                                                transactionSenderAddress,
                                                                                operation.FromAddress, operation.TokenAddress, operation.ToAddress);

                await _hotWalletTransactionRepository.SaveAsync(new HotWalletCashoutTransaction()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                });

                var message = new CoinTransactionMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash
                };

                //Observe transaction
                await _transactionMonitoringQueue.PutRawMessageAsync(
                    Newtonsoft.Json.JsonConvert.SerializeObject(message));

                var notificationMessage = new LykkePayErc20TransferNotificationMessage()
                {
                    OperationId     = transaction.OperationId,
                    TransactionHash = trHash,
                    Balance         = balance.ToString() //At the starting moment(may change at the end of the execution)
                };

                await _transactionStartedNotificationQueue.PutRawMessageAsync(
                    Newtonsoft.Json.JsonConvert.SerializeObject(notificationMessage));
            }
            catch (ClientSideException ex)
            {
                if (operation == null)
                {
                    return;
                }

                TransferEvent @event = new TransferEvent(transaction.OperationId,
                                                         "",
                                                         operation.Amount.ToString(),
                                                         operation.TokenAddress,
                                                         operation.FromAddress,
                                                         operation.ToAddress,
                                                         "",
                                                         0,
                                                         SenderType.EthereumCore,
                                                         EventType.Failed,
                                                         WorkflowType.LykkePay,
                                                         DateTime.UtcNow);

                await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob), "Execute",
                                                operation.ToJson(), ex);

                await _rabbitQueuePublisher.PublshEvent(@event);
            }
            catch (Exception ex)
            {
                if (transaction == null)
                {
                    return;
                }

                if (ex.Message != transaction.LastError)
                {
                    await _logger.WriteWarningAsync(nameof(LykkePayErc20DepositTransferStarterJob),
                                                    "Execute", transaction.ToJson(), "transaction.OperationId");
                }

                transaction.LastError = ex.Message;
                transaction.DequeueCount++;
                context.MoveMessageToEnd(transaction.ToJson());
                context.SetCountQueueBasedDelay(_settings.EthereumCore.MaxQueueDelay, 200);

                await _logger.WriteErrorAsync(nameof(LykkePayErc20DepositTransferStarterJob), "Execute",
                                              transaction.ToJson(), ex);
            }
        }
        /// <param name="depositContractAddress"></param>
        /// <param name="erc20TokenAddress"></param>
        /// <param name="destinationAddress"></param>
        /// <returns>TransactionHash</returns>
        public async Task <string> RecievePaymentFromDepositContractAsync(string depositContractAddress,
                                                                          string erc20TokenAddress, string destinationAddress, string tokenAmount)
        {
            var depositContract = await _contractRepository.GetByContractAddress(depositContractAddress);

            if (depositContract == null)
            {
                throw new ClientSideException(ExceptionType.WrongParams, $"DepositContractAddress {depositContractAddress} does not exist");
            }

            var amount  = System.Numerics.BigInteger.Parse(tokenAmount);
            var balance = await _ercInterfaceService.GetBalanceForExternalTokenAsync(depositContractAddress, erc20TokenAddress);

            if (balance == 0 || amount > balance)
            {
                throw new ClientSideException(ExceptionType.NotEnoughFunds,
                                              $"Not enough tokens to proceed with withdrawal detected at deposit address {depositContractAddress}. " +
                                              $"Current balance: {balance}");
            }

            var guidStr = Guid.NewGuid().ToString();

            var message = new Lykke.Service.EthereumCore.Core.Messages.LykkePay.LykkePayErc20TransferMessage()
            {
                OperationId = guidStr
            };

            var existingOperation = await _operationsRepository.GetAsync(guidStr);

            if (existingOperation != null)
            {
                throw new ClientSideException(ExceptionType.EntityAlreadyExists, "Try again later");
            }

            var transactionSenderAddress = _appSettings.Airlines.AirlinesAddress;
            var estimationResult         = await Erc223SharedService.EstimateDepositTransferAsync(_web3,
                                                                                                  _appSettings.EthereumCore.Erc223DepositContract.Abi,
                                                                                                  transactionSenderAddress,
                                                                                                  depositContractAddress,
                                                                                                  erc20TokenAddress,
                                                                                                  destinationAddress,
                                                                                                  amount);

            if (!estimationResult)
            {
                throw new ClientSideException(ExceptionType.WrongDestination, $"Can't estimate transfer {depositContractAddress}, {erc20TokenAddress}, {destinationAddress}");
            }

            await _operationsRepository.SaveAsync(new HotWalletOperation()
            {
                Amount        = amount,
                FromAddress   = depositContractAddress,
                OperationId   = guidStr,
                OperationType = HotWalletOperationType.Cashin,
                ToAddress     = destinationAddress,
                TokenAddress  = erc20TokenAddress
            });

            await _transferQueue.PutRawMessageAsync(Newtonsoft.Json.JsonConvert.SerializeObject(message));

            return(guidStr);
        }