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