import { useEffect, useMemo, useState } from 'react' import Link from 'next/link' import { Card } from '@/libs/frontend-ui-primitives' import { getMissionControlRelayLabel, getMissionControlRelays, missionControlApi, type MissionControlBridgeStatusResponse, type MissionControlRelayPayload, type MissionControlRelaySnapshot, } from '@/services/api/missionControl' import { statsApi, type ExplorerStats } from '@/services/api/stats' import { explorerFeaturePages } from '@/data/explorerOperations' import { summarizeChainActivity } from '@/utils/activityContext' import ActivityContextPanel from '@/components/common/ActivityContextPanel' import FreshnessTrustNote from '@/components/common/FreshnessTrustNote' import { resolveEffectiveFreshness } from '@/utils/explorerFreshness' type FeedState = 'connecting' | 'live' | 'fallback' interface RelayLaneCard { key: string label: string status: string profile: string sourceChain: string destinationChain: string queueSize: number processed: number failed: number lastPolled: string bridgeAddress: string issueScope: string | null issueMessage: string | null inventoryShortfallWei: string | null inventoryRequiredWei: string | null inventoryAvailableWei: string | null } const relayOrder = ['mainnet_cw', 'mainnet_weth', 'bsc', 'avax', 'avax_cw', 'avax_to_138'] function relativeAge(isoString?: string): string { if (!isoString) return 'Unknown' const parsed = Date.parse(isoString) if (!Number.isFinite(parsed)) return 'Unknown' const seconds = Math.max(0, Math.round((Date.now() - parsed) / 1000)) if (seconds < 60) return `${seconds}s ago` const minutes = Math.round(seconds / 60) if (minutes < 60) return `${minutes}m ago` const hours = Math.round(minutes / 60) return `${hours}h ago` } function shortAddress(value?: string): string { if (!value) return 'Unspecified' if (value.length <= 14) return value return `${value.slice(0, 6)}...${value.slice(-4)}` } function resolveSnapshot(relay?: MissionControlRelayPayload): MissionControlRelaySnapshot | null { return relay?.url_probe?.body || relay?.file_snapshot || null } function relayPolicyCue(snapshot: MissionControlRelaySnapshot | null): string | null { if (!snapshot) return null if (snapshot.last_error?.scope === 'bridge_inventory') { return 'Queued release waiting on bridge inventory' } if (snapshot.last_error?.scope === 'bridge_inventory_probe') { return 'Bridge inventory check is temporarily unavailable' } if (String(snapshot.status || '').toLowerCase() === 'paused' && snapshot.monitoring?.delivery_enabled === false) { return 'Delivery disabled by policy' } return null } function laneToneClasses(status: string): string { const normalized = status.toLowerCase() if (['degraded', 'stale', 'stopped', 'down', 'snapshot-error'].includes(normalized)) { return 'border-red-200 bg-red-50/80 dark:border-red-900/60 dark:bg-red-950/20' } if (['paused', 'starting'].includes(normalized)) { return 'border-amber-200 bg-amber-50/80 dark:border-amber-900/60 dark:bg-amber-950/20' } return 'border-emerald-200 bg-emerald-50/80 dark:border-emerald-900/60 dark:bg-emerald-950/20' } function statusPillClasses(status: string): string { const normalized = status.toLowerCase() if (['degraded', 'stale', 'stopped', 'down', 'snapshot-error'].includes(normalized)) { return 'bg-red-100 text-red-700 dark:bg-red-900/50 dark:text-red-100' } if (['paused', 'starting'].includes(normalized)) { return 'bg-amber-100 text-amber-800 dark:bg-amber-900/50 dark:text-amber-100' } return 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/50 dark:text-emerald-100' } function formatWeiToToken(value?: string | null): string { if (!value) return 'Unknown' try { const raw = BigInt(value) const whole = raw / 10n ** 18n const fractional = (raw % 10n ** 18n).toString().padStart(18, '0').slice(0, 6).replace(/0+$/, '') return fractional ? `${whole.toString()}.${fractional}` : whole.toString() } catch { return value } } function ActionLink({ href, label, external, }: { href: string label: string external?: boolean }) { const className = 'inline-flex items-center text-sm font-semibold text-primary-600 hover:underline' const text = `${label} ->` if (external) { return ( {text} ) } return ( {text} ) } export default function BridgeMonitoringPage({ initialBridgeStatus = null, initialStats = null, }: { initialBridgeStatus?: MissionControlBridgeStatusResponse | null initialStats?: ExplorerStats | null }) { const [bridgeStatus, setBridgeStatus] = useState(initialBridgeStatus) const [stats, setStats] = useState(initialStats) const [feedState, setFeedState] = useState(initialBridgeStatus ? 'fallback' : 'connecting') const page = explorerFeaturePages.bridge useEffect(() => { let cancelled = false const loadSnapshot = async () => { try { const [snapshot, latestStats] = await Promise.all([ missionControlApi.getBridgeStatus(), statsApi.get().catch(() => null), ]) if (!cancelled) { setBridgeStatus(snapshot) if (latestStats) { setStats(latestStats) } } } catch (error) { if (!cancelled && process.env.NODE_ENV !== 'production') { console.warn('Failed to load bridge monitoring snapshot:', error) } } } loadSnapshot() const unsubscribe = missionControlApi.subscribeBridgeStatus( (status) => { if (!cancelled) { setBridgeStatus(status) setFeedState('live') } }, (error) => { if (!cancelled) { setFeedState('fallback') } if (process.env.NODE_ENV !== 'production') { console.warn('Bridge monitoring live stream issue:', error) } } ) return () => { cancelled = true unsubscribe() } }, []) const activityContext = useMemo( () => summarizeChainActivity({ blocks: [], transactions: [], latestBlockNumber: stats?.latest_block, latestBlockTimestamp: null, freshness: resolveEffectiveFreshness(stats, bridgeStatus), diagnostics: stats?.diagnostics ?? bridgeStatus?.data?.diagnostics ?? null, }), [bridgeStatus, stats], ) const relayLanes = useMemo((): RelayLaneCard[] => { const relays = getMissionControlRelays(bridgeStatus) if (!relays) return [] const orderIndex = new Map(relayOrder.map((key, index) => [key, index])) return Object.entries(relays) .map(([key, relay]) => { const snapshot = resolveSnapshot(relay) const status = String(snapshot?.status || (relay.file_snapshot_error ? 'snapshot-error' : 'configured')).toLowerCase() return { key, label: getMissionControlRelayLabel(key), status: snapshot?.last_error?.scope === 'bridge_inventory' ? 'underfunded' : snapshot?.last_error?.scope === 'bridge_inventory_probe' ? 'warning' : status, profile: snapshot?.service?.profile || key, sourceChain: snapshot?.source?.chain_name || 'Unknown', destinationChain: snapshot?.destination?.chain_name || 'Unknown', queueSize: snapshot?.queue?.size ?? 0, processed: snapshot?.queue?.processed ?? 0, failed: snapshot?.queue?.failed ?? 0, lastPolled: relativeAge(snapshot?.last_source_poll?.at), bridgeAddress: snapshot?.destination?.relay_bridge_default || snapshot?.destination?.relay_bridge || snapshot?.source?.bridge_filter || '', issueScope: snapshot?.last_error?.scope || null, issueMessage: snapshot?.last_error?.message || null, inventoryShortfallWei: snapshot?.last_error?.shortfall || null, inventoryRequiredWei: snapshot?.last_error?.required_amount || null, inventoryAvailableWei: snapshot?.last_error?.available_amount || null, } }) .sort((left, right) => { const leftIndex = orderIndex.get(left.key) ?? Number.MAX_SAFE_INTEGER const rightIndex = orderIndex.get(right.key) ?? Number.MAX_SAFE_INTEGER return leftIndex - rightIndex || left.label.localeCompare(right.label) }) }, [bridgeStatus]) const chainStatus = bridgeStatus?.data?.chains?.['138'] const overallStatus = bridgeStatus?.data?.status || 'unknown' const checkedAt = relativeAge(bridgeStatus?.data?.checked_at) return (
{page.eyebrow}

{page.title}

{page.description}

{page.note ? (

{page.note}

) : null}
Relay Fleet
{overallStatus}
{relayLanes.length} managed lanes visible
Feed: {feedState === 'live' ? 'Live SSE' : feedState === 'fallback' ? 'Snapshot fallback' : 'Connecting'}
Chain 138 RPC
{chainStatus?.status || 'unknown'}
Head age: {chainStatus?.head_age_sec != null ? `${chainStatus.head_age_sec.toFixed(1)}s` : 'Unknown'}
Latency: {chainStatus?.latency_ms != null ? `${chainStatus.latency_ms}ms` : 'Unknown'}
Last Check
{checkedAt}
Public status JSON and live stream are both active.
{relayLanes.map((lane) => (
{lane.label}
{`${lane.sourceChain} -> ${lane.destinationChain}`}
{lane.status}
Profile
{lane.profile}
Queue
{lane.queueSize}
Processed
{lane.processed}
Failed
{lane.failed}
Last polled: {lane.lastPolled}
Bridge: {shortAddress(lane.bridgeAddress)}
{relayPolicyCue(resolveSnapshot((getMissionControlRelays(bridgeStatus) || {})[lane.key])) ? (
{relayPolicyCue(resolveSnapshot((getMissionControlRelays(bridgeStatus) || {})[lane.key]))}
) : null} {lane.issueScope === 'bridge_inventory' ? (
Bridge inventory below required release amount
Shortfall: {formatWeiToToken(lane.inventoryShortfallWei)} WETH
Required: {formatWeiToToken(lane.inventoryRequiredWei)} WETH ยท Available: {formatWeiToToken(lane.inventoryAvailableWei)} WETH
) : null}
))}
{page.actions.map((action) => (
{action.title}

{action.description}

))}
) }