zeekayandHanzo Dev 41e5e20e5f Answer the swap form on the paths it actually calls
The swap form calls /v1/quote and /v1/swappable_tokens, not
/v1/graph/{chain}/{subgraph}/quote. Only the prefixed form existed here, so a
TypeScript service stayed in front of this one to serve the unprefixed paths,
and the two answered the same questions from two copies of the book.

A swap request already says which chain it is for — a quote names the chain of
each token, an approval names its chain, a swap carries the quote it came from.
So the chain comes out of the request rather than the path, and the request is
handed to the mux that chain's subgraph already registered. The handler values
are the ones the prefixed routes use; nothing is built twice.

Swap and approval need this chain's router. Without one configured there is no
address to address a transaction to, so those two routes are absent rather than
serving calldata signed against nothing.

The subgraph that backs a chain's swap routes is the one holding an AMM book.
Picking whichever subgraph started first would have been a race, and a CLOB
schema has no pools to price against.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 17:02:43 -07:00
2026-08-03 21:01:59 -07:00

explorer

Explorer

Single-binary Lux explorer: chain indexer, GraphQL engine, and SPA frontend in one Go process. Imports luxfi/indexer and luxfi/graph as libraries; embeds the luxfi/explore Next.js build via go:embed.

ghcr.io/luxfi/explorer
  -> indexes one or more EVM chains  (luxfi/indexer)
  -> exposes per-chain GraphQL       (luxfi/graph)
  -> serves embedded SPA + REST API  (luxfi/explore)

Routes

Path Purpose
/ SPA (embedded; SPA-routing fallback)
/envs.js Runtime config window.ENV = {...}
/icon.svg, /logo.svg Per-host brand assets (disk override)
/health Service health
/v1/indexer/* Default chain explorer API
/v1/indexer/{slug}/* Per-chain explorer API
/v1/explorer/{slug}/* Legacy alias for /v1/indexer/{slug}
/v1/graph/{slug}/{subgraph}/graphql Per-chain, per-subgraph GraphQL
/v1/explorer/admin/chains[/{slug}] Runtime registry CRUD
/v1/explorer/realtime WebSocket realtime hub
/v1/explorer/realtime/stats Realtime stats

Quick Start

docker run -p 8090:8090 \
  -v $(pwd)/chains.example.yaml:/etc/explorer/chains.yaml:ro \
  -v explorer-data:/data \
  ghcr.io/luxfi/explorer:latest \
  --config /etc/explorer/chains.yaml

Or with compose:

docker compose up

Configuration

chains.yaml is the single source of truth. Every aspect of the deploy -- chains, branding, GraphQL subgraphs, network switcher links -- is per-chain customizable. See chains.example.yaml for the full schema.

data_dir: /data
http_addr: :8090

brand_default:
  name: Lux Explorer
  coin: LUX
  accent_color: "#5cf"

networks:
  - { label: Mainnet, domain: explorer.lux.network, chain_id: 96369 }

chains:
  - slug: cchain
    name: Lux C-Chain
    chain_id: 96369
    type: evm
    rpc: https://api.lux.network/v1/bc/C/rpc
    coin: LUX
    enabled: true
    default: true

    indexer:
      poll_interval: 30s

    graph:
      enabled: true
      subgraphs:
        - { name: amm, schema: amm, enabled: true }

    brand:
      name: Lux C-Chain
      accent_color: "#5cf"

$ENV_VAR substitution in rpc: / ws: lets one file template every network -- mount it read-only and inject secrets via env vars.

Per-Network Customization

The same image powers every deploy. To rebrand for a different network:

  1. Mount a different chains.yaml.
  2. Optionally mount icon.svg / logo.svg at the path referenced by chains.yaml -- they are read from disk per-request, not embedded.
  3. Set environment variables to fill the ${VAR} placeholders.

No image rebuild needed. The SPA reads chain list and brand at runtime from /envs.js.

Runtime Registry

Chains can be added, updated, and removed at runtime without restart:

# Add
curl -X POST http://localhost:8090/v1/explorer/admin/chains \
  -H 'Content-Type: application/json' \
  -d '{"slug":"new","name":"New","chain_id":123,"rpc":"http://node:8545","type":"evm"}'

# List
curl http://localhost:8090/v1/explorer/admin/chains

# Remove
curl -X DELETE http://localhost:8090/v1/explorer/admin/chains/new

Adding a chain spawns its indexer + (optional) graph workers; removing cancels them and frees the routes.

mDNS Discovery

Set EXPLORER_MDNS=true to auto-register chains advertised by local Lux nodes (_luxd._tcp, _zood._tcp, _hanzod._tcp, _parsd._tcp). The explorer queries info.getChains on the discovered node and registers each tracked chain. mDNS-sourced chains never override config-sourced entries.

Storage Layout

/data/
  cchain/
    query/indexer.db         (SQLite WAL, indexer)
    {zapdb}/                 (KV store)
    graph/
      amm/graph.db           (SQLite WAL, per-subgraph)
  zoo/
    ...

Each chain (and each subgraph) owns an isolated SQLite + KV store. WAL is optionally streamed to S3 with PQ encryption when REPLICATE_S3_ENDPOINT is set -- see luxfi/indexer/daemon.

Local Build

# Sibling layout: ~/work/lux/{indexer,graph,explorer}
cd ~/work/lux/explorer
go build -o explorer .

go.mod uses local replace directives pointing at ../indexer and ../graph. Production CI clones all three repos into the same layout inside the Docker build.

Endpoints (curl examples)

curl http://localhost:8090/health
curl http://localhost:8090/v1/indexer/cchain/blocks/latest
curl -X POST http://localhost:8090/v1/graph/cchain/amm/graphql \
  -H 'Content-Type: application/json' \
  -d '{"query":"{ swaps(first:5) { id amount0 amount1 } }"}'
S
Description
Lux Explorer
Readme BSD-3-Clause
1.1 MiB
Languages
Go 97.3%
Dockerfile 1.7%
Shell 0.8%
HTML 0.2%