diff --git a/packages/page-staking/src/Overview/SummaryGeneral.tsx b/packages/page-staking/src/Overview/SummaryGeneral.tsx
index 2d2b92f25dc6..4c1e8c9d3109 100644
--- a/packages/page-staking/src/Overview/SummaryGeneral.tsx
+++ b/packages/page-staking/src/Overview/SummaryGeneral.tsx
@@ -85,11 +85,17 @@ totalStaked } }: Props) {
>{inflation.toFixed(1)}%
('returns')}>
- 0) && Number.isFinite(stakedReturn)}
- >
- {stakedReturn.toFixed(1)}%
-
+ {stakedReturn !== null
+ ? 0) &&
+ Number.isFinite(stakedReturn)}
+ // eslint-disable-next-line @typescript-eslint/indent
+ >
+ {stakedReturn.toFixed(1)}%
+
+ : '0%'}
diff --git a/packages/page-staking/src/Overview/SummaryNominators.tsx b/packages/page-staking/src/Overview/SummaryNominators.tsx
index e0b2398f3f0d..23904aeabd81 100644
--- a/packages/page-staking/src/Overview/SummaryNominators.tsx
+++ b/packages/page-staking/src/Overview/SummaryNominators.tsx
@@ -64,9 +64,11 @@ function SummaryNominators ({ targets: { maxNominatorsCount,
help={t('Number of nominators backing active validators in the current era.')}
label={t('active')}
>
-
- {formatNumber(nominatorActiveCount)}
-
+ {nominatorActiveCount !== null
+ ?
+ {formatNumber(nominatorActiveCount)}
+
+ : '-'}
@@ -91,9 +93,11 @@ function SummaryNominators ({ targets: { maxNominatorsCount,
help={t('Minimum threshold stake among active nominators.')}
label={t('min active thrs')}
>
-
- {nominatorMinActiveThreshold}
-
+ {nominatorMinActiveThreshold !== null
+ ?
+ {nominatorMinActiveThreshold}
+
+ : '-'}
diff --git a/packages/page-staking/src/Overview/SummaryValidators.tsx b/packages/page-staking/src/Overview/SummaryValidators.tsx
index b2517cca25e2..6e31baf1ea29 100644
--- a/packages/page-staking/src/Overview/SummaryValidators.tsx
+++ b/packages/page-staking/src/Overview/SummaryValidators.tsx
@@ -61,9 +61,11 @@ function SummaryValidators ({ targets:
help={t('Count of active validators.')}
label={t('active')}
>
-
- {validatorActiveCount}
-
+ {validatorActiveCount !== null
+ ?
+ {validatorActiveCount}
+
+ : '-'}
@@ -88,9 +90,11 @@ function SummaryValidators ({ targets:
help={t('Minimum threshold stake among active validators.')}
label={t('min active thrs')}
>
-
- {validatorMinActiveThreshold}
-
+ {validatorMinActiveThreshold !== null
+ ?
+ {validatorMinActiveThreshold}
+
+ : '-'}
diff --git a/packages/page-staking/src/types.ts b/packages/page-staking/src/types.ts
index d3c96ae13ec6..b51dbfe25ec1 100644
--- a/packages/page-staking/src/types.ts
+++ b/packages/page-staking/src/types.ts
@@ -66,7 +66,7 @@ export interface ValidatorInfo extends ValidatorInfoRank {
numNominators: number;
numRecentPayouts: number;
skipRewards: boolean;
- stakedReturn: number;
+ stakedReturn: number | null;
stakedReturnCmp: number;
validatorPrefs?: ValidatorPrefs | ValidatorPrefsTo196;
withReturns?: boolean;
@@ -96,15 +96,15 @@ export interface SortedTargets {
validators?: ValidatorInfo[];
validatorIds?: string[];
waitingIds?: string[];
- nominatorActiveCount?: number;
- nominatorElectingCount?: number;
+ nominatorActiveCount?: number | null;
+ nominatorElectingCount?: number | null;
nominatorIntentionCount?: number;
- validatorActiveCount?: number;
+ validatorActiveCount?: number | null;
validatorIntentionCount?: number;
validatorWaitingCount?: number;
- nominatorMinActiveThreshold?: string;
+ nominatorMinActiveThreshold?: string | null;
nominatorMaxElectingCount?: u32 | null;
- validatorMinActiveThreshold?: string;
+ validatorMinActiveThreshold?: string | null;
}
export interface PoolAccounts {
diff --git a/packages/page-staking/src/useSortedTargets.ts b/packages/page-staking/src/useSortedTargets.ts
index a6151a1f9b9c..e02a5f242990 100644
--- a/packages/page-staking/src/useSortedTargets.ts
+++ b/packages/page-staking/src/useSortedTargets.ts
@@ -9,7 +9,7 @@ import type { SortedTargets, TargetSortBy, ValidatorInfo } from './types';
import { useEffect, useMemo, useState } from 'react';
-import { createNamedHook, useAccounts, useApi, useCall, useCallMulti, useInflation } from '@polkadot/react-hooks';
+import { createNamedHook, useAccounts, useApi, useCall, useCallMulti, useInflation, usePrevious } from '@polkadot/react-hooks';
import { AccountId32 } from '@polkadot/types/interfaces';
import { PalletStakingExposure, PalletStakingIndividualExposure } from '@polkadot/types/lookup';
import { arrayFlatten, BN, BN_HUNDRED, BN_MAX_INTEGER, BN_ONE, BN_ZERO, formatBalance } from '@polkadot/util';
@@ -57,10 +57,10 @@ const OPT_MULTI = {
historyDepth,
maxNominatorsCount: optMaxNominatorsCount && optMaxNominatorsCount.isSome
? optMaxNominatorsCount.unwrap()
- : undefined,
+ : new BN(0),
maxValidatorsCount: optMaxValidatorsCount && optMaxValidatorsCount.isSome
? optMaxValidatorsCount.unwrap()
- : undefined,
+ : new BN(0),
minNominatorBond,
minValidatorBond,
totalIssuance
@@ -192,7 +192,7 @@ function extractSingle (api: ApiPromise, allAccounts: string[], derive: DeriveSt
rankOverall: 0,
rankReward: 0,
skipRewards,
- stakedReturn: 0,
+ stakedReturn: null,
stakedReturnCmp: 0,
validatorPrefs,
withReturns
@@ -212,7 +212,7 @@ function addReturns (inflation: Inflation, baseInfo: Partial): Pa
avgStaked && !avgStaked.isZero() && validators.forEach((v): void => {
if (!v.skipRewards && v.withReturns) {
- const adjusted = avgStaked.mul(BN_HUNDRED).imuln(inflation.stakedReturn).div(v.bondTotal);
+ const adjusted = avgStaked.mul(BN_HUNDRED).imuln(inflation.stakedReturn || 0).div(v.bondTotal);
// in some cases, we may have overflows... protect against those
v.stakedReturn = (adjusted.gt(BN_MAX_INTEGER) ? BN_MAX_INTEGER : adjusted).toNumber() / BN_HUNDRED.toNumber();
@@ -293,23 +293,43 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted
const waitingInfo = useCall(api.derive.staking.waitingInfo, [{ ...DEFAULT_FLAGS_WAITING, withLedger }]);
const lastEraInfo = useCall(api.derive.session.info, undefined, OPT_ERA);
const [stakers, setStakers] = useState<[StorageKey<[u32, AccountId32]>, PalletStakingExposure][]>([]);
- const [stakersTotal, setStakersTotal] = useState();
- const [nominatorMinActiveThreshold, setNominatorMinActiveThreshold] = useState('');
+ const [stakersTotal, setStakersTotal] = useState();
+ const [nominatorMinActiveThreshold, setNominatorMinActiveThreshold] = useState('');
const [nominatorMaxElectingCount, setNominatorMaxElectingCount] = useState();
- const [nominatorElectingCount, setNominatorElectingCount] = useState();
- const [nominatorActiveCount, setNominatorActiveCount] = useState();
- const [validatorActiveCount, setValidatorActiveCount] = useState();
+ const [nominatorElectingCount, setNominatorElectingCount] = useState();
+ const [nominatorActiveCount, setNominatorActiveCount] = useState();
+ const [validatorActiveCount, setValidatorActiveCount] = useState();
- const [calcStakers, setCalcStakers] = useState(false);
+ const [timerDone, setTimerDone] = useState(false);
+
+ const prevStakersLength = usePrevious(stakers.length);
+
+ useEffect(() => {
+ // This timer is set to wait for 10 seconds in order to identify
+ // if api has finished loading the staking info. If at the end of this timer
+ // the values from API are not yet loaded, then it is assumed that the
+ // API is not returning any values (meaning probable misconfiguration).
+ // This is for covering edge cases (e.g. staking pallet is included
+ // in apps but not used - stakers = 0).
+ const apiTimer = setTimeout(() => setTimerDone(true), 10000);
+
+ return () => {
+ clearTimeout(apiTimer);
+ };
+ }, []);
useEffect(() => {
- if (stakers[0] && stakers[0][1]) {
+ if (prevStakersLength !== stakers.length && stakers[0] && stakers[0][1]) {
setStakersTotal(stakers[0][1].total.toBn());
+ } else if (stakers.length === 0) {
+ if (timerDone) {
+ setStakersTotal(null);
+ }
}
- }, [stakers]);
+ }, [prevStakersLength, stakers, timerDone]);
useEffect(() => {
- if (stakers.length && !calcStakers) {
+ if (stakers.length !== prevStakersLength) {
const assignments: Map = new Map();
stakers.sort((a, b) => a[1].total.toBn().cmp(b[1].total.toBn())).map((x) => x[1].others).flat(1).forEach((x) => {
@@ -319,20 +339,24 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted
assignments.set(nominator, val ? amount.toBn().add(val) : amount.toBn());
});
-
const nominatorStakes = Array.from(assignments);
nominatorStakes.sort((a, b) => a[1].cmp(b[1]));
-
setNominatorMaxElectingCount(api.consts.electionProviderMultiPhase?.maxElectingVoters);
-
setNominatorElectingCount(assignments.size);
setNominatorActiveCount(assignments.size);
setNominatorMinActiveThreshold(nominatorStakes[0] ? b(nominatorStakes[0][1], api) : '');
setValidatorActiveCount(stakers.length);
- setCalcStakers(true);
+ } else if (stakers.length === 0) {
+ if (timerDone) {
+ setNominatorMaxElectingCount(api.consts.electionProviderMultiPhase?.maxElectingVoters);
+ setNominatorElectingCount(null);
+ setNominatorActiveCount(null);
+ setNominatorMinActiveThreshold(null);
+ setValidatorActiveCount(null);
+ }
}
- }, [api, calcStakers, stakers]);
+ }, [api, prevStakersLength, stakers, timerDone]);
const baseInfo = useMemo(
() => electedInfo && lastEraInfo && totalIssuance && waitingInfo
@@ -343,6 +367,12 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted
const inflation = useInflation(baseInfo?.totalStaked);
+ useEffect(() => {
+ if (!inflation.stakedReturn && timerDone) {
+ inflation.stakedReturn = null;
+ }
+ }, [inflation, inflation.stakedReturn, timerDone]);
+
const curEra = useCall