import React, { useEffect, useState } from 'react'; import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Activity, Wifi, WifiOff, Users, Box, TrendingUp } from 'lucide-react'; export const NetworkStats: React.FC = () => { const { api, isApiReady, error } = usePezkuwi(); const [blockNumber, setBlockNumber] = useState(0); const [blockHash, setBlockHash] = useState(''); const [finalizedBlock, setFinalizedBlock] = useState(0); const [validatorCount, setValidatorCount] = useState(0); const [collatorCount, setCollatorCount] = useState(0); const [nominatorCount, setNominatorCount] = useState(0); const [peers, setPeers] = useState(0); useEffect(() => { if (!api || !isApiReady) return; let unsubscribeNewHeads: () => void; let unsubscribeFinalizedHeads: () => void; let intervalId: NodeJS.Timeout; const subscribeToBlocks = async () => { try { // Subscribe to new blocks unsubscribeNewHeads = await api.rpc.chain.subscribeNewHeads((header) => { setBlockNumber(header.number.toNumber()); setBlockHash(header.hash.toHex()); }); // Subscribe to finalized blocks unsubscribeFinalizedHeads = await api.rpc.chain.subscribeFinalizedHeads((header) => { setFinalizedBlock(header.number.toNumber()); }); // Update validator count, collator count, nominator count, and peer count every 3 seconds const updateNetworkStats = async () => { try { const health = await api.rpc.system.health(); // 1. Fetch Validators let vCount = 0; try { if (api.query.session?.validators) { const validators = await api.query.session.validators(); if (validators) { vCount = validators.length; } } } catch (err) { if (import.meta.env.DEV) console.warn('Failed to fetch validators', err); } // 2. Fetch Collators (Invulnerables) let cCount = 0; try { if (api.query.collatorSelection?.invulnerables) { const invulnerables = await api.query.collatorSelection.invulnerables(); if (invulnerables) { cCount = invulnerables.length; } } } catch (err) { if (import.meta.env.DEV) console.warn('Failed to fetch collators', err); } // 3. Count Nominators let nCount = 0; try { const nominators = await api.query.staking?.nominators.entries(); if (nominators) { nCount = nominators.length; } } catch { if (import.meta.env.DEV) console.warn('Staking pallet not available, nominators = 0'); } setValidatorCount(vCount); setCollatorCount(cCount); setNominatorCount(nCount); setPeers(health.peers.toNumber()); } catch (err) { if (import.meta.env.DEV) console.error('Failed to update network stats:', err); } }; // Initial update await updateNetworkStats(); // Update every 3 seconds intervalId = setInterval(updateNetworkStats, 3000); } catch (err) { if (import.meta.env.DEV) console.error('Failed to subscribe to blocks:', err); } }; subscribeToBlocks(); return () => { if (unsubscribeNewHeads) unsubscribeNewHeads(); if (unsubscribeFinalizedHeads) unsubscribeFinalizedHeads(); if (intervalId) clearInterval(intervalId); }; }, [api, isApiReady]); if (error) { return ( Network Disconnected

{error}

Make sure your validator node is running at ws://127.0.0.1:9944

); } if (!isApiReady) { return ( Connecting to Network... ); } return (
{/* Connection Status */} Network Status
Connected {peers} peers
{/* Latest Block */} Latest Block
#{blockNumber.toLocaleString()}
{blockHash.slice(0, 10)}...{blockHash.slice(-8)}
{/* Finalized Block */} Finalized Block
#{finalizedBlock.toLocaleString()}
{blockNumber - finalizedBlock} blocks behind
{/* Validators */} Active Validators
{validatorCount}
Validating blocks
{/* Collators */} Active Collators
{collatorCount}
Producing blocks
{/* Nominators */} Active Nominators
{nominatorCount}
Staking to validators
); };