diff --git a/dashboard/src/components/Contracts.tsx b/dashboard/src/components/Contracts.tsx
new file mode 100644
index 00000000..706e8305
--- /dev/null
+++ b/dashboard/src/components/Contracts.tsx
@@ -0,0 +1,104 @@
+import {
+ Card,
+ Table,
+ TableBody,
+ TableCell,
+ TableContainer,
+ TableHead,
+ TableRow,
+} from '@mui/material';
+import {
+ Chain,
+ chainToChainId,
+ chainToPlatform,
+ chains,
+ contracts,
+ rpc,
+} from '@wormhole-foundation/sdk-base';
+import { useEffect, useState } from 'react';
+import { useNetworkContext } from '../contexts/NetworkContext';
+import CollapsibleSection from './CollapsibleSection';
+import { callContractMethod, getMethodId } from '@wormhole-foundation/wormhole-monitor-common';
+
+const coreBridgeChains = chains.filter((chain) => contracts.coreBridge.get('Mainnet', chain));
+
+function useGetGuardianSet(chain: Chain, address: string | undefined) {
+ const network = useNetworkContext();
+ const [guardianSet, setGuardianSet] = useState<[bigint | null, string | null]>([null, null]);
+ useEffect(() => {
+ setGuardianSet([null, null]);
+ if (!address) return;
+ let cancelled = false;
+ if (chainToPlatform(chain) === 'Evm') {
+ const rpcUrl = rpc.rpcAddress(network.currentNetwork.env, chain);
+ if (!rpcUrl) return;
+ (async () => {
+ try {
+ const gsi = await callContractMethod(
+ rpcUrl,
+ address,
+ getMethodId('getCurrentGuardianSetIndex()')
+ );
+ if (cancelled) return;
+ const gs = await callContractMethod(
+ rpcUrl,
+ address,
+ getMethodId('getGuardianSet(uint32)'),
+ gsi.substring(2) // strip 0x
+ );
+ if (cancelled) return;
+ setGuardianSet([BigInt(gsi), gs]);
+ } catch (e) {}
+ })();
+ }
+ return () => {
+ cancelled = true;
+ };
+ }, [network.currentNetwork.env, chain, address]);
+ return guardianSet;
+}
+
+function CoreBridgeInfo({ chain, address }: { chain: Chain; address: string | undefined }) {
+ const guardianSet = useGetGuardianSet(chain, address);
+ const guardianSetIndex = guardianSet[0]?.toString();
+ if (!address) return null;
+ return (
+
+ {chain}
+ {chainToChainId(chain)}
+ {address}
+ {guardianSetIndex}
+
+ );
+}
+
+function Contracts() {
+ return (
+
+
+
+
+
+
+ Chain Name
+ Chain ID
+ Address
+ Guardian Set Index
+
+
+
+ {coreBridgeChains.map((chain: Chain) => (
+
+ ))}
+
+
+
+
+
+ );
+}
+export default Contracts;
diff --git a/dashboard/src/components/Main.tsx b/dashboard/src/components/Main.tsx
index c1fe3c2c..c704d257 100644
--- a/dashboard/src/components/Main.tsx
+++ b/dashboard/src/components/Main.tsx
@@ -1,4 +1,9 @@
-import { GitHub } from '@mui/icons-material';
+import {
+ AnalyticsOutlined,
+ GitHub,
+ ReceiptLongOutlined,
+ SyncAltOutlined,
+} from '@mui/icons-material';
import { AppBar, Box, Button, Hidden, IconButton, Toolbar, Typography } from '@mui/material';
import { NavLink, Route, Switch, useLocation } from 'react-router-dom';
import useChainHeartbeats from '../hooks/useChainHeartbeats';
@@ -6,6 +11,7 @@ import useHeartbeats from '../hooks/useHeartbeats';
import useLatestRelease from '../hooks/useLatestRelease';
import WormholeStatsIcon from '../icons/WormholeStatsIcon';
import Alerts from './Alerts';
+import Contracts from './Contracts';
import Home from './Home';
import Metrics from './Metrics';
import NTTMetrics from './NTTMetrics';
@@ -33,21 +39,53 @@ function NavLinks() {
-
+
Dashboard
+
+
+
+
+
+ Contracts
+
+
- Metrics
+
+
+
+
+ Metrics
+
- NTT
+
+
+
+
+ NTT
+
>
);
@@ -70,7 +119,7 @@ function Main() {
return (
<>
-
+
@@ -100,6 +149,9 @@ function Main() {
+
+
+