Skip to content
Holits
Blockchain2 min readBy Holits

Why we decode Ethereum transactions before they're confirmed

A block explorer tells you what already happened. MEV, gas spikes and liquidations are decided in the seconds a transaction spends pending — which is why the mempool, not the chain, is where the signal lives.

A confirmed transaction is a historical record. By the time it's in a block, every question that made it interesting — was this a sandwich attack, was this liquidation profitable, was the gas market about to move — has already been answered, and the answer is no longer actionable by anyone watching. The window where that information is still useful is the seconds or minutes a transaction spends pending, in the mempool, before a block includes it.

That window is also where the data is hardest to work with. Pending transactions arrive as raw calldata, sometimes truncated by the node relaying them, frequently calling contracts with no published ABI, and always without the benefit of hindsight a confirmed block gives you.

Decode what you can, flag what you can't — never drop it

The instinct when building a decoder is to treat "couldn't decode this" as a dead end: log it, move on. We built it the other way. The decoder runs a waterfall — ABI-based decoding first, a Sourcify lookup next, then DEX-swap and liquidation-specific heuristics — and publishes a decoded event regardless of whether any of those steps succeeded, with an explicit success: false flag when nothing matched. A transaction that can't be classified yet is still a transaction that happened, and knowing that something unrecognized occurred is itself a signal worth keeping, especially in a system whose whole purpose is surfacing activity nobody has categorized yet.

Enrichment as the expensive path, used only when needed

Calldata gets truncated by default before it enters the fast path, because fetching full calldata and trace data for every pending transaction would make the hot path exactly as slow as the slowest transaction in it. Only the transactions the fast path couldn't fully decode trigger a background enrichment round-trip for the missing data, followed by a re-decode. The trade-off is explicit: pay the expensive lookup only for the fraction of traffic that actually needs it, and accept a small amount of latency on that fraction in exchange for keeping everything else fast.

Letting the decoder repair itself

An unknown function selector today doesn't have to stay unknown. A Model-Context-Protocol tool server exposes ABI-fetch, trace, and reclassification tools, and a periodic LLM-driven triage pass summarizes what the decoder is currently failing to classify and calls those tools to try to resolve it — gated behind dry-run and auto-apply flags, so decoder coverage improves over time without an unreviewed change reaching production on its own. The alternative — a human periodically triaging a backlog of unknown selectors — doesn't scale with the rate new contracts get deployed; a closed loop that proposes its own fixes, with a human still gating what gets applied, does.

None of this makes mempool decoding cheap. It makes it honest about where the cost belongs: fast and approximate on the hot path, slower and thorough only where a transaction has already proven it needs the extra work.