27 Commits
Author SHA1 Message Date
zeekayandHanzo Dev 858d9ca25b Typecheck on the native compiler, keep 6 for the tools
typescript 7.0.2 is the native compiler and exports no JavaScript API, so
typescript-eslint and @typescript-eslint/eslint-plugin cannot read it —
typescript-eslint refuses TS 7 outright. It comes in aliased as tsgo and does
the typechecking; typescript stays on the 6 line for the tools. lint:tsc
reports the same 239 diagnostics under both compilers.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 22:44:21 -07:00
zeekayandHanzo Dev f434a701dc dex: show the AMM trades that are actually landing, next to an honest CLOB
The AMM subgraph has been indexed and at chain head the whole time; nothing
read it. /dex queried only the native CLOB at 0x9999, which has genuinely
never emitted an event, so the page reported MARKETS 0 / VOLUME 0 while ~1
swap per block was landing in the WLUX/LZOO pool.

Wire the amm subgraph in as a SECOND venue rather than folding it into the
first. The two are different exchanges and their figures are never summed —
each section reports only its own state, so "live" can never be borrowed by
the venue that is not trading.

Swaps were rendering as anonymous EVM traffic; they now read as trades:
pool-signed amounts resolved into sold/bought, scaled by each token's own
decimals, with symbols, pair, fee tier and links to sender, block and txn.

Status uses the words already in use elsewhere (useNetworkValidators,
useFeeSplit) — live/idle/empty/unavailable/unknown — defined once in
lib/api/venueStatus. There is deliberately no 'stale': separating a stalled
indexer from a quiet venue needs an indexer head this API does not serve.

