//return whether we have sent to rabbit or not private async Task <bool> SendCompleteEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction) { try { var operation = await GetOperationAsync(transactionHash, operationId); if (operation == null) { return(false); } string amount; Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType type; switch (operation.OperationType) { case HotWalletOperationType.Cashout: amount = operation.Amount.ToString(); type = Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType.CashoutCompleted; break; case HotWalletOperationType.Cashin: await _hotWalletService.RemoveCashinLockAsync(operation.TokenAddress, operation.FromAddress); amount = (await _cashinEventRepository.GetAsync(transactionHash)).Amount; type = Lykke.Job.EthereumCore.Contracts.Enums.HotWalletEventType.CashinCompleted; break; default: return(false); } HotWalletEvent @event = new HotWalletEvent(operation.OperationId, transactionHash, operation.FromAddress, operation.ToAddress, amount, operation.TokenAddress, type); await _rabbitQueuePublisher.PublshEvent(@event); return(true); } catch (Exception e) { await _log.WriteErrorAsync(nameof(HotWalletMonitoringTransactionJob), "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow); SendMessageToTheQueueEnd(context, transaction, 100); return(false); } }
//return whether we have sent to rabbit or not private async Task <bool> SendCompleteEvent(string transactionHash, string operationId, bool success, QueueTriggeringContext context, CoinTransactionMessage transaction) { try { var operation = await GetOperationAsync(transactionHash, operationId); if (operation == null) { return(false); } (BigInteger? amount, string blockHash, ulong blockNumber)transferedInfo = (null, null, 0); string amount = operation.Amount.ToString(); switch (operation.OperationType) { case HotWalletOperationType.Cashout: break; case HotWalletOperationType.Cashin: //There will be nothing to index in failed event if (success) { transferedInfo = await _transactionEventsService.IndexCashinEventsForErc20TransactionHashAsync( transactionHash); if (transferedInfo.amount == null || transferedInfo.amount == 0) { //Not yet indexed SendMessageToTheQueueEnd(context, transaction, 10000); return(false); } amount = transferedInfo.amount.ToString(); } break; default: return(false); } EventType eventType = success ? EventType.Completed : EventType.Failed; TransferEvent @event = new TransferEvent(operation.OperationId, transactionHash, amount, operation.TokenAddress, operation.FromAddress, operation.ToAddress, transferedInfo.blockHash, transferedInfo.blockNumber, SenderType.EthereumCore, eventType, WorkflowType.Airlines, DateTime.UtcNow); await _rabbitQueuePublisher.PublshEvent(@event); return(true); } catch (Exception e) { await _log.WriteErrorAsync(nameof(AirlinesHotWalletMonitoringTransactionJob), "SendCompletedCoinEvent", $"trHash: {transactionHash}", e, DateTime.UtcNow); SendMessageToTheQueueEnd(context, transaction, 100); return(false); } }
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 IndexCashinEventsForErc20Deposits() { var indexerStatusResponse = await _indexerApi.ApiIsAliveGetWithHttpMessagesAsync(); if (indexerStatusResponse.Response.IsSuccessStatusCode) { var responseContent = await indexerStatusResponse.Response.Content.ReadAsStringAsync(); var indexerStatus = JObject.Parse(responseContent); var lastIndexedBlock = BigInteger.Parse(indexerStatus["blockchainTip"].Value <string>()); var lastSyncedBlock = await GetLastSyncedBlockNumber(HotWalletMarker); while (lastSyncedBlock <= lastIndexedBlock) { //Get all transfers from block var transfersResponse = await _indexerApi.ApiErc20TransferHistoryGetErc20TransfersPostAsync ( new GetErc20TransferHistoryRequest { BlockNumber = (long)lastSyncedBlock, } ); switch (transfersResponse) { case IEnumerable <Erc20TransferHistoryResponse> transfers: foreach (var transfer in transfers) { // Ignore transfers from not deposit contract addresses var checkResult = await _depositContractService.ContainsWithTypeAsync(transfer.To); if (!checkResult.Item1) { continue; } string trHash = transfer.TransactionHash ?? ""; string id = (await _airHotWalletCashoutTransactionRepository.GetByTransactionHashAsync(trHash)) ?.OperationId ?? (await _lpHotWalletCashoutTransactionRepository.GetByTransactionHashAsync(trHash)) ?.OperationId ?? null; await _rabbitQueuePublisher.PublshEvent(new TransferEvent(id, transfer.TransactionHash, transfer.TransferAmount, transfer.Contract, transfer.FromProperty, transfer.To, transfer.BlockHash, (ulong)transfer.BlockNumber, SenderType.Customer, EventType.Detected, (Job.EthereumCore.Contracts.Enums.LykkePay.WorkflowType)checkResult.Item2, DateTime.UtcNow )); } break; case ApiException exception: throw new Exception($"Ethereum indexer responded with error: {exception.Error.Message}"); default: throw new Exception($"Ethereum indexer returned unexpected response"); } var blockCurrent = (await _indexerApi.ApiBlockNumberByBlockNumberGetAsync((long)lastSyncedBlock)) as BlockResponse; if (blockCurrent == null) { return; } var parentBlock = await _blockSyncedRepository.GetByPartitionAndHashAsync(HotWalletMarker, blockCurrent.ParentHash); if (parentBlock == null) { lastSyncedBlock--; await _blockSyncedRepository.DeleteByPartitionAndHashAsync(HotWalletMarker, blockCurrent.ParentHash); continue; } await _blockSyncedRepository.InsertAsync(new BlockSyncedByHash() { BlockNumber = lastSyncedBlock.ToString(), Partition = HotWalletMarker, BlockHash = blockCurrent.BlockHash }); lastSyncedBlock++; } } else { throw new Exception("Can not obtain ethereum indexer status."); } }
public async Task PublishEvent(ICoinEvent coinEvent) { var @event = GetCoinEvent(coinEvent); string coinEventSerialized = Newtonsoft.Json.JsonConvert.SerializeObject(@event); await _rabbitPublisher.PublshEvent(coinEventSerialized); }
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); } }