Staking render opt. useEffect/useState -> useMemo (#3408)
* Staking render opt. useEffect/useState -> useMemo * Targets opt * Simplify * Couple more * Targets useEffect * Update packages/page-staking/src/Actions/Account/BondExtra.tsx * Additional * Cleanup * Unused state * Update packages/page-staking/src/index.tsx * Update packages/page-staking/src/index.tsx
This commit is contained in:
parent
3ccb6ae98a
commit
8c07ccc83f
@ -6,7 +6,8 @@ import { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/ty
|
||||
import { AmountValidateState } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { InputAddress, InputBalance, Modal, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BalanceFree } from '@polkadot/react-query';
|
||||
@ -22,32 +23,35 @@ interface Props {
|
||||
stashId: string;
|
||||
}
|
||||
|
||||
function calcBalance (api: ApiPromise, stakingInfo?: DeriveStakingAccount, stashBalance?: DeriveBalancesAll): BN | null {
|
||||
if (stakingInfo && stakingInfo.stakingLedger && stashBalance) {
|
||||
const sumUnlocking = (stakingInfo.unlocking || []).reduce((acc, { value }) => acc.iadd(value), new BN(0));
|
||||
const redeemable = stakingInfo.redeemable || BN_ZERO;
|
||||
const available = stashBalance.freeBalance.sub(stakingInfo.stakingLedger.active.unwrap()).sub(sumUnlocking).sub(redeemable);
|
||||
|
||||
return available.gt(api.consts.balances.existentialDeposit)
|
||||
? available.sub(api.consts.balances.existentialDeposit)
|
||||
: BN_ZERO;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function BondExtra ({ controllerId, onClose, stakingInfo, stashId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [amountError, setAmountError] = useState<AmountValidateState | null>(null);
|
||||
const [maxAdditional, setMaxAdditional] = useState<BN | undefined>();
|
||||
const [maxBalance] = useState<BN | undefined>();
|
||||
const [startBalance, setStartBalance] = useState<BN | null>(null);
|
||||
const stashBalance = useCall<DeriveBalancesAll>(api.derive.balances.all, [stashId]);
|
||||
const currentAmount = useMemo(
|
||||
() => stakingInfo && stakingInfo.stakingLedger?.active.unwrap(),
|
||||
[stakingInfo]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
if (stakingInfo && stakingInfo.stakingLedger && stashBalance) {
|
||||
const sumUnlocking = stakingInfo.unlocking?.reduce((acc, { value }) => acc.add(value), BN_ZERO) || BN_ZERO;
|
||||
const redeemable = stakingInfo.redeemable || BN_ZERO;
|
||||
const available = stashBalance.freeBalance.sub(stakingInfo.stakingLedger.active.unwrap()).sub(sumUnlocking).sub(redeemable);
|
||||
|
||||
setStartBalance(
|
||||
available.gt(api.consts.balances.existentialDeposit)
|
||||
? available.sub(api.consts.balances.existentialDeposit)
|
||||
: BN_ZERO
|
||||
);
|
||||
}
|
||||
}, [api, stakingInfo, stashBalance]);
|
||||
const startBalance = useMemo(
|
||||
() => calcBalance(api, stakingInfo, stashBalance),
|
||||
[api, stakingInfo, stashBalance]
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -83,7 +87,6 @@ function BondExtra ({ controllerId, onClose, stakingInfo, stashId }: Props): Rea
|
||||
params={stashId}
|
||||
/>
|
||||
}
|
||||
maxValue={maxBalance}
|
||||
onChange={setMaxAdditional}
|
||||
/>
|
||||
<ValidateAmount
|
||||
|
||||
@ -9,7 +9,7 @@ import { SortedTargets } from '../../types';
|
||||
import { Slash } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useContext, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressInfo, AddressMini, AddressSmall, Badge, Button, Menu, Popup, StakingBonded, StakingRedeemable, StakingUnbonding, StatusContext, TxButton } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useToggle } from '@polkadot/react-hooks';
|
||||
@ -38,6 +38,17 @@ interface Props {
|
||||
validators?: string[];
|
||||
}
|
||||
|
||||
function extractSlashes (stashId: string, allSlashes: [BN, UnappliedSlash[]][] = []): Slash[] {
|
||||
return allSlashes
|
||||
.map(([era, all]) => ({
|
||||
era,
|
||||
slashes: all.filter(({ others, validator }) =>
|
||||
validator.eq(stashId) || others.some(([nominatorId]) => nominatorId.eq(stashId))
|
||||
)
|
||||
}))
|
||||
.filter(({ slashes }) => slashes.length);
|
||||
}
|
||||
|
||||
function Account ({ allSlashes, className = '', info: { controllerId, destination, destinationId, hexSessionIdNext, hexSessionIdQueue, isLoading, isOwnController, isOwnStash, isStashNominating, isStashValidating, nominating, sessionIds, stakingLedger, stashId }, isDisabled, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
@ -59,27 +70,11 @@ function Account ({ allSlashes, className = '', info: { controllerId, destinatio
|
||||
const [isSettingsOpen, toggleSettings] = useToggle();
|
||||
const [isUnbondOpen, toggleUnbond] = useToggle();
|
||||
const [isValidateOpen, toggleValidate] = useToggle();
|
||||
const [hasBonded, setHasBonded] = useState(false);
|
||||
const [slashes, setSlashes] = useState<Slash[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
stakingAccount?.stakingLedger && setHasBonded(
|
||||
!stakingAccount.stakingLedger.active.isEmpty
|
||||
);
|
||||
}, [stakingAccount]);
|
||||
|
||||
useEffect((): void => {
|
||||
allSlashes && setSlashes(
|
||||
allSlashes
|
||||
.map(([era, all]) => ({
|
||||
era,
|
||||
slashes: all.filter(({ others, validator }) =>
|
||||
validator.eq(stashId) || others.some(([nominatorId]) => nominatorId.eq(stashId))
|
||||
)
|
||||
}))
|
||||
.filter(({ slashes }) => slashes.length)
|
||||
);
|
||||
}, [allSlashes, stashId]);
|
||||
const slashes = useMemo(
|
||||
() => extractSlashes(stashId, allSlashes),
|
||||
[allSlashes, stashId]
|
||||
);
|
||||
|
||||
const withdrawFunds = useCallback(
|
||||
() => {
|
||||
@ -95,6 +90,8 @@ function Account ({ allSlashes, className = '', info: { controllerId, destinatio
|
||||
[api, controllerId, queueExtrinsic, spanCount]
|
||||
);
|
||||
|
||||
const hasBonded = !!stakingAccount?.stakingLedger && !stakingAccount.stakingLedger.active.isEmpty;
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td className='badge together'>
|
||||
|
||||
@ -6,7 +6,7 @@ import { StakerState } from '@polkadot/react-hooks/types';
|
||||
import { SortedTargets } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Button, Table } from '@polkadot/react-components';
|
||||
import { useAvailableSlashes } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
@ -37,7 +37,11 @@ function sortStashes (a: StakerState, b: StakerState): number {
|
||||
return (a.isStashValidating ? 1 : (a.isStashNominating ? 5 : 99)) - (b.isStashValidating ? 1 : (b.isStashNominating ? 5 : 99));
|
||||
}
|
||||
|
||||
function extractState (ownStashes: StakerState[]): State {
|
||||
function extractState (ownStashes?: StakerState[]): State {
|
||||
if (!ownStashes) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
bondedTotal: ownStashes.reduce((total: BN, { stakingLedger }) =>
|
||||
stakingLedger
|
||||
@ -51,11 +55,11 @@ function extractState (ownStashes: StakerState[]): State {
|
||||
function Actions ({ className = '', isInElection, ownStashes, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const allSlashes = useAvailableSlashes();
|
||||
const [{ bondedTotal, foundStashes }, setState] = useState<State>({});
|
||||
|
||||
useEffect((): void => {
|
||||
ownStashes && setState(extractState(ownStashes));
|
||||
}, [ownStashes]);
|
||||
const { bondedTotal, foundStashes } = useMemo(
|
||||
() => extractState(ownStashes),
|
||||
[ownStashes]
|
||||
);
|
||||
|
||||
const header = useMemo(() => [
|
||||
[t('stashes'), 'start', 2],
|
||||
|
||||
@ -5,22 +5,18 @@
|
||||
import { DeriveSessionInfo } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BN_ONE } from '@polkadot/util';
|
||||
|
||||
export default function useUnbondDuration (): BN | undefined {
|
||||
const { api } = useApi();
|
||||
const sessionInfo = useCall<DeriveSessionInfo>(api.derive.session.info, []);
|
||||
const [duration, setDuration] = useState<BN | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
// We check sessionLength, for aura this is not properly calculated (not exposed),
|
||||
// so the actual real block value would be invalid here
|
||||
sessionInfo && sessionInfo.sessionLength.gt(BN_ONE) && setDuration(
|
||||
sessionInfo.eraLength.mul(api.consts.staking.bondingDuration)
|
||||
);
|
||||
}, [api, sessionInfo]);
|
||||
|
||||
return duration;
|
||||
return useMemo(
|
||||
() => (sessionInfo && sessionInfo.sessionLength.gt(BN_ONE))
|
||||
? sessionInfo.eraLength.mul(api.consts.staking.bondingDuration)
|
||||
: undefined,
|
||||
[api, sessionInfo]
|
||||
);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ interface Props {
|
||||
|
||||
function Favorite ({ address, className, isFavorite, toggleFavorite }: Props): React.ReactElement<Props> {
|
||||
const _onFavorite = useCallback(
|
||||
(): void => toggleFavorite(address),
|
||||
() => toggleFavorite(address),
|
||||
[address, toggleFavorite]
|
||||
);
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { EraIndex, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { AddressMini, Expander, Spinner } from '@polkadot/react-components';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
@ -20,24 +20,26 @@ interface Chilled {
|
||||
chilled: string[];
|
||||
}
|
||||
|
||||
function extractChilled (nominators: [string, EraIndex, number][] = [], slashingSpans?: SlashingSpans | null): Chilled {
|
||||
const chilled = slashingSpans
|
||||
? nominators
|
||||
.filter(([, submittedIn]) => !slashingSpans.lastNonzeroSlash.isZero() && slashingSpans.lastNonzeroSlash.gte(submittedIn))
|
||||
.map(([who]) => who)
|
||||
: [];
|
||||
const active = nominators
|
||||
.filter(([who]) => !chilled.includes(who))
|
||||
.map(([who]) => who);
|
||||
|
||||
return { active, chilled };
|
||||
}
|
||||
|
||||
function NominatedBy ({ nominators, slashingSpans }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ active, chilled }, setChilled] = useState<Chilled>({ active: [], chilled: [] });
|
||||
|
||||
useEffect((): void => {
|
||||
if (nominators) {
|
||||
const chilled = slashingSpans
|
||||
? nominators
|
||||
.filter(([, submittedIn]) => !slashingSpans.lastNonzeroSlash.isZero() && slashingSpans.lastNonzeroSlash.gte(submittedIn))
|
||||
.map(([who]) => who)
|
||||
: [];
|
||||
const active = nominators
|
||||
.filter(([who]) => !chilled.includes(who))
|
||||
.map(([who]) => who);
|
||||
|
||||
setChilled({ active, chilled });
|
||||
}
|
||||
}, [nominators, slashingSpans]);
|
||||
const { active, chilled } = useMemo(
|
||||
() => extractChilled(nominators, slashingSpans),
|
||||
[nominators, slashingSpans]
|
||||
);
|
||||
|
||||
return (
|
||||
<td className='start all'>
|
||||
|
||||
@ -6,7 +6,7 @@ import { Balance, EraIndex, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
import { DeriveAccountInfo, DeriveStakingQuery } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import { AddressSmall, Icon } from '@polkadot/react-components';
|
||||
import { checkVisibility } from '@polkadot/react-components/util';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
@ -78,18 +78,16 @@ function Address ({ address, className = '', filterName, hasQueries, isElected,
|
||||
const slashingSpans = useCall<SlashingSpans | null>(!isMain && api.query.staking.slashingSpans, [address], {
|
||||
transform: (opt: Option<SlashingSpans>) => opt.unwrapOr(null)
|
||||
});
|
||||
const [{ commission, nominators, stakeOther, stakeOwn }, setStakingState] = useState<StakingState>({ nominators: [] });
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
|
||||
useEffect((): void => {
|
||||
stakingInfo && setStakingState(expandInfo(stakingInfo));
|
||||
}, [stakingInfo]);
|
||||
const { commission, nominators, stakeOther, stakeOwn } = useMemo(
|
||||
() => stakingInfo ? expandInfo(stakingInfo) : { nominators: [] },
|
||||
[stakingInfo]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
accountInfo && setIsVisible(
|
||||
checkVisibility(api, address, accountInfo, filterName, withIdentity)
|
||||
);
|
||||
}, [api, accountInfo, address, filterName, withIdentity]);
|
||||
const isVisible = useMemo(
|
||||
() => accountInfo ? checkVisibility(api, address, accountInfo, filterName, withIdentity) : true,
|
||||
[api, accountInfo, address, filterName, withIdentity]
|
||||
);
|
||||
|
||||
const _onQueryStats = useCallback(
|
||||
(): void => {
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveHeartbeats, DeriveStakingOverview } from '@polkadot/api-derive/ty
|
||||
import { AccountId, EraIndex, Nominations } from '@polkadot/types/interfaces';
|
||||
import { Authors } from '@polkadot/react-query/BlockAuthors';
|
||||
|
||||
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
import { useApi, useCall, useLoadingDelay } from '@polkadot/react-hooks';
|
||||
import { BlockAuthorsContext } from '@polkadot/react-query';
|
||||
@ -97,25 +97,21 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
|
||||
const { byAuthor, eraPoints } = useContext(isIntentions ? EmptyAuthorsContext : BlockAuthorsContext);
|
||||
const recentlyOnline = useCall<DeriveHeartbeats>(!isIntentions && api.derive.imOnline?.receivedHeartbeats, []);
|
||||
const nominators = useCall<[StorageKey, Option<Nominations>][]>(isIntentions && api.query.staking.nominators.entries as any, []);
|
||||
const [{ elected, validators, waiting }, setFiltered] = useState<Filtered>({});
|
||||
const [nameFilter, setNameFilter] = useState<string>('');
|
||||
const [nominatedBy, setNominatedBy] = useState<Record<string, [string, EraIndex, number][]> | null>();
|
||||
const [withIdentity, setWithIdentity] = useState(false);
|
||||
|
||||
// we have a very large list, so we use a loading delay
|
||||
const isLoading = useLoadingDelay();
|
||||
|
||||
useEffect((): void => {
|
||||
stakingOverview && setFiltered(
|
||||
getFiltered(stakingOverview, favorites, next)
|
||||
);
|
||||
}, [favorites, next, stakingOverview]);
|
||||
const { elected, validators, waiting } = useMemo(
|
||||
() => stakingOverview ? getFiltered(stakingOverview, favorites, next) : {},
|
||||
[favorites, next, stakingOverview]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
nominators && setNominatedBy(
|
||||
extractNominators(nominators)
|
||||
);
|
||||
}, [nominators]);
|
||||
const nominatedBy = useMemo(
|
||||
() => nominators ? extractNominators(nominators) : null,
|
||||
[nominators]
|
||||
);
|
||||
|
||||
const headerWaiting = useMemo(() => [
|
||||
[t('intentions'), 'start', 2],
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { PayoutValidator } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { AddressMini, AddressSmall, Expander } from '@polkadot/react-components';
|
||||
import { BlockToTime, FormatBalance } from '@polkadot/react-query';
|
||||
|
||||
@ -27,32 +27,33 @@ interface State {
|
||||
oldestEra?: BN;
|
||||
}
|
||||
|
||||
function extractState (payout: PayoutValidator): State {
|
||||
const eraStr = createErasString(payout.eras.map(({ era }) => era));
|
||||
const nominators = payout.eras.reduce((nominators: Record<string, BN>, { stashes }): Record<string, BN> => {
|
||||
Object.entries(stashes).forEach(([stashId, value]): void => {
|
||||
if (nominators[stashId]) {
|
||||
nominators[stashId] = nominators[stashId].add(value);
|
||||
} else {
|
||||
nominators[stashId] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return nominators;
|
||||
}, {});
|
||||
|
||||
return { eraStr, nominators, numNominators: Object.keys(nominators).length, oldestEra: payout.eras[0]?.era };
|
||||
}
|
||||
|
||||
function Validator ({ className = '', isDisabled, payout }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [{ eraStr, nominators, numNominators, oldestEra }, setState] = useState<State>({
|
||||
eraStr: '',
|
||||
nominators: {},
|
||||
numNominators: 0
|
||||
});
|
||||
|
||||
const { eraStr, nominators, numNominators, oldestEra } = useMemo(
|
||||
() => extractState(payout),
|
||||
[payout]
|
||||
);
|
||||
|
||||
const eraBlocks = useEraBlocks(oldestEra);
|
||||
|
||||
useEffect((): void => {
|
||||
const eraStr = createErasString(payout.eras.map(({ era }) => era));
|
||||
const nominators = payout.eras.reduce((nominators: Record<string, BN>, { stashes }): Record<string, BN> => {
|
||||
Object.entries(stashes).forEach(([stashId, value]): void => {
|
||||
if (nominators[stashId]) {
|
||||
nominators[stashId] = nominators[stashId].add(value);
|
||||
} else {
|
||||
nominators[stashId] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return nominators;
|
||||
}, {});
|
||||
|
||||
setState({ eraStr, nominators, numNominators: Object.keys(nominators).length, oldestEra: payout.eras[0]?.era });
|
||||
}, [payout]);
|
||||
|
||||
return (
|
||||
<tr className={className}>
|
||||
<td
|
||||
|
||||
@ -6,7 +6,7 @@ import { Forcing } from '@polkadot/types/interfaces';
|
||||
import { DeriveSessionProgress } from '@polkadot/api-derive/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BN_ONE } from '@polkadot/util';
|
||||
|
||||
@ -15,11 +15,10 @@ export default function useEraBlocks (era?: BN): BN | undefined {
|
||||
const depth = useCall<BN>(api.query.staking.historyDepth, []);
|
||||
const progress = useCall<DeriveSessionProgress>(api.derive.session.progress, []);
|
||||
const forcing = useCall<Forcing>(api.query.staking.forceEra, []);
|
||||
const [duration, setDuration] = useState<BN | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
depth && era && forcing && progress && progress.sessionLength.gt(BN_ONE) && setDuration(
|
||||
(
|
||||
return useMemo(
|
||||
() => (depth && era && forcing && progress && progress.sessionLength.gt(BN_ONE))
|
||||
? (
|
||||
forcing.isForceAlways
|
||||
? progress.sessionLength
|
||||
: progress.eraLength
|
||||
@ -33,8 +32,7 @@ export default function useEraBlocks (era?: BN): BN | undefined {
|
||||
? progress.sessionProgress
|
||||
: progress.eraProgress
|
||||
)
|
||||
);
|
||||
}, [api, depth, era, forcing, progress]);
|
||||
|
||||
return duration;
|
||||
: undefined,
|
||||
[depth, era, forcing, progress]
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,23 +5,21 @@
|
||||
import { EraIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { Option } from '@polkadot/types';
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
export default function useStakerPayouts (): BN {
|
||||
const { api } = useApi();
|
||||
const [stakerPayoutAfter, setState] = useState<BN>(
|
||||
isFunction(api.tx.staking.payoutStakers)
|
||||
? new BN(0)
|
||||
: new BN(1_000_000_000)
|
||||
);
|
||||
const migrateEraOpt = useCall<Option<EraIndex>>(api.query.staking?.migrateEra, []);
|
||||
|
||||
useEffect((): void => {
|
||||
migrateEraOpt && migrateEraOpt.isSome && setState(migrateEraOpt.unwrap());
|
||||
}, [migrateEraOpt]);
|
||||
|
||||
return stakerPayoutAfter;
|
||||
return useMemo(
|
||||
() => (migrateEraOpt && migrateEraOpt.isSome && migrateEraOpt.unwrap()) || (
|
||||
isFunction(api.tx.staking.payoutStakers)
|
||||
? new BN(0)
|
||||
: new BN(1_000_000_000)
|
||||
),
|
||||
[api, migrateEraOpt]
|
||||
);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { DeriveStakerPoints } from '@polkadot/api-derive/types';
|
||||
import { ChartInfo, LineDataEntry, Props } from './types';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Chart, Spinner } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
|
||||
@ -41,19 +41,20 @@ function extractPoints (points: DeriveStakerPoints[]): ChartInfo {
|
||||
function ChartPoints ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [{ chart, labels }, setChart] = useState<ChartInfo>({ chart: [], labels: [] });
|
||||
const stakerPoints = useCall<DeriveStakerPoints[]>(api.derive.staking.stakerPoints, [validatorId, true]);
|
||||
|
||||
const { chart, labels } = useMemo(
|
||||
() => stakerPoints
|
||||
? extractPoints(stakerPoints)
|
||||
: { chart: [], labels: [] },
|
||||
[stakerPoints]
|
||||
);
|
||||
|
||||
const legends = useMemo(() => [
|
||||
t<string>('points'),
|
||||
t<string>('average')
|
||||
], [t]);
|
||||
|
||||
useEffect((): void => {
|
||||
stakerPoints && setChart(
|
||||
extractPoints(stakerPoints)
|
||||
);
|
||||
}, [stakerPoints]);
|
||||
|
||||
return (
|
||||
<div className='staking--Chart'>
|
||||
<h1>{t<string>('era points')}</h1>
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveEraRewards, DeriveOwnSlashes, DeriveStakerPoints } from '@polkado
|
||||
import { ChartInfo, LineDataEntry, Props } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Chart, Spinner } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { formatBalance } from '@polkadot/util';
|
||||
@ -55,26 +55,28 @@ function extractRewards (erasRewards: DeriveEraRewards[], ownSlashes: DeriveOwnS
|
||||
function ChartRewards ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [{ chart, labels }, setChart] = useState<ChartInfo>({ chart: [], labels: [] });
|
||||
const ownSlashes = useCall<DeriveOwnSlashes[]>(api.derive.staking.ownSlashes, [validatorId, true]);
|
||||
const erasRewards = useCall<DeriveEraRewards[]>(api.derive.staking.erasRewards, []);
|
||||
const stakerPoints = useCall<DeriveStakerPoints[]>(api.derive.staking.stakerPoints, [validatorId, true]);
|
||||
|
||||
const { currency, divisor } = useMemo((): { currency: string; divisor: BN } => ({
|
||||
currency: formatBalance.getDefaults().unit,
|
||||
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
|
||||
}), []);
|
||||
|
||||
const { chart, labels } = useMemo(
|
||||
() => (erasRewards && ownSlashes && stakerPoints)
|
||||
? extractRewards(erasRewards, ownSlashes, stakerPoints, divisor)
|
||||
: { chart: [], labels: [] },
|
||||
[divisor, erasRewards, ownSlashes, stakerPoints]
|
||||
);
|
||||
|
||||
const legends = useMemo(() => [
|
||||
t<string>('{{currency}} slashed', { replace: { currency } }),
|
||||
t<string>('{{currency}} rewards', { replace: { currency } }),
|
||||
t<string>('{{currency}} average', { replace: { currency } })
|
||||
], [currency, t]);
|
||||
|
||||
useEffect((): void => {
|
||||
erasRewards && ownSlashes && stakerPoints && setChart(
|
||||
extractRewards(erasRewards, ownSlashes, stakerPoints, divisor)
|
||||
);
|
||||
}, [divisor, erasRewards, ownSlashes, stakerPoints]);
|
||||
|
||||
return (
|
||||
<div className='staking--Chart'>
|
||||
<h1>{t<string>('rewards & slashes')}</h1>
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveOwnExposure } from '@polkadot/api-derive/types';
|
||||
import { ChartInfo, LineDataEntry, Props } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Chart, Spinner } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { formatBalance } from '@polkadot/util';
|
||||
@ -49,24 +49,26 @@ function extractStake (exposures: DeriveOwnExposure[], divisor: BN): ChartInfo {
|
||||
function ChartStake ({ validatorId }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [{ chart, labels }, setChart] = useState<ChartInfo>({ chart: [], labels: [] });
|
||||
const ownExposures = useCall<DeriveOwnExposure[]>(api.derive.staking.ownExposures, [validatorId, true]);
|
||||
|
||||
const { currency, divisor } = useMemo((): { currency: string; divisor: BN } => ({
|
||||
currency: formatBalance.getDefaults().unit,
|
||||
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
|
||||
}), []);
|
||||
|
||||
const { chart, labels } = useMemo(
|
||||
() => ownExposures
|
||||
? extractStake(ownExposures, divisor)
|
||||
: { chart: [], labels: [] },
|
||||
[divisor, ownExposures]
|
||||
);
|
||||
|
||||
const legends = useMemo(() => [
|
||||
t<string>('{{currency}} clipped', { replace: { currency } }),
|
||||
t<string>('{{currency}} total', { replace: { currency } }),
|
||||
t<string>('{{currency}} average', { replace: { currency } })
|
||||
], [currency, t]);
|
||||
|
||||
useEffect((): void => {
|
||||
ownExposures && setChart(
|
||||
extractStake(ownExposures, divisor)
|
||||
);
|
||||
}, [divisor, ownExposures]);
|
||||
|
||||
return (
|
||||
<div className='staking--Chart'>
|
||||
<h1>{t<string>('elected stake')}</h1>
|
||||
|
||||
@ -6,7 +6,7 @@ import { DeriveSessionProgress } from '@polkadot/api-derive/types';
|
||||
import { SlashEra } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
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';
|
||||
@ -18,24 +18,20 @@ interface Props {
|
||||
slash: SlashEra;
|
||||
}
|
||||
|
||||
function calcBlocks (slashDeferDuration: BN, era: BN, { activeEra, eraLength, eraProgress }: DeriveSessionProgress): [BN, BN] {
|
||||
return [
|
||||
activeEra.sub(era).subn(1).mul(eraLength).add(eraProgress),
|
||||
slashDeferDuration.mul(eraLength)
|
||||
];
|
||||
}
|
||||
|
||||
function Header ({ slash: { era, nominators, reporters, total, validators } }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const sessionInfo = useCall<DeriveSessionProgress>(api.derive.session?.progress, []);
|
||||
const [[blockProgress, blockEnd], setBlocks] = useState([new BN(0), new BN(0)]);
|
||||
|
||||
useEffect((): void => {
|
||||
sessionInfo && setBlocks(
|
||||
calcBlocks(api.consts.staking.slashDeferDuration, era, sessionInfo)
|
||||
);
|
||||
}, [api, era, sessionInfo]);
|
||||
const [blockProgress, blockEnd] = useMemo(
|
||||
() => sessionInfo
|
||||
? [
|
||||
sessionInfo.activeEra.sub(era).subn(1).mul(sessionInfo.eraLength).add(sessionInfo.eraProgress),
|
||||
api.consts.staking.slashDeferDuration.mul(sessionInfo.eraLength)
|
||||
]
|
||||
: [new BN(0), new BN(0)],
|
||||
[api, era, sessionInfo]
|
||||
);
|
||||
|
||||
return (
|
||||
<tr>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressMini, Button, InputAddress, Modal, Static, TxButton } from '@polkadot/react-components';
|
||||
import { useToggle } from '@polkadot/react-hooks';
|
||||
@ -26,14 +26,12 @@ interface IdState {
|
||||
function Nominate ({ className = '', isDisabled, ownNominators, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const [ids, setIds] = useState<IdState | null>(null);
|
||||
const [filter, setFilter] = useState<string[]>([]);
|
||||
const [isOpen, toggleOpen] = useToggle();
|
||||
|
||||
useEffect((): void => {
|
||||
ownNominators && setFilter(
|
||||
ownNominators.map(({ stashId }) => stashId)
|
||||
);
|
||||
}, [ownNominators]);
|
||||
const filter = useMemo(
|
||||
() => (ownNominators || []).map(({ stashId }) => stashId),
|
||||
[ownNominators]
|
||||
);
|
||||
|
||||
const _onChangeStash = useCallback(
|
||||
(accountId?: string | null): void => {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import { Balance } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { SummaryBox, CardSummary } from '@polkadot/react-components';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
@ -19,25 +19,21 @@ interface Props {
|
||||
totalStaked?: BN;
|
||||
}
|
||||
|
||||
interface Progress {
|
||||
hideValue: true;
|
||||
total: BN;
|
||||
value: BN;
|
||||
}
|
||||
|
||||
function Summary ({ lastReward, numNominators, numValidators, totalStaked }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const totalIssuance = useCall<Balance>(api.query.balances?.totalIssuance, []);
|
||||
const [progressStake, setProgressStake] = useState<Progress | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
totalIssuance && totalStaked && totalStaked.gtn(0) && setProgressStake({
|
||||
hideValue: true,
|
||||
total: totalIssuance,
|
||||
value: totalStaked
|
||||
});
|
||||
}, [totalIssuance, totalStaked]);
|
||||
const progressStake = useMemo(
|
||||
() => totalIssuance && totalStaked && totalStaked.gtn(0)
|
||||
? {
|
||||
hideValue: true,
|
||||
total: totalIssuance,
|
||||
value: totalStaked
|
||||
}
|
||||
: undefined,
|
||||
[totalIssuance, totalStaked]
|
||||
);
|
||||
|
||||
return (
|
||||
<SummaryBox>
|
||||
|
||||
@ -7,7 +7,7 @@ import { UnappliedSlash } from '@polkadot/types/interfaces';
|
||||
import { ValidatorInfo } from '../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||
import { ApiPromise } from '@polkadot/api';
|
||||
import { AddressSmall, Badge, Checkbox, Icon } from '@polkadot/react-components';
|
||||
import { checkVisibility } from '@polkadot/react-components/util';
|
||||
@ -33,11 +33,6 @@ interface Props {
|
||||
withIdentity: boolean;
|
||||
}
|
||||
|
||||
interface Slash {
|
||||
era: BN;
|
||||
slashes: UnappliedSlash[];
|
||||
}
|
||||
|
||||
function checkIdentity (api: ApiPromise, accountInfo: DeriveAccountInfo): boolean {
|
||||
let hasIdentity = false;
|
||||
|
||||
@ -62,26 +57,24 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, [info.accountId]);
|
||||
const [isVisible, setVisibility] = useState(true);
|
||||
const [slashes, setSlashes] = useState<Slash[]>([]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (accountInfo) {
|
||||
info.hasIdentity = checkIdentity(api, accountInfo);
|
||||
setVisibility(checkVisibility(api, info.key, accountInfo, filterName, withIdentity));
|
||||
}
|
||||
}, [accountInfo, api, filterName, info, withIdentity]);
|
||||
}, [api, accountInfo, info]);
|
||||
|
||||
useEffect((): void => {
|
||||
allSlashes && setSlashes(
|
||||
allSlashes
|
||||
.map(([era, slashes]) => ({
|
||||
era,
|
||||
slashes: slashes.filter(({ validator }) => validator.eq(info.accountId))
|
||||
}))
|
||||
.filter(({ slashes }) => slashes.length)
|
||||
);
|
||||
}, [allSlashes, info]);
|
||||
const isVisible = useMemo(
|
||||
() => accountInfo ? checkVisibility(api, info.key, accountInfo, filterName, withIdentity) : true,
|
||||
[accountInfo, api, filterName, info, withIdentity]
|
||||
);
|
||||
|
||||
const slashes = useMemo(
|
||||
() => (allSlashes || [])
|
||||
.map(([era, all]) => ({ era, slashes: all.filter(({ validator }) => validator.eq(info.accountId)) }))
|
||||
.filter(({ slashes }) => slashes.length),
|
||||
[allSlashes, info]
|
||||
);
|
||||
|
||||
const _onQueryStats = useCallback(
|
||||
(): void => {
|
||||
|
||||
@ -34,6 +34,11 @@ interface SortState {
|
||||
sortFromMax: boolean;
|
||||
}
|
||||
|
||||
const CLASSES: Record<string, string> = {
|
||||
rankBondOther: 'ui--media-1600',
|
||||
rankNumNominators: 'ui--media-1200'
|
||||
};
|
||||
|
||||
function sort (sortBy: TargetSortBy, sortFromMax: boolean, validators: ValidatorInfo[]): number[] {
|
||||
return [...Array(validators.length).keys()]
|
||||
.sort((a, b) =>
|
||||
@ -48,34 +53,40 @@ function sort (sortBy: TargetSortBy, sortFromMax: boolean, validators: Validator
|
||||
);
|
||||
}
|
||||
|
||||
function extractNominees (ownNominators: StakerState[] = []): string[] {
|
||||
const myNominees: string[] = [];
|
||||
|
||||
ownNominators.forEach(({ nominating = [] }: StakerState): void => {
|
||||
nominating.forEach((nominee: string): void => {
|
||||
!myNominees.includes(nominee) && myNominees.push(nominee);
|
||||
});
|
||||
});
|
||||
|
||||
return myNominees;
|
||||
}
|
||||
|
||||
function Targets ({ className = '', isInElection, ownStashes, targets: { calcWith, lastReward, nominators, setCalcWith, totalStaked, validators }, toggleFavorite }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const allSlashes = useAvailableSlashes();
|
||||
const ownNominators = useOwnNominators(ownStashes);
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
const [sorted, setSorted] = useState<number[] | undefined>();
|
||||
const [myNominees, setMyNominees] = useState<string[]>([]);
|
||||
const [nameFilter, setNameFilter] = useState<string>('');
|
||||
const [withElected, setWithElected] = useState(false);
|
||||
const [withIdentity, setWithIdentity] = useState(false);
|
||||
const [{ sortBy, sortFromMax }, setSortBy] = useState<SortState>({ sortBy: 'rankOverall', sortFromMax: true });
|
||||
const [sorted, setSorted] = useState<number[] | undefined>();
|
||||
|
||||
// We are using an effect here to get this async. Sorting will have a double-render, however it allows
|
||||
// the page to immediately display (with loading), whereas useMemo would have a laggy interface
|
||||
// (the same applies for changing the sort order, state here is more effective)
|
||||
useEffect((): void => {
|
||||
validators && setSorted(
|
||||
sort(sortBy, sortFromMax, validators)
|
||||
);
|
||||
validators && setSorted(sort(sortBy, sortFromMax, validators));
|
||||
}, [sortBy, sortFromMax, validators]);
|
||||
|
||||
useEffect((): void => {
|
||||
ownNominators && setMyNominees(
|
||||
ownNominators.reduce((myNominees: string[], nominator): string[] =>
|
||||
(nominator.nominating || []).reduce((myNominees: string[], nominee): string[] => {
|
||||
!myNominees.includes(nominee) && myNominees.push(nominee);
|
||||
|
||||
return myNominees;
|
||||
}, myNominees), [])
|
||||
);
|
||||
}, [ownNominators]);
|
||||
const myNominees = useMemo(
|
||||
() => extractNominees(ownNominators),
|
||||
[ownNominators]
|
||||
);
|
||||
|
||||
const _sort = useCallback(
|
||||
(newSortBy: TargetSortBy) => setSortBy(({ sortBy, sortFromMax }) => ({
|
||||
@ -121,24 +132,16 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { calcWit
|
||||
[t]
|
||||
);
|
||||
|
||||
const classes = useMemo(
|
||||
(): Record<string, string> => ({
|
||||
rankBondOther: 'ui--media-1600',
|
||||
rankNumNominators: 'ui--media-1200'
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
const header = useMemo(() => [
|
||||
[t('validators'), 'start', 3],
|
||||
...['rankNumNominators', 'rankComm', 'rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'].map((header) => [
|
||||
<>{labels[header]}<Icon icon={sortBy === header ? (sortFromMax ? 'chevron-down' : 'chevron-up') : 'minus'} /></>,
|
||||
`${sorted ? `isClickable ${sortBy === header ? 'ui--highlight--border' : ''} number` : 'number'} ${classes[header] || ''}`,
|
||||
`${sorted ? `isClickable ${sortBy === header ? 'ui--highlight--border' : ''} number` : 'number'} ${CLASSES[header] || ''}`,
|
||||
1,
|
||||
(): void => _sort(header as 'rankComm')
|
||||
]),
|
||||
[]
|
||||
], [_sort, classes, labels, sortBy, sorted, sortFromMax, t]);
|
||||
], [_sort, labels, sortBy, sorted, sortFromMax, t]);
|
||||
|
||||
const filter = useMemo(() => (
|
||||
sorted && (
|
||||
|
||||
@ -4,16 +4,11 @@
|
||||
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export default function useOwnNominators (ownStashes?: StakerState[]): StakerState[] | undefined {
|
||||
const [state, setState] = useState<StakerState[] | undefined>();
|
||||
|
||||
useEffect((): void => {
|
||||
ownStashes && setState(
|
||||
ownStashes.filter(({ isOwnController, isStashValidating }) => isOwnController && !isStashValidating)
|
||||
);
|
||||
}, [ownStashes]);
|
||||
|
||||
return state;
|
||||
return useMemo(
|
||||
() => ownStashes ? ownStashes.filter(({ isOwnController, isStashValidating }) => isOwnController && !isStashValidating) : undefined,
|
||||
[ownStashes]
|
||||
);
|
||||
}
|
||||
|
||||
@ -4,10 +4,9 @@
|
||||
|
||||
import { DeriveStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { AppProps as Props } from '@polkadot/react-components/types';
|
||||
import { StakerState } from '@polkadot/react-hooks/types';
|
||||
import { ElectionStatus } from '@polkadot/types/interfaces';
|
||||
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Route, Switch } from 'react-router';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
@ -28,18 +27,11 @@ import { STORE_FAVS_BASE } from './constants';
|
||||
import { useTranslation } from './translate';
|
||||
import useSortedTargets from './useSortedTargets';
|
||||
|
||||
interface Validators {
|
||||
next?: string[];
|
||||
validators?: string[];
|
||||
}
|
||||
|
||||
function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const { pathname } = useLocation();
|
||||
const [{ next }, setValidators] = useState<Validators>({});
|
||||
const [ownValidators, setOwnValidators] = useState<StakerState[]>([]);
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const allStashes = useStashIds();
|
||||
const ownStashes = useOwnStashInfos();
|
||||
@ -49,10 +41,24 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
const isInElection = useCall<boolean>(api.query.staking?.eraElectionStatus, [], {
|
||||
transform: (status: ElectionStatus) => status.isOpen
|
||||
});
|
||||
|
||||
const hasQueries = useMemo(
|
||||
() => hasAccounts && !!(api.query.imOnline?.authoredBlocks) && !!(api.query.staking.activeEra),
|
||||
[api, hasAccounts]
|
||||
);
|
||||
|
||||
const next = useMemo(
|
||||
() => (allStashes && stakingOverview)
|
||||
? allStashes.filter((address) => !stakingOverview.validators.includes(address as any))
|
||||
: undefined,
|
||||
[allStashes, stakingOverview]
|
||||
);
|
||||
|
||||
const ownValidators = useMemo(
|
||||
() => (ownStashes || []).filter(({ isStashValidating }) => isStashValidating),
|
||||
[ownStashes]
|
||||
);
|
||||
|
||||
const items = useMemo(() => [
|
||||
{
|
||||
isRoot: true,
|
||||
@ -88,30 +94,16 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
text: t<string>('Validator stats')
|
||||
}
|
||||
].filter((q): q is { name: string; text: string } => !!q), [api, slashes, t]);
|
||||
|
||||
const hiddenTabs = useMemo(
|
||||
(): string[] => {
|
||||
return !hasAccounts
|
||||
? ['actions', 'payouts', 'query']
|
||||
: !hasQueries
|
||||
? ['returns', 'query']
|
||||
: [];
|
||||
},
|
||||
(): string[] => !hasAccounts
|
||||
? ['actions', 'payouts', 'query']
|
||||
: !hasQueries
|
||||
? ['returns', 'query']
|
||||
: [],
|
||||
[hasAccounts, hasQueries]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
allStashes && stakingOverview && setValidators({
|
||||
next: allStashes.filter((address) => !stakingOverview.validators.includes(address as any)),
|
||||
validators: stakingOverview.validators.map((a) => a.toString())
|
||||
});
|
||||
}, [allStashes, stakingOverview]);
|
||||
|
||||
useEffect((): void => {
|
||||
ownStashes && setOwnValidators(
|
||||
ownStashes.filter(({ isStashValidating }) => isStashValidating)
|
||||
);
|
||||
}, [ownStashes]);
|
||||
|
||||
return (
|
||||
<main className={`staking--App ${className}`}>
|
||||
<HelpOverlay md={basicMd as string} />
|
||||
|
||||
Loading…
Reference in New Issue
Block a user