zeekayandHanzo Dev 5ce7dca138 fix(explorer): a zero that means "unavailable" is a lie
Every figure below was rendered as 0 on explore.lux.network while the
chain said otherwise. None of them was missing data — the queries asked
for columns that are not there, or read columns nothing writes.

  /addresses answered {"items":[]} while /stats reported 72 addresses:
  the ORDER BY named `transactions_count`, which is the Blockscout-legacy
  spelling; luxfi/indexer's evm_addresses spells it `tx_count`. SQLite
  raised "no such column", the handler swallowed it into an empty page,
  and it stayed empty forever. Same guess broke evm_tokens' address
  column. detectColumn now asks the database instead.

  Token "Holders 0" sat directly above a populated holder list:
  evm_tokens.holder_count is never written by the indexer. Holders and
  transfers are now counted from evm_token_balances / evm_token_transfers
  in the same statement that selects the token — one query, not N+1 — and
  the token list is ranked by that count instead of by a column of zeros.

  Address balance read 0 for every address, WLUX's 159,126,795.518 LUX
  included: evm_addresses.balance is written as the literal '0' on first
  sight and never updated. Current state belongs to the chain, not to an
  index of it, so balances now come from the node — one batched
  eth_getBalance per page. No node configured, or no answer, means the
  balance is unavailable, and unavailable is null.

  "Burnt fees 100%" on every block: burnt_fees was gas_used x base_fee,
  which is the Ethereum EIP-1559 burn. Lux does not burn it. creditTxFee
  credits the whole fee to the coinbase (0x0100..0000, no code, no key)
  unless the fee split is active, and it has never fired on mainnet.

  average_block_time was structurally 0 — subtracting two TEXT datetimes
  in SQLite yields 0 — and gas_used_today compared those TEXT timestamps
  lexically against datetime('now'), matching nothing. Both go through
  strftime('%s') now, and transactions_today is counted rather than
  hardcoded nil.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 14:25:21 -07:00
2026-06-28 20:37:39 -07:00
2026-07-26 10:13:40 -07:00
2026-06-28 20:37:40 -07:00

indexer

Explorer

Single-binary block explorer for EVM and multi-chain networks. Indexes, serves API, embeds frontend. Zero external dependencies.

