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.
This commit is contained in:
zeekay
2026-07-26 09:11:38 -07:00
parent 56383ca7fd
commit 1a686d955f
18 changed files with 126 additions and 128 deletions
+1
View File
@@ -1,3 +1,4 @@
ECDL
Kaniko
parsdao
platformvm
+1 -1
View File
@@ -67,7 +67,7 @@ export const PRIMARY_VMS: ReadonlyArray<PrimaryVm> = [
vm: 'PVM',
vmId: 'rWhpuQPF1kb72esV2momhMuTYGkEb1oL29pt2EBXWsBY6MALT',
chainId: null,
description: 'The P-Chain manages validators, staking, and subnet creation across the network.',
description: 'The P-Chain manages validators, staking, and L1 creation across the network.',
view: 'platform',
},
{
+1
View File
@@ -33,6 +33,7 @@
],
// words - list of words to be always considered correct
"words": [
"platformvm",
"aatx",
"CLOB",
"hanzoai",
@@ -0,0 +1,35 @@
---
# Public ingress for the Lux mainnet explorer frontend.
#
# explore.lux.network is served by the dedicated Next.js deployment
# `explore-fe-lux` (ghcr.io/luxfi/explore), NOT the Go `explorer` binary. The
# Go binary only backs the API host api-explore.lux.network (separate ingress,
# ingress-api-mainnet.yaml). explore-fe-lux is self-contained: it renders every
# app page, serves /assets/envs.js from its own make_envs at startup, and
# proxies P-chain reads via its /api/pchain Next.js route — so the whole host
# routes to it. This replaces the earlier drift where the plain Ingress pointed
# explore.lux.network at explorer:80 (the Go binary's embedded, stale build).
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: explore-lux-network
namespace: lux-mainnet
labels: {app: explore-fe, brand: lux, network: mainnet}
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: ingress
tls:
- hosts: [explore.lux.network]
secretName: explore-lux-network-tls
rules:
- host: explore.lux.network
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: explore-fe-lux
port:
number: 80
+3 -3
View File
@@ -3,7 +3,7 @@
export { getPChain, getInfo, getHealth } from './client';
export { useCurrentValidators } from './useCurrentValidators';
export { useBlockchains } from './useBlockchains';
export { useSubnets } from './useSubnets';
export { useNets } from './useNets';
export { useChainHeights } from './useChainHeights';
export type {
@@ -11,10 +11,10 @@ export type {
PChainDelegator,
PChainRewardOwner,
PChainBlockchain,
PChainSubnet,
PChainNet,
GetCurrentValidatorsResponse,
GetBlockchainsResponse,
GetSubnetsResponse,
GetNetsResponse,
ValidatorStats,
} from './types';
+8 -4
View File
@@ -30,14 +30,18 @@ export interface PChainValidator {
readonly delegators: ReadonlyArray<PChainDelegator> | null;
}
// Wire field is `netID` — the node's platform.getBlockchains emits netID
// (Lux nomenclature: a sovereign L1 is a Network). See
// ~/work/lux/node/vms/platformvm/service.go GetBlockchainsResponse.
export interface PChainBlockchain {
readonly id: string;
readonly name: string;
readonly subnetID: string;
readonly netID: string;
readonly vmID: string;
}
export interface PChainSubnet {
// Wire shape of platform.getNets → APINet.
export interface PChainNet {
readonly id: string;
readonly controlKeys: ReadonlyArray<string>;
readonly threshold: string;
@@ -53,8 +57,8 @@ export interface GetBlockchainsResponse {
readonly blockchains: ReadonlyArray<PChainBlockchain>;
}
export interface GetSubnetsResponse {
readonly subnets: ReadonlyArray<PChainSubnet>;
export interface GetNetsResponse {
readonly nets: ReadonlyArray<PChainNet>;
}
// Aggregated validator statistics
-53
View File
@@ -1,53 +0,0 @@
// React Query hook for platform.getSubnets.
// Returns the list of all subnets on the P-chain.
// Uses the server-side /api/pchain proxy to bypass CORS.
import { useQuery } from '@tanstack/react-query';
import React from 'react';
import type { PChainSubnet } from './types';
const SUBNETS_STALE_TIME_MS = 300_000;
const SUBNETS_QUERY_KEY = 'pchain:subnets' as const;
const EMPTY_SUBNETS: ReadonlyArray<PChainSubnet> = [];
async function fetchSubnets(): Promise<ReadonlyArray<PChainSubnet>> {
const res = await fetch('/api/pchain', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
method: 'platform.getSubnets',
params: {},
id: 1,
}),
});
if (!res.ok) {
throw new Error(`P-chain proxy returned ${ res.status }`);
}
const json = await res.json() as { result?: { subnets?: ReadonlyArray<PChainSubnet> } };
return json.result?.subnets ?? [];
}
export function useSubnets() {
const query = useQuery({
queryKey: [ SUBNETS_QUERY_KEY ],
queryFn: fetchSubnets,
staleTime: SUBNETS_STALE_TIME_MS,
});
const subnets = React.useMemo(
() => query.data ?? EMPTY_SUBNETS,
[ query.data ],
);
return {
subnets,
isLoading: query.isLoading,
isError: query.isError,
error: query.error,
refetch: query.refetch,
};
}
+1 -1
View File
@@ -8,7 +8,7 @@
// This store captures `req.headers.host` at the start of each SSR render
// (see `_document.tsx`), so `getHostname()` on the server returns the
// actual requested host. The same docker image serves all chains on the
// Lux primary network (C, Zoo, Hanzo, SPC, Pars subnets) at distinct
// Lux primary network (C, Zoo, Hanzo, SPC, Pars L1s) at distinct
// explore-*.lux.network hosts — one image, N hosts.
//
// Client side is unaffected: `window.location.hostname` continues to be
+1 -1
View File
@@ -1,7 +1,7 @@
// White-label brand detection.
//
// Source code in this repo is Lux-only. Per-chain identity (Zoo / Hanzo / SPC /
// Pars subnets on Lux primary network) is rendered with each chain's logo and
// Pars L1s on Lux primary network) is rendered with each chain's logo and
// name as network metadata — see `configs/app/chainRegistry.ts`. The explorer
// brand itself is always Lux.
//
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@luxfi/explore",
"version": "1.1.13",
"version": "1.1.14",
"private": false,
"homepage": "https://github.com/luxfi/explore#readme",
"engines": {
+2 -1
View File
@@ -41,7 +41,8 @@ function parseChainEntries(src) {
/** Display name for a federated chain. C-Chain (Contract Chain) is handled
* separately by the caller; other chains use the short `name` field plus
* " Network". Sovereign L1s are Networks — never "Subnets" (Lux nomenclature). */
* " Network". Sovereign L1s are Networks (Lux nomenclature; never the
* legacy upstream term for them). */
function networkDisplayName(rawName) {
return `${ rawName.trim() } Network`;
}
+4 -4
View File
@@ -11,28 +11,28 @@ const explorers = [
minTxs: 45,
},
{
name: 'Zoo Subnet',
name: 'Zoo L1',
url: 'https://explore-zoo.lux.network',
apiUrl: 'https://api-explore-zoo.lux.network',
minBlocks: 10,
minTxs: 45,
},
{
name: 'Hanzo Subnet',
name: 'Hanzo L1',
url: 'https://explore-hanzo.lux.network',
apiUrl: 'https://api-explore-hanzo.lux.network',
minBlocks: 9,
minTxs: 45,
},
{
name: 'SPC Subnet',
name: 'SPC L1',
url: 'https://explore-spc.lux.network',
apiUrl: 'https://api-explore-spc.lux.network',
minBlocks: 9,
minTxs: 45,
},
{
name: 'Pars Subnet',
name: 'Pars L1',
url: 'https://explore-pars.lux.network',
apiUrl: 'https://api-explore-pars.lux.network',
minBlocks: 9,
+11 -8
View File
@@ -1,14 +1,14 @@
// Cross-chain bridge/teleporter page for the Lux multi-chain explorer.
// Shows cross-chain transfers between Primary Network chains and L1 subnets.
// Shows cross-chain transfers between Primary Network chains and sovereign L1s.
import { Skeleton } from '@luxfi/ui/skeleton';
import { Tag } from '@luxfi/ui/tag';
import React from 'react';
import config from 'configs/app';
import { useBridgeData } from 'lib/api/bchain';
import { useBlockchains, useCurrentValidators } from 'lib/api/pchain';
import type { PChainBlockchain } from 'lib/api/pchain';
import { Skeleton } from '@luxfi/ui/skeleton';
import { Tag } from '@luxfi/ui/tag';
import PageTitle from 'ui/shared/Page/PageTitle';
// ── Constants ──
@@ -61,7 +61,10 @@ interface ChainPairCardProps {
}
const ChainPairCard = ({ source, destination, status }: ChainPairCardProps) => (
<div className="flex items-center py-3 px-4 border-b border-[var(--color-border-divider)] hover:bg-[var(--color-gray-50)] dark:hover:bg-[var(--color-whiteAlpha-50)] transition-colors duration-150 gap-4 flex-wrap lg:flex-nowrap">
<div className={ `
flex items-center py-3 px-4 border-b border-[var(--color-border-divider)] hover:bg-[var(--color-gray-50)]
dark:hover:bg-[var(--color-whiteAlpha-50)] transition-colors duration-150 gap-4 flex-wrap lg:flex-nowrap
` }>
<div className="min-w-[160px] shrink-0">
<span className="font-medium text-sm text-[var(--color-text-primary)]">
{ source }
@@ -93,13 +96,13 @@ const BridgePage = () => {
const { stats: bridgeStats, isLoading: bridgeLoading } = useBridgeData();
const l1Chains = React.useMemo<ReadonlyArray<PChainBlockchain>>(
() => blockchains.filter((c) => c.subnetID !== PRIMARY_NETWORK_ID),
() => blockchains.filter((c) => c.netID !== PRIMARY_NETWORK_ID),
[ blockchains ],
);
const isLoading = chainsLoading || validatorsLoading || bridgeLoading;
// Build bridge pairs: Primary chain <-> L1 subnets
// Build bridge pairs: Primary chain <-> sovereign L1s
const bridgePairs = React.useMemo(() => {
const pairs: Array<{ source: string; destination: string; status: 'active' | 'coming_soon' }> = [];
@@ -211,8 +214,8 @@ const BridgePage = () => {
</span>
<span className="text-sm text-[var(--color-text-secondary)] leading-relaxed">
The bridge enables cross-chain asset transfers between the Primary Network
chains and L1 subnet chains. Atomic swaps between core chains (C, P, X) are
currently active. Teleporter-based transfers to L1 subnets are
chains and sovereign L1 chains. Atomic swaps between core chains (C, P, X) are
currently active. Transfers to sovereign L1s are
coming soon via the B-Chain bridge relay.
</span>
</div>
+22 -22
View File
@@ -7,7 +7,7 @@ import React from 'react';
import config from 'configs/app';
import type { PrimaryVm } from 'configs/app/primaryChains';
import { getPrimaryVm } from 'configs/app/primaryChains';
import { useBlockchains, useCurrentValidators, useSubnets } from 'lib/api/pchain';
import { useBlockchains, useCurrentValidators, useNets } from 'lib/api/pchain';
import type { PChainBlockchain, PChainValidator } from 'lib/api/pchain';
import { cn } from 'lib/utils/cn';
import DexPage from 'ui/dex/DexPage';
@@ -21,14 +21,14 @@ const LUX_DECIMALS = 6;
// Primary-network VM identity (name / vm / vmId / description / view) comes from
// the single source of truth in configs/app/primaryChains.ts.
const SUBNET_CHAIN_IDS: Readonly<Record<string, number>> = {
const L1_EVM_CHAIN_IDS: Readonly<Record<string, number>> = {
zoo: 200200,
hanzo: 36963,
spc: 36911,
pars: 494949,
};
const SUBNET_DESCRIPTIONS: Readonly<Record<string, string>> = {
const L1_DESCRIPTIONS: Readonly<Record<string, string>> = {
zoo: 'Zoo is an L1 blockchain on the network for the Zoo Labs Foundation open AI research network.',
hanzo: 'Hanzo is an L1 blockchain on the network for Hanzo AI infrastructure and agent frameworks.',
spc: 'SPC is an L1 blockchain on the network.',
@@ -180,7 +180,7 @@ const ChainDetailPage = () => {
const { blockchains, isLoading: chainsLoading } = useBlockchains();
const { validators, isLoading: validatorsLoading } = useCurrentValidators();
const { subnets } = useSubnets();
const { nets } = useNets();
const { stats: indexerStats } = useChainIndexerStats(slug);
const resolvedChain = React.useMemo<{
@@ -206,25 +206,25 @@ const ChainDetailPage = () => {
resolvedChain.blockchain?.name ??
slug;
const chainDescription = resolvedChain.meta?.description ??
SUBNET_DESCRIPTIONS[slug] ??
L1_DESCRIPTIONS[slug] ??
`${ chainName } is a blockchain on the network.`;
const blockchainId = resolvedChain.blockchain?.id ?? '';
const subnetId = resolvedChain.blockchain?.subnetID ?? (resolvedChain.isPrimary ? PRIMARY_NETWORK_ID : '');
const netId = resolvedChain.blockchain?.netID ?? (resolvedChain.isPrimary ? PRIMARY_NETWORK_ID : '');
const vmId = resolvedChain.blockchain?.vmID ?? resolvedChain.meta?.vmId ?? '';
const vmName = KNOWN_VM_IDS[vmId] ?? (vmId ? truncateId(vmId) : 'Unknown');
const chainId = SUBNET_CHAIN_IDS[slug];
const chainId = L1_EVM_CHAIN_IDS[slug];
const explorerUrl = EXPLORER_URLS[slug];
const isLoading = chainsLoading || validatorsLoading;
const subnet = React.useMemo(
() => subnets.find((s) => s.id === subnetId),
[ subnets, subnetId ],
const net = React.useMemo(
() => nets.find((n) => n.id === netId),
[ nets, netId ],
);
const subnetChains = React.useMemo(
() => subnetId ? blockchains.filter((c) => c.subnetID === subnetId) : [],
[ blockchains, subnetId ],
const netChains = React.useMemo(
() => netId ? blockchains.filter((c) => c.netID === netId) : [],
[ blockchains, netId ],
);
const totalStake = React.useMemo(
@@ -281,11 +281,11 @@ const ChainDetailPage = () => {
</div>
<div className="p-4 border border-[var(--color-border-divider)] rounded-lg bg-[var(--color-gray-50)] dark:bg-[var(--color-whiteAlpha-50)]">
<span className="block text-xs text-[var(--color-text-secondary)] font-semibold uppercase tracking-wider mb-1">
Subnet Chains
Chains on Network
</span>
<Skeleton loading={ isLoading }>
<span className="text-xl font-bold text-[var(--color-text-primary)]">
{ subnetChains.length }
{ netChains.length }
</span>
</Skeleton>
</div>
@@ -295,7 +295,7 @@ const ChainDetailPage = () => {
</span>
<Skeleton loading={ isLoading }>
<span className="text-xl font-bold text-[var(--color-text-primary)]">
{ subnet?.threshold ?? '-' }
{ net?.threshold ?? '-' }
</span>
</Skeleton>
</div>
@@ -308,13 +308,13 @@ const ChainDetailPage = () => {
<div className="border border-[var(--color-border-divider)] rounded-lg overflow-hidden">
<InfoRow label="Chain Name" value={ chainName }/>
{ blockchainId && <InfoRow label="Blockchain ID" value={ blockchainId } isMono canCopy/> }
{ subnetId && <InfoRow label="Subnet ID" value={ subnetId } isMono canCopy/> }
{ netId && <InfoRow label="Network ID" value={ netId } isMono canCopy/> }
<InfoRow label="VM Name" value={ vmName }/>
{ vmId && <InfoRow label="VM ID" value={ vmId } isMono canCopy/> }
{ chainId != null && <InfoRow label="EVM Chain ID" value={ String(chainId) }/> }
{ explorerUrl && <InfoRow label="Explorer" value={ explorerUrl }/> }
{ resolvedChain.isPrimary && <InfoRow label="Network" value="Primary Network"/> }
{ !resolvedChain.isPrimary && <InfoRow label="Network" value="L1 Subnet"/> }
{ !resolvedChain.isPrimary && <InfoRow label="Network" value="Sovereign L1"/> }
</div>
</div>
@@ -351,13 +351,13 @@ const ChainDetailPage = () => {
</div>
) }
{ subnetChains.length > 0 && (
{ netChains.length > 0 && (
<div className="mb-6">
<div className="flex items-center gap-2 mb-3">
<span className="text-sm font-semibold text-[var(--color-text-primary)]">
Chains in Subnet
Chains on This Network
</span>
<Tag size="sm" variant="subtle">{ subnetChains.length }</Tag>
<Tag size="sm" variant="subtle">{ netChains.length }</Tag>
</div>
<div className="border border-[var(--color-border-divider)] rounded-lg overflow-hidden">
<div className="hidden lg:flex px-4 py-2 gap-4 border-b border-[var(--color-border-divider)]">
@@ -371,7 +371,7 @@ const ChainDetailPage = () => {
VM
</div>
</div>
{ subnetChains.map((chain) => (
{ netChains.map((chain) => (
<div
key={ chain.id }
className={ cn(
+14 -8
View File
@@ -1,14 +1,14 @@
import { Skeleton } from '@luxfi/ui/skeleton';
import React from 'react';
import { cn } from 'lib/utils/cn';
import { Link } from 'toolkit/next/link';
import { Skeleton } from '@luxfi/ui/skeleton';
interface ChainRowProps {
readonly name: string;
readonly fullName?: string;
readonly blockchainId?: string;
readonly subnetId?: string;
readonly netId?: string;
readonly vmId?: string;
readonly vmLabel?: string;
readonly chainId?: number | null;
@@ -31,7 +31,7 @@ const ChainRow = ({
name,
fullName,
blockchainId,
subnetId,
netId,
vmLabel,
chainId,
isActive = true,
@@ -68,23 +68,29 @@ const ChainRow = ({
{ blockchainId ? truncateId(blockchainId) : '\u2014' }
</div>
{ /* Subnet ID column */ }
{ /* Network ID column */ }
<div
className="flex-1 min-w-0 font-mono text-sm text-[var(--color-text-secondary)] overflow-hidden text-ellipsis whitespace-nowrap hidden lg:block"
title={ subnetId }
title={ netId }
>
{ subnetId ? truncateId(subnetId) : '\u2014' }
{ netId ? truncateId(netId) : '\u2014' }
</div>
{ /* VM badge */ }
<div className="flex items-center gap-2 shrink-0">
{ vmLabel && (
<div className="bg-[var(--color-gray-100)] dark:bg-[var(--color-whiteAlpha-100)] text-[var(--color-text-secondary)] rounded-sm px-2 py-0.5 text-xs font-mono whitespace-nowrap">
<div className={ `
bg-[var(--color-gray-100)] dark:bg-[var(--color-whiteAlpha-100)] text-[var(--color-text-secondary)]
rounded-sm px-2 py-0.5 text-xs font-mono whitespace-nowrap
` }>
{ vmLabel }
</div>
) }
{ chainId != null && (
<div className="bg-[var(--color-gray-100)] dark:bg-[var(--color-whiteAlpha-100)] text-[var(--color-text-secondary)] rounded-sm px-2 py-0.5 text-xs font-mono whitespace-nowrap">
<div className={ `
bg-[var(--color-gray-100)] dark:bg-[var(--color-whiteAlpha-100)] text-[var(--color-text-secondary)]
rounded-sm px-2 py-0.5 text-xs font-mono whitespace-nowrap
` }>
{ chainId }
</div>
) }
+16 -16
View File
@@ -21,7 +21,7 @@ const PRIMARY_NETWORK_ID = '11111111111111111111111111111111LpoYY' as const;
// configs/app/primaryChains.ts (mirrored from the node registry), so this page
// and the chain-detail page can never drift apart.
const SUBNET_CHAIN_IDS: Readonly<Record<string, number>> = {
const L1_EVM_CHAIN_IDS: Readonly<Record<string, number>> = {
Zoo: 200200,
Hanzo: 36963,
SPC: 36911,
@@ -30,7 +30,7 @@ const SUBNET_CHAIN_IDS: Readonly<Record<string, number>> = {
const TAB_IDS = {
primary: 'primary',
subnets: 'subnets',
l1: 'l1',
} as const;
type TabId = typeof TAB_IDS[keyof typeof TAB_IDS];
@@ -42,10 +42,10 @@ const EMPTY_L1_CHAINS: ReadonlyArray<PChainBlockchain> = [];
// ---------------------------------------------------------------------------
interface TableHeaderProps {
readonly showSubnetId: boolean;
readonly showNetId: boolean;
}
const TableHeader = ({ showSubnetId }: TableHeaderProps) => (
const TableHeader = ({ showNetId }: TableHeaderProps) => (
<div className="hidden lg:flex px-4 py-2 gap-4 border-b border-[var(--color-border-divider)]">
<div className="min-w-[180px] max-w-[220px] shrink-0 text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Chain
@@ -53,9 +53,9 @@ const TableHeader = ({ showSubnetId }: TableHeaderProps) => (
<div className="flex-1 text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Blockchain ID
</div>
{ showSubnetId && (
{ showNetId && (
<div className="flex-1 text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Subnet ID
Network ID
</div>
) }
<div className="shrink-0 w-[120px] text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
@@ -103,15 +103,15 @@ const ChainsPage = () => {
if (!blockchains.length) {
return EMPTY_L1_CHAINS;
}
return blockchains.filter((chain) => chain.subnetID !== PRIMARY_NETWORK_ID);
return blockchains.filter((chain) => chain.netID !== PRIMARY_NETWORK_ID);
}, [ blockchains ]);
const handlePrimaryClick = React.useCallback(() => {
setActiveTab(TAB_IDS.primary);
}, []);
const handleSubnetsClick = React.useCallback(() => {
setActiveTab(TAB_IDS.subnets);
const handleL1Click = React.useCallback(() => {
setActiveTab(TAB_IDS.l1);
}, []);
return (
@@ -134,15 +134,15 @@ const ChainsPage = () => {
/>
<TabButton
label={ `L1 / L2 / L3${ l1Chains.length > 0 ? ` (${ l1Chains.length })` : '' }` }
isActive={ activeTab === TAB_IDS.subnets }
onClick={ handleSubnetsClick }
isActive={ activeTab === TAB_IDS.l1 }
onClick={ handleL1Click }
/>
</div>
{ /* Primary Network tab */ }
{ activeTab === TAB_IDS.primary && (
<div className="border border-[var(--color-border-divider)] rounded-md overflow-hidden">
<TableHeader showSubnetId={ false }/>
<TableHeader showNetId={ false }/>
{ PRIMARY_VMS.map((chain) => (
<ChainRow
key={ chain.name }
@@ -158,9 +158,9 @@ const ChainsPage = () => {
) }
{ /* L1/L2/L3 tab */ }
{ activeTab === TAB_IDS.subnets && (
{ activeTab === TAB_IDS.l1 && (
<div className="border border-[var(--color-border-divider)] rounded-md overflow-hidden">
<TableHeader showSubnetId/>
<TableHeader showNetId/>
{ isLoading && (
<div className="px-4 py-6">
<Skeleton loading={ true } h="20px" mb={ 3 }/>
@@ -179,10 +179,10 @@ const ChainsPage = () => {
key={ chain.id }
name={ chain.name }
blockchainId={ chain.id }
subnetId={ chain.subnetID }
netId={ chain.netID }
vmId={ chain.vmID }
vmLabel={ resolveVmLabel(chain.vmID) }
chainId={ SUBNET_CHAIN_IDS[chain.name] ?? null }
chainId={ L1_EVM_CHAIN_IDS[chain.name] ?? null }
isActive
href={ `/chains/${ chain.name.toLowerCase() }` }
/>
+4 -4
View File
@@ -38,7 +38,7 @@ const ChainSwitcher = () => {
const currentNetwork = getCurrentNetwork();
const isMainChain = current.name === 'C-Chain';
// For white-label subnets (Zoo, Pars, Hanzo, SPC), only show that chain's
// For white-label L1s (Zoo, Pars, Hanzo, SPC), only show that chain's
// mainnet/testnet entries — don't show the full Lux ecosystem.
// For the main C-Chain explorer, show all chains.
const chains = React.useMemo(() => {
@@ -46,11 +46,11 @@ const ChainSwitcher = () => {
if (isMainChain) {
return allChains;
}
// Filter to only chains with the same branding (same subnet)
// Filter to only chains with the same branding (same network)
return allChains.filter((c) => c.branding.brandName === current.branding.brandName);
}, [ currentNetwork.network, isMainChain, current.branding.brandName ]);
// For subnets, show network switcher (mainnet/testnet) instead of chain switcher
// For L1s, show network switcher (mainnet/testnet) instead of chain switcher
const availableNetworks = React.useMemo(() => {
if (isMainChain) return [];
return NETWORKS.filter((net) => {
@@ -100,7 +100,7 @@ const ChainSwitcher = () => {
</PopoverTrigger>
<PopoverContent w="240px">
<PopoverBody className="p-1">
{ /* Network switcher for subnets (e.g. Zoo Mainnet / Zoo Testnet) */ }
{ /* Network switcher for L1s (e.g. Zoo Mainnet / Zoo Testnet) */ }
{ availableNetworks.length > 1 && (
<>
<div className="px-2 py-1.5">
+1 -1
View File
@@ -55,7 +55,7 @@ const NetworkStats = () => {
const isLoading = validatorsLoading || chainsLoading;
const l1Count = React.useMemo(
() => blockchains.filter((c) => c.subnetID !== PRIMARY_NETWORK_ID).length,
() => blockchains.filter((c) => c.netID !== PRIMARY_NETWORK_ID).length,
[ blockchains ],
);