Stale rebrand/merge residue committed by the Blockscout->Lux branding commits
(999e2e8a8, 10beed198) -- backups a find/replace pass wrote, then `git add -A`
swept in. Nothing in the repo, CI, Dockerfiles or the wider workspace refers to
any of them; all canonical counterparts are intact:
- .gitignore.orig, README.md.orig, instrumentation.node.ts.orig,
package.json.orig, pnpm-lock.yaml.orig, yarn.lock.orig, public/envs.js.orig,
next.config.js.bak (~1.9 MB, mostly the two dead lockfile copies)
- README.md.orig and public/envs.js.orig still contained raw `<<<<<<< HEAD`
conflict markers -- unusable as anything but residue.
- public/envs.js is not a source file at all: the real one is
/public/assets/envs.js, generated by deploy/scripts/entrypoint.sh (gitignored).
Also restores `*.orig`/`*.rej`/`*.bak` to .gitignore (upstream had `*.orig`;
the fork dropped it, which is why this residue could be committed).
Co-authored-by: Hanzo Dev <[email protected]>
Two root causes broke the vitest unit suite (6 suites failing → 29/29 green):
1. @growthbook/growthbook-react was intentionally removed from package.json in
the registry-cleanup commit (888ef29) and is unused anywhere in the app, but
the shared test render helpers (vitest/lib.tsx, playwright/TestApp.tsx) still
wrapped children in a dead <GrowthBookProvider>. Vite failed to resolve the
missing module, killing 5 suites that use the helper. Fix: remove the dead
import + wrapper (the provider held no growthbook instance and no feature
flags are consumed — it was a no-op). Do not re-add a deleted dependency.
2. tests/e2e/live-explorers.spec.ts is a Playwright live-infra E2E spec (imports
@playwright/test, hits live explorer URLs). It matched the vitest include glob
**/*.spec.ts and crashed the run. Fix: exclude **/tests/e2e/** from vitest —
those run under the Playwright runner (pnpm test:pw*), not the unit suite.
Also normalized pre-existing over-indentation in TestApp.tsx return block.
Before: 6 failed / 24 passed suites. After: 29/29 suites, 267/267 tests green.
Co-authored-by: Hanzo Dev <[email protected]>
v1.1.11 landed the three money values (Reward/Value/Fee) at 4.54:1 and
killed the overflow + Menu-button clip, but the network-utilization %
mapped to --color-badge-bright-green-fg (#25855A) measured 4.39:1 on the
real #FAFAFA card background (not pure white) -- 0.11 under bar.
Add dedicated semantic status tokens (--color-status-good/warn/bad) with
darker light-theme shades (#1A7A47 / #B45309 / #B91C1C -> 5.1 / 4.8 / 6.1
:1 on #FAFAFA) and light shades for dark theme, and point
getNetworkUtilizationParams at them. All four home-page values now clear
4.5:1 on every brand.
Co-authored-by: Hanzo Dev <[email protected]>
Three write-once-deploy-4 root causes from the 4-res audit:
1. Horizontal overflow (454px @1280, 38px @1920). The shared layout
Container forced 'min-w-[100vw] lg:min-w-[fit-content]', promoting the
whole page to its ~1734px max-content width so stat/block cards clipped
past the viewport. Make it a fluid column ('w-full min-w-0') and add
'min-w-0' to MainColumn so the flex chain shrinks and inner
overflow-x-auto regions scroll internally -> document.scrollWidth ==
innerWidth at every breakpoint.
2. White-on-white values (block Reward / tx Value / tx Fee /
network-utilization%). These passed legacy Chakra dot-tokens
('text.secondary', 'green.600'...) straight into an inline CSS 'color',
which browsers drop as invalid -> the text inherited white. Add one
resolveColorToken() helper (dot-token -> var(--color-*)), apply it in
SimpleValue (the value chokepoint for Reward/Value/Fee), and point
getNetworkUtilizationParams at real theme vars (bright badge fg shades,
all >=4.5:1 on the light card bg).
3. Broken 'Menu' button: the wallet/settings button stacked a 20px icon
over its label in a bare div inside a 32px overflow-hidden button (the
'51px in 32px' clip that read as a stray hamburger at lg+). Lay the
icon+label out horizontally (flex items-center gap-2, sized icon).
Co-authored-by: Hanzo Dev <[email protected]>
Two mobile-QA defects across the block explorers.
Defect 1 (org explorers showed goerli stub data): api-explore.{zoo,hanzo,
pars}.network served the self-signed INGRESS DEFAULT CERT (no cert-manager
Ingress for those SNIs), so browsers rejected every FE fetch and react-query
fell back to the goerli stubs behind stuck skeletons. FE wiring + backend were
already correct (all 6 chains served real per-chain data via the existing
IngressRoutes). Fix: dedicated letsencrypt-prod Ingress per org api host ->
explorer-unified:8090, mirroring api-explore-lux-network. Certs issued, all
three now ssl_verify=0, real data flows. Also source per-org brand identity
(NETWORK_NAME/currency) in the configmaps so titles render the right brand.
Defect 2 (mobile 390px detail pages: dropped labels, horizontal clip, tab
overlap): three shared-UI fixes.
- Footer: was the real horizontal-overflow driver — inline gridTemplateColumns
minmax(auto,470px) never collapsed on mobile. Now grid-cols-1 on mobile.
- DetailedInfo.ItemValue: add min-w-0 + mobile wrap so long address/hash values
stop forcing the page wider than the viewport (desktop unchanged).
- AdaptiveTabsList: @luxfi/ui Tabs silently discard Chakra style props
(overflowX/w/flexShrink/...), so express mobile scroll + shrink-0 as
className. Tab strip now scrolls instead of collapsing/overlapping.
Co-authored-by: Hanzo Dev <[email protected]>
Three UI-correctness fixes surfaced by visual inspection of explore.lux.network:
- DetailedInfo (block/tx/address detail pages): every field label + value
rendered white-on-white because the grid inherited the @luxfi/ui Tamagui
`t_dark` theme color while the page is in light mode. The two most-used
drill-downs were unreadable. Anchor the container to
--color-text-primary and labels to --color-text-secondary so text follows
the app color mode instead of the stray Tamagui theme.
- StatsWidget (home metric cards): the value had no explicit color and
inherited the same Tamagui white -> near-invisible "Total blocks /
transactions / addresses". Anchor to --color-text-primary.
- Validators dashboard + Network Overview: dropped the "Connected" (0/5) and
"Avg Uptime" (0.0%) widgets/columns. platform.getCurrentValidators reports
connected=null and uptime=0 for every validator on the public RPC (the API
node does not track peer uptime), so those numbers were fabricated-looking
zeros. Only chain-verifiable metrics (count, stake, delegation fee,
delegators) remain -- real data, no fake numbers.
Co-authored-by: Hanzo Dev <[email protected]>
The explorer Go backend answers by Host header via the ingress default backend,
so api-explore.lux.network responded 200 with no ingress rule — but with no TLS
block for that SNI the controller served the self-signed INGRESS DEFAULT CERT,
which browsers reject (ERR_CERT_AUTHORITY_INVALID). The explorer FE was stuck in
skeleton loaders. Add a dedicated Ingress -> explorer-unified:8090 with a
letsencrypt-prod TLS block (mirrors api-explore.osage.network). Verified live:
explore.lux.network now loads live blocks/txns/stats (ssl_verify_result=0).
Co-authored-by: Hanzo Dev <[email protected]>
The zoo/hanzo/pars brand deployments had been crashlooping at 0 ready since
June: their brand images bake NETWORK_NAME/currency/colors, but the runtime
envs-validator requires NETWORK_NAME in the rendered env and the brand
configmaps never set it — so every pod restart died on "NEXT_PUBLIC_NETWORK_NAME
is a required field". The lux configmap already carries SKIP_ENVS_VALIDATION for
the identical OIDC-placeholder reason; add it to the three brand configmaps
(mainnet + testnet) so they boot on their baked brand identity. Restores the
three brand explorers and lets the just-corrected chainId (pars 494949) + /v1
RPC configmap values take effect.
Co-authored-by: Hanzo Dev <[email protected]>
The deploy job's DO_API_TOKEN jq could parse-error on a non-JSON KMS response
and abort under set -e before the DIGITALOCEAN_ACCESS_TOKEN fallback; swallow it.
LLM.md records the v1.1.6 chain-switcher fix + live Playwright verification and
flags wallet-connect (needs NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID secret) + the
pre-existing intermittent homepage error-boundary.
Co-authored-by: Hanzo Dev <[email protected]>
ChainSwitcher put onClick={handleToggle} on the PopoverTrigger's button, which
double-toggled against PopoverTrigger's own open/close handling (net: stays
closed) — the switcher never opened, so no chains were selectable. Removed the
redundant onClick + unused handleToggle, matching the working popover pattern
(UserProfileDesktop). PopoverTrigger now drives open state via onOpenChange.
Co-authored-by: Hanzo Dev <[email protected]>
explore-fe-lux gets account/OIDC via runtime env; OIDC_* lack build-time
placeholders so SKIP_ENVS_VALIDATION=true (canonical_build.sh approach) —
make_envs still writes them to window.__envs. LLM.md records deploy path,
OIDC wiring, the useProvider crash, and Pars(7070/494949)+devnet(96370/96367)
chainId flags.
Co-authored-by: Hanzo Dev <[email protected]>
The KMS login (curl -sf | jq) had no failure guard, so under Actions' default
bash -eo pipefail a failed KMS login aborted the step before the
DIGITALOCEAN_ACCESS_TOKEN fallback ran (deploy failed instead of falling back).
Swallow curl/jq errors so the fallback path executes.
Co-authored-by: Hanzo Dev <[email protected]>
v1.1.4 changed useProvider to return null (react-query v5 forbids undefined)
but only switched the lib/web3 hooks. Four UI consumers kept
'const { data: { wallet } = {} } = useProvider()'; '= {}' defaults only
undefined, not null, so null data threw 'Cannot destructure property wallet
of {} as it is null' -> whole-page error boundary. Fixed to
'useProvider().data ?? {}' in ChainWidget, MultichainEcosystemsTableItem,
NetworkAddToWallet, AddressAddToWallet (+ wrap a pre-existing 169-char line).
Co-authored-by: Hanzo Dev <[email protected]>
The deploy job restarted deployment/lux-frontend-mainnet, which does not
exist (real name: explore-fe-lux, per deploy/k8s/explore-fe + the mainnet
ingressroute), and its KMS DO_API_TOKEN fetch returns empty so every deploy
silently skipped. Fix: (1) fall back to the DIGITALOCEAN_ACCESS_TOKEN repo
secret when KMS has no token; (2) 'kubectl set image' explore-fe-lux to the
freshly built ghcr.io/luxfi/explore:sha-<short> across mainnet/testnet/devnet
and wait on the mainnet rollout.
Co-authored-by: Hanzo Dev <[email protected]>
generate-well-known.js regenerates public/.well-known/explore.json from the
chain registry at build time, so the v1.1.4 hand-edit of the output file was
overwritten by the generator (which still appended ' Subnet'). Fix the
generator — the single source of truth — so the shipped manifest actually
uses 'Network' (Lux nomenclature; no 'Subnet'). Regenerated file is in sync
(wellknown:check passes). Pars id follows the registry (7070) to keep the
manifest consistent with its source; the 7070-vs-live-494949 question is
tracked separately in LLM.md.
Co-authored-by: Hanzo Dev <[email protected]>
- wallet: brandFamilyChains derives sibling networks (Lux mainnet/testnet/devnet)
from the multi-tenant chain registry so connect/add presents EVERY registered
network for the brand, not just the env chain; dedupe by id.
- login/menu: useProvider queryFn returns null (react-query v5 forbids undefined)
+ callers switched from '= {}' destructure default to '?? {}' so a null result
no longer crashes the wallet hooks (the busted-menu root cause).
- manifest: fix Pars chainId 7070 -> 494949 (regression) and drop the forbidden
'Subnet' term from sovereign L1 network names (-> '<Brand> Network').
- Validators page + /api/pchain proxy present; rebuild makes the live 5-validator
view visible on explore.lux.network.
Co-authored-by: Hanzo Dev <[email protected]>
Request the indexer's bound `symbol` in the markets query and use it as the
authoritative pair label when it parses as BASE/QUOTE, falling back to live
token-metadata resolution then short addresses. One pair, one source of truth.
Co-authored-by: Hanzo Dev <[email protected]>
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.
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.
12 cases over simulated request hosts: lux primary lists every L1 incl Osage;
hanzo/zoo/pars/osage are registered L2s, never the primary explorer (no
lux-primary / cross-L1 leak), switcher own-only; pars chainId 7070; keystone —
unregistered white-label hosts are never the primary explorer.
- isPrimaryNetworkExplorer(): require !isWhiteLabelMode() so unregistered
white-label hosts (explore.zoo.network, explore.pars.network) stop rendering
the Lux primary-network panels + cross-L1 list (NetworkOverview). Decomplects
'is this the primary explorer' from 'is the current chain EVM'.
- register explore.zoo.network/.ngo + explore.pars.network as their own L2
hostnames -> own-scoped chain/network switcher, brand parity with hanzo.
- add Osage L1 (mainnet 1872 / testnet 1871 / devnet 1873) + OSAGE_BRANDING so
the universal Lux explorer lists it.
- Pars chainId -> deployed truth (env + backend chains.yaml): mainnet
494949->7070, devnet 494951->7072.
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.
The /validators view fetched real validators (5, 2.5e18 weight) but rendered
white-on-white in light mode — data present in DOM, invisible to users. Set
text-[var(--color-text-primary)] on each of the three lux validator component
roots so all text inherits the theme-flipping color token. Also replace the
hardcoded "LUX" stake unit with config.chain.currency.symbol (AI on Hanzo),
matching NetworkStats. No decimals change (6dp is the codebase convention,
consistent with P-chain getMinStake).
When no stats microservice is configured (config.features.stats.isEnabled
false), don't fire the stats:lines request that can only fail — the Stats page
already renders the live Network Overview + an honest 'being indexed' note.
Avoids a wasted erroring query on every Stats page load.
Investor-grade data, no fabricated placeholders:
- pages/api/pchain.ts: derive node origin via new URL().origin instead of a
regex that only stripped /ext/bc/C/rpc. Non-C-chain brands (hanzo:
/ext/bc/hanzo/rpc) built a wrong URL -> HTML 404 -> JSON parse crash, so
Validators/Total Stake/Connected/Uptime all showed 0/-. Now the P-chain
proxy resolves correctly and the real validator set + stake render. Parse
upstream defensively with a clear error instead of an opaque message.
- lib/api/dchain/useDexData.ts: delete all hardcoded DEMO_* markets/orders/
trades/pools/overview. Return only real D-Chain (DexVM) data or honest-empty.
- ui/dex/DexPage.tsx: honest empty/error states per tab (no fake rows).
- stubs/stats.ts: neutralize STATS_COUNTER (title empty, value 0) so the
'Placeholder Counter 9.074M' stub can never surface as real data.
- ui/pages/Stats.tsx: gate indexer-backed counters/charts on
config.features.stats.isEnabled; always show the live chain-sourced Network
Overview, with an honest 'being indexed' note when no stats microservice.
- ui/snippets/navigation/horizontal/NavLinkGroup.tsx: popover Content defaulted
to overflow-hidden + tight py, clipping the last nav item (Verified
Contracts). Override to overflow-visible with padding; bottom-start placement.
The header logo is resolved by request Host via chainRegistry getCurrentChain()
-> branding.logoContent (real @hanzo blocky-H for hanzo.network; no hardcoded
triangle). Documents the 3 render spots, the L1/L2/L3 vm gating, the on-cluster
kaniko fan-out build (no GitHub builders), and the lux-mainnet explore-fe deploy
+ universe declared state.
The Tooltip/Popover (@luxfi/ui → @hanzogui/tooltip → @hanzogui/[email protected],
hard-pinned) crashed with 'Cannot read properties of undefined (reading
setReference)' (×189, every row) under React 19 → Next error boundary 500 once
real indexer data renders. Force the fixed 3.0.6 patch via pnpm override.
- Add explore.hanzo.network / explore.hanzo.ai (+ test/dev) to the Hanzo
chainRegistry hostnames so isWhiteLabelMode()==false on those hosts →
HANZO_BRANDING (real geometric-H logo) resolves and the network/chain
selectors enable (the dropdown was empty + showed the generic triangle).
- Hanzo testnet chainId 36964 → 36962 (LP-018; was duplicated with devnet).
- TopBar is desktop-only (hidden lg:block); HeaderMobile already block lg:hidden
→ exactly one header per breakpoint (was a double header < lg).
Replaces non-canonical scale-set / org-prefixed labels with the
existing labels every arcd host registers with. Matches evo for
amd64 and spark for arm64. No new labels added.
After shipping the first createGui-only patch (7784eb5), the runtime error
changed from "Err0" → "Missing theme." The root cause:
useThemeState in @hanzogui/web throws if ThemeStateContext.Provider
isn't above the component (parentId === ""). Calling createGui populates
the global config, but the tree must also be wrapped in <GuiProvider>
which materialises the ThemeProvider + GuiRoot + ComponentContext chain.
Three deltas in this commit:
1. createGui now receives explicit light/dark themes and color/space/
size/radius/zIndex tokens. Empty {} no longer satisfies getThemes
Deduped + downstream theme spreads. The tokens are minimal but
non-empty; @luxfi/ui uses Tailwind for styling, so we only need
hanzogui's internals to accept the config.
2. Top of render tree now wraps everything in <GuiProvider config>.
GuiProvider renders ComponentContext → ThemeProvider → GuiRoot
(isRootRoot=true), giving useThemeState the parent context it
requires.
3. createGui call moved out of the typeof-window block — server and
client both need it (SSR also instantiates @luxfi/ui frames at
module load, even though component render is gated by hydration).
@luxfi/ui components (Badge, Button, Tooltip, etc.) call @hanzogui's
getConfig() at module init and throw "Err0" when no config exists.
The previous commit removed the createGui call to avoid an Object.keys
crash on undefined themes, but that broke component loading instead.
createGui crashes when configIn.themes is undefined (Object.keys(undefined)
in getThemesDeduped). Passing themes: {} routes through scanAllSheets
which returns safely. tokens: {} matches the noTokens guard at line 27.
Net effect: createGui completes, globalThis.__guiConfig is populated,
and downstream @hanzogui/* components find a non-null config.
The evo native runner has filesystem corruption — last 3 runs failed
at actions/checkout step ("Can't find action.yml under
/home/z/.arcd/runner/_work/_actions/actions/checkout/v4").
lux-build-linux-amd64 ARC pool on lux-k8s is healthy (listener idle,
0 runners, max 10). Per LLM.md cross-org rule, luxfi/* workflows must
target lux-build pool — never hanzo-build.