Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
d1d4313851
commit
92819639d0
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -171,9 +171,7 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
const applaused = transactionApplaused?.finalized ?? false;
|
||||
const clappedAmount = transactionApplaused?.clapped_amount ?? 0n;
|
||||
const clappedPercentage = clappedAmount * 100n / (totalStakedAmount ?? 1n);
|
||||
const step = finalization > 0
|
||||
? 0
|
||||
: applaused ? 2 : 1;
|
||||
const clapsPercentage = (numberOfClaps ?? 0) * 100 / (authorities?.length ?? 1);
|
||||
|
||||
return {
|
||||
...watchTransaction,
|
||||
@ -182,14 +180,15 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
numberOfClaps,
|
||||
clappedAmount,
|
||||
clappedPercentage,
|
||||
step,
|
||||
clapsPercentage,
|
||||
}
|
||||
}, [
|
||||
transactionApplaused,
|
||||
finalityDelay,
|
||||
watchTransaction,
|
||||
blockNumber,
|
||||
totalStakedAmount
|
||||
totalStakedAmount,
|
||||
authorities
|
||||
]);
|
||||
|
||||
const filteredStoredTransactions = useMemo(() => {
|
||||
@ -287,7 +286,7 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
const newStoredTransactions = [transaction, ...storedTransactions];
|
||||
setStoredTransactions(newStoredTransactions);
|
||||
localStorage.setItem(STORAGE_PREFIX, JSON.stringify(newStoredTransactions));
|
||||
setActiveTxIndex(newStoredTransactions.length - 1)
|
||||
setActiveTxIndex(0)
|
||||
}
|
||||
|
||||
return (
|
||||
@ -322,7 +321,7 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={12} height="100%">
|
||||
<Paper
|
||||
sx={{ height: isBigScreen ? "100%" : "150px", paddingBottom: "0 !important", marginBottom: "0 !important" }}
|
||||
sx={{ height: isBigScreen ? "100%" : "142px", paddingBottom: "0 !important", marginBottom: "0 !important" }}
|
||||
fullWidth
|
||||
enableBackground
|
||||
>
|
||||
|
||||
@ -30,6 +30,7 @@ import { useBalance } from "../../hooks/tokens";
|
||||
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { formatNumber, formatCurrency, timeConverter } from "../../helpers";
|
||||
@ -165,10 +166,14 @@ export const BridgeCardAction = ({
|
||||
UpperSwapCard={<SwapCard
|
||||
id={`bridge-token-receiver`}
|
||||
inputWidth={"100%"}
|
||||
value={receiver}
|
||||
onChange={event => setReceiver(event.currentTarget.value)}
|
||||
value={convertedReceiver ? sliceString(receiver, 15, -10) : receiver}
|
||||
onChange={event => setReceiver(convertedReceiver ? "" : event.currentTarget.value)}
|
||||
inputProps={{ "data-testid": "fromInput" }}
|
||||
placeholder="GHOST address (sf prefixed)"
|
||||
endString={convertedReceiver
|
||||
? <GhostStyledIcon color="success" viewBox="0 0 25 25" component={CheckCircleIcon} />
|
||||
: undefined
|
||||
}
|
||||
type="text"
|
||||
maxWidth="100%"
|
||||
/>}
|
||||
@ -190,18 +195,18 @@ export const BridgeCardAction = ({
|
||||
gap="10px"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
<Box width="100%" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
{gatekeeperAddressEmpty && (
|
||||
<Box maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
<Typography mr="10px" variant="body2" color="textSecondary">
|
||||
<Box display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
<Typography align="justify" mr="10px" variant="body2" color="textSecondary">
|
||||
<em>
|
||||
There is no connected gatekeeper on {chainName} network. Propose gatekeeper on this network to make authorities listen to it.
|
||||
There is no connected gatekeeper on {chainName} network yet.
|
||||
</em>
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{!gatekeeperAddressEmpty && (
|
||||
<Box width="100%" maxWidth="416px" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
<Box width="100%" display="flex" justifyContent={isVerySmallScreen ? "end" : "space-between"}>
|
||||
{!isVerySmallScreen && <Typography fontSize="12px" lineHeight="15px">Gatekeeper:</Typography>}
|
||||
<Link
|
||||
fontSize="12px"
|
||||
|
||||
@ -84,7 +84,7 @@ export const BridgeHeader = ({
|
||||
<Grid item xs={isSmallScreen ? 12 : 4}>
|
||||
<Metric
|
||||
isLoading={timeToNextEpoch === undefined}
|
||||
metric={formatTime(timeToNextEpoch)}
|
||||
metric={timeToNextEpoch < 30 ? "Rotating..." : formatTime(timeToNextEpoch)}
|
||||
label="Rotation in"
|
||||
tooltip="Bridge Stability Index refreshes every 10 minutes with new validator blocks; resets each Era when the validator set is updated."
|
||||
/>
|
||||
@ -97,7 +97,7 @@ export const BridgeHeader = ({
|
||||
tooltip="Maximum estimated time for finalizing bridge transactions based on the latest update."
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item gap={2} xs={12}>
|
||||
<Grid item gap={2} xs={12} style={{ paddingTop: "0" }} >
|
||||
<Box position="relative" margin="4.5px 0">
|
||||
<LinearProgress
|
||||
variant="determinate"
|
||||
@ -123,16 +123,16 @@ export const BridgeHeader = ({
|
||||
<Typography variant="body1" sx={{ fontWeight: 'bold' }}>
|
||||
Bridge Stability {bridgeStability
|
||||
? `${formatNumber(bridgeStability, 0)}% ${progressBarPostfix}`
|
||||
: "Unknown"
|
||||
: "N/A"
|
||||
}
|
||||
</Typography>
|
||||
<InfoTooltip message={
|
||||
<Box width="350px">
|
||||
<Typography>0% - 50%: Bridge Stability ❌ Do NOT Bridge</Typography>
|
||||
<Typography>50% - 70%: Bridge Stability ⚠️ Critical Risk</Typography>
|
||||
<Typography>70% - 80%: Bridge Stability ⚠️ High Risk</Typography>
|
||||
<Typography>80% - 90%: Bridge Stability ✅ Moderate Risk</Typography>
|
||||
<Typography>90% - 100%: Bridge Stability ✅ Safe</Typography>
|
||||
<Box width="220px">
|
||||
<Typography>0% - 50%: ❌ Do NOT Bridge</Typography>
|
||||
<Typography>50% - 70%: ⚠️ Critical Risk</Typography>
|
||||
<Typography>70% - 80%: ⚠️ High Risk</Typography>
|
||||
<Typography>80% - 90%: ✅ Moderate Risk</Typography>
|
||||
<Typography>90% - 100%: ✅ Safe</Typography>
|
||||
</Box>
|
||||
} />
|
||||
</Box>
|
||||
|
||||
@ -8,6 +8,7 @@ import ThumbDownAltIcon from '@mui/icons-material/ThumbDownAlt';
|
||||
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import AssuredWorkloadIcon from '@mui/icons-material/AssuredWorkload';
|
||||
import AccountBalanceIcon from '@mui/icons-material/AccountBalance';
|
||||
|
||||
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
|
||||
import ArrowRightIcon from '@mui/icons-material/ArrowRight';
|
||||
@ -88,11 +89,57 @@ export const BridgeModal = ({
|
||||
>
|
||||
<Box display="flex" gap="1.5rem" flexDirection="column" marginTop=".8rem">
|
||||
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
|
||||
{currentRecord?.finalization > 0 && (
|
||||
<>
|
||||
<Box
|
||||
sx={{
|
||||
transition: "all 0.8s ease",
|
||||
transform: currentRecord?.finalization > 0 && "scale(1.2)",
|
||||
color: currentRecord?.finalization === 0 && theme.colors.primary[300]
|
||||
}}
|
||||
width="120px"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<GhostStyledIcon
|
||||
sx={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
animation: currentRecord?.finalization > 0 && 'rotateHourGlass 2s ease-in-out infinite',
|
||||
'@keyframes rotateHourGlass': {
|
||||
'0%': { transform: 'rotate(0deg)' },
|
||||
'15%': { transform: 'rotate(0deg)' },
|
||||
'85%': { transform: 'rotate(180deg)' },
|
||||
'100%': { transform: 'rotate(180deg)' },
|
||||
},
|
||||
}}
|
||||
viewBox="0 0 25 25"
|
||||
component={HourglassBottomIcon}
|
||||
/>
|
||||
<Typography variant="caption">Finalization</Typography>
|
||||
<Typography variant="caption">
|
||||
{(currentRecord?.finalization ?? 0).toString()} blocks left
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<GhostStyledIcon
|
||||
sx={{
|
||||
transition: "all 0.3s ease",
|
||||
opacity: currentRecord?.finalization > 0 && "0.2"
|
||||
}}
|
||||
component={ArrowRightIcon}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
transition: "all 0.8s ease",
|
||||
transform: currentRecord?.step === 0 && "scale(1.2)",
|
||||
color: currentRecord?.step > 0 && theme.colors.primary[300]
|
||||
transition: "all 0.3s ease",
|
||||
opacity: currentRecord?.finalization > 0 && "0.2",
|
||||
transform: currentRecord?.finalization === 0 && currentRecord?.clappedPercentage < 50n && "scale(1.2)",
|
||||
color: currentRecord?.clappedPercentage > 50n && theme.colors.primary[300]
|
||||
}}
|
||||
width="120px"
|
||||
display="flex"
|
||||
@ -100,38 +147,42 @@ export const BridgeModal = ({
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<GhostStyledIcon
|
||||
sx={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
animation: currentRecord?.step === 0 && 'rotateHourGlass 2s ease-in-out infinite',
|
||||
'@keyframes rotateHourGlass': {
|
||||
'0%': { transform: 'rotate(0deg)' },
|
||||
'15%': { transform: 'rotate(0deg)' },
|
||||
'85%': { transform: 'rotate(180deg)' },
|
||||
'100%': { transform: 'rotate(180deg)' },
|
||||
},
|
||||
}}
|
||||
viewBox="0 0 25 25"
|
||||
component={HourglassBottomIcon}
|
||||
/>
|
||||
<Typography variant="caption">Finalization</Typography>
|
||||
<Typography variant="caption">
|
||||
{(currentRecord?.finalization ?? 0).toString()} blocks left
|
||||
</Typography>
|
||||
<Box display="flex" flexDirection="column" justifyContent="center" alignItems="center">
|
||||
{currentRecord?.clappedPercentage < 50n
|
||||
? <GhostStyledIcon
|
||||
sx={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
animation: currentRecord?.finalization === 0 && 'bounce 2s ease-in-out infinite',
|
||||
'@keyframes bounce': {
|
||||
'0%, 20%, 50%, 80%, 75%, 100%': { transform: 'translateY(0)' },
|
||||
'40%': { transform: 'translateY(-4px)' },
|
||||
'60%': { transform: 'translateY(-2px)' },
|
||||
},
|
||||
}}
|
||||
viewBox="0 0 25 25"
|
||||
component={AccountBalanceIcon}
|
||||
/>
|
||||
: <GhostStyledIcon sx={{ width: "35px", height: "35px" }} viewBox="0 0 25 25" component={AssuredWorkloadIcon} />
|
||||
}
|
||||
<Typography variant="caption">Capital Backed</Typography>
|
||||
<Typography variant="caption">
|
||||
{(currentRecord?.clappedAmount ?? 0n) / 10n**18n} {ghstSymbol} ({currentRecord?.clappedPercentage ?? 0}%)
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<GhostStyledIcon
|
||||
sx={{ transition: "all 0.3s ease", opacity: currentRecord?.step < 1 && "0.2" }}
|
||||
sx={{ transition: "all 0.3s ease", opacity: currentRecord?.clappedPercentage < 50n && "0.2" }}
|
||||
component={ArrowRightIcon}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
transition: "all 0.3s ease",
|
||||
opacity: currentRecord?.step < 1 && "0.2",
|
||||
transform: currentRecord?.step === 1 && "scale(1.2)",
|
||||
color: currentRecord?.step > 1 && theme.colors.primary[300]
|
||||
opacity: currentRecord?.finalization > 0 && "0.2",
|
||||
transform: currentRecord?.finalization === 0 && currentRecord?.clapsPercentage < 50 && "scale(1.2)",
|
||||
color: currentRecord?.clapsPercentage > 50 && theme.colors.primary[300]
|
||||
}}
|
||||
width="120px"
|
||||
display="flex"
|
||||
@ -140,14 +191,14 @@ export const BridgeModal = ({
|
||||
alignItems="center"
|
||||
>
|
||||
<Box display="flex" flexDirection="row" justifyContent="center" alignItems="center">
|
||||
{currentRecord?.step <= 1
|
||||
{currentRecord?.clapsPercentage < 50
|
||||
? (
|
||||
<>
|
||||
<GhostStyledIcon
|
||||
sx={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
animation: currentRecord?.step === 1 && 'rotateRightHand 2s ease-in-out infinite',
|
||||
animation: currentRecord?.finalization === 0 && 'rotateRightHand 2s ease-in-out infinite',
|
||||
'@keyframes rotateRightHand': {
|
||||
'0%': { transform: 'rotateX(360deg)' },
|
||||
'15%': { transform: 'rotateX(360deg)' },
|
||||
@ -163,7 +214,7 @@ export const BridgeModal = ({
|
||||
sx={{
|
||||
width: "35px",
|
||||
height: "35px",
|
||||
animation: currentRecord?.step === 1 && 'rotateRightHand 2s ease-in-out infinite',
|
||||
animation: currentRecord?.finalization === 0 && 'rotateRightHand 2s ease-in-out infinite',
|
||||
'@keyframes rotateRightHand': {
|
||||
'0%': { transform: 'rotateX(0deg)' },
|
||||
'15%': { transform: 'rotateX(0deg)' },
|
||||
@ -195,50 +246,36 @@ export const BridgeModal = ({
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<GhostStyledIcon
|
||||
sx={{
|
||||
transition: "all 0.3s ease",
|
||||
opacity: currentRecord?.step < 2 && "0.2"
|
||||
}}
|
||||
component={ArrowRightIcon}
|
||||
/>
|
||||
{currentRecord?.finalization === 0 && (
|
||||
<>
|
||||
<GhostStyledIcon
|
||||
sx={{ transition: "all 0.3s ease", opacity: !currentRecord?.applaused && "0.2" }}
|
||||
component={ArrowRightIcon}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
transition: "all 0.3s ease",
|
||||
opacity: currentRecord?.step < 2 && "0.2",
|
||||
transform: currentRecord?.step === 2 && "scale(1.2)",
|
||||
color: currentRecord?.step === 2 && theme.colors.primary[300]
|
||||
}}
|
||||
width="120px"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
{currentRecord?.applaused
|
||||
? <>
|
||||
<GhostStyledIcon
|
||||
sx={{ width: "35px", height: "35px" }}
|
||||
viewBox="0 0 25 25"
|
||||
component={CheckCircleIcon}
|
||||
/>
|
||||
<Typography variant="caption">Applaused</Typography>
|
||||
<Typography variant="caption">Check Receiver</Typography>
|
||||
</>
|
||||
: <>
|
||||
<GhostStyledIcon
|
||||
sx={{ width: "35px", height: "35px" }}
|
||||
viewBox="0 0 25 25"
|
||||
component={AssuredWorkloadIcon}
|
||||
/>
|
||||
<Typography variant="caption">Capital Backed</Typography>
|
||||
<Typography variant="caption">
|
||||
{(currentRecord?.clappedAmount ?? 0n) / 10n**18n} {ghstSymbol} ({currentRecord?.clappedPercentage ?? 0}%)
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
transition: "all 0.3s ease",
|
||||
opacity: !currentRecord?.applaused && "0.2",
|
||||
transform: currentRecord?.applaused && "scale(1.2)",
|
||||
color: currentRecord?.applaused && theme.colors.primary[300]
|
||||
}}
|
||||
width="120px"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="start"
|
||||
alignItems="center"
|
||||
>
|
||||
<GhostStyledIcon
|
||||
sx={{ width: "35px", height: "35px" }}
|
||||
viewBox="0 0 25 25"
|
||||
component={CheckCircleIcon}
|
||||
/>
|
||||
<Typography variant="caption">Applaused</Typography>
|
||||
<Typography variant="caption">Check Receiver</Typography>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box display="flex" flexDirection="column" gap="5px" padding="0.6rem 0">
|
||||
|
||||
@ -15,6 +15,8 @@ import {
|
||||
} 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';
|
||||
@ -32,6 +34,7 @@ export const ValidatorTable = ({
|
||||
providerDetail,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const [sortedBy, setsortedBy] = useState(undefined);
|
||||
|
||||
const stabilityColor = useMemo(() => {
|
||||
const red = Math.round(255 * (1 - bridgeStability / 100));
|
||||
@ -39,6 +42,65 @@ export const ValidatorTable = ({
|
||||
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 (
|
||||
<Box width="100%" height="340px" display="flex" flexDirection="column" justifyContent="space-between" gap="10px">
|
||||
{!providerDetail && <Box sx={{ borderRadius: "15px", background: theme.colors.paper.background }} width="100%" height="100%" display="flex" justifyContent="center">
|
||||
@ -64,11 +126,11 @@ export const ValidatorTable = ({
|
||||
<Table sx={{ marginTop: "0px" }} stickyHeader aria-label="sticky available validators">
|
||||
<TableHead>
|
||||
<TableRow sx={{ height: "40px" }}>
|
||||
<BridgeHeaderTableCell value="Validator" background={theme.colors.paper.cardHover} borderTopLeftRadius="3px" />
|
||||
<BridgeHeaderTableCell value="Last Acitve" background={theme.colors.paper.cardHover} tooltip="GHOST Validators must submit block commitments every 10 minutes to remain active." />
|
||||
<BridgeHeaderTableCell value="Block Height" background={theme.colors.paper.cardHover} tooltip="The latest EVM block height reported by the Validator." />
|
||||
<BridgeHeaderTableCell 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." />
|
||||
<BridgeHeaderTableCell value="Status" background={theme.colors.paper.cardHover} borderTopRightRadius="3px" tooltip="Active and disabled validators fore the current GHOST Epoch." />
|
||||
<BridgeHeaderTableCell index={0} changeSort={() => setsortedBy(undefined)} value="Validator" background={theme.colors.paper.cardHover} borderTopLeftRadius="3px" />
|
||||
<BridgeHeaderTableCell index={1} sortedBy={sortedBy} changeSort={() => 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." />
|
||||
<BridgeHeaderTableCell index={2} sortedBy={sortedBy} changeSort={() => setsortedBy(prev => changeSort(prev, 2))} value="Block Height" background={theme.colors.paper.cardHover} tooltip="The latest EVM block height reported by the Validator." />
|
||||
<BridgeHeaderTableCell index={3} sortedBy={sortedBy} changeSort={() => 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." />
|
||||
<BridgeHeaderTableCell index={4} sortedBy={sortedBy} changeSort={() => setsortedBy(prev => changeSort(prev, 4))} value="Status" background={theme.colors.paper.cardHover} borderTopRightRadius="3px" tooltip="Active and disabled validators fore the current GHOST Epoch." />
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
@ -105,6 +167,9 @@ export const ValidatorTable = ({
|
||||
}
|
||||
|
||||
const BridgeHeaderTableCell = ({
|
||||
index=0,
|
||||
sortedBy=undefined,
|
||||
changeSort,
|
||||
align="center",
|
||||
borderTopRightRadius="0px",
|
||||
borderTopLeftRadius="0px",
|
||||
@ -127,11 +192,21 @@ const BridgeHeaderTableCell = ({
|
||||
background,
|
||||
fontSize,
|
||||
padding,
|
||||
cursor: "pointer",
|
||||
}}
|
||||
>
|
||||
<Box display="flex" justifyContent="center">
|
||||
<Box onClick={changeSort} display="flex" justifyContent="center" alignItems="center">
|
||||
{value}
|
||||
{tooltip && <InfoTooltip message={tooltip} />}
|
||||
{tooltip
|
||||
? Math.abs(sortedBy) !== index
|
||||
? <InfoTooltip message={tooltip} />
|
||||
: <GhostStyledIcon
|
||||
sx={{ width: "12px", height: "12px" }}
|
||||
viewBox="0 0 25 25"
|
||||
component={sortedBy > 0 ? KeyboardArrowDownIcon : KeyboardArrowUpIcon}
|
||||
/>
|
||||
: <></>
|
||||
}
|
||||
</Box>
|
||||
</TableCell>
|
||||
)
|
||||
|
||||
@ -42,9 +42,13 @@ export const sortBondsByDiscount = (bonds) => {
|
||||
return Array.from(bonds).filter((bond) => !bond.isSoldOut).sort((a, b) => (a.discount.gt(b.discount) ? -1 : 1));
|
||||
};
|
||||
|
||||
export const timeConverter = (time) => {
|
||||
export const timeConverter = (time, max = 7200, maxText = "long ago") => {
|
||||
const seconds = Number(time);
|
||||
const mins = Math.floor(seconds / 60);
|
||||
const secs = seconds % 60;
|
||||
return `${mins}m ${secs < 10 ? '0' : ''}${secs}s`;
|
||||
if (mins > max) {
|
||||
return maxText;
|
||||
} else {
|
||||
return `${mins}m ${secs < 10 ? '0' : ''}${secs}s`;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user