Also decomplected while here: one fetcher for both subgraphs, and an
unreachable endpoint is no longer flattened into "empty" — an outage and a
venue that has never traded are different facts and now render differently.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-07 08:03:30 -07:00
zeekayandHanzo Dev 334eb9a388 auth: delete the upstream auth; Hanzo IAM is the only gate
The explorer shipped three sign-in systems. Two were Blockscout's and both
spoke to its /account/* backend, which this deployment does not run — measured,
every one of those endpoints 404s on api-explore.lux.network:

  /account/get_csrf 404    /account/send_otp 404     /account/siwe_message 404
  /account/user/info 404   /account/confirm_otp 404  /account/authenticate_via_* 404

So none of it could ever have worked. It is deleted, not re-pointed: the email
+ one-time-code modal, the SIWE wallet login, the CSRF handshake that fired on
every page render, the Dynamic.xyz provider, and the dev page that let you
paste a session cookie by hand. Rewriting any of it against our own backend
would have been building custom auth, which we do not do.

What is left is the standard OIDC client we already had, finished:

  * PKCE (S256). This client is public — it runs in a browser and can hold no
    secret — so the verifier is the only thing binding a redeemed code to the
    page that asked for it. IAM advertises S256 in its discovery document and
    we were not sending a challenge at all.
  * userinfo went to <issuer>/api/userinfo, which is a 404 and an /api/ path we
    do not serve. It is /v1/iam/oauth/userinfo, per IAM's own discovery.
  * logout now ends the IdP session (end_session_endpoint) instead of only
    dropping our cookie and leaving the user signed in upstream.

All three live in lib/oidc.ts, which already said never to rebuild these paths
at a call site.

The header's provider switch collapses to two states — signed in with IAM, or
account disabled and this is the wallet/settings menu. `rewards` is stated off:
its gate read authProvider === 'auth0', so it was already unreachable.

The ConfigMaps carried NEXT_PUBLIC_OIDC_CLIENT_ID=lux-explore-client-id, a
client that has never existed, so authorize answered 400 unknown client_id.
IAM names clients <org>-explore; all four brands now say so, and hanzo/zoo/pars
get the OIDC keys they never had.

Typecheck: no new error classes, production errors 26 -> 22.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 19:18:31 -07:00
zeekayandHanzo Dev a3caebadf2 search: the icon sat on the placeholder, and validators counted one L1 of four
The magnifying glass was drawn over the leading "S" of the placeholder. The
cause is not the search bar: @luxfi/ui's InputGroup measures its adornments
correctly and then throws the measurement away, because the clone is guarded on
`displayName === 'FieldInput'` — a component that does not exist in the package
— and the props it would have set (`ps`/`pe`) are Chakra style props that the
Tailwind/gui Input spreads onto the DOM and ignores. Both halves are leftovers
of the Chakra→Tailwind port, so EVERY InputGroup in the app is unpadded; the
header is just the one you look at every day. Patched at the primitive
(patches/@[email protected]) rather than hand-tuning padding at one call
site: the offset is the measured width of the actual adornment, so it stays
right when the adornment changes, and FilterInput / revoke's SearchInput / the
showcase are fixed by the same change. Measured at 1440x900: the icon overlapped
the text by 24px and the kbd chip by 52px; both are now 0.

The measurement also moves to a layout effect and stops blanking value and
placeholder on first render. That blanking was inert while the guard swallowed
the clone; with the clone live it flips the field from controlled to
uncontrolled on mount, which React warns about and which can eat a keystroke.

ClearButton spread `className` over its own visibility classes, so a caller
passing one clobbered `invisible` — the header showed a clear button on an empty
field. Merged instead, at the component.

Placeholder is now "Search or ask anything", and Cmd/Ctrl-K opens an assistant.
Nothing in this repo, @luxfi/ui or @hanzo/gui ships a command palette or chat
(no cmdk, no palette, one dialog primitive), so this is the smallest thing that
works: the existing Dialog, one global listener, and a server-side proxy that
holds the credential. The key is deliberately not NEXT_PUBLIC_, so a deployment
without one gets a dialog that says exactly that instead of failing silently.
Off unless NEXT_PUBLIC_AI_ASSISTANT_ENABLED is set — Ctrl+K is the browser's
address-bar shortcut and claiming it to show an error would be worse than not
claiming it.

Validators read 5 on a network running 20. useCurrentValidators asks ONE
P-Chain, the one this deployment points at, but Lux, Hanzo, Zoo and Pars are
each sovereign L1s with their own P-Chain and their own validator set — measured
2026-08-06, five each, with four disjoint NodeID sets. The registry gains the
node origin per chain; absence of it is the signal that a chain has no publicly
reachable node (SPC and Osage are in-cluster only), and those are reported
`unavailable` rather than counted as zero. Stake is deliberately NOT summed:
each chain bonds its own currency.

The proxy resolves `?chain=` through the registry and never from the request, so
it cannot be pointed at an arbitrary origin. The new envs are registered with
the envs-validator and documented in ENVS.md — the schema rejects unknown
NEXT_PUBLIC_* variables and runs at container start, so an undeclared one stops
the pod from booting.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 19:17:01 -07:00
zeekayandHanzo Dev 827ae46c8a light mode: every heading and address was white-on-white
The gui provider pins `t_dark` on <body> client-side in BOTH schemes (the
server sends a bare <body>), and its theme block maps
  .t_dark { --color: var(--c-color-background) }  /* #ffffff */