explorer
├── /v1/explorer/*        Explorer API v2 REST endpoints
├── /v1/explorer/graphql   GraphQL endpoint
├── /v1/explorer/ws        WebSocket / SSE realtime subscriptions
├── /*                     Embedded frontend (Next.js static export)
├── /health                Healthcheck
│
├── SQLite (WAL)           Zero-config embedded database
├── ZapDB               Fast KV layer for hot lookups
└── Replicate → S3         E2E PQ encrypted streaming backups

Quick Start

# Build
go build -o explorer ./cmd/explorer/

# Run — indexes chain, serves API + frontend on :8090
./explorer --rpc=http://localhost:9650/ext/bc/C/rpc

# That's it. Open http://localhost:8090

Docker

docker build -f Dockerfile.explorer -t explorer .
docker run -p 8090:8090 -v explorer-data:/data \
  -e RPC_ENDPOINT=http://node:9650/ext/bc/C/rpc \
  explorer

E2E Post-Quantum Encrypted Streaming Backups

Every SQLite write is continuously streamed to S3, encrypted end-to-end before leaving the process:

SQLite WAL → Replicate → age (ML-KEM-768 ∥ X25519) → S3
                          ↑
                          NIST FIPS 203 lattice KEM
                          + classical ECDH (defense-in-depth)
                          + ChaCha20-Poly1305 AEAD (64KB chunks)
                          + fresh ephemeral keys per segment
                          + forward secrecy (keys zeroed after use)
  • Quantum-resistant: ML-KEM-768 protects against harvest-now-decrypt-later
  • Hybrid: secure if either classical or post-quantum algorithm holds
  • Streaming: never buffers the full database — 64KB encrypted chunks
  • Point-in-time: restore to any transaction ID, not just the latest snapshot
  • No key rotation: each WAL segment generates fresh ephemeral keypairs
  • Zero trust S3: data is ciphertext before it leaves the process, S3 never sees plaintext
# Generate PQ keypair
age-keygen -pq -o explorer.key

# Enable streaming backups
export REPLICATE_S3_ENDPOINT=https://s3.amazonaws.com
export REPLICATE_S3_BUCKET=explorer-backups
export REPLICATE_AGE_RECIPIENT=age1pq1...  # from explorer.key

# Restore to point-in-time
explorer restore --from=s3://explorer-backups/mainnet --to=./restored.db \
  --age-identity=explorer.key --txid=12345

Architecture

┌─────────────────────────────────────────────────────────┐
│                      explorer binary                     │
│                                                           │
│  ┌──────────────┐  ┌───────────────┐  ┌──────────────┐  │
│  │  Indexer      │  │  API Server   │  │  Frontend    │  │
│  │  goroutines   │  │  (Base)       │  │  (embedded)  │  │
│  │               │  │               │  │              │  │
│  │  EVM blocks   │  │ /v1/explorer  │  │  Next.js     │  │
│  │  txs, logs    │  │  REST, GQL    │  │  static      │  │
│  │  tokens       │  │  WebSocket    │  │  export      │  │
│  │  traces       │  │  SSE          │  │  go:embed    │  │
│  │  DeFi, NFTs   │  │  JSON-RPC     │  │              │  │
│  └───────┬───────┘  └───────┬───────┘  └──────────────┘  │
│          │ write             │ read-only                   │
│          ▼                   ▼                             │
│  ┌─────────────────────────────────────┐                  │
│  │  SQLite (WAL)  +  ZapDB (KV)     │                  │
│  └──────────────────┬──────────────────┘                  │
│                     │ continuous WAL stream                │
│  ┌──────────────────▼──────────────────┐                  │
│  │  S3 (E2E PQ encrypted)              │                  │
│  │  ML-KEM-768 + X25519 + ChaCha20    │                  │
│  │  point-in-time restore to any TXID  │                  │
│  └─────────────────────────────────────┘                  │
└───────────────────────────────────────────────────────────┘

Rust Static Libraries (Optional)

Pure Go covers everything by default. The Rust verification services can optionally be compiled as a static library and linked via CGO:

explorer-rs/ffi/ → libexplorer_ffi.a
  explorer_verify_solidity()     smart-contract-verifier
  explorer_verify_vyper()        vyper verification
  explorer_lookup_signature()    sig-provider (4-byte selectors)
  explorer_lookup_event()        event signature lookup
  explorer_free()                memory management
# Pure Go (default)
go build -o explorer ./cmd/explorer/

# With Rust linked
cd explorer-rs/ffi && cargo build --release
CGO_ENABLED=1 CGO_LDFLAGS="-L... -lexplorer_ffi" go build -tags rustffi -o explorer ./cmd/explorer/

Chains

EVM (C-Chain + L2 EVM Chains)

Full EVM explorer feature parity:

  • Blocks (consensus, uncle, EIP-4844 blobs, Lux extended header)
  • Transactions (type 0-3, internal traces via 3 tracer types, state changes)
  • Tokens (ERC-20, ERC-721, ERC-1155, batch transfers, historical balances)
  • Smart contracts (Solidity/Vyper verification, proxy detection, ABI decode)
  • Account Abstraction (ERC-4337 UserOps, bundlers, paymasters)
  • MEV (sandwich, frontrun, backrun, liquidation detection)
  • DeFi (AMM V2/V3, perps, lending, staking, bridges, CLOB, NFT marketplaces)
  • Search, statistics, gas oracle, charts, user accounts, API keys, watchlists

DAG Chains (X, A, B, Q, T, Z, K)

Chain Purpose
X-Chain Asset exchange, UTXOs, atomic swaps
A-Chain AI compute, attestations, model registration
B-Chain Cross-chain bridge, proof validation
Q-Chain Quantum finality, lattice proofs
T-Chain Threshold MPC, key shares
Z-Chain Zero-knowledge, shielded transfers
K-Chain PQ key management, certificate lifecycle

Multi-Chain (100+ External)

Parallel indexer: Ethereum, Polygon, Arbitrum, Optimism, Base, BSC, Solana, Bitcoin (Ordinals/Runes/BRC-20), Cosmos, Aptos, Sui, NEAR, Tron, TON, and more.

API

All endpoints under /v1/explorer/.

GET  /v1/explorer/blocks
GET  /v1/explorer/blocks/{hash_or_number}
GET  /v1/explorer/transactions
GET  /v1/explorer/transactions/{hash}
GET  /v1/explorer/transactions/{hash}/token-transfers
GET  /v1/explorer/transactions/{hash}/internal-transactions
GET  /v1/explorer/transactions/{hash}/logs
GET  /v1/explorer/addresses/{hash}
GET  /v1/explorer/addresses/{hash}/transactions
GET  /v1/explorer/addresses/{hash}/tokens
GET  /v1/explorer/tokens
GET  /v1/explorer/tokens/{address}/holders
GET  /v1/explorer/smart-contracts/{address}
POST /v1/explorer/smart-contracts/{address}/verify
GET  /v1/explorer/search
GET  /v1/explorer/stats
POST /v1/explorer/graphql
WS   /v1/explorer/ws
POST /v1/explorer/rpc  (Etherscan-compatible)

White-Label

Zero branding in code. Everything at runtime:

explorer --chain-name="My Chain" --coin=MYC --chain-id=12345

Configuration

# Required
RPC_ENDPOINT=http://localhost:9650/ext/bc/C/rpc

# Optional
DATA_DIR=~/.explorer/data
HTTP_ADDR=:8090
CHAIN_ID=96369
CHAIN_NAME=My Chain
COIN_SYMBOL=ETH

# PQ Encrypted Backups
REPLICATE_S3_ENDPOINT=https://s3.amazonaws.com
REPLICATE_S3_BUCKET=explorer-backups
REPLICATE_AGE_RECIPIENT=age1pq1...

Testing

go test ./...                  # 431+ tests, 22 packages
go test ./explorer/...         # API layer (35 tests)
go test ./evm/...              # EVM indexer
go test ./storage/...          # Storage backends

License

MIT

S
Description
▼ LUX Network indexer and explorer.
Readme MIT
6.6 MiB
Languages
Go 92.3%
TypeScript 6.4%
PLpgSQL 1%
Shell 0.2%