Мониторинг мемпула Ethereum на Python: Скрипт Whale Alerts через Web3.py

Мониторинг мемпула Ethereum на Python: Скрипт Whale Alerts через Web3.py Python & API
Пошаговое руководство от Quant-разработчика по созданию асинхронного скрипта на Python для отслеживания крупных транзакций (китов) в мемпуле Ethereum с помощью Web3.py.
Суть: Асинхронный Python-скрипт на базе Web3.py подключается к ноде Ethereum через WebSockets, подписывается на поток транзакций в мемпуле (pendingTransactions), декодирует вызовы смарт-контрактов «на лету» и мгновенно фильтрует переводы ETH и стейблкоинов (USDT/USDC) объемом выше заданного лимита.

Исходный код

В сфере количественного анализа и алгоритмической торговли скорость получения информации определяет альфу. Мемпул (mempool) Ethereum — это «зал ожидания» блокчейна, где транзакции находятся перед тем, как валидаторы включат их в блок. Мониторинг мемпула позволяет крупным игрокам детектировать крупные перемещения средств (Whale Alerts) до их фактического исполнения. Это дает возможность прогнозировать локальные изменения ликвидности, выявлять крупные рыночные ордера на DEX и защищать свои алгоритмы от проскальзывания.

При разработке торговых систем, таких как Реализация Grid Bot для Bybit на Python: Гайд Quant-разработчика, скорость обработки рыночных событий является ключевым фактором успеха. Ниже представлен готовый к продакшену асинхронный скрипт. Он подключается к ноде Ethereum через WebSocket, подписывается на поток транзакций в мемпуле и в реальном времени фильтрует переводы ETH, а также стейблкоинов USDT и USDC, превышающие заданные лимиты.

import asyncio
import logging
from web3 import AsyncWeb3
from web3.providers import AsyncWebSocketProvider

# Настройка логирования
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)

# Укажите ваш WSS URL (например, Alchemy или Infura)
WSS_NODE_URL = "wss://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY"

# Пороговые значения для фильтрации (Whale Alerts)
ETH_THRESHOLD = 50.0       # в ETH
USDT_THRESHOLD = 100000.0  # в USDT ($100k)
USDC_THRESHOLD = 100000.0  # в USDC ($100k)

# Адреса смарт-контрактов стейблкоинов
USDT_ADDRESS = "0xdAC17F958D2ee523a2206206994597C13D831ec7"
USDC_ADDRESS = "0xA0b86991c6218b36c1d19D4a2e9Eb0ce3606eB48"

# Селектор метода transfer(address,uint256) -> 0xa9059cbb
ERC20_TRANSFER_SIGNATURE = "0xa9059cbb"

def decode_erc20_transfer(input_data: str) -> tuple[str, float] | None:
    """Быстрое декодирование транзакции transfer без загрузки полного ABI"""
    try:
        if not input_data or len(input_data) < 138:
            return None
        
        method_id = input_data[:10]
        if method_id != ERC20_TRANSFER_SIGNATURE:
            return None
        
        # Извлекаем адрес получателя (убираем лишние нули слева)
        to_address = "0x" + input_data[34:74]
        # Извлекаем сумму перевода в hex и переводим в int
        value_hex = input_data[74:138]
        raw_value = int(value_hex, 16)
        return to_address, raw_value
    except Exception:
        return None

async def process_transaction(w3: AsyncWeb3, tx_hash: str):
    """Получение деталей транзакции и проверка лимитов"""
    try:
        tx = await w3.eth.get_transaction(tx_hash)
        if tx is None:
            return

        # 1. Проверка транзакций с нативным ETH
        if tx['value'] > 0:
            eth_amount = w3.from_wei(tx['value'], 'ether')
            if eth_amount >= ETH_THRESHOLD:
                logger.info(f"[WHALE ETH] {eth_amount:.2f} ETH | From: {tx['from']} -> To: {tx['to']} | Hash: {tx_hash.hex()}")
                return

        # 2. Проверка транзакций со стейблкоинами (ERC-20)
        if tx['to'] and tx['to'].lower() in [USDT_ADDRESS.lower(), USDC_ADDRESS.lower()]:
            decoded = decode_erc20_transfer(tx['input'])
            if decoded:
                to_addr, raw_val = decoded
                decimals = 6  # Для USDT и USDC
                amount = raw_val / (10 ** decimals)
                
                threshold = USDT_THRESHOLD if tx['to'].lower() == USDT_ADDRESS.lower() else USDC_THRESHOLD
                if amount >= threshold:
                    token_symbol = "USDT" if tx['to'].lower() == USDT_ADDRESS.lower() else "USDC"
                    logger.info(f"[WHALE {token_symbol}] {amount:,.2f} {token_symbol} | From: {tx['from']} -> To: {to_addr} | Hash: {tx_hash.hex()}")

    except Exception:
        # Игнорируем ошибки (транзакция могла быть быстро отменена или уже смайнена)
        pass

async def main():
    async with AsyncWeb3(AsyncWebSocketProvider(WSS_NODE_URL)) as w3:
        if not await w3.is_connected():
            logger.error("Не удалось подключиться к ноде Ethereum")
            return
        
        logger.info("Успешное подключение к WebSocket. Подписка на мемпул...")
        
        # Подписка на новые pending транзакции
        subscription_id = await w3.eth.subscribe("pendingTransactions")
        
        async for response in w3.socket.process_subscriptions():
            tx_hash = response["result"]
            # Запускаем обработку транзакции асинхронно, чтобы не блокировать поток мемпула
            asyncio.create_task(process_transaction(w3, tx_hash))

if __name__ == "__main__":
    asyncio.run(main())

Разбор параметров

  • AsyncWebSocketProvider: Асинхронный провайдер для работы с WebSocket-соединением. Использование WebSockets критически важно для мемпула, так как HTTP-запросы (polling) создают слишком большую задержку и нагрузку на ноду.
  • pendingTransactions: Тип подписки, который указывает ноде отправлять хэши всех транзакций, поступающих в мемпул, еще до их валидации.
  • decode_erc20_transfer: Кастомная функция для быстрого парсинга сырых данных транзакции (input). Вместо тяжелой загрузки полного ABI контракта, мы напрямую проверяем сигнатуру метода transfer(address,uint256), которая в шестнадцатеричном формате всегда равна 0xa9059cbb. Это значительно ускоряет обработку.
  • asyncio.create_task: Метод для неблокирующего запуска обработчика транзакции. Для параллельной обработки сотен транзакций в секунду мы используем асинхронность, аналогично тому, как реализуется Пакетная отмена ордеров (Batch Cancel) на Bybit V5 через asyncio.gather.

Как запустить

Для запуска скрипта вам понадобятся установленный Python версии 3.10+ и библиотека web3. Установите зависимость командой pip install web3.

Замените значение переменной WSS_NODE_URL на адрес вашей WebSocket-ноды (например, от Alchemy, Infura или собственной ноды Geth). Запустите скрипт в терминале. Он начнет выводить логи при обнаружении крупных транзакций.

Если в дальнейшем вы планируете масштабировать свои торговые инструменты на другие сети, например Solana, рекомендуем ознакомиться с руководством по Подпись запросов Ed25519 для Jupiter DEX на Python: Гайд.

Оцените статью
FinFluct