Add hook for targets (#2642)
This commit is contained in:
parent
d5019a0c21
commit
0e74acd19f
@ -14,8 +14,8 @@ import { useTranslation } from '../translate';
|
||||
|
||||
interface Props {
|
||||
lastReward?: BN;
|
||||
numNominators: number;
|
||||
numValidators: number;
|
||||
numNominators?: number;
|
||||
numValidators?: number;
|
||||
totalStaked?: BN;
|
||||
}
|
||||
|
||||
@ -59,9 +59,11 @@ function Summary ({ lastReward, numNominators, numValidators, totalStaked }: Pro
|
||||
{percentage}
|
||||
</CardSummary>
|
||||
)}
|
||||
<CardSummary label={t('validators/nominators')}>
|
||||
{numValidators}/{numNominators}
|
||||
</CardSummary>
|
||||
{numValidators && numNominators && (
|
||||
<CardSummary label={t('validators/nominators')}>
|
||||
{numValidators}/{numNominators}
|
||||
</CardSummary>
|
||||
)}
|
||||
{lastReward?.gtn(0) && (
|
||||
<CardSummary label={t('last reward')}>
|
||||
<FormatBalance
|
||||
|
||||
@ -2,209 +2,60 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveStakingElected, DeriveSessionIndexes } from '@polkadot/api-derive/types';
|
||||
import { Balance, ValidatorPrefs, ValidatorPrefsTo196 } from '@polkadot/types/interfaces';
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
import { ValidatorInfo } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { Icon, InputBalance, Table, Button } from '@polkadot/react-components';
|
||||
import { useAccounts, useApi, useCall, useDebounce, useFavorites } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { useDebounce } from '@polkadot/react-hooks';
|
||||
|
||||
import { STORE_FAVS_BASE } from '../constants';
|
||||
import { useTranslation } from '../translate';
|
||||
import Nominate from './Nominate';
|
||||
import Summary from './Summary';
|
||||
import Validator from './Validator';
|
||||
import useOwnNominators from './useOwnNominators';
|
||||
import useSortedTargets from './useSortedTargets';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
ownStashes?: StakerState[];
|
||||
}
|
||||
|
||||
interface AllInfo {
|
||||
nominators: string[];
|
||||
sorted?: ValidatorInfo[];
|
||||
totalStaked?: BN;
|
||||
validators: ValidatorInfo[];
|
||||
}
|
||||
|
||||
type SortBy = 'rankOverall' | 'rankBondOwn' | 'rankBondOther' | 'rankBondTotal' | 'rankComm';
|
||||
|
||||
const MAX_NOMINATIONS = 16;
|
||||
const PERBILL = new BN(1_000_000_000);
|
||||
|
||||
function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
return list
|
||||
.filter((a) => a.bondTotal.gtn(0))
|
||||
.sort((a, b): number => b.commissionPer - a.commissionPer)
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankComm = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOther.cmp(a.bondOther))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOther = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOwn.cmp(a.bondOwn))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOwn = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondTotal.cmp(a.bondTotal))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondTotal = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.validatorPayment.cmp(a.validatorPayment))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankPayment = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.rewardSplit.cmp(b.rewardSplit))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankReward = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => {
|
||||
const cmp = b.rewardPayout.cmp(a.rewardPayout);
|
||||
|
||||
return cmp !== 0
|
||||
? cmp
|
||||
: a.rankReward === b.rankReward
|
||||
? a.rankPayment === b.rankPayment
|
||||
? b.rankBondTotal - a.rankBondTotal
|
||||
: b.rankPayment - a.rankPayment
|
||||
: b.rankReward - a.rankReward;
|
||||
})
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankOverall = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.isFavorite === b.isFavorite
|
||||
? 0
|
||||
: (a.isFavorite ? -1 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo: DeriveStakingElected, favorites: string[], lastReward = new BN(1)): AllInfo {
|
||||
const nominators: string[] = [];
|
||||
let totalStaked = new BN(0);
|
||||
const perValidatorReward = lastReward.divn(electedInfo.info.length);
|
||||
const validators = sortValidators(
|
||||
electedInfo.info.map(({ accountId, exposure: _exposure, validatorPrefs }): ValidatorInfo => {
|
||||
const exposure = _exposure || {
|
||||
others: registry.createType('Vec<IndividualExposure>'),
|
||||
own: registry.createType('Compact<Balance>'),
|
||||
total: registry.createType('Compact<Balance>')
|
||||
};
|
||||
const prefs = (validatorPrefs as (ValidatorPrefs | ValidatorPrefsTo196)) || {
|
||||
commission: registry.createType('Compact<Perbill>')
|
||||
};
|
||||
const bondOwn = exposure.own.unwrap();
|
||||
const bondTotal = exposure.total.unwrap();
|
||||
const validatorPayment = (prefs as ValidatorPrefsTo196).validatorPayment
|
||||
? (prefs as ValidatorPrefsTo196).validatorPayment.unwrap() as BN
|
||||
: (prefs as ValidatorPrefs).commission.unwrap().mul(perValidatorReward).div(PERBILL);
|
||||
const key = accountId.toString();
|
||||
const rewardSplit = perValidatorReward.sub(validatorPayment);
|
||||
const rewardPayout = rewardSplit.gtn(0)
|
||||
? amount.mul(rewardSplit).div(amount.add(bondTotal))
|
||||
: new BN(0);
|
||||
const isNominating = exposure.others.reduce((isNominating, indv): boolean => {
|
||||
const nominator = indv.who.toString();
|
||||
|
||||
if (!nominators.includes(nominator)) {
|
||||
nominators.push(nominator);
|
||||
}
|
||||
|
||||
return isNominating || allAccounts.includes(nominator);
|
||||
}, allAccounts.includes(key));
|
||||
|
||||
totalStaked = totalStaked.add(bondTotal);
|
||||
|
||||
return {
|
||||
accountId,
|
||||
bondOther: bondTotal.sub(bondOwn),
|
||||
bondOwn,
|
||||
bondShare: 0,
|
||||
bondTotal,
|
||||
commissionPer: (((prefs as ValidatorPrefs).commission?.unwrap() || new BN(0)).toNumber() / 10_000_000),
|
||||
isCommission: !!(prefs as ValidatorPrefs).commission,
|
||||
isFavorite: favorites.includes(key),
|
||||
isNominating,
|
||||
key,
|
||||
numNominators: exposure.others.length,
|
||||
rankBondOther: 0,
|
||||
rankBondOwn: 0,
|
||||
rankBondTotal: 0,
|
||||
rankComm: 0,
|
||||
rankOverall: 0,
|
||||
rankPayment: 0,
|
||||
rankReward: 0,
|
||||
rewardPayout,
|
||||
rewardSplit,
|
||||
validatorPayment
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return { nominators, totalStaked, validators };
|
||||
}
|
||||
|
||||
function sort (sortBy: SortBy, sortFromMax: boolean, validators: ValidatorInfo[]): ValidatorInfo[] {
|
||||
return [...validators]
|
||||
.sort((a, b): number => sortFromMax
|
||||
? a[sortBy] - b[sortBy]
|
||||
: b[sortBy] - a[sortBy]
|
||||
function sort (sortBy: SortBy, sortFromMax: boolean, validators: ValidatorInfo[]): number[] {
|
||||
return [...Array(validators.length).keys()]
|
||||
.sort((a, b) =>
|
||||
sortFromMax
|
||||
? validators[a][sortBy] - validators[b][sortBy]
|
||||
: validators[b][sortBy] - validators[a][sortBy]
|
||||
)
|
||||
.sort((a, b): number => a.isFavorite === b.isFavorite
|
||||
? 0
|
||||
: (a.isFavorite ? -1 : 1)
|
||||
.sort((a, b) =>
|
||||
validators[a].isFavorite === validators[b].isFavorite
|
||||
? 0
|
||||
: (validators[a].isFavorite ? -1 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const ownNominators = useOwnNominators(ownStashes);
|
||||
const lastEra = useCall<BN>(api.derive.session.indexes, [], {
|
||||
transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gtn(0) ? activeEra.subn(1) : new BN(0)
|
||||
}) || new BN(0);
|
||||
const lastReward = useCall<BN>(api.query.staking.erasValidatorReward, [lastEra], {
|
||||
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
|
||||
});
|
||||
const [_amount, setAmount] = useState<BN | undefined>(new BN(1_000));
|
||||
const electedInfo = useCall<DeriveStakingElected>(api.derive.staking.electedInfo, []);
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [{ nominators, sorted, totalStaked, validators }, setWorkable] = useState<AllInfo>({ nominators: [], validators: [] });
|
||||
const [{ sortBy, sortFromMax }, setSortBy] = useState<{ sortBy: SortBy; sortFromMax: boolean }>({ sortBy: 'rankOverall', sortFromMax: true });
|
||||
const amount = useDebounce(_amount);
|
||||
const labels = useMemo(
|
||||
(): Record<string, string> => ({
|
||||
rankBondOther: t('other stake'),
|
||||
rankBondOwn: t('own stake'),
|
||||
rankBondTotal: t('total stake'),
|
||||
rankComm: t('commission'),
|
||||
rankOverall: t('profit/era est')
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
const { lastReward, nominators, toggleFavorite, totalStaked, validators } = useSortedTargets(amount);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [sorted, setSorted] = useState<number[] | undefined>();
|
||||
const [{ sortBy, sortFromMax }, setSortBy] = useState<{ sortBy: SortBy; sortFromMax: boolean }>({ sortBy: 'rankOverall', sortFromMax: true });
|
||||
|
||||
useEffect((): void => {
|
||||
validators && setSorted(
|
||||
sort(sortBy, sortFromMax, validators)
|
||||
);
|
||||
}, [sortBy, sortFromMax, validators]);
|
||||
|
||||
const _sort = useCallback(
|
||||
(newSortBy: SortBy) => setSortBy(({ sortBy, sortFromMax }) => ({
|
||||
@ -227,21 +78,23 @@ function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
|
||||
const _selectProfitable = useCallback(
|
||||
() => setSelected(
|
||||
validators
|
||||
(validators || [])
|
||||
.filter((_, index) => index < MAX_NOMINATIONS)
|
||||
.map(({ key }) => key)
|
||||
),
|
||||
[validators]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
if (electedInfo) {
|
||||
const { nominators, totalStaked, validators } = extractInfo(allAccounts, amount, electedInfo, favorites, lastReward);
|
||||
const sorted = sort(sortBy, sortFromMax, validators);
|
||||
|
||||
setWorkable({ nominators, sorted, totalStaked, validators });
|
||||
}
|
||||
}, [allAccounts, amount, electedInfo, favorites, lastReward, sortBy, sortFromMax]);
|
||||
const labels = useMemo(
|
||||
(): Record<string, string> => ({
|
||||
rankBondOther: t('other stake'),
|
||||
rankBondOwn: t('own stake'),
|
||||
rankBondTotal: t('total stake'),
|
||||
rankComm: t('commission'),
|
||||
rankOverall: t('profit/era est')
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
|
||||
const header = useMemo(() => [
|
||||
[t('validators'), 'start', 3],
|
||||
@ -269,14 +122,14 @@ function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
<div className={className}>
|
||||
<Summary
|
||||
lastReward={lastReward}
|
||||
numNominators={nominators.length}
|
||||
numValidators={validators.length}
|
||||
numNominators={nominators?.length}
|
||||
numValidators={validators?.length}
|
||||
totalStaked={totalStaked}
|
||||
/>
|
||||
<Button.Group>
|
||||
<Button
|
||||
icon='check'
|
||||
isDisabled={!validators.length || !ownNominators?.length}
|
||||
isDisabled={!validators?.length || !ownNominators?.length}
|
||||
label={t('Select best')}
|
||||
onClick={_selectProfitable}
|
||||
/>
|
||||
@ -291,12 +144,12 @@ function Targets ({ className, ownStashes }: Props): React.ReactElement<Props> {
|
||||
filter={filter}
|
||||
header={header}
|
||||
>
|
||||
{sorted?.map((info): React.ReactNode =>
|
||||
{validators && sorted?.map((index): React.ReactNode =>
|
||||
<Validator
|
||||
canSelect={selected.length < MAX_NOMINATIONS}
|
||||
info={info}
|
||||
isSelected={selected.includes(info.key)}
|
||||
key={info.key}
|
||||
info={validators[index]}
|
||||
isSelected={selected.includes(validators[index].key)}
|
||||
key={validators[index].key}
|
||||
toggleFavorite={toggleFavorite}
|
||||
toggleSelected={_toggleSelected}
|
||||
/>
|
||||
|
||||
@ -29,3 +29,11 @@ export interface ValidatorInfo {
|
||||
rewardSplit: BN;
|
||||
validatorPayment: BN;
|
||||
}
|
||||
|
||||
export interface SortedTargets {
|
||||
lastReward?: BN;
|
||||
nominators?: string[];
|
||||
toggleFavorite: (accountId: string) => void;
|
||||
totalStaked?: BN;
|
||||
validators?: ValidatorInfo[];
|
||||
}
|
||||
|
||||
167
packages/page-staking/src/Targets/useSortedTargets.ts
Normal file
167
packages/page-staking/src/Targets/useSortedTargets.ts
Normal file
@ -0,0 +1,167 @@
|
||||
// Copyright 2017-2020 @polkadot/app-staking authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveSessionIndexes, DeriveStakingElected } from '@polkadot/api-derive/types';
|
||||
import { Balance, ValidatorPrefs, ValidatorPrefsTo196 } from '@polkadot/types/interfaces';
|
||||
import { SortedTargets, ValidatorInfo } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { registry } from '@polkadot/react-api';
|
||||
import { useAccounts, useApi, useCall, useFavorites } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
|
||||
import { STORE_FAVS_BASE } from '../constants';
|
||||
|
||||
const PERBILL = new BN(1_000_000_000);
|
||||
|
||||
function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
return list
|
||||
.filter((a) => a.bondTotal.gtn(0))
|
||||
.sort((a, b): number => b.commissionPer - a.commissionPer)
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankComm = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOther.cmp(a.bondOther))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOther = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondOwn.cmp(a.bondOwn))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondOwn = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.bondTotal.cmp(a.bondTotal))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankBondTotal = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => b.validatorPayment.cmp(a.validatorPayment))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankPayment = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.rewardSplit.cmp(b.rewardSplit))
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankReward = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => {
|
||||
const cmp = b.rewardPayout.cmp(a.rewardPayout);
|
||||
|
||||
return cmp !== 0
|
||||
? cmp
|
||||
: a.rankReward === b.rankReward
|
||||
? a.rankPayment === b.rankPayment
|
||||
? b.rankBondTotal - a.rankBondTotal
|
||||
: b.rankPayment - a.rankPayment
|
||||
: b.rankReward - a.rankReward;
|
||||
})
|
||||
.map((info, index): ValidatorInfo => {
|
||||
info.rankOverall = index + 1;
|
||||
|
||||
return info;
|
||||
})
|
||||
.sort((a, b): number => a.isFavorite === b.isFavorite
|
||||
? 0
|
||||
: (a.isFavorite ? -1 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
function extractInfo (allAccounts: string[], amount: BN = new BN(0), electedInfo: DeriveStakingElected, favorites: string[], lastReward = new BN(1)): Partial<SortedTargets> {
|
||||
const nominators: string[] = [];
|
||||
let totalStaked = new BN(0);
|
||||
const perValidatorReward = lastReward.divn(electedInfo.info.length);
|
||||
const validators = sortValidators(
|
||||
electedInfo.info.map(({ accountId, exposure: _exposure, validatorPrefs }): ValidatorInfo => {
|
||||
const exposure = _exposure || {
|
||||
others: registry.createType('Vec<IndividualExposure>'),
|
||||
own: registry.createType('Compact<Balance>'),
|
||||
total: registry.createType('Compact<Balance>')
|
||||
};
|
||||
const prefs = (validatorPrefs as (ValidatorPrefs | ValidatorPrefsTo196)) || {
|
||||
commission: registry.createType('Compact<Perbill>')
|
||||
};
|
||||
const bondOwn = exposure.own.unwrap();
|
||||
const bondTotal = exposure.total.unwrap();
|
||||
const validatorPayment = (prefs as ValidatorPrefsTo196).validatorPayment
|
||||
? (prefs as ValidatorPrefsTo196).validatorPayment.unwrap() as BN
|
||||
: (prefs as ValidatorPrefs).commission.unwrap().mul(perValidatorReward).div(PERBILL);
|
||||
const key = accountId.toString();
|
||||
const rewardSplit = perValidatorReward.sub(validatorPayment);
|
||||
const rewardPayout = rewardSplit.gtn(0)
|
||||
? amount.mul(rewardSplit).div(amount.add(bondTotal))
|
||||
: new BN(0);
|
||||
const isNominating = exposure.others.reduce((isNominating, indv): boolean => {
|
||||
const nominator = indv.who.toString();
|
||||
|
||||
if (!nominators.includes(nominator)) {
|
||||
nominators.push(nominator);
|
||||
}
|
||||
|
||||
return isNominating || allAccounts.includes(nominator);
|
||||
}, allAccounts.includes(key));
|
||||
|
||||
totalStaked = totalStaked.add(bondTotal);
|
||||
|
||||
return {
|
||||
accountId,
|
||||
bondOther: bondTotal.sub(bondOwn),
|
||||
bondOwn,
|
||||
bondShare: 0,
|
||||
bondTotal,
|
||||
commissionPer: (((prefs as ValidatorPrefs).commission?.unwrap() || new BN(0)).toNumber() / 10_000_000),
|
||||
isCommission: !!(prefs as ValidatorPrefs).commission,
|
||||
isFavorite: favorites.includes(key),
|
||||
isNominating,
|
||||
key,
|
||||
numNominators: exposure.others.length,
|
||||
rankBondOther: 0,
|
||||
rankBondOwn: 0,
|
||||
rankBondTotal: 0,
|
||||
rankComm: 0,
|
||||
rankOverall: 0,
|
||||
rankPayment: 0,
|
||||
rankReward: 0,
|
||||
rewardPayout,
|
||||
rewardSplit,
|
||||
validatorPayment
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
return { nominators, totalStaked, validators };
|
||||
}
|
||||
|
||||
export default function useSortedTargets (amount?: BN): SortedTargets {
|
||||
const { api } = useApi();
|
||||
const { allAccounts } = useAccounts();
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const electedInfo = useCall<DeriveStakingElected>(api.derive.staking.electedInfo, []);
|
||||
const lastEra = useCall<BN>(api.derive.session.indexes, [], {
|
||||
transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gtn(0) ? activeEra.subn(1) : new BN(0)
|
||||
});
|
||||
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], {
|
||||
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
|
||||
});
|
||||
const [state, setState] = useState<SortedTargets>({ toggleFavorite });
|
||||
|
||||
useEffect((): void => {
|
||||
electedInfo && setState(({ toggleFavorite }) => ({
|
||||
...extractInfo(allAccounts, amount, electedInfo, favorites, lastReward),
|
||||
lastReward,
|
||||
toggleFavorite
|
||||
}));
|
||||
}, [allAccounts, amount, electedInfo, favorites, lastReward]);
|
||||
|
||||
return state;
|
||||
}
|
||||
@ -120,9 +120,6 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
<Route path={[`${basePath}/query/:value`, `${basePath}/query`]}>
|
||||
<Query />
|
||||
</Route>
|
||||
<Route path={`${basePath}/targets`}>
|
||||
<Targets ownStashes={ownStashes} />
|
||||
</Route>
|
||||
<Route path={`${basePath}/waiting`}>
|
||||
<Overview
|
||||
className={`${basePath}/waiting` === pathname ? '' : 'staking--hidden'}
|
||||
@ -140,6 +137,10 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
|
||||
ownStashes={ownStashes}
|
||||
validators={validators}
|
||||
/>
|
||||
<Targets
|
||||
className={pathname === `${basePath}/targets` ? '' : 'staking--hidden'}
|
||||
ownStashes={ownStashes}
|
||||
/>
|
||||
<Overview
|
||||
className={basePath === pathname ? '' : 'staking--hidden'}
|
||||
hasQueries={hasQueries}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user