Terrain
A Solana block explorer stack built from the messy parts up.
Built from the messy parts up
Most people building in crypto slap a UI on top of someone else's API and call it a day. Fair enough. We went the other way and built the machinery underneath it.
Terrain turns raw Solana and Raydium-style market activity into something humans can actually use: price charts, wallet history, transaction feeds, trader rankings, live token updates, the lot. Under the hood that meant parsing ugly blockchain data, figuring out what actually happened, storing the important bits, and pushing it back out in realtime.
Three parts, one system: a Rust parser engine, a TypeScript backend, and a frontend that makes all that chain chaos readable.
Gallery
Readable at speed
Terrain Parser
Blockchains don't hand you neat little labels like "swap", "transfer", or "this wallet dumped the token and left." They hand you a pile of low-level state changes and basically wish you good luck.
So we built a Rust parser that takes raw Solana transactions and turns them into stuff you can actually use: swaps, prices, balances, live updates.
It does more than just index chain data. It figures out what happened, who likely did it, and feeds the rest of the system from there.
The part where raw blockchain data stops being gibberish.
This was proper systems work: Rust crates, worker pools, batching, retries, caches, Docker, AWS, Kafka, WebSockets. Less "look I made an app," more "we built the engine room too."
Pipeline walkthrough
Data pipeline from chain transaction to live client update.
Playback
1000 tx / 2.3s
One pipeline pass from raw transaction intake to live client output.
Each wave shows one pass moving through the pipeline.
1000 transactionssynced burstBatch completion
1000 tx completed
- 01processing
Raw txstream
Solana RPC and Kafka payloads arrive as noisy transaction data.
InputKafka raw topicdecode payloadOutputtransaction + metaslotblockTimemeta.err - 02queued
CtTransactionparser
A shared Rust engine normalizes signer, UBO, fees, token deltas, and inner instructions.
Inputtransaction + metanormalize semanticsOutputCtTransactionsignerubotoken deltas - 03queued
Actionreconstruction
Low-level value changes resolve into swaps, transfers, and wallet-aware events.
InputValueChange[]group deltasOutputswap / transfercombineinferlabel - 04queued
Pool-awarepricing
Raydium pool context, SOL reference price, and pool metadata produce market prices.
Inputpool id + SOL/USDderive market priceOutputPriceItemPoolMetaMoka cacheprice_usd - 05queued
Enrichedevent
Structured token updates leave the ingest worker with token and pool headers.
InputPriceItembatch + publishOutputTokenUpdateheadersroundinglz4 - 06queued
Productoutputs
Postgres receives analytics while subscribed clients receive live price updates.
InputTokenUpdatefan outOutputDB + token roomtoken_prices_newJOIN_ROOMPRICE_UPDATE
Terrain Backend
The API nobody sees first, but everything depends on.
Once the parser did its job, this service turned the data into actual product features. Token charts. Transaction history. Wallet views. Trader leaderboards. The sort of stuff users click around in without thinking too much about how annoying it is to make.
The interesting part here is that it doesn't just dump chain data back at the frontend and walk away. It calculates things like realized P/L, ROI, bought vs sold volume, unrealized exposure, trader rankings, filtered history, and wallet-level views. Basically: raw blockchain activity in, readable trading intelligence out.
Also included: versioned APIs, request tracing, Sentry, mock endpoints for frontend work, Docker, Fly, AWS. The boring grown-up stuff that keeps things alive.
Built in Node, TypeScript and Postgres, the backend combines precomputed analytics, cached transaction records, live Solana RPC enrichment, and parser-service fallbacks. Which is a fancy way of saying: it does whatever it needs to do to return something useful, fast.
The part where raw blockchain data stops being gibberish.
superhuge
Terrain note
Terrain Frontend
Because raw chain data looks like nonsense unless you do the work.
Most blockchain explorers feel like they were designed by and for people who enjoy suffering. Huge tables, mystery labels, JSON vibes, good luck out there.
No big deal.
Terrain took a more useful route. Search-first, fast to navigate, and built around the idea that users want to jump straight to a wallet, transaction, token or protocol without getting lost in some overcomplicated menu.
Once there, the UI translates blockchain movement into something readable: who sent what, who received what, what got swapped, what changed, and why it matters.
It also pulls in protocol views, wallet filters, transaction drill-downs, live token pricing, websocket updates, and client-side chart transformations.
Domain model
A few stable models make the product readable.
Terrain does not push raw chain payloads straight into the UI. It shapes them into stable transaction, token, and balance models that the rest of the product can actually build on.
That modeling layer is what makes live pricing, holder changes, wallet views, and feed updates feel coherent instead of improvised.
One stack, not a facade
Terrain is a Solana block explorer stack built from the messy parts up: parser, backend, frontend, and the operational glue that makes the whole thing feel fast instead of fragile.
What it actually took
So in short, Rust workspace, multi-crate parser core, CtTransaction, Solana transaction/meta payloads, instruction-vector walks, inner-instruction traversal, signer and UBO heuristics, fee surfaces, owner-level token balance deltas, token-account-level diffs, Raydium AMM pool extraction, pool-id resolution, quote/base price math, SOL/USD anchoring, Kafka consumers, Kafka republish, async channels, worker pools, batched processing, Postgres persistence, price-update listeners, holder-ingest signature pagination, historical replay, parser-debugger flows, Moka cache hits, connection pools, exponential backoff, Axum listeners, WebSocket fan-out, Socket.IO token rooms, multi-stage Docker, env-driven config, AWS CDK, ECS services, Fargate tasks, ALB wiring, snapshot diffing, and just enough distributed-systems trauma to keep it honest.
Architecture X-Ray
Separate services, one event-processing platform.
kafka-ingest
Consumes raw transactions, parses pricing, republishes structured events.
Kafka in -> TokenUpdate out
block_parser
Central domain engine for transaction semantics, actions, pool lookup, and pricing.
RPC/meta -> CtTransaction
db-saver
Persists price and balance updates into analytics tables.
Kafka event -> Postgres
axum-webserver
Fans out live token prices to Socket.IO rooms by token address.
Kafka event -> WebSocket
holder-ingest
Replays historical signatures for holder and balance analysis.
RPC history -> BalanceUpdate
infra
Ships separate Rust binaries through Docker and AWS ECS/Fargate.
Docker -> ECS services











