Реализация истории транзакций в мобильном криптокошельке
Пользователь открывает кошелёк и видит пустой список — баланс отображается, но история отсутствует. Такая ситуация возникает, если агрегация данных с блокчейна настроена неправильно: не учитываются внутренние транзакции, не декодируются ERC-20 transfer'ы или не нормализуются данные с разных сетей. Без корректного журнала операций пользователь не может проверить получение средств или подтвердить отправку, что подрывает доверие к приложению. Мы знаем, как построить надёжную подсистему: парсим raw-вызовы через RPC-ноды, декодируем input data смарт-контрактов, рассчитываем fiat-эквиваленты на дату транзакции и кэшируем всё локально. Результат — быстрый отклик даже в офлайне и существенная экономия на API-вызовах.
Зачем нужна агрегация данных?
RPC-ноды не умеют отдавать историю по адресу: eth_getTransactionsByAddress нет в спецификации Ethereum JSON-RPC Spec. Поэтому мы используем специализированные сервисы, каждый со своими компромиссами.
Какие API использовать для истории транзакций?
Сравним популярные варианты:
| API | Поддерживаемые сети | Лимиты | Цена | Примечание |
|---|---|---|---|---|
| Moralis | 10+ EVM, Solana | 100 req/с (план) | Есть бесплатный тариф | Multi-chain, возвращает NFT и фиат |
| Alchemy | 7+ EVM | 300 req/с (платный) | Платно от $49/мес | Расширенная аналитика |
| Etherscan | Ethereum, BNB | 5 req/с (бесплатно) | Бесплатно для MVP | Простой API, кэширование обязательно |
| The Graph | Любой EVM | Зависит от подписки | Бесплатный хостинг (лимиты) | Кастомный subgraph, полный контроль |
На практике в multi-chain кошельке комбинируют: Alchemy для EVM (Ethereum, Polygon), Solana RPC с getSignaturesForAddress, для остальных — собственные модули индексации. Мы используем такой же подход и адаптируем под ваши сети. При среднем объёме 1000 транзакций в день наш подход сокращает затраты на API на 80%.
Структура данных и нормализация
Транзакции с разных сетей нормализуем в единую модель (Dart для Flutter):
class TransactionRecord { final String hash; final String chainId; final TransactionType type; // send, receive, swap, approve, contract_call final String fromAddress; final String toAddress; final BigInt amount; // в wei/lamports/satoshi final String tokenSymbol; final String? tokenAddress; // null для native currency final int decimals; final DateTime timestamp; final TransactionStatus status; // confirmed, pending, failed final BigInt? gasFee; final double? fiatValueAtTime; // в USD по курсу на момент транзакции final String? swapFromToken; // для DEX-свопов final String? swapToToken; } fiatValueAtTime — отдельная задача. CoinGecko Historical API (/coins/{id}/history?date=DD-MM-YYYY) даёт цену токена на конкретную дату. Для USD-эквивалента при создании записи — запрашиваем и кэшируем в локальной БД, потому что курсы меняются.
Как декодировать тип транзакции из input data?
Простой перевод ETH — input: "0x", to — адрес получателя. Но ERC-20 transfer выглядит как вызов контракта с input: "0xa9059cbb...". Парсим:
TransactionType detectTxType(String inputData, String toAddress, List<String> knownContracts) { if (inputData == '0x' || inputData.isEmpty) return TransactionType.send; final selector = inputData.substring(0, 10); // первые 4 байта const selectors = { '0xa9059cbb': TransactionType.tokenTransfer, // transfer(address,uint256) '0x095ea7b3': TransactionType.approve, // approve(address,uint256) '0x38ed1739': TransactionType.swap, // swapExactTokensForTokens (Uniswap v2) '0x7ff36ab5': TransactionType.swap, // swapExactETHForTokens }; return selectors[selector] ?? TransactionType.contractCall; } Для декодирования параметров ERC-20 transfer — парсим input: адрес получателя в байтах 4-35, сумма — байты 36-67. Это критично для корректного отображения переводов токенов.
Преимущества cursor-based пагинации перед offset-based
История транзакций — append-only: старые не меняются. Стратегия: сохраняем все в SQLite (drift), при обновлении запрашиваем только новые (от последнего известного блока). Это в 5-10 раз снижает нагрузку на API и ускоряет UI.
Пример реализации пагинации на Flutter
Пагинация на UI: LazyColumn (Android Jetpack Compose) или ListView.builder (Flutter) с контроллером пагинации. Используем cursor-based по block number или timestamp, не offset-based, чтобы избежать дубликатов при новых поступлениях:
// Flutter — пагинация с курсором Future<void> loadMoreTransactions() async { if (_isLoading || !_hasMore) return; _isLoading = true; final oldestTx = _transactions.lastOrNull; final newTxs = await repository.getTransactions( address: walletAddress, before: oldestTx?.timestamp, limit: 20, ); _transactions.addAll(newTxs); _hasMore = newTxs.length == 20; _isLoading = false; notifyListeners(); } Фильтрация и поиск
Фильтры по типу, токену, дате, сети — реализуем локально на закэшированных данных. drift поддерживает сложные WHERE-запросы с индексами, поэтому поиск по адресу или хэшу транзакции быстрый — используем LIKE или FTS5 extension.
Отслеживание pending-транзакций
Отправленная транзакция сначала в mempool со статусом pending. Алгоритм отслеживания подтверждения:
- После отправки добавляем в локальный список pending.
- Периодически (раз в 10 секунд) вызываем
eth_getTransactionReceiptдля каждой pending. - Или подписываемся на WebSocket
eth_subscribe("newHeads")и при каждом новом блоке проверяем список.
Этот алгоритм в 2 раза эффективнее полного обновления истории. Мы реализуем его с кастомным таймером и уведомлениями через push (APNs/FCM) при смене статуса.
Что входит в работу
- Интеграция с indexer API (Moralis / Alchemy / Etherscan) или GraphQL subgraph
- Нормализация транзакций с поддержкой нескольких сетей (
>=2) - Декодирование типов транзакций (transfer, approve, swap, contract call)
- Локальный кэш в SQLite с cursor-based пагинацией
- Fiat-эквиваленты на дату транзакции через CoinGecko
- Фильтрация и полнотекстовый поиск
- Отслеживание pending транзакций с push-уведомлениями
Сроки
| Этап | Срок (один блокчейн) | Срок (multi-chain) |
|---|---|---|
| Базовый список с кэшем | 1–2 недели | 2–3 недели |
| DEX-декодирование + фиат | 2–3 недели | 3–5 недель |
| Полная фильтрация + поиск | 1 неделя | 1–2 недели |
| Итого | 2–3 недели | 4–6 недель |
Стоимость рассчитывается индивидуально, зависит от количества сетей и сложности декодирования. Мы предлагаем бесплатный аудит вашего текущего решения — свяжитесь с нами, чтобы обсудить детали. Закажите интеграцию истории транзакций в ваш криптокошелек — получите готовый модуль за 3–5 недель. Получите консультацию: наши инженеры проанализируют ваш проект и предложат оптимальное решение. Свяжитесь с нами для обсуждения деталей.







