import { useEffect, useState, useMemo } from "react";
import {
Box,
Paper,
Table,
TableBody,
TableCell,
TableContainer,
TableHead,
TableRow,
Typography,
Link,
LinearProgress,
} from "@mui/material";
import { useTheme } from "@mui/material/styles";
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import WarningIcon from '@mui/icons-material/Warning';
import CancelIcon from '@mui/icons-material/Cancel';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import { PrimaryButton } from "../../components/Button";
import { GHOST_CONNECT } from "../../constants/ecosystem";
export const ValidatorTable = ({
currentTime,
currentBlock,
latestCommits,
isVerySmallScreen,
bridgeStability,
providerDetail,
}) => {
const theme = useTheme();
const [sortedBy, setsortedBy] = useState(undefined);
const stabilityColor = useMemo(() => {
const red = Math.round(255 * (1 - bridgeStability / 100));
const green = Math.round(255 * (bridgeStability / 100));
return `rgb(${red}, ${green}, 0)`;
}, [bridgeStability]);
const sortedCommits = useMemo(() => {
return latestCommits?.sort((a, b) => {
let one = 0;
let two = 0;
switch (sortedBy) {
case -1:
one = Number(b.lastUpdated ?? 0n);
two = Number(a.lastUpdated ?? 0n);
break;
case 1:
one = Number(a.lastUpdated ?? 0n);
two = Number(b.lastUpdated ?? 0n);
break;
case -2:
one = Number(b.lastStoredBlock ?? 0n);
two = Number(a.lastStoredBlock ?? 0n);
break;
case 2:
one = Number(a.lastStoredBlock ?? 0n);
two = Number(b.lastStoredBlock ?? 0n);
break;
case -3:
one = Number(b.lastStoredBlock ?? 0n);
two = Number(a.lastStoredBlock ?? 0n);
break;
case 3:
one = Number(a.lastStoredBlock ?? 0n);
two = Number(b.lastStoredBlock ?? 0n);
break;
case -4:
one = b.disabled ? 1 : 0;
two = a.disabled ? 1 : 0;
break;
case 4:
one = a.disabled ? 1 : 0;
two = b.disabled ? 1 : 0;
break;
default:
break;
}
return one - two;
}) ?? [];
}, [latestCommits, sortedBy]);
const changeSort = (prevValue, newValue) => {
if (prevValue === undefined) {
return newValue;
}
const prevValueAbs = Math.abs(prevValue);
const newValueAbs = Math.abs(newValue);
if (prevValueAbs === newValueAbs) {
return prevValue * -1;
} else {
return newValue;
}
}
return (
{!providerDetail &&
GHOST Connect is not detected on your browser!
Download GHOST Connect browser extension for real-time visibility into validator status and related transaction risks.
Important: The GHOST Connect is optional, but be aware that your bridge transaction will succeed or fail irreversibly based on the condition of the validators.
window.open(GHOST_CONNECT, '_blank', 'noopener,noreferrer')}>
Get GHOST Connect
}
{providerDetail &&
setsortedBy(undefined)} value="Validator" background={theme.colors.paper.cardHover} borderTopLeftRadius="3px" />
setsortedBy(prev => changeSort(prev, 1))} value="Last Acitve" background={theme.colors.paper.cardHover} tooltip="GHOST Validators must submit block commitments every 10 minutes to remain active." />
setsortedBy(prev => changeSort(prev, 2))} value="Block Height" background={theme.colors.paper.cardHover} tooltip="The latest EVM block height reported by the Validator." />
setsortedBy(prev => changeSort(prev, 3))} value="Block Delayed" background={theme.colors.paper.cardHover} tooltip="Block delays under 4 hours are safe. Block delays over 4 hours risk bridge transaction failure." />
setsortedBy(prev => changeSort(prev, 4))} value="Status" background={theme.colors.paper.cardHover} borderTopRightRadius="3px" tooltip="Active and disabled validators fore the current GHOST Epoch." />
{latestCommits?.map((commit, index) => {
return (
)
})}
}
See Validator Telemetry
)
}
const BridgeHeaderTableCell = ({
index=0,
sortedBy=undefined,
changeSort,
align="center",
borderTopRightRadius="0px",
borderTopLeftRadius="0px",
borderBottomRightRadius="0px",
borderBottomLeftRadius="0px",
background="transparent",
fontSize="12px",
padding="0px",
tooltip,
value
}) => {
return (
{value}
{tooltip
? Math.abs(sortedBy) !== index
?
: 0 ? KeyboardArrowDownIcon : KeyboardArrowUpIcon}
/>
: <>>
}
)
}
const BridgeTableCell = ({
align="center",
borderTopRightRadius="0px",
borderTopLeftRadius="0px",
borderBottomRightRadius="0px",
borderBottomLeftRadius="0px",
background="transparent",
fontSize="10px",
padding="0px",
value
}) => {
return (
{value}
)
}
const ValidatorRow = ({
colors,
index,
currentTime,
currentBlock,
commit,
}) => {
const background = index % 2 === 1 ? "" : colors.gray[750];
return (
)
}
const sliceString = (string, first, second) => {
if (!string) return "";
return string.slice(0, first) + "..." + string.slice(second);
}
const getTimeAgo = (timestampDiff) => {
const seconds = Math.floor(timestampDiff / 1000);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (seconds < 60) return `${seconds}s ago`;
if (minutes < 60) return `${minutes}m ago`;
if (hours < 24) return `${hours}h ago`;
return "long ago";
}
const blockDelayIcon = (colors, timestampDiff) => {
let color = colors.feedback.error;
let icon = CancelIcon;
if (timestampDiff < 900000n) {
color = colors.feedback.success;
icon = CheckCircleIcon;
} else if (timestampDiff < 2700000n) {
color = colors.feedback.warning;
icon = WarningIcon;
}
return (
)
}
const statusIcon = (colors, disabled) => {
let color = colors.feedback.success;
let icon = CheckCircleIcon;
if (disabled === true) {
color = colors.feedback.error;
icon = CancelIcon;
}
return (
)
}