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

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску 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, аналітика газових цін — усе це будується на читанні мемпула в реальному часі.

Доступ до мемпула: три рівні

Рівень 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;  // транзакція могла бути dropped

  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: 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, тощо
const UNISWAP_V3_ABI = [...]; // exactInputSingle, тощо

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;

    // Шукаємо вже відправлені транзакції на той же пул з вищим газом
    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.