chainindex
Reorg-safe, embeddable blockchain indexing engine.
chainindex is a lightweight library that fetches blocks, detects reorgs, decodes events, calls your handler functions, and saves checkpoints. Think of it as the missing primitive between raw blocks and your database — without the weight of The Graph or Ponder.
Features
| Feature | Description |
|---|---|
| Reorg detection | 4-scenario reorg detection (short, deep, node switch, RPC inconsistency) |
| Checkpoint recovery | Crash-safe — resumes from last saved checkpoint |
| Factory tracking | Auto-track child contracts from factory events (Uniswap, Compound, etc.) |
| Entity system | Structured storage with typed schemas, CRUD, queries, and reorg rollback |
| Dead letter queue | Failed handlers retry with exponential backoff |
| Idempotency | Deterministic IDs + side-effect guards for safe reorg replay |
| Call trace indexing | Index internal transactions (CALL, DELEGATECALL, CREATE) |
| Event streaming | Cursor-based streaming for downstream consumers |
| Data export | Export to JSONL/CSV for analytics pipelines (DuckDB, BigQuery) |
| Block handlers | Interval handlers (every N blocks), setup handlers (run once) |
| GraphQL query layer | Auto-generated schema from entities, filter/sort/paginate |
| Parallel backfill | Concurrent segment processing for fast historical sync |
| Multi-chain indexer | Single engine coordinating N chains with cross-chain event bus |
| Solana indexer | Slot tracking, program log parsing, Anchor events, account filters |
| Hot-reload config | Update indexer configs at runtime without restart |
| Multi-chain finality | Pre-configured for 12 chains (Ethereum, Polygon, Arbitrum, Solana, etc.) |
| 4 storage backends | Memory (dev), SQLite (embedded), PostgreSQL (production), RocksDB (high-throughput) |
| 4 language bindings | TypeScript, Python, Go, Java |
Install
Rust
[]
= "0.1"
= "0.1"
= { = "0.1", = ["memory"] }
npm / Node.js
Python
Quick Start (Rust)
use IndexerConfig;
use ;
use IndexContext;
use ;
// 1. Configure
let config = IndexerConfig ;
// 2. Register event handlers
let mut registry = new;
// ... register your EventHandler implementations
// 3. Start indexing (with chainindex-evm)
use IndexerBuilder;
let cfg = new
.chain
.from_block
.confirmation_depth
.batch_size
.build_config;
Quick Start (TypeScript)
import { IndexerConfig, InMemoryStorage, EventFilter } from '@chainfoundry/chainindex';
const config = new IndexerConfig({
id: 'uniswap-v3',
chain: 'ethereum',
fromBlock: 19_000_000,
confirmationDepth: 12,
batchSize: 500,
});
const filter = EventFilter.forAddress('0x1F98431c8aD98523631AE4a59f267346ea31F984');
Architecture
chainindex/
├── crates/
│ ├── chainindex-core/ # Core engine (24 modules, 245 tests)
│ │ ├── backfill.rs # Parallel backfill engine
│ │ ├── block_handler.rs # Interval + setup handlers
│ │ ├── checkpoint.rs # Checkpoint persistence + recovery
│ │ ├── cursor.rs # Block cursor advancement
│ │ ├── dlq.rs # Dead letter queue
│ │ ├── entity.rs # Entity/table system
│ │ ├── error.rs # Error types
│ │ ├── export.rs # JSONL/CSV export
│ │ ├── factory.rs # Factory contract tracking
│ │ ├── finality.rs # 12-chain finality models
│ │ ├── graphql.rs # GraphQL schema + query executor
│ │ ├── handler.rs # Event/block/reorg handler traits
│ │ ├── hotreload.rs # Hot-reload configuration
│ │ ├── idempotency.rs # Reorg-safe handler replay
│ │ ├── indexer.rs # Config + state types
│ │ ├── metrics.rs # Block lag, RPC stats, handler latency
│ │ ├── multichain.rs # Multi-chain coordinator + event bus
│ │ ├── reorg.rs # 4-scenario reorg detection
│ │ ├── streaming.rs # Cursor-based event streaming
│ │ ├── trace.rs # Call trace indexing
│ │ ├── tracker.rs # Sliding window block tracker
│ │ └── types.rs # BlockSummary, EventFilter, IndexContext
│ ├── chainindex-evm/ # EVM-specific indexer (builder, fetcher, loop)
│ ├── chainindex-solana/ # Solana indexer (slots, program logs, Anchor)
│ └── chainindex-storage/ # Memory, SQLite, Postgres, RocksDB backends
├── cli/ # CLI binary
├── examples/ # 16 runnable examples
└── bindings/
├── node/ # TypeScript (napi-rs)
├── python/ # Python (PyO3 + maturin)
├── go/ # Go (C FFI)
└── java/ # Java (JNI)
Module Reference
Factory Contract Tracking
Track child contracts deployed by factory patterns (Uniswap V3, Compound, etc.):
use ;
let registry = new;
registry.register;
// Feed events through — child addresses auto-tracked
if let Some = registry.process_event
// Get all addresses for EventFilter
let all_addrs = registry.get_all_addresses;
Entity System
Structured storage with typed schemas:
use *;
let schema = new
.primary_key
.field
.field
.field
.build;
let store = new;
store.register_schema.await?;
store.upsert.await?;
// Reorg rollback — delete entities after fork block
store.delete_after_block.await?;
Dead Letter Queue
Failed handlers retry with exponential backoff:
use ;
let dlq = new;
// On handler failure
dlq.push;
// Retry ready entries
let ready = dlq.pop_ready;
for entry in ready
GraphQL Query Layer
Auto-generated GraphQL schema from entity definitions:
use ;
use *;
// Generate schema from entities
let mut schema = new;
schema.add_entity;
// Execute queries
let executor = new;
let result = executor.execute.await;
// Single entity by ID
let result = executor.execute.await;
// Introspection
let sdl = executor.introspect; // returns GraphQL SDL string
Parallel Backfill
Concurrent block range processing for fast historical sync:
use *;
let config = BackfillConfig ;
let engine = new;
let result = engine.run.await?;
println!;
Multi-Chain Indexer
Coordinate multiple chains from a single engine:
use *;
let coordinator = new;
// Health monitoring
let health = coordinator.health.await;
// Cross-chain event bus
let bus = new;
let mut rx = bus.subscribe;
bus.push;
// Sync status
let mut sync = new;
sync.update;
sync.all_caught_up; // within 1000 blocks of tips
Solana Indexer
Slot tracking, program log parsing, and Anchor event decoding:
use *;
let builder = new
.from_slot
.program // Raydium
.exclude_votes
.confirmation;
// Parse transaction logs
let logs = parse_transaction_logs;
// Slot tracking with skip detection
let mut tracker = new;
tracker.push_slot?;
let skipped = tracker.skipped_slots_in_range;
Hot-Reload Configuration
Update configs at runtime without restart:
use *;
let manager = new;
manager.register_config.await;
// Subscribe to changes
let mut rx = manager.subscribe.await.unwrap;
// Update config — validates, diffs, notifies subscribers
let result = manager.update_config.await?;
println!;
// Dynamic filter updates
let reloader = new;
reloader.add_address.await;
reloader.remove_address.await;
RocksDB Storage Backend
High-throughput embedded storage:
use RocksDbStorage;
let storage = in_memory; // or RocksDbStorage::open("./data")?
// Events — natural block-order via key encoding
storage.insert_events_batch?;
let transfers = storage.events_by_schema?;
let range = storage.events_in_block_range?;
// Reorg rollback
storage.rollback_after?;
Call Trace Indexing
Index internal transactions from debug_traceBlock or trace_block:
use *;
// Parse Geth traces
let traces = parse_geth_traces?;
// Parse Parity/OpenEthereum traces
let traces = parse_parity_traces?;
// Filter
let filter = new
.with_address
.with_selector
.exclude_reverted;
let matching: = traces.iter.filter.collect;
Event Streaming
Cursor-based streaming for downstream consumers:
use ;
let mut stream = new;
// Producer pushes events
stream.push;
// Consumer reads batches
let cursor = initial;
let batch = stream.next_batch?;
// Save batch.cursor for resume after crash
Data Export
use ;
let config = ExportConfig ;
let mut file = create?;
let stats = export_events?;
println!;
Finality Models
Pre-configured for 12 chains:
| Chain | Safe | Finalized | Block Time | Reorg Window |
|---|---|---|---|---|
| Ethereum | 32 | 64 | 12s | 128 |
| Polygon | 128 | 256 | 2s | 512 |
| Arbitrum | 0 | 1 | 250ms | 64 |
| Optimism | 0 | 1 | 2s | 64 |
| Base | 0 | 1 | 2s | 64 |
| BSC | 15 | 15 | 3s | 64 |
| Avalanche | 1 | 1 | 2s | 32 |
| Solana | 1 | 32 | 400ms | 256 |
| Fantom | 1 | 1 | 1s | 32 |
| Scroll | 0 | 1 | 3s | 64 |
| zkSync | 0 | 1 | 1s | 64 |
| Linea | 0 | 1 | 12s | 64 |
Examples
16 runnable examples covering every feature:
Test Coverage
324 tests, 0 failures
chainindex-core: 245 tests (graphql, backfill, multichain, hotreload, factory, entity,
DLQ, idempotency, trace, streaming, export, block_handler,
checkpoint, tracker, reorg, finality, metrics, types, handler)
chainindex-evm: 4 tests (builder, fetcher)
chainindex-solana: 33 tests (slot tracking, program logs, Anchor, filters, builder)
chainindex-storage: 29 tests (memory, RocksDB KV store, checkpoint, events)
doc-tests: 13 tests
License
MIT