public async Task <List <TxHistory> > GetHistory(TxDirectionType direction, string address, int take, string afterHash) { var observation = await _observationRepository.GetAsync(address); if (observation == null) { return(new List <TxHistory>()); } List <TxHistory> result; string afterPagingToken = null; if (!string.IsNullOrEmpty(afterHash)) { var tx = await _horizonService.GetTransactionDetails(afterHash); if (tx == null || !afterHash.Equals(tx.Hash, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException($"No transaction found. hash={afterHash}"); } afterPagingToken = tx.PagingToken; } if (_balanceService.IsDepositBaseAddress(address)) { result = await GetDepositBaseHistory(direction, address, take, afterPagingToken); } else { result = await GetBaseAddressHistory(direction, address, take, afterPagingToken); } return(result); }
private async Task ProcessBroadcastInProgress(Guid operationId) { try { var broadcast = await _broadcastRepository.GetAsync(operationId); if (broadcast == null) { throw new BusinessException($"Broadcast for observed operation not found. operationId={operationId})"); } var tx = await _horizonService.GetTransactionDetails(broadcast.Hash); if (tx == null) { // transaction still in progress return; } var paymentOp = _horizonService.GetFirstPaymentFromTransaction(tx); broadcast.State = TxBroadcastState.Completed; broadcast.Amount = paymentOp.Amount.InnerValue; broadcast.Fee = tx.FeePaid; broadcast.CreatedAt = tx.CreatedAt; broadcast.Ledger = tx.Ledger; await _broadcastRepository.MergeAsync(broadcast); await _observationRepository.DeleteIfExistAsync(operationId.ToString()); } catch (Exception ex) { var broadcast = new TxBroadcast { State = TxBroadcastState.Failed, Error = ex.Message, ErrorCode = TxExecutionError.Unknown }; await _broadcastRepository.MergeAsync(broadcast); await _observationRepository.DeleteIfExistAsync(operationId.ToString()); await _log.WriteErrorAsync(nameof(TransactionService), nameof(ProcessBroadcastInProgress), $"Failed to process in progress broadcast. operationId={operationId})", ex); } }
private async Task <int> QueryAndProcessPayments(string address, PaymentContext context) { int count = 0; var payments = await _horizonService.GetPayments(address, StellarSdkConstants.OrderAsc, context.Cursor); if (payments == null) { await _log.WriteWarningAsync(nameof(TransactionHistoryService), nameof(QueryAndProcessPayments), $"Address not found: {address}"); context.Cursor = null; return(count); } context.Cursor = null; foreach (var payment in payments.Embedded.Records) { try { context.Cursor = payment.PagingToken; count++; // create_account, payment or account_merge if (payment.TypeI == (int)OperationType.OperationTypeEnum.CREATE_ACCOUNT || payment.TypeI == (int)OperationType.OperationTypeEnum.PAYMENT && Core.Domain.Asset.Stellar.TypeName.Equals(payment.AssetType, StringComparison.OrdinalIgnoreCase) || payment.TypeI == (int)OperationType.OperationTypeEnum.ACCOUNT_MERGE) { if (context.Transaction == null || !context.Transaction.Hash.Equals(payment.TransactionHash, StringComparison.OrdinalIgnoreCase)) { var tx = await _horizonService.GetTransactionDetails(payment.TransactionHash); context.Transaction = tx ?? throw new BusinessException($"Transaction not found. hash={payment.TransactionHash}"); context.AccountMerge = 0; } var history = new TxHistory { AssetId = Core.Domain.Asset.Stellar.Id, Hash = payment.TransactionHash, PaymentId = payment.Id, CreatedAt = payment.CreatedAt, Memo = GetMemo(context.Transaction) }; // create_account if (payment.TypeI == (int)OperationType.OperationTypeEnum.CREATE_ACCOUNT) { history.FromAddress = payment.Funder; history.ToAddress = payment.Account; history.PaymentType = PaymentType.CreateAccount; decimal amount = Decimal.Parse(payment.StartingBalance); history.Amount = Convert.ToInt64(amount * One.Value); } // payment else if (payment.TypeI == (int)OperationType.OperationTypeEnum.PAYMENT) { history.FromAddress = payment.From; history.ToAddress = payment.To; history.PaymentType = PaymentType.Payment; decimal amount = Decimal.Parse(payment.Amount); history.Amount = Convert.ToInt64(amount * One.Value); } // account_merge else if (payment.TypeI == (int)OperationType.OperationTypeEnum.ACCOUNT_MERGE) { history.FromAddress = payment.Account; history.ToAddress = payment.Into; history.PaymentType = PaymentType.AccountMerge; var resultXdrBase64 = context.Transaction.ResultXdr; history.Amount = _horizonService.GetAccountMergeAmount(resultXdrBase64, context.AccountMerge); context.AccountMerge++; } else { throw new BusinessException($"Invalid payment type. type=${payment.TypeI}"); } history.OperationId = await _txBroadcastRepository.GetOperationId(payment.TransactionHash); if (address.Equals(history.ToAddress, StringComparison.OrdinalIgnoreCase)) { await _txHistoryRepository.InsertOrReplaceAsync(context.TableId, TxDirectionType.Incoming, history); context.Sequence++; } if (address.Equals(history.FromAddress, StringComparison.OrdinalIgnoreCase)) { await _txHistoryRepository.InsertOrReplaceAsync(context.TableId, TxDirectionType.Outgoing, history); context.Sequence++; } } } catch (Exception ex) { throw new BusinessException($"Failed to process payment of transaction. payment={payment?.Id}, hash={context?.Transaction?.Hash}", ex); } } return(count); }
private async Task ProcessBroadcastInProgress(Guid operationId) { TxBroadcast broadcast = null; try { broadcast = await _broadcastRepository.GetAsync(operationId); if (broadcast == null) { await _observationRepository.DeleteIfExistAsync(operationId.ToString()); throw new BusinessException($"Broadcast for observed operation not found. operationId={operationId}"); } var tx = await _horizonService.GetTransactionDetails(broadcast.Hash); if (tx == null) { // transaction still in progress return; } if (!broadcast.Hash.Equals(tx.Hash, StringComparison.OrdinalIgnoreCase)) { throw new BusinessException($"Transaction hash mismatch. actual={tx.Hash}, expected={broadcast.Hash}"); } var operation = _horizonService.GetFirstOperationFromTxEnvelopeXdr(tx.EnvelopeXdr); var operationType = operation.Discriminant.InnerValue; // ReSharper disable once SwitchStatementMissingSomeCases switch (operationType) { case OperationType.OperationTypeEnum.CREATE_ACCOUNT: { broadcast.Amount = operation.CreateAccountOp.StartingBalance.InnerValue; break; } case OperationType.OperationTypeEnum.PAYMENT: { broadcast.Amount = operation.PaymentOp.Amount.InnerValue; break; } case OperationType.OperationTypeEnum.ACCOUNT_MERGE: { broadcast.Amount = _horizonService.GetAccountMergeAmount(tx.ResultXdr, 0); break; } default: throw new BusinessException($"Unsupported operation type. type={operationType}"); } DateTime.TryParse(tx.CreatedAt, out var createdAt); broadcast.State = TxBroadcastState.Completed; broadcast.Fee = tx.FeeCharged; broadcast.CreatedAt = createdAt; broadcast.Ledger = tx.Ledger * 10; await _broadcastRepository.MergeAsync(broadcast); await _observationRepository.DeleteIfExistAsync(operationId.ToString()); } catch (Exception ex) { if (broadcast != null) { broadcast.State = TxBroadcastState.Failed; broadcast.Error = ex.Message; broadcast.ErrorCode = TxExecutionError.Unknown; await _broadcastRepository.MergeAsync(broadcast); await _observationRepository.DeleteIfExistAsync(operationId.ToString()); } throw new BusinessException($"Failed to process in progress broadcast. operationId={operationId}", ex); } }