// Copyright 2017-2025 @polkadot/app-staking-async authors & contributors // SPDX-License-Identifier: Apache-2.0 import type { DeriveStakingOverview } from '@polkadot/api-derive/types'; import type { SortedTargets } from '@polkadot/app-staking/types'; import type { Option, u32 } from '@polkadot/types-codec'; import type { BN } from '@polkadot/util'; import React, { useEffect, useState } from 'react'; import { CardSummary, styled, SummaryBox } from '@polkadot/react-components'; import { useApi, useCall } from '@polkadot/react-hooks'; import { formatNumber } from '@polkadot/util'; import { useTranslation } from '../translate.js'; import SummarySession from './SummarySession.js'; interface Props { className?: string; nominators?: string[]; stakingOverview?: DeriveStakingOverview; targets: SortedTargets; } const OPT_CURRENTERA = { transform: (currentEra: Option): BN | null => currentEra.unwrapOr(null) }; const useActiveNominators = () => { const { api } = useApi(); const currentEra = useCall(api.query.staking.currentEra, undefined, OPT_CURRENTERA); const [exposedNominators, setExposedNominators] = useState([]); useEffect(() => { const getExposedNominators = async () => { const exposedNominators = (await api.query.staking.erasStakersPaged.entries(currentEra)).map(([_, value]) => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access return (value).unwrap().others.map((x) => x.who.toString()); }).flat(); setExposedNominators([...new Set(exposedNominators)]); }; getExposedNominators().catch(console.log); }, [api.query.staking.erasStakersPaged, currentEra]); return exposedNominators; }; function Summary ({ className = '', stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, waitingIds } }: Props): React.ReactElement { const { t } = useTranslation(); const activeNominators = useActiveNominators(); const percent = %; return (
{stakingOverview ? <>{formatNumber(stakingOverview.validators.length)} / {formatNumber(stakingOverview.validatorCount)} : 999 / 999 } {waitingIds ? formatNumber(waitingIds.length) : 99 } {activeNominators ? ( <> {formatNumber(activeNominators.length)} {counterForNominators && ( <> / {formatNumber(counterForNominators)} )} ) : 999 / 999 }
{(idealStake > 0) && Number.isFinite(idealStake) && ( <>{(idealStake * 100).toFixed(1)}{percent} )} {(stakedFraction > 0) && ( <>{(stakedFraction * 100).toFixed(1)}{percent} )} {(inflation > 0) && Number.isFinite(inflation) && ( <>{inflation.toFixed(1)}{percent} )}
); } const StyledSummaryBox = styled(SummaryBox)` .validator--Account-block-icon { display: inline-block; margin-right: 0.75rem; margin-top: -0.25rem; vertical-align: middle; } .validator--Summary-authors { .validator--Account-block-icon+.validator--Account-block-icon { margin-left: -1.5rem; } } .percent { font-size: var(--font-percent-tiny); } `; export default React.memo(Summary);