private async Task <(int Count, string Cursor)> ProcessDeposits(string cursor) { var transactions = await _horizonService.GetTransactions(_depositBaseAddress, OrderDirection.ASC, cursor); var count = 0; var walletsToRefresh = new HashSet <(string assetId, string address)>(); var asset = _blockchainAssetsService.GetNativeAsset(); cursor = null; foreach (var transaction in transactions) { try { cursor = transaction.PagingToken; count++; // skip outgoing transactions and transactions without memo var memo = _horizonService.GetMemo(transaction); if (_depositBaseAddress.Equals(transaction.SourceAccount, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(memo)) { continue; } // transaction XDR doesn't contain operation IDs, // make a dedicated request to get operations var operations = await _horizonService.GetTransactionOperations(transaction.Hash); if (operations == null) { continue; } foreach (var op in operations) { string toAddress = null; long amount = 0; switch (op.Type.ToLower()) { case "payment": var payment = (PaymentOperationResponse)op; if (payment.AssetType == "native") { toAddress = payment.To; amount = asset.ParseDecimal(payment.Amount); } break; case "account_merge": var accountMerge = (AccountMergeOperationResponse)op; toAddress = accountMerge.Into; amount = _horizonService.GetAccountMergeAmount(transaction.ResultMetaXdr, accountMerge.SourceAccount); break; case "path_payment": var pathPayment = (PathPaymentStrictReceiveOperationResponse)op; if (pathPayment.AssetType == "native") { toAddress = pathPayment.To; amount = asset.ParseDecimal(pathPayment.Amount); } break; default: continue; } var addressWithExtension = $"{toAddress}{Constants.PublicAddressExtension.Separator}{memo.ToLower()}"; if (!ForbiddenCharacterAzureStorageUtils.IsValidRowKey(memo)) { await _log.WriteErrorAsync(nameof(BalanceService), nameof(ProcessDeposits), addressWithExtension, new Exception("Possible cashin skipped. It has forbiddden characters in memo.")); continue; } var observation = await _observationRepository.GetAsync(addressWithExtension); if (observation == null) { continue; } await _walletBalanceRepository.RecordOperationAsync(asset.Id, addressWithExtension, transaction.Ledger * 10, op.Id, transaction.Hash, amount); walletsToRefresh.Add((asset.Id, addressWithExtension)); } } catch (Exception ex) { throw new BusinessException($"Failed to process transaction. hash={transaction?.Hash}", ex); } } await _walletBalanceRepository.RefreshBalance(walletsToRefresh); if (!string.IsNullOrEmpty(cursor)) { await _keyValueStoreRepository.SetAsync(GetPagingTokenKey, cursor); } return(count, cursor); }