Разработка бота для мониторинга мемпула

Проектируем и разрабатываем блокчейн-решения полного цикла: от архитектуры смарт-контрактов до запуска DeFi-протоколов, NFT-маркетплейсов и криптобирж. Аудит безопасности, токеномика, интеграция с существующей инфраструктурой.
Показано 1 из 1Все 1306 услуг
Разработка бота для мониторинга мемпула
Сложный
~3-5 дней
Часто задаваемые вопросы

Направления блокчейн-разработки

Этапы блокчейн-разработки

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1307
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1219
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    920
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1148
  • image_logo-advance_0.webp
    Разработка логотипа компании B2B Advance
    611
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    886

Разработка бота для мониторинга мемпула

Мемпул — это не просто очередь транзакций. Это источник данных о намерениях: что собирается сделать рынок в следующем блоке. MEV-ботов, которые зарабатывают на этих данных, на Ethereum работают сотни. Но мониторинг мемпула нужен не только для арбитража — liquidation-боты DeFi-протоколов, системы раннего обнаружения атак, front-running protection, аналитика gas prices — всё это строится на чтении мемпула в реальном времени.

Доступ к мемпулу: три уровня

Уровень 1: публичный mempool через RPC

Самый простой способ — eth_subscribe("newPendingTransactions") через WebSocket:

import { ethers } from 'ethers';

const wsProvider = new ethers.WebSocketProvider(
  `wss://eth-mainnet.g.alchemy.com/v2/${process.env.ALCHEMY_KEY}`
);

wsProvider.on('pending', async (txHash: string) => {
  const tx = await wsProvider.getTransaction(txHash);
  if (!tx) return;  // транзакция могла быть дропнута

  await processPendingTx(tx);
});

Проблема: публичные RPC видят только часть мемпула. Нода раскрывает pending-транзакцию только после того, как распространит её в сеть. Приватные транзакции (через Flashbots, MEV Blocker, Cowswap) — не видны вообще.

Уровень 2: собственная нода с txpool API

Запустите собственный Geth/Reth с открытым txpool namespace:

# Geth с включённым txpool API
geth --mainnet \
  --http \
  --http.api eth,net,txpool \
  --ws \
  --ws.api eth,net,txpool \
  --txpool.globalslots 8192 \
  --txpool.accountslots 64

Теперь доступен txpool_content — полный снапшот мемпула:

const txpoolContent = await provider.send('txpool_content', []);
// { pending: { [address]: { [nonce]: Transaction } }, queued: { ... } }

const txpoolStatus = await provider.send('txpool_status', []);
// { pending: '0x1234', queued: '0x56' }

Собственная нода видит все транзакции, которые p2p-сеть передаёт через неё. Это значительно полнее публичных RPC.

Уровень 3: node colocation + peering

Для серьёзного MEV или latency-critical задач: размещение ноды в том же дата-центре, что и крупные валидаторы (Equinix NY5, Amsterdam AMS3). Настройка максимального числа peers и подключение к специализированным mesh-сетям:

# Подключение к Bloxroute BDN (Blockchain Distribution Network)
geth --mainnet \
  --bootnodes "enode://bloxroute-peer@..." \
  --maxpeers 100

Это снижает latency с ~200ms до ~50ms до получения транзакции. Критично для front-running ботов, но избыточно для большинства мониторинговых задач.

Декодирование транзакций

Raw транзакция — это набор байт. Чтобы понять, что она делает, нужно декодировать calldata:

import { Interface, FunctionFragment } from 'ethers';

// Загружаем ABI известных протоколов
const UNISWAP_V2_ABI = [...]; // swapExactTokensForTokens, etc.
const UNISWAP_V3_ABI = [...]; // exactInputSingle, etc.

const uniV2Iface = new Interface(UNISWAP_V2_ABI);
const uniV3Iface = new Interface(UNISWAP_V3_ABI);

function decodeUniswapTx(tx: ethers.TransactionResponse): DecodedSwap | null {
  if (!tx.data || tx.data === '0x') return null;

  const selector = tx.data.slice(0, 10);  // первые 4 байта

  // Попытка декодировать как Uniswap V3 exactInputSingle
  if (selector === '0x414bf389') {
    try {
      const decoded = uniV3Iface.decodeFunctionData('exactInputSingle', tx.data);
      return {
        protocol: 'UniswapV3',
        tokenIn: decoded.params.tokenIn,
        tokenOut: decoded.params.tokenOut,
        amountIn: decoded.params.amountIn,
        amountOutMinimum: decoded.params.amountOutMinimum,
        recipient: decoded.params.recipient,
      };
    } catch { return null; }
  }

  return null;
}

Для неизвестных контрактов — 4byte.directory API для поиска сигнатуры по селектору:

async function resolveSelector(selector: string): Promise<string | null> {
  const res = await fetch(`https://www.4byte.directory/api/v1/signatures/?hex_signature=${selector}`);
  const data = await res.json();
  return data.results[0]?.text_signature ?? null;
}

