Case study
← All workToken launchpad indexer
A Rust indexer following the tip of a BNB Chain launchpad and DEX, with historical backfill running the same binary.
- Client
- Undisclosed — under NDA
- Year
- 2026
- Discipline
- Blockchain · Real-time data
- Status
- Delivered
Context
A token launchpad and the exchange around it produce state that is worthless a second late. Prices, new launches, holder movements and liquidity all change at block cadence, and the interface has to answer questions about them in milliseconds — which a blockchain node cannot do. The work was the layer in between: something that follows the chain continuously, understands what it is looking at, and keeps a queryable picture of the present.
Constraints
Chains do not cooperate with indexers. They reorganise, so a block treated as final can be withdrawn; RPC endpoints rate-limit and drop connections; and the process will be restarted, mid-block, at the worst possible moment. Anything written has to be recoverable to a known point rather than approximately correct. On top of that, the historical record had to be rebuilt from genesis while the live feed kept running, without the two racing each other into the same rows.
Architecture
One binary, two modes, selected by environment: a tip follower and a historical backfill, each persisting its own checkpoint so either can be killed and resumed independently. In deployment they are two separate services against the same code. The live mode writes PostgreSQL and also publishes to Redis feeds and a WebSocket channel; backfill writes PostgreSQL only, because nobody is waiting on a live event from three months ago. Storage is split by concern rather than pooled — chain state, matched patterns, application data and wallet data each have their own PostgreSQL instance — so a heavy analytical query against historical chain data cannot slow down the request serving somebody's page.
01
One binary in two modes, not two programs.
A separate backfill tool drifts from the live indexer, and the drift is invisible: both keep working, but they disagree about how to parse an event, and the disagreement surfaces months later as a gap in the data nobody can explain. Sharing the parsing and the writes means a bug is found once.
What it cost
Mode-specific behaviour has to be handled inside one codebase, and the checkpoint logic is more complex than either program alone would need.
02
A separate PostgreSQL per concern instead of one shared database.
The workloads have nothing in common. Chain indexing is write-heavy and append-mostly; pattern matching is analytical; the application does small indexed reads. Pooling them means the slowest query sets the latency for everyone.
What it cost
Four databases to run, migrate and back up, and any question that spans two of them has to be answered in the application rather than in a join.
03
Rust for the indexer, TypeScript for everything above it.
The indexer is the one component where a stall is unrecoverable — fall behind the tip and the backlog grows faster than it can be cleared. Predictable memory and no garbage-collection pauses matter there. Above it, iteration speed matters more than microseconds.
What it cost
Two languages in one system, and a smaller pool of people who can safely change the most critical component.
- Services in production
- 12
- read from deployment
- Indexer modes deployed separately
- 2
- read from deployment
- PostgreSQL instances, one per concern
- 4
- read from deployment
- Redis instances
- 2
- read from deployment
- Indexer language
- Rust
- counted in source
Twelve services in production: two indexer modes, a pattern matcher, smart-wallet handling, a frontend, a Telegram client, four PostgreSQL instances and two Redis. The live and backfill split is visible in the deployment itself rather than being a claim about the code.
Stack
Rust · PostgreSQL · Redis · WebSocket · BNB Chain · TypeScript