against scheme-INVARIANT sources (--c-color-background #ffffff,
--c-color-text #000000). So every Tamagui text component resolved --color
to #ffffff unconditionally — correct by accident on dark, invisible on
light. Measured 1.00:1 at all four widths on /address/*, /validators and
the landing page: "Address details", the ADDRESS ITSELF, "Validators",
and six section headings. Pixel ground truth on the 1440 light heading
band: 135,034 of 136,000 pixels pure white, no glyphs at all.

A user clicking a Safe from lux.cloud landed on a page that never showed
which address they were looking at.

Only --color is corrected: the background already tracks html.light via
the Tailwind --lux-* layer. Not the earlier suspect — the circular
`--color-x: var(--color-x)` tokens are inert, because unlayered :root
beats @layer theme regardless of order.

Tamagui goes in cspell.jsonc words[], which is the real dictionary —
.cspell-words.txt is listed in ignorePaths, so it is a file cspell skips,
not a word source.

Co-Authored-By: Hanzo Dev <[email protected]>
2026-08-06 16:37:42 -07:00
zeekay dee054786f test(perf): the harness that turns "feels slow" into a number
Two scripts, any Lux surface, from a URL. mobile-sweep reads scrollWidth vs
clientWidth AND every element's box at 393x852 — a body that cannot scroll is
not proof the content fits, it is proof the content is unreachable. perf adds
4x CPU + Slow-3G over CDP and reports LCP with the element that won it, CLS, an
INP proxy, long tasks, and bytes by type.

First run against production, as the before number:

  explore.lux.network        overflowPx  clippedEls  worstRight  small<44
    /                                 0          64        1113        53
    /blocks                           4           6         397        15
    /txs                            823        2820        1216        14
    /accounts                         0           0           0        11

  exchange.lux.network
    /                                 0           5         402        17
    /swap                             0           0           0        18
    /explore                          0         445         927         7
    /positions                        0           0           0         6

/txs pushes a 393px phone sideways by 823px and puts 2820 elements off the side
of the screen; /explore on the exchange clips 445 of them into a body that will
not scroll to reach them.

Screenshots are gated on a positive assertion — more than 200 characters of
text — because a shot of a spinner, a login wall or an unstyled flash is worse
than no shot.
2026-07-27 18:39:49 -07:00
zeekay 608243e03a refactor(pchain): Subnet -> Net; add the mainnet explorer ingress
Drops "subnet" from the P-chain surface, which is not our vocabulary:

    useSubnets          -> useNets      (useSubnets.ts deleted)
    PChainSubnet        -> PChainNet
    GetSubnetsResponse  -> GetNetsResponse

One "subnet" deliberately survives — `subnetID` in lib/api/pchain/wire.spec.ts.
That is the UPSTREAM P-chain wire field, read through a raw
Record<string,string> cast, so renaming it would break response parsing. The
rule is about our own language, not about lying to the wire.

Also adds deploy/k8s/explore-fe/ingress-mainnet.yaml. explore.lux.network is
served by the Next.js deployment explore-fe-lux (ghcr.io/luxfi/explore), not
the Go `explorer` binary, which only backs api-explore.lux.network. This
replaces drift where the plain Ingress pointed explore.lux.network at
explorer:80 — the Go binary's embedded, stale build.

Two pre-commit gates had to be satisfied, both legitimate:
- eslint max-len: three className lines (BridgePage 230 chars, ChainRow 188 x2)
  exceeded the 160 limit; wrapped.
- cspell: `platformvm` flagged unknown. It is the P-chain VM's actual name, so
  it is added to cspell.jsonc `words` rather than renamed in code. (Note for
  the next person: .cspell-words.txt is listed under ignorePaths — it is a file
  excluded from checking, NOT the dictionary. Adding a word there does nothing.)

Verified: tsc --noEmit reports ZERO errors in every file this commit touches and
zero stale-symbol errors, so the rename resolves completely. The project's 223
pre-existing errors are all in untouched files and unchanged by this commit.
2026-07-26 09:11:38 -07:00
zeekay 38babd2690 Merge remote-tracking branch 'origin/main' into fix/dex-real-subgraph
# Conflicts:
#	lib/api/dchain/useDexData.ts
#	ui/dex/DexPage.tsx
2026-06-30 15:27:12 -07:00
zeekay e179668caa fix(dex): wire /dex to live cchain dex subgraph; fix block txs crash
Replace the hardcoded demo DEX dataset (incl. the placeholder pi-million 24h volume) with live markets + fills from the C-Chain 0x9999 subgraph at the per-network /v1/graph/cchain/dex/graphql endpoint, selected via config.apis.general like every other resource. Token pairs resolve through the existing token metadata endpoint, falling back to a short address. Empty/unavailable nets render 'No active markets' — never mock.

Guard tx.transaction_types in TxsListItem so coinbase/precompile txs (absent transaction_types in the API payload) no longer crash the block Transactions tab; drop an unused destructured prop. Allow CLOB in cspell.
2026-06-28 23:05:03 -07:00
zeekay eac4772aab feat(explore/primary-vms): handle all 10 Lux-primary VM types + route D-Chain to the DEX UI
The Lux primary network is a family of VM-specialized chains (C=EVM, X=UTXO,
D=DEX, P=platform, + A/B/Q/T/Z/G/K/O/R/I/M), mirrored from node/node/vms.go.

- configs/app/primaryChains.ts: single source of truth (PRIMARY_VMS, getPrimaryVm,
  per-VM `view`, hasBespokeView, chainsAwaitingBespokeView). Decomplects the two
  lists that duplicated this — ChainsPage + ChainDetailPage now consume it
  (ChainDetailPage drops ~110 lines of copy).
- D-Chain (DexVM) renders the DEX order-book UI (ui/dex/DexPage) via view:'dex',
  not a generic block list. C-Chain=EVM core, P-Chain=platform/validators,
  X-Chain + the custom VMs render the generic detail fallback (info + indexer DAG
  stats + validators) — graceful, never blank. chainsAwaitingBespokeView() FLAGS
  the backlog: X-Chain (UTXO) + the 11 custom VMs still need a bespoke view.
- These VMs are Lux-primary, so the primary-VM surfaces (Chains/DEX/Bridge/AI
  Compute nav + /chains, /chains/[slug], /dex pages) gate on
  isPrimaryNetworkExplorer(). ui/shared/PrimaryNetworkGuard backstops direct-URL
  access so the VMs never leak onto a brand explorer (Hanzo/Zoo/Pars).
- Tests: configs/app/primaryChains.spec.ts (26 cases) — registry, per-VM view,
  bespoke-view flag, and the lux-only visibility keystone. 38/38 green with the
  existing chainRegistry.spec.ts.
2026-06-28 15:08:34 -07:00
zeekay 507a7da852 fix(realtime): swap Phoenix WebSocket transport for backend SSE
The FE connected realtime via Phoenix Channels (wss .../socket/v2), but the
Go backend serves no Phoenix endpoint — its realtime is SSE at
/v1/base/realtime (envelope {event,chain,data}). Result: repeating ws 404 ->
'Live updates temporarily delayed'.

lib/socket/sse.ts reimplements the slice of the Phoenix Socket/Channel API the
27 realtime consumers use, backed by one EventSource, behind the existing
SocketProvider/useSocketChannel/useSocketMessage seam — no consumer changes.
Envelope routes via an optional topic field or a hub-channel translation table
(blocks -> blocks:new_block/new_block today). CT harness becomes a tiny SSE
server with the same signatures. 12 vitest cases cover it.

Frontend-only; chain/indexer untouched. Data freshness remains gated on the
separate C-Chain-rollback/indexer-reorg owner items.
2026-06-28 09:01:10 -07:00
zeekay 3d1120861a feat(brand): per-(brand x env) image overlay — ghcr.io/{brand-org}/explore-{env}
The branded build now fans out into 8 (4 brands × mainnet+testnet)
production images:

  ghcr.io/luxfi/explore-{mainnet,testnet}:v1.0.0
  ghcr.io/zooai/explore-{mainnet,testnet}:v1.0.0
  ghcr.io/hanzoai/explore-{mainnet,testnet}:v1.0.0
  ghcr.io/parsdao/explore-{mainnet,testnet}:v1.0.0

Each image is a thin ENV overlay of ghcr.io/luxfi/explore (the Next.js
runtime) — brand identity is baked at build time via NEXT_PUBLIC_*
build-args. The brand's GHCR namespace owns the image per the workspace
container-registry rules (no cross-org mixing).

deploy/scripts/branded_build.sh is the source of truth for tuple -> env
mapping; the actual image fan-out is performed via crane mutate -e which
produces an instant ENV-only layer (no Kaniko, no tar of build context —
same hash blob mounts across namespaces).

The corresponding K8s manifests (per-brand Deployment + Service per
namespace, IngressRoute patches routing catch-all to FE) live in the
cluster directly.
2026-06-10 13:24:39 -07:00
zeekay 81a00208a8 decomplect: drop '' from spell-check dictionaries
OSS lux/explore should not carry tenant-specific brand strings.
2026-05-21 10:47:22 -07:00
zeekay 07a96bd1cd ci: lux-only build — never push other-brand images to luxfi org
Per registry-separation rule (CLAUDE.md "Container Registries — NEVER
MIX"), luxfi/explore must only push the Lux brand. Other-brand images
(hanzoai/explore, zooai/explore, etc.) build from each brand's own
org repo, never from this one.

The source still supports white-label brand selection at runtime
(lib/white-label.ts inspects hostname or NEXT_PUBLIC_BRAND), so a
single Lux image can serve any brand at deploy time. Build-time brand
baking is reserved for per-org image variants — out of scope here.

Also drops the Lux-only frontend rollout list down to mainnet/testnet/
devnet — non-Lux deployments live in their own clusters.
2026-05-05 23:13:40 -07:00
zeekay 5e30074537 wip: scaffold Vite 8 + React 19 SPA under vite/ — Next.js port blocked
The existing Next.js app (99 pages, 427 next/* imports, 177 files using
next/router, 154 using nextjs-routes types, 105 using getServerSideProps) is
far too coupled to rewrite in one pass. A full migration is ~2–4 weeks of
mechanical work.

This commit adds the scaffold only:

  vite/package.json      — Vite 8 + React 19.2 + wouter, no Next deps
  vite/vite.config.ts    — aliases at ../lib, ../ui, ../configs, … so source
                           migrates file-by-file instead of moving
  vite/tsconfig.json     — mirrors the aliases for TypeScript
  vite/index.html        — security headers as <meta> (was nextjs/headers.js)
  vite/src/main.tsx      — React 19 root bootstrap
  vite/src/env.ts        — runtime envs: window.__envs first, import.meta.env
                           fallback, maps NEXT_PUBLIC_API_HOST → VITE_API_BASE_URL
  vite/src/redirects.ts  — full port of nextjs/redirects.js (69 client rules)
  vite/src/App.tsx       — wouter router with CatchAll redirect resolver
  vite/Dockerfile        — 2-stage → ghcr.io/hanzoai/spa
  vite/LLM.md            — what's done, what's left, how to finish

The production Dockerfile at ../Dockerfile still builds the Next.js app. This
scaffold is independent; nothing in the existing tree is deleted or moved.

See vite/LLM.md for the full migration plan and 9 critical TODOs before this
can replace the Next.js build.
2026-04-19 00:05:37 -07:00
zeekay 3813d03918 feat: explorer FE — stateless SSR, chain selector, localnet, registry cleanup 2026-03-01 18:00:00 -08:00
zeekay 0e381fd911 feat: add all chain VM accessors and wire DEX/Bridge to luxnet SDK
- Add centralized chain accessor module (lib/api/luxnet/chains.ts) exposing
  all 14 chain VMs (C/X/P/A/B/D/G/I/K/O/Q/R/T/Z) plus utility APIs
- Refactor pchain/client.ts to re-export from centralized module
- Wire DEX page to use DexVMAPI (getStats, getPools, getMarkets) with
  demo data fallback when D-Chain RPC is unavailable
- Add B-Chain bridge API module (lib/api/bchain/) using BridgeVMAPI
  (getSignerSetInfo, getWaitlist) for signer set stats
- Update Bridge page to show live bridge signer count from B-Chain
- Fix stale E2E tests: use >= minimum thresholds instead of exact
  counts (block/tx counts grow over time), use describe blocks
2026-02-27 12:05:23 -08:00
zeekay 2c4fcde2d2 feat: multichain explorer pages, chain switcher, indexer integration
- Network Overview homepage with P-chain stats and chain cards
- Chain detail page with validator info and indexer stats
- Chain switcher dropdown in top bar
- A-chain AI client with indexer API integration
- D-chain DEX data hook with live indexer fallback
- E2E tests for all 5 live explorer instances
- Test transaction sender script
- Remove verbose comments, section dividers, and console.log scaffolding
2026-02-27 10:46:56 -08:00
zeekay 3725171b32 feat: add AI compute dashboard and DEX orderbook pages
- A-Chain (AI): dashboard with models, providers, attestations tabs
- D-Chain (DEX): markets, orderbook, trades, pools tabs
- API hooks with mock data (ready for indexer integration)
- Navigation items and route registration for /ai and /dex
2026-02-26 14:05:43 -08:00
zeekay 8c1f6b438b feat: update all chain definitions to 14 primary network chains
Add D-Chain (DexVM), G-Chain (GraphVM), I-Chain (IdentityVM),
O-Chain (OracleVM), R-Chain (RelayVM) to match ~/work/lux/node
vms_allvms.go. Fix VM names: ThresholdVM, BridgeVM, KeyVM.
Update chain count from 9 to 14 across all pages.
2026-02-26 11:10:12 -08:00
zeekay f6eb142582 feat: add Bridge page and Network Stats to Stats page
- Add /bridge page with cross-chain transfer route tracker
- Show bridge routes between Primary Network chains and L1 subnets
- Add Network Stats section (validators, chains, stake, uptime) to Stats page
- Add Bridge to navigation menu
- Register /bridge route in nextjs-routes, metadata, and mixpanel
2026-02-26 00:47:24 -08:00
zeekay 2ade6a7320 feat: native multi-platform Docker builds (amd64 + arm64)
Replace slow single-platform build with parallel native builds:
- amd64 on ubuntu-latest, arm64 on ubuntu-24.04-arm
- Registry-based caching per platform
- Multi-platform manifest merge
- Auto-deploy to DOKS on push to main
2026-02-26 00:11:22 -08:00
zeekay 3c8d48e0e0 feat: unified multi-chain explorer with Network Overview, Chains, and Validators pages
- Replace single-chain homepage with Network Overview showing P-chain stats,
  primary chains (C/P/X/A/B/Q/T/Z/K), and L1 subnet chains
- Add Chains directory page with Primary Network / L1-L2-L3 tabs
- Add Validators page with Dashboard, Validators list, and Delegators tabs
- Add P-chain API hooks (useCurrentValidators, useBlockchains, useSubnets)
- Apply monochrome theme: replace all blue/purple/cyan/teal with gray
- Add Geist Mono font support
- Add Chains and Validators to navigation sidebar
- Update route types, metadata, analytics, and cspell for new pages
2026-02-25 22:53:10 -08:00
zeekay cda4e3c4e8 Add node-datachannel stub with polyfill subpath for next build
@libp2p/webrtc imports @ipshipyard/node-datachannel/polyfill which
requires native C++ compilation. Stub provides ESM+CJS exports for
both main and polyfill subpaths with no-op WebRTC classes. Also
ignores pnpm-lock.yaml in cspell and copies stubs dir in Dockerfiles.
2026-02-24 18:07:34 -08:00
tom goriunovandGitHub e6e83ee4e7 Add cross-chain transaction tracking and visualization (#3227)
* envs and feature setup

* home page layout

* cross-chain txs page

* cross-chain transfers page

* cross-chain transfers on tx page

* cross-chain messages on tx page

* cross-chain tx page

* cross-chain tx details token transfers

* stats and counters for lists

* migrate to new API version

* add swagger

* mobile lists

* placeholder views on address page

* tests

* fix adaptive tabs initial state when there is only one tab in the list

* tweaks

* bug fixes

* fix tests

* update screenshots

* migrate to new api version

* add chain label tooltip, copy button to txs in lists and same address highlight

* list views on address page

* fix tests

* fix links to current address in the list views

* update tooltips

* update tables layout

* review fixes

* fix spelling

* update screenshots

* update screenshot
2026-02-19 15:14:32 +01:00
tom goriunovandGitHub 401dc58402 Smart wallet auth (#3220)
* basic login button implementation

* refactoring and add dynamic auth button to the other pages

* connect wallet without authenticating an user

* login and logout user in blockscout account

* refactoring and RPC overrides

* authenticate user with connected wallet

* add ENS to profile button

* override evm chains and fix contract write with waas wallet

* disconnect wallet and fix docker build

* add dynamic imports to components that use web3 wallet packages

* refactor user profile directory

* clean up

* tweaks

* update pw ci script

* fix "connect wallet" button behavior

* handle linking email to profile with dynamic provider

* fix test

* async loading web3 hooks and remove dynamic loading from components that use them

* fix pw tests

* add account navigation in horizontal layout when auth provider is dynamic

* fix address verification modal

* review fixes

* spelling fixes
2026-02-18 19:42:44 +01:00
tom goriunovandGitHub fd52568916 Integrate cspell (#3279)
* add cspell config

* remove eslint plugin

* adjust pre-commit hook and add check to CI

* fixes
2026-02-13 07:48:43 +01:00