From ec266921acaa99c690b0dd6b743202382ab2493b Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 27 Aug 2024 22:11:28 +0200 Subject: [PATCH 1/4] Update API bindings --- src/oasis-nexus/generated/api.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/oasis-nexus/generated/api.ts b/src/oasis-nexus/generated/api.ts index 15e208a0d..adcfb7115 100644 --- a/src/oasis-nexus/generated/api.ts +++ b/src/oasis-nexus/generated/api.ts @@ -1193,6 +1193,8 @@ export type ProposalVotesAllOf = { votes: ProposalVote[]; }; +export type ProposalVotes = List & ProposalVotesAllOf; + /** * The state of the proposal. */ @@ -1537,20 +1539,36 @@ export interface Validator { start_date: string; /** The voting power of this validator. */ voting_power: number; +} + +export interface ValidatorAggStats { + /** The total number of delegators in the network. */ + total_delegators: number; + /** The total amount of token staked to validators. */ + total_staked_balance: TextBigInt; /** The total voting power across all validators. */ - voting_power_total: number; + total_voting_power: number; } /** - * A list of validators registered at the consensus layer. + * A list of validators registered at the consensus layer, plus summary +statistics across all consensus validators. */ export type ValidatorListAllOf = { + /** Summary statistics across all consensus validators. */ + stats: ValidatorAggStats; validators: Validator[]; }; export type ValidatorList = List & ValidatorListAllOf; +export interface ValidatorsResponse { + /** Summary statistics across all consensus validators. */ + stats: ValidatorAggStats; + validator_list: ValidatorList; +} + /** * An entity registered at the consensus layer. @@ -1966,8 +1984,6 @@ the query would return with limit=infinity. total_count: number; } -export type ProposalVotes = List & ProposalVotesAllOf; - /** * A list of consensus blocks. @@ -2885,7 +2901,7 @@ export const GetConsensusValidatorsAddress = ( ) => { - return GetConsensusValidatorsAddressMutator( + return GetConsensusValidatorsAddressMutator( {url: `/${encodeURIComponent(String(network))}/consensus/validators/${encodeURIComponent(String(address))}`, method: 'GET', signal }, options); From 03870fd10692ca84d36b45e9d3c4db44555691c2 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 27 Aug 2024 22:47:42 +0200 Subject: [PATCH 2/4] Sync validators UI with API changes --- src/app/components/Validators/index.tsx | 9 ++-- .../ConsensusDashboardPage/Validators.tsx | 1 + .../ValidatorSnapshot.tsx | 9 ++-- .../ValidatorDetailsPage/VotingPowerCard.tsx | 13 +++--- src/app/pages/ValidatorDetailsPage/index.tsx | 21 +++++---- src/app/pages/ValidatorsPage/index.tsx | 16 ++++++- src/oasis-nexus/api.ts | 45 ++++++++++--------- 7 files changed, 69 insertions(+), 45 deletions(-) diff --git a/src/app/components/Validators/index.tsx b/src/app/components/Validators/index.tsx index 7d6459cd8..e138bb3ca 100644 --- a/src/app/components/Validators/index.tsx +++ b/src/app/components/Validators/index.tsx @@ -2,7 +2,7 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import { Table, TableCellAlign, TableColProps } from '../../components/Table' -import { Validator } from '../../../oasis-nexus/api' +import { Validator, ValidatorAggStats } from '../../../oasis-nexus/api' import { TablePaginationProps } from '../Table/TablePagination' import { StatusIcon } from '../StatusIcon' import { RoundedBalance } from '../RoundedBalance' @@ -18,9 +18,10 @@ type ValidatorsProps = { isLoading: boolean limit: number pagination: false | TablePaginationProps + stats: ValidatorAggStats | undefined } -export const Validators: FC = ({ isLoading, limit, pagination, validators }) => { +export const Validators: FC = ({ isLoading, limit, pagination, validators, stats }) => { const { t } = useTranslation() const { network } = useRequiredScopeParam() @@ -71,9 +72,9 @@ export const Validators: FC = ({ isLoading, limit, pagination, align: TableCellAlign.Right, content: ( <> - {typeof validator?.voting_power === 'number' && validator?.voting_power_total > 0 + {typeof validator?.voting_power === 'number' && stats?.total_voting_power ? t('common.valuePair', { - value: validator.voting_power / validator.voting_power_total, + value: validator.voting_power / stats.total_voting_power, formatParams: { value: { style: 'percent', diff --git a/src/app/pages/ConsensusDashboardPage/Validators.tsx b/src/app/pages/ConsensusDashboardPage/Validators.tsx index 3655b6154..f458fdd4e 100644 --- a/src/app/pages/ConsensusDashboardPage/Validators.tsx +++ b/src/app/pages/ConsensusDashboardPage/Validators.tsx @@ -47,6 +47,7 @@ export const ValidatorsCard: FC<{ scope: SearchScope }> = ({ scope }) => { = ({ scope, validator }) => { +export const ValidatorSnapshot: FC = ({ scope, validator, stats }) => { const { t } = useTranslation() const theme = useTheme() @@ -44,7 +45,7 @@ export const ValidatorSnapshot: FC = ({ scope, validator - + diff --git a/src/app/pages/ValidatorDetailsPage/VotingPowerCard.tsx b/src/app/pages/ValidatorDetailsPage/VotingPowerCard.tsx index e98c265bb..762aeab75 100644 --- a/src/app/pages/ValidatorDetailsPage/VotingPowerCard.tsx +++ b/src/app/pages/ValidatorDetailsPage/VotingPowerCard.tsx @@ -2,16 +2,17 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' -import { Validator } from '../../../oasis-nexus/api' +import { Validator, ValidatorAggStats } from '../../../oasis-nexus/api' import { SnapshotTextCard } from '../../components/Snapshots/SnapshotCard' import { COLORS } from 'styles/theme/colors' import { VerticalProgressBar } from 'app/components/ProgressBar' type VotingPowerCardProps = { - validator?: Validator + validator: Validator | undefined + stats: ValidatorAggStats | undefined } -export const VotingPowerCard: FC = ({ validator }) => { +export const VotingPowerCard: FC = ({ validator, stats }) => { const { t } = useTranslation() return ( @@ -26,14 +27,14 @@ export const VotingPowerCard: FC = ({ validator }) => { } withContentPadding={false} > - {typeof validator?.voting_power === 'number' && validator?.voting_power_total > 0 && ( + {typeof validator?.voting_power === 'number' && stats?.total_voting_power && ( {t('validator.votingPowerOverall')} {t('common.valuePair', { - value: validator.voting_power / validator.voting_power_total, + value: validator.voting_power / stats.total_voting_power, formatParams: { value: { style: 'percent', @@ -46,7 +47,7 @@ export const VotingPowerCard: FC = ({ validator }) => { diff --git a/src/app/pages/ValidatorDetailsPage/index.tsx b/src/app/pages/ValidatorDetailsPage/index.tsx index 8b8a94137..0c78bf95c 100644 --- a/src/app/pages/ValidatorDetailsPage/index.tsx +++ b/src/app/pages/ValidatorDetailsPage/index.tsx @@ -6,7 +6,7 @@ import Card from '@mui/material/Card' import CardContent from '@mui/material/CardContent' import Divider from '@mui/material/Divider' import Grid from '@mui/material/Grid' -import { Validator, useGetConsensusValidatorsAddress } from '../../../oasis-nexus/api' +import { Validator, ValidatorAggStats, useGetConsensusValidatorsAddress } from '../../../oasis-nexus/api' import { useScreenSize } from '../../hooks/useScreensize' import { useFormattedTimestampStringWithDistance } from '../../hooks/useFormattedTimestamp' import { RouterTabs } from '../../components/RouterTabs' @@ -42,7 +42,8 @@ export const ValidatorDetailsPage: FC = () => { const { address } = useLoaderData() as AddressLoaderData const validatorQuery = useGetConsensusValidatorsAddress(scope.network, address) const { isLoading, data } = validatorQuery - const validator = data?.data + const validator = data?.data.validators[0] + const stats = data?.data.stats const transactionsLink = useHref('') const eventsLink = useHref(`events#${eventsContainerId}`) const delegatorsLink = useHref(`delegators#${delegatorsContainerId}`) @@ -52,9 +53,9 @@ export const ValidatorDetailsPage: FC = () => { return ( - + - + @@ -80,13 +81,14 @@ export const ValidatorDetailsPage: FC = () => { type ValidatorDetailsCardProps = { isLoading: boolean validator: Validator | undefined + stats: ValidatorAggStats | undefined } -const ValidatorDetailsCard: FC = ({ isLoading, validator }) => { +const ValidatorDetailsCard: FC = ({ isLoading, validator, stats }) => { return ( - + ) @@ -97,7 +99,8 @@ export const ValidatorDetailsView: FC<{ isLoading?: boolean validator: Validator | undefined standalone?: boolean -}> = ({ detailsPage, isLoading, validator, standalone = false }) => { + stats: ValidatorAggStats | undefined +}> = ({ detailsPage, isLoading, validator, standalone = false, stats }) => { const { t } = useTranslation() const { isMobile } = useScreenSize() const formattedTime = useFormattedTimestampStringWithDistance(validator?.start_date) @@ -140,12 +143,12 @@ export const ValidatorDetailsView: FC<{
{validator.voting_power.toLocaleString()}
)} - {typeof validator.voting_power === 'number' && validator.voting_power_total > 0 && ( + {typeof validator.voting_power === 'number' && stats?.total_voting_power && ( <>
{t('validator.totalShare')}
{t('common.valuePair', { - value: validator.voting_power / validator.voting_power_total, + value: validator.voting_power / stats.total_voting_power, formatParams: { value: { style: 'percent', diff --git a/src/app/pages/ValidatorsPage/index.tsx b/src/app/pages/ValidatorsPage/index.tsx index 9a7403fa0..c7cf14090 100644 --- a/src/app/pages/ValidatorsPage/index.tsx +++ b/src/app/pages/ValidatorsPage/index.tsx @@ -63,6 +63,7 @@ export const ValidatorsPage: FC = () => { > {tableView === TableLayout.Horizontal && ( { {isLoading && [...Array(PAGE_SIZE).keys()].map(key => ( - + ))} {!isLoading && validatorsData?.validators.map(validator => ( - + ))} )} diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index fa421d2f3..7af5502f0 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -1088,7 +1088,6 @@ export const useGetConsensusValidatorsAddressHistory: typeof generated.useGetCon }, }) } - export const useGetConsensusValidatorsAddress: typeof generated.useGetConsensusValidatorsAddress = ( network, address, @@ -1101,26 +1100,32 @@ export const useGetConsensusValidatorsAddress: typeof generated.useGetConsensusV ...options?.request, transformResponse: [ ...arrayify(axios.defaults.transformResponse), - (validator: generated.Validator, headers, status) => { - if (status !== 200) return validator + (data: generated.ValidatorList, headers, status): ExtendedValidatorList => { + if (status !== 200) return data + const validators = data.validators.map((validator): generated.Validator => { + return { + ...validator, + escrow: { + ...validator.escrow, + active_balance: validator.escrow?.active_balance + ? fromBaseUnits(validator.escrow.active_balance, consensusDecimals) + : undefined, + active_balance_24: validator.escrow?.active_balance_24 + ? fromBaseUnits(validator.escrow.active_balance_24, consensusDecimals) + : undefined, + debonding_balance: validator.escrow?.debonding_balance + ? fromBaseUnits(validator.escrow.debonding_balance, consensusDecimals) + : undefined, + self_delegation_balance: validator.escrow?.self_delegation_balance + ? fromBaseUnits(validator.escrow.self_delegation_balance, consensusDecimals) + : undefined, + }, + ticker, + } + }) return { - ...validator, - escrow: { - ...validator.escrow, - active_balance: validator.escrow.active_balance - ? fromBaseUnits(validator.escrow.active_balance, consensusDecimals) - : undefined, - active_balance_24: validator.escrow?.active_balance_24 - ? fromBaseUnits(validator.escrow.active_balance_24, consensusDecimals) - : undefined, - debonding_balance: validator.escrow?.debonding_balance - ? fromBaseUnits(validator.escrow.debonding_balance, consensusDecimals) - : undefined, - self_delegation_balance: validator.escrow?.self_delegation_balance - ? fromBaseUnits(validator.escrow.self_delegation_balance, consensusDecimals) - : undefined, - }, - ticker, + ...data, + validators, } }, ...arrayify(options?.request?.transformResponse), From 67dc2dcefc8e6b072a1a4071c085e0c4bb453b46 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 27 Aug 2024 22:48:29 +0200 Subject: [PATCH 3/4] Remove validators from proposals loading state --- src/app/pages/ProposalDetailsPage/index.tsx | 3 ++- src/app/pages/ProposalsPage/index.tsx | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app/pages/ProposalDetailsPage/index.tsx b/src/app/pages/ProposalDetailsPage/index.tsx index 3019b147a..9f66f6e07 100644 --- a/src/app/pages/ProposalDetailsPage/index.tsx +++ b/src/app/pages/ProposalDetailsPage/index.tsx @@ -70,7 +70,7 @@ const VoteLoadingProblemIndicator: FC = () => { } export const ProposalDetailView: FC<{ - proposal: Proposal + proposal: Proposal | undefined highlightedPart?: string isLoading?: boolean totalVotesLoading?: boolean @@ -91,6 +91,7 @@ export const ProposalDetailView: FC<{ const { t } = useTranslation() const { isMobile } = useScreenSize() if (isLoading) return + if (!proposal) return null const proposalType = getTypeNameForProposal(t, proposal) diff --git a/src/app/pages/ProposalsPage/index.tsx b/src/app/pages/ProposalsPage/index.tsx index 8dca90fb3..9a3168d3e 100644 --- a/src/app/pages/ProposalsPage/index.tsx +++ b/src/app/pages/ProposalsPage/index.tsx @@ -13,7 +13,6 @@ import { LoadMoreButton } from '../../components/LoadMoreButton' import { useRequiredScopeParam } from '../../hooks/useScopeParam' import { NetworkProposalsList } from '../../components/NetworkProposalsList' import { CardHeaderWithCounter } from '../../components/CardHeaderWithCounter' -import { ValidatorDetailsView } from '../ValidatorDetailsPage' import { VerticalList } from '../../components/VerticalList' import { ProposalDetailView } from '../ProposalDetailsPage' @@ -81,7 +80,7 @@ export const ProposalsPage: FC = () => { {isLoading && [...Array(PAGE_SIZE).keys()].map(key => ( - + ))} {!isLoading && proposalsData?.proposals.map(proposal => ( From ee44ac4da776946c9605cd8db714fdba6754b694 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Tue, 27 Aug 2024 23:22:47 +0200 Subject: [PATCH 4/4] Provide missing data to validator snapshot cards --- .changelog/1518.trivial.md | 1 + .../ConsensusDashboardPage/ConsensusSnapshot.tsx | 11 ++++++++--- .../ConsensusDashboardPage/SnapshotDelegators.tsx | 10 ++++++---- .../ConsensusDashboardPage/SnapshotStaked.tsx | 15 ++++++++++----- .../ConsensusDashboardPage/SnapshotValidators.tsx | 14 ++++++-------- src/oasis-nexus/api.ts | 14 ++++++++++++++ 6 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 .changelog/1518.trivial.md diff --git a/.changelog/1518.trivial.md b/.changelog/1518.trivial.md new file mode 100644 index 000000000..6b4019f35 --- /dev/null +++ b/.changelog/1518.trivial.md @@ -0,0 +1 @@ +Provide missing data to validator snapshot cards diff --git a/src/app/pages/ConsensusDashboardPage/ConsensusSnapshot.tsx b/src/app/pages/ConsensusDashboardPage/ConsensusSnapshot.tsx index bb91738c0..19d9583eb 100644 --- a/src/app/pages/ConsensusDashboardPage/ConsensusSnapshot.tsx +++ b/src/app/pages/ConsensusDashboardPage/ConsensusSnapshot.tsx @@ -1,7 +1,9 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import { SearchScope } from '../../../types/searchScope' +import { useGetConsensusValidators } from '../../../oasis-nexus/api' import { Snapshot, StyledGrid } from '../../components/Snapshots/Snapshot' +import { API_MAX_TOTAL_COUNT } from '../../config' import { SnapshotEpoch } from './SnapshotEpoch' import { SnapshotDelegators } from './SnapshotDelegators' import { SnapshotValidators } from './SnapshotValidators' @@ -9,6 +11,9 @@ import { SnapshotStaked } from './SnapshotStaked' export const ConsensusSnapshot: FC<{ scope: SearchScope }> = ({ scope }) => { const { t } = useTranslation() + const validatorsQuery = useGetConsensusValidators(scope.network, { limit: API_MAX_TOTAL_COUNT }) + const validators = validatorsQuery.data?.data.validators + const stats = validatorsQuery.data?.data.stats return ( @@ -16,13 +21,13 @@ export const ConsensusSnapshot: FC<{ scope: SearchScope }> = ({ scope }) => { - + - + - + ) diff --git a/src/app/pages/ConsensusDashboardPage/SnapshotDelegators.tsx b/src/app/pages/ConsensusDashboardPage/SnapshotDelegators.tsx index b833874d6..8e858cfd4 100644 --- a/src/app/pages/ConsensusDashboardPage/SnapshotDelegators.tsx +++ b/src/app/pages/ConsensusDashboardPage/SnapshotDelegators.tsx @@ -4,15 +4,17 @@ import Box from '@mui/material/Box' import { SnapshotTextCard } from '../../components/Snapshots/SnapshotCard' import { useScreenSize } from '../../hooks/useScreensize' -export const SnapshotDelegators: FC = () => { +type SnapshotDelegatorsProps = { + totalDelegators: number | undefined +} + +export const SnapshotDelegators: FC = ({ totalDelegators }) => { const { t } = useTranslation() const { isMobile } = useScreenSize() - // TODO: provide delegators number when API is ready - const delegators = undefined return ( - {delegators} + {totalDelegators && {totalDelegators.toLocaleString()}} ) } diff --git a/src/app/pages/ConsensusDashboardPage/SnapshotStaked.tsx b/src/app/pages/ConsensusDashboardPage/SnapshotStaked.tsx index ab39dbab0..7fbee09c4 100644 --- a/src/app/pages/ConsensusDashboardPage/SnapshotStaked.tsx +++ b/src/app/pages/ConsensusDashboardPage/SnapshotStaked.tsx @@ -3,13 +3,18 @@ import { Trans, useTranslation } from 'react-i18next' import Box from '@mui/material/Box' import Typography from '@mui/material/Typography' import { COLORS } from '../../../styles/theme/colors' +import { Ticker } from '../../../types/ticker' import { RoundedBalance } from '../../components/RoundedBalance' import { SnapshotTextCard } from '../../components/Snapshots/SnapshotCard' -export const SnapshotStaked: FC = () => { +type SnapshotStakedProps = { + ticker: Ticker | undefined + totalStaked: string | undefined +} + +export const SnapshotStaked: FC = ({ totalStaked, ticker }) => { const { t } = useTranslation() - // TODO: provide label and staked values when API is ready, validate percentageValue formatting - const staked = undefined + // TODO: provide totalCirculation const percentageValue = undefined return ( @@ -43,9 +48,9 @@ export const SnapshotStaked: FC = () => { ) } > - {staked && ( + {totalStaked && ( - + )} diff --git a/src/app/pages/ConsensusDashboardPage/SnapshotValidators.tsx b/src/app/pages/ConsensusDashboardPage/SnapshotValidators.tsx index efe82edac..d987343a6 100644 --- a/src/app/pages/ConsensusDashboardPage/SnapshotValidators.tsx +++ b/src/app/pages/ConsensusDashboardPage/SnapshotValidators.tsx @@ -1,11 +1,9 @@ import { FC } from 'react' import { useTranslation } from 'react-i18next' import { TFunction } from 'i18next' -import { useGetConsensusValidators, Validator } from '../../../oasis-nexus/api' -import { SearchScope } from '../../../types/searchScope' +import { Validator } from '../../../oasis-nexus/api' import { SnapshotCard } from '../../components/Snapshots/SnapshotCard' import { PieChart } from '../../components/charts/PieChart' -import { API_MAX_TOTAL_COUNT } from '../../config' type ValidatorsStats = { label: string @@ -22,12 +20,12 @@ function countValidatorsState(t: TFunction, validators: Validator[] | undefined) ] } -export const SnapshotValidators: FC<{ scope: SearchScope }> = ({ scope }) => { - const { t } = useTranslation() - const { network } = scope +type SnapshotValidatorsProps = { + validators: Validator[] | undefined +} - const validatorsQuery = useGetConsensusValidators(network, { limit: API_MAX_TOTAL_COUNT }) - const validators = validatorsQuery.data?.data.validators +export const SnapshotValidators: FC = ({ validators }) => { + const { t } = useTranslation() return ( diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 7af5502f0..be7991161 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -117,6 +117,10 @@ declare module './generated/api' { export interface Validator { ticker: Ticker } + + export interface ValidatorAggStats { + ticker: Ticker + } } export const isAccountEmpty = (account: RuntimeAccount | Account) => { @@ -1047,6 +1051,11 @@ export const useGetConsensusValidators: typeof generated.useGetConsensusValidato validators.forEach(validator => map.set(validator.entity_address, validator)) return { ...data, + stats: { + ...data.stats, + total_staked_balance: fromBaseUnits(data.stats.total_staked_balance, consensusDecimals), + ticker, + }, validators, map, } @@ -1126,6 +1135,11 @@ export const useGetConsensusValidatorsAddress: typeof generated.useGetConsensusV return { ...data, validators, + stats: { + ...data.stats, + total_staked_balance: fromBaseUnits(data.stats.total_staked_balance, consensusDecimals), + ticker, + }, } }, ...arrayify(options?.request?.transformResponse),