Mirror decodes BOT Chain DEX activity into a queryable database, then periodically writes that database back into blob transactions on BOT Chain itself. Wipe the disk and Mirror rebuilds from the chain alone: a day of data returns in under a minute, the full three-million-row history in under ten. Every checkpoint is verified by content hash on the way back in. No snapshots, no S3 bucket, no trust in the operator.
Indexers die. Databases get wiped, servers lapse, and every DEX dashboard you rely on is one missed invoice away from a blank page. Mirror treats the chain itself as the backup medium: the same BOT Chain that produced the swap events also carries compressed, hash-verified checkpoints of the decoded database inside EIP-4844 blob transactions. Anyone can run npm run restore and re-derive the entire query layer from public chain data, verifying every byte against on-chain content hashes.
1,122,000+ swaps decoded across 8 auto-discovered pools. 220+ checkpoints registered on-chain. Full-history restore measured at 575.3 seconds for 2,970,398 rows across 207 hash-verified checkpoints, with zero retries.
| Checkpoints | Pool detail |
|---|---|
![]() |
![]() |
| Integrations | Overview |
![]() |
![]() |
BOT Chain ships the full blob API (eth_getBlobSidecarByTxHash, eth_blobBaseFee). Mirror is built on it: checkpoints are gzip-compressed MIR1 envelopes packed into blob sidecars (126,975 usable bytes per blob, up to 3 blobs per transaction). Blob base fee on BOT Chain is 1 wei, so a full database checkpoint costs about a cent.
// daemon/src/chain.ts - read a checkpoint back from the chain
const sidecar = await rpc("eth_getBlobSidecarByTxHash", [txHash, true]);
const bytes = blobsToBytes(sidecar.blobSidecar.blobs); // own decoder: viem's
const hash = keccak256(bytes); // fromBlobs truncates
if (hash !== onchain.contentHash) throw new BadHash(); // multi-blob payloadsEvery checkpoint is registered in MirrorRegistry (Solidity, 8 Foundry tests). The registry also carries a beacon struct with Mirror's latest aggregates, so other contracts can read DEX state without running an indexer. BeaconReader is a separately deployed consumer proving the composability:
// contracts/src/BeaconReader.sol - any contract can do this
function wbotPriceE6() external view returns (uint64 price, uint64 asOfBlock, uint64 epoch) {
IMirrorRegistry.Beacon memory b = registry.beacon();
return (b.priceUsdtE6, b.lastIndexedBlock, b.latestEpoch);
}Mirror watches the official BDEX (Uniswap-v3-style) factory for PoolCreated events and picks up new pools mid-chunk without a restart. All 8 pools in the table on the overview page were discovered this way, not configured.
The ingestor runs a paced cursor loop against rpc.botchain.ai with adaptive chunk bisection (the RPC caps responses at 10 MB), nudged by ws-rpc.botchain.ai new-head events. The integrations page cross-checks Mirror's derived WBOT price against the official BOT price API and shows agreement within a 5 percent window, live.
BOT Chain mainnet 677 (read side)
┌────────────────────────────────────────────┐
│ BDEX pools ── Swap/Transfer/PoolCreated │
└──────────────────────┬─────────────────────┘
│ paced getLogs cursor loop
v
┌─────────────────────┐ REST + WS :3400
│ mirrord (daemon) ├──────────────────────► Next.js web
│ decode → SQLite │ /pools /ohlcv /blobs (6 pages)
└──────────┬──────────┘ /checkpoints /beacon
│ every ~400 blocks: MIR1 envelope
│ (varint+dict+gzip, keccak contentHash)
v
BOT Chain testnet 968 (write side)
┌────────────────────────────────────────────┐
│ blob tx (1-3 blobs) + MirrorRegistry │
│ sidecar carries data registerCheckpoint│
│ + Beacon update │
└──────────────────────┬─────────────────────┘
│ wipe? crash? new machine?
v
┌─────────────────────┐
│ npm run restore │ walk epoch chain from latestEpoch,
│ │ fetch sidecars, verify contentHash,
└─────────────────────┘ rebuild DB, resume live tail
SEGMENT checkpoints are immutable closed block ranges chained by prevEpoch. A rolling HEAD checkpoint covers the newest blocks. Restore walks the chain backward from latestEpoch(), so it discovers exactly the live set. A calldata mirror of the latest HEAD guards against blob retention expiry.
| Layer | Technology |
|---|---|
| Daemon | TypeScript, Node 24, viem, better-sqlite3, Fastify (REST + WebSocket), c-kzg |
| Contracts | Solidity 0.8, Foundry (MirrorRegistry, BeaconReader, DemoToken) |
| Web | Next.js 15, lightweight-charts |
| Storage | SQLite locally; EIP-4844 blob sidecars + calldata mirror on-chain |
| Infra | Docker Compose (daemon + web, healthchecked) |
cd daemon && npm test # 6 passing - MIR1 codec round-trip incl. offline blob leg
cd contracts && forge test # 8 passing - checkpoint chain, beacon, access controlThe codec suite proves an envelope survives encode, blob packing, unpacking, and decode byte-identically and stays inside the compression budget. The contract suite covers epoch ordering, hash storage, writer gating, and the BeaconReader consumer path.
- Clone and configure:
git clone https://github.com/dmustapha/mirror && cd mirror cp .env.example .env # works read-only out of the box; add a key only to write checkpoints
- Point it at the live registry (already deployed, already full of checkpoints):
# in .env REGISTRY_ADDRESS=0x6460389f70B59c0503186494534e911F181bD911 REGISTRY_DEPLOY_BLOCK=15492396 - Restore the database from chain data alone. Watch per-epoch
hash✓lines print:cd daemon && npm install && npm run restore
- Start the daemon and open the dashboard:
npm run daemon # API on :3400, resumes the live tail cd ../web && npm install && npm run dev # dashboard on :3401
- Or bring up everything with Docker:
docker compose up
- Read the beacon from the chain directly, no Mirror needed:
cast call 0x6460389f70B59c0503186494534e911F181bD911 "beacon()((uint64,uint128,uint32,uint64,uint64))" --rpc-url https://rpc.bohr.life
| Method | Endpoint | Description |
|---|---|---|
| GET | /pools |
All auto-discovered pools with price and 24h aggregates |
| GET | /pools/:addr/swaps |
Decoded swaps, explorer deep link per row |
| GET | /pools/:addr/ohlcv?interval=1h&buckets=200 |
Candles over the full decoded history |
| GET | /transfers/:addr |
USDT transfers touching an address |
| GET | /checkpoints |
The on-chain checkpoint chain, newest first |
| GET | /blobs |
Every blob transaction Mirror has seen (its own checkpoints) |
| GET | /beacon |
On-chain beacon vs local aggregates, side by side |
| GET | /status |
Head, lag, row counts, database size |
| GET | /proof |
Machine-readable twin of daemon/submission/proof.md |
| WS | /ws |
Live swap and checkpoint feed |
| Contract | Address (BOT Chain testnet 968) | Description |
|---|---|---|
| MirrorRegistry | 0x6460389f70B59c0503186494534e911F181bD911 |
Checkpoint chain, content hashes, beacon, calldata mirror head |
| BeaconReader | 0xC76FE7B723388C656FcF79bdB10DF52329369D67 |
Independent consumer contract reading the beacon |
| DemoToken | 0xd4cff27b91f70629EEA4C63B2241CBAeCE4A57B9 |
Demo ERC-20 for on-camera pool discovery |
| What | Link |
|---|---|
| First blob transaction (1-blob probe) | 0x5fceb6...66881a |
| First 3-blob transaction | 0x5fee81...da08e7 |
| Every checkpoint, epoch by epoch | daemon/submission/proof.md (600+ transaction links) |
| Timed restore reports | daemon/submission/proof.md, measured numbers only |
git clone https://github.com/dmustapha/mirror && cd mirror
cp .env.example .env
cd daemon && npm install && npm run daemon
cd ../web && npm install && npm run dev| Variable | Description |
|---|---|
RPC_URL / WSS_URL |
Read side: official BOT Chain mainnet endpoints (defaults work) |
WRITE_RPC_URL / WRITE_CHAIN_ID / WRITE_EXPLORER_BASE |
Write side: where checkpoints and the registry live (testnet 968 by default; mainnet flip is these 3 vars) |
PRIVATE_KEY |
Funded key for writing checkpoints. Restore and read paths need no key |
REGISTRY_ADDRESS / REGISTRY_DEPLOY_BLOCK |
MirrorRegistry address and deploy block |
START_BLOCK |
Backfill start (defaults to BDEX factory deploy) |
CHECKPOINT_MODE |
blob (default) or calldata fallback |
API_PORT / NEXT_PUBLIC_API_URL |
Daemon API port and the URL the web app calls |
mirror/
├── daemon/ # mirrord: ingest, decode, checkpoint, restore, API
│ ├── src/
│ │ ├── ingest.ts # cursor-loop ingestor, pool auto-discovery, WSS nudge
│ │ ├── codec.ts # MIR1 envelope: varint+dict+gzip, keccak contentHash
│ │ ├── checkpoint.ts # SEGMENT/HEAD lifecycle, BlobSink + CalldataSink
│ │ ├── restore.ts # epoch-chain walk, hash-verified blob pulls
│ │ ├── api.ts # Fastify REST + WS feed
│ │ └── store.ts # SQLite schema and queries
│ ├── scripts/ # hour-0 spike, demo-state verifier, pool creator
│ └── submission/ # proof.md: every checkpoint tx, timed restore reports
├── contracts/ # Foundry: MirrorRegistry, BeaconReader, DemoToken + 8 tests
├── web/ # Next.js dashboard: overview, pool detail, checkpoints,
│ # blob activity, integrations, proof
└── docker-compose.yml # daemon + web, healthchecked
Network context: BOT Chain mainnet is chain 677 (https://rpc.botchain.ai, explorer https://scan.botchain.ai). The checkpoint registry lives on BOT Chain testnet 968 (https://rpc.bohr.life), which the event rules accept; moving to mainnet is a three-variable env change.
Built for the BOT Chain Builder Challenge #1.
MIT



