Files
zeekay 16c8d71a90 bridge: stop printing four numbers nobody measured, and drop the dead SDK with them
/bridge showed CONNECTED CHAINS 14, BRIDGE ROUTES 4, BRIDGE SIGNERS 5, ACTIVE
ROUTES 4 — the same four values on lux, hanzo and zoo, while its only backing
RPC 404'd twice per page load. Not one was measured:

  * Connected Chains counted the keys of CHAIN_LABELS, a hardcoded lookup table,
    plus an L1 list that comes back empty. It is 14 because the map has 14
    entries.
  * Bridge Routes and Active Routes counted four pairs unshifted into an array
    three lines above, each hardcoded status: 'active'.
  * Bridge Signers asked the B-Chain and, when that failed, printed the P-Chain
    VALIDATOR count under a signer label. A real number, under the wrong name —
    the worst of the four, because it moves.

The B-Chain is not served on any prefix: api.<brand>.network answers 404 to
/ext/bc/B/rpc and to /v1/bc/B/rpc alike, so there is no live bridge data to
show and the page has been reporting constants as measurements. Fixing the
path would have moved the 404, not the lie.

So the cards are gone, and with them lib/api/bchain, whose whole job was to
swallow that failure into zeroes. That leaves lib/api/luxnet with no caller —
the P-Chain hooks go through the /v1/pchain proxy, and getPChain/getInfo/
getHealth were re-exported and never called — so it goes too, and the luxnet
dependency with it: 889 lines of lockfile, an elliptic-curve SDK this app
never used at runtime. The retired /ext/ prefix now has no caller here at all.

What stays is what is true: the routes table, C/P/X atomic swaps marked active
and L1 transfers marked coming soon, and the paragraph that says so.

Measured, 1440x900: before, two POST https://api.lux.network/ext/bc/B/rpc per
load, both 404, four stat cards reading 14/4/5/4. After, zero /ext/ or /bc/
requests, zero stat cards, the four route rows unchanged.

Also: name every field when a broadcast becomes a block. toApiBlock spread the
raw EVMBlock and renamed three keys, so gas_used, gas_limit, base_fee_per_gas
and parent_hash stayed camelCase and arrived undefined — a live-pushed row
rendered 0 gas beside REST rows showing the real number, the same block reading
two ways depending on how it got here. It now builds the API shape explicitly,
field for field, matching formatBlock in luxfi/indexer.
2026-08-08 06:44:20 -07:00

153 lines
5.5 KiB
TypeScript

// Cross-chain bridge/teleporter page for the Lux multi-chain explorer.
// 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 { useBlockchains } from 'lib/api/pchain';
import type { PChainBlockchain } from 'lib/api/pchain';
import PageTitle from 'ui/shared/Page/PageTitle';
// ── Constants ──
const PRIMARY_NETWORK_ID = '11111111111111111111111111111111LpoYY';
// ── Sub-components ──
interface ChainPairCardProps {
readonly source: string;
readonly destination: string;
readonly status: 'active' | 'coming_soon';
}
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="min-w-[160px] shrink-0">
<span className="font-medium text-sm text-[var(--color-text-primary)]">
{ source }
</span>
</div>
<span className="text-[var(--color-text-secondary)] text-sm shrink-0">
{ '\u2192' }
</span>
<div className="min-w-[160px] shrink-0">
<span className="font-medium text-sm text-[var(--color-text-primary)]">
{ destination }
</span>
</div>
<div className="ml-0 lg:ml-auto flex items-center">
{ status === 'active' ? (
<Tag size="sm" variant="subtle" className="bg-good/10 text-good dark:bg-good/10 dark:text-good">Active</Tag>
) : (
<Tag size="sm" variant="subtle">Coming Soon</Tag>
) }
</div>
</div>
);
// ── Main component ──
const BridgePage = () => {
const { blockchains, isLoading } = useBlockchains();
const l1Chains = React.useMemo<ReadonlyArray<PChainBlockchain>>(
() => blockchains.filter((c) => c.netID !== PRIMARY_NETWORK_ID),
[ blockchains ],
);
// Build bridge pairs: Primary chain <-> sovereign L1s
const bridgePairs = React.useMemo(() => {
const pairs: Array<{ source: string; destination: string; status: 'active' | 'coming_soon' }> = [];
// C-Chain <-> each L1
for (const chain of l1Chains) {
pairs.push({ source: 'C-Chain', destination: chain.name, status: 'coming_soon' });
pairs.push({ source: chain.name, destination: 'C-Chain', status: 'coming_soon' });
}
// X-Chain <-> C-Chain (atomic swaps)
pairs.unshift(
{ source: 'X-Chain', destination: 'C-Chain', status: 'active' },
{ source: 'C-Chain', destination: 'X-Chain', status: 'active' },
{ source: 'P-Chain', destination: 'C-Chain', status: 'active' },
{ source: 'C-Chain', destination: 'P-Chain', status: 'active' },
);
return pairs;
}, [ l1Chains ]);
return (
<>
<PageTitle
title="Bridge"
secondRow={ (
<div className="text-sm text-[var(--color-text-secondary)]">
Cross-chain transfers between { config.chain.name || 'network' } chains
</div>
) }
/>
{ /* Bridge routes table */ }
<div className="border border-[var(--color-border-divider)] rounded-lg overflow-hidden">
<div className="px-4 py-3 font-semibold text-sm text-[var(--color-text-primary)] border-b border-[var(--color-border-divider)]">
Bridge Routes
</div>
{ /* Table header */ }
<div className="hidden lg:flex px-4 py-2 gap-4 border-b border-[var(--color-border-divider)]">
<div className="min-w-[160px] shrink-0 text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Source
</div>
<div className="w-5 shrink-0"/>
<div className="min-w-[160px] shrink-0 text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Destination
</div>
<div className="ml-auto text-[var(--color-text-secondary)] font-semibold text-xs uppercase tracking-wider">
Status
</div>
</div>
{ /* Loading */ }
{ isLoading && (
<div className="px-4 py-6">
<Skeleton loading h="16px" mb={ 3 }/>
<Skeleton loading h="16px" mb={ 3 }/>
<Skeleton loading h="16px" mb={ 3 }/>
<Skeleton loading h="16px"/>
</div>
) }
{ /* Routes */ }
{ !isLoading && bridgePairs.map((pair, idx) => (
<ChainPairCard
key={ `${ pair.source }-${ pair.destination }-${ idx }` }
source={ pair.source }
destination={ pair.destination }
status={ pair.status }
/>
)) }
</div>
{ /* Info card */ }
<div className="mt-6 p-5 border border-[var(--color-border-divider)] rounded-lg bg-[var(--color-gray-50)] dark:bg-[var(--color-whiteAlpha-50)]">
<span className="font-semibold text-sm text-[var(--color-text-primary)] mb-2 block">
{ config.chain.name || 'Network' } Bridge
</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 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>
</>
);
};
export default React.memo(BridgePage);