Паттерны использования: что реально делают с мемпулом

Liquidation bot — мониторинг позиций на Aave/Compound, которые близки к liquidation threshold:

// Слушаем цены в мемпуле — транзакции Chainlink oracle updater
async function watchChainlinkUpdates(tx: ethers.TransactionResponse) {
  if (tx.to !== CHAINLINK_AGGREGATOR) return;

  // Декодируем новую цену из calldata
  const newPrice = decodeOracleUpdate(tx.data);

  // Симулируем liquidation с новой ценой
  const liquidatablePositions = await simulateLiquidations(newPrice);

  if (liquidatablePositions.length > 0) {
    // Отправляем liquidation транзакцию с более высоким gas price
    await sendLiquidationTx(liquidatablePositions[0], {
      maxFeePerGas: tx.maxFeePerGas! * 2n,  // обгоняем оракул
    });
  }
}

Sandwich attack detection — для защиты вашего протокола:

interface SandwichCandidate {
  victimTx: string;
  frontRunTx: string;
  estimatedProfit: bigint;
}

async function detectPotentialSandwich(
  pendingTxs: Map<string, ethers.TransactionResponse>
): Promise<SandwichCandidate[]> {
  const candidates: SandwichCandidate[] = [];

  for (const [hash, tx] of pendingTxs) {
    const swap = decodeUniswapTx(tx);
    if (!swap) continue;

    // Ищем уже отправленные транзакции на тот же пул с более высоким gas
    const frontRuns = [...pendingTxs.values()].filter(other =>
      other.hash !== hash &&
      decodeUniswapTx(other)?.tokenIn === swap.tokenIn &&
      other.maxFeePerGas! > tx.maxFeePerGas!
    );

    if (frontRuns.length > 0) {
      candidates.push({ victimTx: hash, frontRunTx: frontRuns[0].hash, estimatedProfit: 0n });
    }
  }

  return candidates;
}

Gas price forecasting — анализ distribution газа в мемпуле для предсказания следующего base fee:

async function analyzeGasDistribution() {
  const txpoolContent = await provider.send('txpool_content', []);
  const gasPrices: bigint[] = [];

  for (const addrTxs of Object.values(txpoolContent.pending)) {
    for (const tx of Object.values(addrTxs as any)) {
      gasPrices.push(BigInt((tx as any).maxFeePerGas || (tx as any).gasPrice));
    }
  }

  gasPrices.sort((a, b) => (a < b ? -1 : 1));

  return {
    p25: gasPrices[Math.floor(gasPrices.length * 0.25)],
    p50: gasPrices[Math.floor(gasPrices.length * 0.50)],  // медиана
    p75: gasPrices[Math.floor(gasPrices.length * 0.75)],
    p95: gasPrices[Math.floor(gasPrices.length * 0.95)],
    count: gasPrices.length,
  };
}

Архитектура высоконагруженного бота

На mainnet Ethereum мемпул содержит 50–200k транзакций. Обработка каждой синхронно невозможна:

WebSocket listener
      │ raw tx hash
      ▼
  Queue (Redis Streams)
      │
  Workers (x8 процессов)
      │ декодирование + фильтрация
      ▼
  Signal Bus (Redis Pub/Sub)
      │ только релевантные события
      ▼
  Strategy Handlers
      │ liquidation / arb / alert
      ▼
  Execution Engine (с rate limiting)

Воркеры декодируют транзакции параллельно, в очередь сигналов попадает только то, что прошло фильтр. Execution engine управляет отправкой транзакций, чтобы не flood-ить сеть при массовых событиях.

Flashbots и приватные транзакции

Часть транзакций никогда не попадает в публичный мемпул — отправляются напрямую валидаторам через Flashbots MEV-Boost или Flashbots Protect. Для мониторинга этого трафика нужен отдельный подход — подписка на Flashbots event stream или партнёрство с block builder'ами.

Для большинства задач (ликвидации, мониторинг протокола) публичного мемпула достаточно. Для конкурентного MEV арбитража — без Flashbots интеграции работать сложно: боты с приватными транзакциями имеют структурное преимущество.

Latency: измерение и оптимизация

class LatencyTracker {
  private samples: number[] = [];

  recordTxSeen(txHash: string, seenAt: number) {
    // seenAt — timestamp получения из WebSocket (performance.now())
    this.txSeenMap.set(txHash, seenAt);
  }

  recordTxMined(txHash: string, blockTimestamp: number) {
    const seenAt = this.txSeenMap.get(txHash);
    if (!seenAt) return;
    this.samples.push(Date.now() - seenAt);
  }

  getP50LatencyMs(): number {
    const sorted = [...this.samples].sort((a, b) => a - b);
    return sorted[Math.floor(sorted.length / 2)];
  }
}

Типичные цифры: публичный Alchemy WebSocket — 150–400ms от broadcast до получения. Собственная нода с 50+ peers — 50–150ms. Node colocation в Equinix — 10–50ms.