Rename staking validators page to Validators (#7378)
* Rename staking validators page to Validators * Adjust * Dedupe summary * Cleanups
This commit is contained in:
parent
8770164425
commit
ba90464cb3
@ -45,8 +45,8 @@ To start off, this repo (along with others in the [@polkadot](https://github.com
|
||||
To get started -
|
||||
|
||||
1. Clone the repo locally, via `git clone https://github.com/polkadot-js/apps <optional local path>`
|
||||
2. Ensure that you have a recent LTS version of Node.js, for development purposes [Node >=10.13.0](https://nodejs.org/en/) is recommended.
|
||||
3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >=1.10.1](https://yarnpkg.com/docs/install) is required.
|
||||
2. Ensure that you have a recent LTS version of Node.js, for development purposes [Node >= 16](https://nodejs.org/en/) is recommended.
|
||||
3. Ensure that you have a recent version of Yarn, for development purposes [Yarn >= 1.22](https://yarnpkg.com/docs/install) is required.
|
||||
4. Install the dependencies by running `yarn`
|
||||
5. Ready! Now you can launch the UI (assuming you have a local Polkadot Node running), via `yarn run start`
|
||||
6. Access the UI via [http://localhost:3000](http://localhost:3000)
|
||||
|
||||
@ -11,6 +11,9 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/polkadot-js/apps.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"version": "0.112.2-15",
|
||||
"workspaces": [
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Copyright 2017-2022 @polkadot/app-staking authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { DeriveSessionIndexes } from '@polkadot/api-derive/types';
|
||||
import type { Option } from '@polkadot/types';
|
||||
import type { Balance } from '@polkadot/types/interfaces';
|
||||
import type { BN } from '@polkadot/util';
|
||||
@ -11,12 +10,13 @@ import React, { useMemo } from 'react';
|
||||
import { CardSummary, SummaryBox } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { BN_ONE, BN_ZERO } from '@polkadot/util';
|
||||
import { BN_ZERO } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
avgStaked?: BN;
|
||||
lastEra?: BN;
|
||||
lowStaked?: BN;
|
||||
minNominated?: BN;
|
||||
minNominatorBond?: BN;
|
||||
@ -27,46 +27,46 @@ interface Props {
|
||||
totalStaked?: BN;
|
||||
}
|
||||
|
||||
const transformReward = {
|
||||
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
|
||||
interface ProgressInfo {
|
||||
hideValue: true;
|
||||
total: BN;
|
||||
value: BN;
|
||||
}
|
||||
|
||||
const OPT_REWARD = {
|
||||
transform: (optBalance: Option<Balance>) =>
|
||||
optBalance.unwrapOrDefault()
|
||||
};
|
||||
|
||||
const transformEra = {
|
||||
transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gt(BN_ZERO) ? activeEra.sub(BN_ONE) : undefined
|
||||
};
|
||||
function getProgressInfo (value?: BN, total?: BN): ProgressInfo | undefined {
|
||||
return value && total && !total.isZero()
|
||||
? {
|
||||
hideValue: true,
|
||||
total,
|
||||
value
|
||||
}
|
||||
: undefined;
|
||||
}
|
||||
|
||||
function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ avgStaked, lastEra, lowStaked, minNominated, minNominatorBond, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const lastEra = useCall<BN | undefined>(api.derive.session.indexes, undefined, transformEra);
|
||||
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], transformReward);
|
||||
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], OPT_REWARD);
|
||||
|
||||
const progressStake = useMemo(
|
||||
() => totalIssuance && totalStaked && totalStaked.gtn(0)
|
||||
? {
|
||||
hideValue: true,
|
||||
total: totalIssuance,
|
||||
value: totalStaked
|
||||
}
|
||||
: undefined,
|
||||
() => getProgressInfo(totalStaked, totalIssuance),
|
||||
[totalIssuance, totalStaked]
|
||||
);
|
||||
|
||||
const progressAvg = useMemo(
|
||||
() => avgStaked && lowStaked && avgStaked.gtn(0)
|
||||
? {
|
||||
hideValue: true,
|
||||
total: avgStaked,
|
||||
value: lowStaked
|
||||
}
|
||||
: undefined,
|
||||
() => getProgressInfo(lowStaked, avgStaked),
|
||||
[avgStaked, lowStaked]
|
||||
);
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
<section className='media--800'>
|
||||
{totalIssuance && totalStaked?.gt(BN_ZERO) && (
|
||||
{progressStake && (
|
||||
<CardSummary
|
||||
label={t<string>('total staked')}
|
||||
progress={progressStake}
|
||||
@ -86,7 +86,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked
|
||||
)}
|
||||
</section>
|
||||
<section className='media--1000'>
|
||||
{avgStaked?.gtn(0) && lowStaked?.gtn(0) && (
|
||||
{progressAvg && (
|
||||
<CardSummary
|
||||
label={`${t<string>('lowest / avg staked')}`}
|
||||
progress={progressAvg}
|
||||
@ -131,7 +131,7 @@ function Summary ({ avgStaked, lowStaked, minNominated, minNominatorBond, staked
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
{lastReward?.gtn(0) && (
|
||||
{lastReward?.gt(BN_ZERO) && (
|
||||
<CardSummary label={t<string>('last reward')}>
|
||||
<FormatBalance
|
||||
value={lastReward}
|
||||
|
||||
@ -14,8 +14,8 @@ import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import MaxBadge from '../MaxBadge';
|
||||
import Favorite from '../Overview/Address/Favorite';
|
||||
import { useTranslation } from '../translate';
|
||||
import Favorite from '../Validators/Address/Favorite';
|
||||
|
||||
interface Props {
|
||||
allSlashes?: [BN, UnappliedSlash[]][];
|
||||
|
||||
@ -192,7 +192,7 @@ const DEFAULT_NAME = { isQueryFiltered: false, nameFilter: '' };
|
||||
|
||||
const DEFAULT_SORT: SortState = { sortBy: 'rankOverall', sortFromMax: true };
|
||||
|
||||
function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lowStaked, medianComm, minNominated, minNominatorBond, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
|
||||
function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lastEra, lowStaked, medianComm, minNominated, minNominatorBond, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const allSlashes = useAvailableSlashes();
|
||||
@ -362,6 +362,7 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
|
||||
<div className={className}>
|
||||
<Summary
|
||||
avgStaked={avgStaked}
|
||||
lastEra={lastEra}
|
||||
lowStaked={lowStaked}
|
||||
minNominated={minNominated}
|
||||
minNominatorBond={minNominatorBond}
|
||||
|
||||
@ -5,7 +5,7 @@ import React from 'react';
|
||||
|
||||
import { MarkWarning } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
import { useTranslation } from '../translate';
|
||||
|
||||
function ActionsBanner (): React.ReactElement<null> | null {
|
||||
const { t } = useTranslation();
|
||||
@ -15,17 +15,16 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
isVisible: boolean;
|
||||
nominators?: string[];
|
||||
stakingOverview?: DeriveStakingOverview;
|
||||
targets: SortedTargets;
|
||||
}
|
||||
|
||||
function Summary ({ className = '', isVisible, stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, nominators, waitingIds } }: Props): React.ReactElement<Props> {
|
||||
function Summary ({ className = '', stakingOverview, targets: { counterForNominators, inflation: { idealStake, inflation, stakedFraction }, nominators, waitingIds } }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<SummaryBox className={`${className} ${isVisible ? '' : ' --hidden'}`}>
|
||||
<SummaryBox className={className}>
|
||||
<section>
|
||||
<CardSummary label={t<string>('validators')}>
|
||||
{stakingOverview
|
||||
@ -12,11 +12,14 @@ import { Button, ToggleGroup } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import ActionsBanner from './ActionsBanner';
|
||||
import CurrentList from './CurrentList';
|
||||
import Summary from './Summary';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
favorites: string[];
|
||||
hasAccounts: boolean;
|
||||
hasQueries: boolean;
|
||||
minCommission?: BN;
|
||||
nominatedBy?: NominatedByMap;
|
||||
@ -29,7 +32,7 @@ interface Props {
|
||||
toggleNominatedBy: () => void;
|
||||
}
|
||||
|
||||
function Overview ({ className = '', favorites, hasQueries, minCommission, nominatedBy, ownStashes, paraValidators, stakingOverview, targets, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
|
||||
function Overview ({ className = '', favorites, hasAccounts, hasQueries, minCommission, nominatedBy, ownStashes, paraValidators, stakingOverview, targets, toggleFavorite, toggleLedger, toggleNominatedBy }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [intentIndex, _setIntentIndex] = useState(0);
|
||||
@ -67,6 +70,13 @@ function Overview ({ className = '', favorites, hasQueries, minCommission, nomin
|
||||
|
||||
return (
|
||||
<div className={`staking--Overview ${className}`}>
|
||||
<Summary
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
/>
|
||||
{hasAccounts && (ownStashes?.length === 0) && (
|
||||
<ActionsBanner />
|
||||
)}
|
||||
<Button.Group>
|
||||
<ToggleGroup
|
||||
onChange={setTypeIndex}
|
||||
@ -16,12 +16,9 @@ import { useAccounts, useApi, useAvailableSlashes, useCall, useCallMulti, useFav
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
import basicMd from './md/basic.md';
|
||||
import Summary from './Overview/Summary';
|
||||
import Actions from './Actions';
|
||||
import ActionsBanner from './ActionsBanner';
|
||||
import Bags from './Bags';
|
||||
import { STORE_FAVS_BASE } from './constants';
|
||||
import Overview from './Overview';
|
||||
import Payouts from './Payouts';
|
||||
import Pools from './Pools';
|
||||
import Query from './Query';
|
||||
@ -30,10 +27,11 @@ import Targets from './Targets';
|
||||
import { useTranslation } from './translate';
|
||||
import useNominations from './useNominations';
|
||||
import useSortedTargets from './useSortedTargets';
|
||||
import Validators from './Validators';
|
||||
|
||||
const HIDDEN_ACC = ['actions', 'payout'];
|
||||
|
||||
const optionsParaValidators = {
|
||||
const OPT_MULTI = {
|
||||
defaultValue: [false, undefined, {}] as [boolean, BN | undefined, Record<string, boolean>],
|
||||
transform: ([eraElectionStatus, minValidatorBond, validators, activeValidatorIndices]: [ElectionStatus | null, BN | undefined, ValidatorId[] | null, ParaValidatorIndex[] | null]): [boolean, BN | undefined, Record<string, boolean>] => [
|
||||
!!eraElectionStatus && eraElectionStatus.isOpen,
|
||||
@ -61,7 +59,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
api.query.staking.minCommission,
|
||||
api.query.session.validators,
|
||||
(api.query.parasShared || api.query.shared)?.activeValidatorIndices
|
||||
], optionsParaValidators);
|
||||
], OPT_MULTI);
|
||||
const ownStashes = useOwnStashInfos();
|
||||
const slashes = useAvailableSlashes();
|
||||
const targets = useSortedTargets(favorites, withLedger);
|
||||
@ -142,11 +140,6 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
}
|
||||
items={items}
|
||||
/>
|
||||
<Summary
|
||||
isVisible={pathname === basePath}
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
/>
|
||||
<Switch>
|
||||
<Route path={`${basePath}/bags`}>
|
||||
<Bags ownStashes={ownStashes} />
|
||||
@ -189,12 +182,10 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
ownStashes={ownStashes}
|
||||
targets={targets}
|
||||
/>
|
||||
{basePath === pathname && hasAccounts && (ownStashes?.length === 0) && (
|
||||
<ActionsBanner />
|
||||
)}
|
||||
<Overview
|
||||
<Validators
|
||||
className={basePath === pathname ? '' : '--hidden'}
|
||||
favorites={favorites}
|
||||
hasAccounts={hasAccounts}
|
||||
hasQueries={hasQueries}
|
||||
minCommission={minCommission}
|
||||
nominatedBy={nominatedBy}
|
||||
|
||||
@ -78,6 +78,7 @@ export interface SortedTargets {
|
||||
counterForValidators?: BN;
|
||||
electedIds?: string[];
|
||||
inflation: Inflation;
|
||||
lastEra?: BN;
|
||||
lowStaked?: BN;
|
||||
medianComm: number;
|
||||
maxNominatorsCount?: BN;
|
||||
|
||||
@ -5,19 +5,22 @@ import type { DeriveHasIdentity } from '@polkadot/api-derive/types';
|
||||
|
||||
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
function transformIdentity ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record<string, DeriveHasIdentity> {
|
||||
return validatorIds.reduce((result: Record<string, DeriveHasIdentity>, validatorId, index): Record<string, DeriveHasIdentity> => {
|
||||
result[validatorId] = hasIdentities[index];
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
||||
type Result = Record<string, DeriveHasIdentity>;
|
||||
|
||||
const OPT_CALL = {
|
||||
transform: ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record<string, DeriveHasIdentity> => {
|
||||
return validatorIds.reduce((result: Record<string, DeriveHasIdentity>, validatorId, index): Record<string, DeriveHasIdentity> => {
|
||||
result[validatorId] = hasIdentities[index];
|
||||
|
||||
return result;
|
||||
}, {});
|
||||
},
|
||||
withParamsTransform: true
|
||||
};
|
||||
|
||||
function useIdentitiesImpl (validatorIds: string[] = []): Result | undefined {
|
||||
const { api } = useApi();
|
||||
const allIdentity = useCall<Result>(api.derive.accounts.hasIdentityMulti, [validatorIds], { transform: transformIdentity, withParamsTransform: true });
|
||||
const allIdentity = useCall<Result>(api.derive.accounts.hasIdentityMulti, [validatorIds], OPT_CALL);
|
||||
|
||||
return allIdentity;
|
||||
}
|
||||
|
||||
@ -231,6 +231,7 @@ function extractBaseInfo (api: ApiPromise, allAccounts: string[], electedDerive:
|
||||
|
||||
return {
|
||||
avgStaked,
|
||||
lastEra: lastEraInfo.lastEra,
|
||||
lowStaked: activeTotals[0] || BN_ZERO,
|
||||
medianComm,
|
||||
minNominated,
|
||||
@ -244,16 +245,18 @@ function extractBaseInfo (api: ApiPromise, allAccounts: string[], electedDerive:
|
||||
};
|
||||
}
|
||||
|
||||
const transformEra = {
|
||||
const OPT_ERA = {
|
||||
transform: ({ activeEra, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
|
||||
activeEra,
|
||||
eraLength,
|
||||
lastEra: activeEra.isZero() ? BN_ZERO : activeEra.subn(1),
|
||||
lastEra: activeEra.isZero()
|
||||
? BN_ZERO
|
||||
: activeEra.sub(BN_ONE),
|
||||
sessionLength
|
||||
})
|
||||
};
|
||||
|
||||
const transformMulti = {
|
||||
const OPT_MULTI = {
|
||||
defaultValue: {},
|
||||
transform: ([historyDepth, counterForNominators, counterForValidators, optMaxNominatorsCount, optMaxValidatorsCount, minNominatorBond, minValidatorBond, totalIssuance]: [BN, BN?, BN?, Option<u32>?, Option<u32>?, BN?, BN?, BN?]): MultiResult => ({
|
||||
counterForNominators,
|
||||
@ -283,10 +286,10 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted
|
||||
api.query.staking.minNominatorBond,
|
||||
api.query.staking.minValidatorBond,
|
||||
api.query.balances?.totalIssuance
|
||||
], transformMulti);
|
||||
], OPT_MULTI);
|
||||
const electedInfo = useCall<DeriveStakingElected>(api.derive.staking.electedInfo, [{ ...DEFAULT_FLAGS_ELECTED, withLedger }]);
|
||||
const waitingInfo = useCall<DeriveStakingWaiting>(api.derive.staking.waitingInfo, [{ ...DEFAULT_FLAGS_WAITING, withLedger }]);
|
||||
const lastEraInfo = useCall<LastEra>(api.derive.session.info, undefined, transformEra);
|
||||
const lastEraInfo = useCall<LastEra>(api.derive.session.info, undefined, OPT_ERA);
|
||||
|
||||
const baseInfo = useMemo(
|
||||
() => electedInfo && lastEraInfo && totalIssuance && waitingInfo
|
||||
|
||||
Loading…
Reference in New Issue
Block a user