Skip to content

Commit

Permalink
feat: uptime blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
evilpeach committed Mar 14, 2024
1 parent b06ae6d commit e8781ca
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { Flex, Text } from "@chakra-ui/react";

import type { ComputedUptime, Ratio } from "lib/types";
import { formatRatio } from "lib/utils";

const LegendItem = ({
label,
color,
value,
percent,
ratio,
}: {
label: string;
color: string;
value: string;
percent: string;
value: number;
ratio: Ratio<number>;
}) => (
<Flex gap={2} w="full">
<Flex w={3} h={3} borderRadius="2px" backgroundColor={color} mt={1} />
Expand All @@ -34,31 +37,38 @@ const LegendItem = ({
{value}
</Text>
<Text variant="body3" color="text.dark">
{percent}
{formatRatio(ratio)}
</Text>
</Flex>
</Flex>
);
export const RecentBlocksLegends = () => {

interface RecentBlocksLegendsProps {
uptime: ComputedUptime;
}

export const RecentBlocksLegends = ({
uptime: { signed, proposed, missed, signedRatio, proposedRatio, missedRatio },
}: RecentBlocksLegendsProps) => {
return (
<Flex direction={{ base: "column", md: "row" }}>
<LegendItem
label="Signed Blocks"
color="primary.main"
value="88"
percent="95.21%"
value={signed}
ratio={signedRatio}
/>
<LegendItem
label="Proposed Blocks"
color="secondary.main"
value="12"
percent="3.00%"
value={proposed}
ratio={proposedRatio}
/>
<LegendItem
label="Missed Blocks"
color="error.dark"
value="3"
percent="1.11%"
value={missed}
ratio={missedRatio}
/>
</Flex>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,69 @@
import { Button, Flex, Heading, Text } from "@chakra-ui/react";
import {
Button,
Flex,
Heading,
Menu,
MenuButton,
MenuItem,
MenuList,
Text,
} from "@chakra-ui/react";
import { isUndefined } from "lodash";
import { useMemo } from "react";

import { PenaltyStatus } from "../../types";
import { useMobile } from "lib/app-provider";
import { CustomIcon } from "lib/components/icon";
import { Loading } from "lib/components/Loading";
import { ValueWithIcon } from "lib/components/ValueWithIcon";
import type { ValidatorUptimeResponse } from "lib/services/validator";
import type { ComputedUptime, Option, Ratio } from "lib/types";
import { formatRatio } from "lib/utils";

import { PenaltyStatusSection } from "./PenaltyStatusSection";
import { RecentBlocksLegends } from "./RecentBlocksLegends";
import { RecentBlocksSection } from "./RecentBlocksSection";

interface UptimeSectionProps {
isDetailPage?: boolean;
uptimeData: Option<ValidatorUptimeResponse>;
setUptimeBlock?: (block: number) => void;
onViewMore?: () => void;
}

export const UptimeSection = ({
uptimeData,
setUptimeBlock,
onViewMore,
isDetailPage = false,
}: UptimeSectionProps) => {
const isMobile = useMobile();

const computed = useMemo<ComputedUptime>(() => {
const data = uptimeData?.uptime;
if (!data)
return {
signed: 0,
proposed: 0,
missed: 0,
signedRatio: 0 as Ratio<number>,
proposedRatio: 0 as Ratio<number>,
missedRatio: 0 as Ratio<number>,
uptimeRatio: 0 as Ratio<number>,
};

return {
signed: data.signedBlocks,
proposed: data.proposedBlocks,
missed: data.missedBlocks,
signedRatio: (data.signedBlocks / data.total) as Ratio<number>,
proposedRatio: (data.proposedBlocks / data.total) as Ratio<number>,
missedRatio: (data.missedBlocks / data.total) as Ratio<number>,
uptimeRatio: ((data.signedBlocks + data.proposedBlocks) /
data.total) as Ratio<number>,
};
}, [uptimeData?.uptime]);

if (isUndefined(uptimeData)) return <Loading />;

return (
<Flex
direction="column"
Expand All @@ -34,9 +79,31 @@ export const UptimeSection = ({
<Heading variant="h6" as="h6" color="text.main">
Uptime
</Heading>
<Text variant="body2" color="text.dark">
Latest 100 Blocks
</Text>
{setUptimeBlock ? (
<Menu>
<MenuButton>
<Text variant="body2" color="text.dark">
Latest 100 Blocks
<CustomIcon name="chevron-down" ml={2} />
</Text>
</MenuButton>
<MenuList>
<MenuItem onClick={() => setUptimeBlock(100)}>
Latest 100 Blocks
</MenuItem>
<MenuItem onClick={() => setUptimeBlock(1000)}>
Latest 1000 Blocks
</MenuItem>
<MenuItem onClick={() => setUptimeBlock(10000)}>
Latest 10000 Blocks
</MenuItem>
</MenuList>
</Menu>
) : (
<Text variant="body2" color="text.dark">
Latest 100 Blocks
</Text>
)}
</Flex>
{onViewMore && !isMobile && (
<Button
Expand All @@ -48,10 +115,10 @@ export const UptimeSection = ({
</Button>
)}
</Flex>
<ValueWithIcon icon="block" value="98.21%" />
<ValueWithIcon icon="block" value={formatRatio(computed.uptimeRatio)} />
</Flex>
<RecentBlocksLegends />
{!isDetailPage && (
<RecentBlocksLegends uptime={computed} />
{onViewMore && (
<>
{isMobile && <RecentBlocksSection />}
<PenaltyStatusSection hasBorder status={PenaltyStatus.Jailed} />{" "}
Expand Down
13 changes: 12 additions & 1 deletion src/lib/pages/validator-details/components/performance/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Flex } from "@chakra-ui/react";
import { useState } from "react";

import { ProposedBlocksTable } from "../tables/ProposedBlocksTable";
import { useValidatorUptime } from "lib/services/validatorService";
import type { ValidatorAddr } from "lib/types";

import { PenaltySection } from "./PenaltySection";
Expand All @@ -12,11 +14,20 @@ interface PerformanceProps {
}

export const Performance = ({ validatorAddress }: PerformanceProps) => {
const [uptimeBlock, setUptimeBlock] = useState(100);
const { data: uptimeData } = useValidatorUptime(
validatorAddress,
uptimeBlock
);

return (
<Flex direction="column" gap={{ base: 4, md: 6 }} pt={6}>
<Flex gap={{ base: 4, md: 6 }} direction={{ base: "column", md: "row" }}>
<Flex flex={{ md: "2" }}>
<UptimeSection isDetailPage />
<UptimeSection
uptimeData={uptimeData}
setUptimeBlock={(block) => setUptimeBlock(block)}
/>
</Flex>
<Flex flex={{ md: "1" }}>
<PenaltySection />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { VotedProposalsTable } from "../tables/VotedProposalsTable";
import { useMobile, useMoveConfig } from "lib/app-provider";
import { CustomIcon } from "lib/components/icon";
import { EmptyState } from "lib/components/state";
import { useValidatorUptime } from "lib/services/validatorService";
import type { ValidatorAddr } from "lib/types";

import { ValidatorDescription } from "./ValidatorDescription";
Expand All @@ -29,6 +30,7 @@ export const ValidatorOverview = ({
}: ValidatorOverviewProps) => {
const isMobile = useMobile();
const move = useMoveConfig({ shouldRedirect: false });
const { data: uptimeData } = useValidatorUptime(validatorAddress, 100);

return (
<>
Expand All @@ -39,7 +41,10 @@ export const ValidatorOverview = ({
direction={{ base: "column", md: "row" }}
>
<VotingPowerOverview />
<UptimeSection onViewMore={onSelectPerformance} />
<UptimeSection
uptimeData={uptimeData}
onViewMore={onSelectPerformance}
/>
</Flex>
{!isMobile && (
<>
Expand Down
2 changes: 1 addition & 1 deletion src/lib/pages/validator-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ const ValidatorDetailsBody = ({
<TabPanels>
<TabPanel p={0} pt={{ base: 2, md: 0 }}>
<ValidatorOverview
validatorAddress={validatorAddress}
onSelectVotes={handleTabChange(TabIndex.Votes)}
onSelectPerformance={handleTabChange(TabIndex.Performance)}
onSelectBondedTokenChanges={handleTabChange(
TabIndex.BondedTokenChanges
)}
validatorAddress={validatorAddress}
/>
</TabPanel>
<TabPanel p={0} pt={{ base: 2, md: 0 }}>
Expand Down
6 changes: 3 additions & 3 deletions src/lib/services/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,12 +177,12 @@ export const getValidatorData = async (
const zValidatorUptimeResponse = z
.object({
uptime: z.object({
signed_block: z.number(),
proposed_block: z.number(),
signed_blocks: z.number(),
proposed_blocks: z.number(),
missed_blocks: z.number(),
total: z.number(),
}),
recent_blocks: z
recent_100_blocks: z
.object({
height: z.number(),
vote: z.nativeEnum(BlockVote),
Expand Down
1 change: 1 addition & 0 deletions src/lib/services/validatorService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ export const useValidatorUptime = (
async () => getValidatorUptime(endpoint, validatorAddress, blocks),
{
retry: 1,
refetchOnWindowFocus: false,
}
);
};
Expand Down
11 changes: 11 additions & 0 deletions src/lib/types/validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { snakeToCamel } from "lib/utils/formatter/snakeToCamel";

import { zBechAddr20, zValidatorAddr } from "./addrs";
import { zBig } from "./big";
import type { Ratio } from "./currency";

export const zValidator = z
.object({
Expand Down Expand Up @@ -43,3 +44,13 @@ export enum BlockVote {
VOTE = "VOTE",
ABSTAIN = "ABSTAIN",
}

export type ComputedUptime = {
signed: number;
proposed: number;
missed: number;
signedRatio: Ratio<number>;
proposedRatio: Ratio<number>;
missedRatio: Ratio<number>;
uptimeRatio: Ratio<number>;
};

0 comments on commit e8781ca

Please sign in to comment.