Skip to content
Holits

Blockchain Infrastructure

Mempool Analyzer

A 13-service Ethereum mempool analytics platform that decodes pending transactions before they're mined, surfacing MEV, gas-market and DeFi liquidation activity in real time.

By Holits

The problem

By the time a transaction is mined, the interesting question about it — was this a sandwich attack, is this liquidation about to be profitable, is the gas market about to spike — is already answered and no longer actionable. That information lives in the mempool, for the seconds or minutes a transaction sits pending, and it arrives as raw, often truncated calldata that has to be decoded before it means anything.

The approach

Thirteen independently deployable services, communicating over a shared message bus (RabbitMQ in production, in-memory for local development, selected by a single environment variable), built on a common internal library so no service calls another directly. A core service subscribes to a node's pending- transaction feed and runs fast sandwich and EIP-1559 heuristics; a decoder turns calldata into structured intent; an enricher fills in what the decoder couldn't from truncated data; and a fan-out of analytics services — gas-station, swap-service, bridge-tracker, liquidation-tracker, auction-tracker, sentiment-service — each consume the decoded stream for their own domain. An API gateway aggregates all of it behind REST and server-sent events for the dashboard.

Decoding as a waterfall, not a gate

A transaction that can't be decoded is still useful — knowing that something unrecognised is happening is itself a signal. The decoder tries ABI-based decoding first, falls back to a Sourcify lookup, then DEX/swap and liquidation heuristics, and publishes a decoded event either way, with success: false when nothing worked, rather than dropping the transaction. Calldata is truncated at 8KB by default to keep the hot path cheap; only the transactions that actually need more get a background enrichment round-trip — fetching full calldata and, where the node supports it, trace data — which is what moves decode coverage from a baseline to meaningfully higher without paying the enrichment cost on every transaction.

A self-improving decoder

Unknown function selectors don't sit in a dead-letter queue waiting for someone to notice. A Model Context Protocol tool server exposes ABI-fetch, trace and reclassification tools, and a periodic LLM-driven triage run summarises decoder failures and calls those tools to resolve them — gated behind dry-run and auto-apply flags, so decoder coverage improves without unreviewed changes reaching production silently.

Hardened by auditing the running system, not its docs

The platform's reliability comes from treating its own operational behaviour, not its architecture diagram, as the thing worth auditing. That practice directly shaped the liquidation-tracker's math — profitability now runs through an on-chain price oracle and protocol-specific collateralisation formulas instead of a single constant standing in for every protocol — and the gas-station's next-block prediction, which follows EIP-1559's actual single-preceding-block formula rather than a smoothed multi-block average, so it reacts correctly during congestion spikes rather than lagging behind them. It's the same discipline applied to the message bus itself: retry and dead-letter behaviour is verified against what the broker's acknowledgement API actually supports, not assumed from the client library's interface.

Design decisions worth noting

  • Numeric precision is a rule, not a convention. All monetary and gas values are required to travel as strings or BigInt, never native JS numbers, specifically to avoid float precision loss on values where a rounding error changes a liquidation's profitability.
  • No service-to-service imports. Cross-service interaction is required to go through the shared library and the message bus, enforced by convention and audited for, so services stay independently deployable.
  • The message bus is swappable by design, not by accident — running the whole 13-service stack against an in-memory bus for local development, and RabbitMQ in production, off a single environment variable, keeps the local dev loop fast without a second code path to maintain.

Case study

See this in production: Architecting a 13-service pipeline to decode the Ethereum mempool in real time.

Availability

Private. Built and operated in-house; not distributed under an open licence.