(api.query.staking.historyDepth, []);
const stakerPayoutsAfter = useStakerPayouts();
- const { allRewards, isLoadingRewards } = useOwnEraRewards(eraSelection[eraSelectionIndex].value);
+ const { allRewards, isLoadingRewards } = useOwnEraRewards(eraSelection[eraSelectionIndex].value, myStashesIndex ? undefined : ownValidators);
const isDisabled = isInElection || !isFunction(api.tx.utility?.batch);
useEffect((): void => {
@@ -154,16 +156,18 @@ function Payouts ({ className = '', isInElection }: Props): React.ReactElement [
- [t('payout/stash'), 'start', 2],
+ [myStashesIndex ? t('payout/stash') : t('overall/validator'), 'start', 2],
[t('eras'), 'start'],
[t('available')],
[('remaining')],
[undefined, undefined, 3]
- ], [t]);
+ ], [myStashesIndex, t]);
const headerValidators = useMemo(() => [
[t('payout/validator'), 'start', 2],
@@ -173,6 +177,11 @@ function Payouts ({ className = '', isInElection }: Props): React.ReactElement
[
+ { isDisabled: !hasOwnValidators, text: t('My validators'), value: 'val' },
+ { text: t('My stashes'), value: 'all' }
+ ], [hasOwnValidators, t]);
+
const footer = useMemo(() => (
|
@@ -185,8 +194,14 @@ function Payouts ({ className = '', isInElection }: Props): React.ReactElement
+
{api.tx.staking.payoutStakers && (
+
)}
-
('No pending payouts for your stashes')}
emptySpinner={t('Retrieving info for the selected eras, this will take some time')}
@@ -216,7 +230,7 @@ function Payouts ({ className = '', isInElection }: Props): React.ReactElement
))}
- {api.tx.staking.payoutStakers && !isLoadingRewards && validators && (validators.length !== 0) && (
+ {api.tx.staking.payoutStakers && (myStashesIndex === 1) && !isLoadingRewards && validators && (validators.length !== 0) && (
+
-
('No active validators to check')}
filter={filter}
diff --git a/packages/react-hooks/src/useOwnEraRewards.ts b/packages/react-hooks/src/useOwnEraRewards.ts
index e70a8452ca..3775948cb9 100644
--- a/packages/react-hooks/src/useOwnEraRewards.ts
+++ b/packages/react-hooks/src/useOwnEraRewards.ts
@@ -2,10 +2,13 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
-import { DeriveStakerReward } from '@polkadot/api-derive/types';
+import { DeriveEraPoints, DeriveEraRewards, DeriveStakerReward } from '@polkadot/api-derive/types';
import { EraIndex } from '@polkadot/types/interfaces';
+import { StakerState } from './types';
import { useEffect, useState } from 'react';
+import { registry } from '@polkadot/react-api';
+import { BN_ZERO } from '@polkadot/util';
import useApi from './useApi';
import useCall from './useCall';
@@ -13,11 +16,21 @@ import useIsMountedRef from './useIsMountedRef';
import { useOwnStashIds } from './useOwnStashes';
interface OwnRewards {
- allRewards?: Record;
+ allRewards?: Record | null;
isLoadingRewards: boolean;
rewardCount: number;
}
+interface ValidatorWithEras {
+ eras: EraIndex[];
+ stashId: string;
+}
+
+interface Filtered {
+ filteredEras: EraIndex[];
+ validatorEras: ValidatorWithEras[];
+}
+
function getRewards ([[stashIds], available]: [[string[]], DeriveStakerReward[][]]): OwnRewards {
const allRewards: Record = {};
@@ -32,30 +45,96 @@ function getRewards ([[stashIds], available]: [[string[]], DeriveStakerReward[][
};
}
-export default function useOwnEraRewards (maxEras?: number): OwnRewards {
+function getValRewards (validatorEras: ValidatorWithEras[], erasPoints: DeriveEraPoints[], erasRewards: DeriveEraRewards[]): OwnRewards {
+ const allRewards: Record = {};
+
+ validatorEras.forEach(({ eras, stashId }): void => {
+ eras.forEach((era): void => {
+ const eraPoints = erasPoints.find((p) => p.era.eq(era));
+ const eraRewards = erasRewards.find((r) => r.era.eq(era));
+
+ if (eraPoints?.eraPoints.gt(BN_ZERO) && eraPoints?.validators[stashId] && eraRewards) {
+ const reward = eraPoints.validators[stashId].mul(eraRewards.eraReward).div(eraPoints.eraPoints);
+
+ if (!reward.isZero()) {
+ const total = registry.createType('Balance', reward);
+
+ if (!allRewards[stashId]) {
+ allRewards[stashId] = [];
+ }
+
+ allRewards[stashId].push({
+ era,
+ eraReward: eraRewards.eraReward,
+ isEmpty: false,
+ isValidator: true,
+ nominating: [],
+ validators: {
+ [stashId]: {
+ total,
+ value: total
+ }
+ }
+ });
+ }
+ }
+ });
+ });
+
+ return {
+ allRewards,
+ isLoadingRewards: false,
+ rewardCount: Object.values(allRewards).filter((rewards) => rewards.length !== 0).length
+ };
+}
+
+export default function useOwnEraRewards (maxEras?: number, ownValidators?: StakerState[]): OwnRewards {
const { api } = useApi();
const mountedRef = useIsMountedRef();
const stashIds = useOwnStashIds();
const allEras = useCall(api.derive.staking?.erasHistoric, []);
- const [filteredEras, setFilteredEras] = useState([]);
- const available = useCall<[[string[]], DeriveStakerReward[][]]>(!!filteredEras.length && stashIds && api.derive.staking?.stakerRewardsMultiEras, [stashIds, filteredEras], { withParams: true });
+ const [{ filteredEras, validatorEras }, setFiltered] = useState({ filteredEras: [], validatorEras: [] });
const [state, setState] = useState({ isLoadingRewards: true, rewardCount: 0 });
+ const stakerRewards = useCall<[[string[]], DeriveStakerReward[][]]>(!ownValidators?.length && !!filteredEras.length && stashIds && api.derive.staking?.stakerRewardsMultiEras, [stashIds, filteredEras], { withParams: true });
+ const erasPoints = useCall(!!validatorEras.length && !!filteredEras.length && api.derive.staking._erasPoints, [filteredEras, false]);
+ const erasRewards = useCall(!!validatorEras.length && !!filteredEras.length && api.derive.staking._erasRewards, [filteredEras, false]);
useEffect((): void => {
- setState({ isLoadingRewards: true, rewardCount: 0 });
- }, [maxEras]);
+ setState({ allRewards: null, isLoadingRewards: true, rewardCount: 0 });
+ }, [maxEras, ownValidators]);
useEffect((): void => {
- allEras && maxEras && setFilteredEras(
- allEras.slice(-1 * maxEras)
+ if (allEras && maxEras) {
+ const filteredEras = allEras.slice(-1 * maxEras);
+ const validatorEras: ValidatorWithEras[] = [];
+
+ if (ownValidators?.length) {
+ ownValidators.forEach(({ stakingLedger, stashId }): void => {
+ if (stakingLedger) {
+ const eras = filteredEras.filter((era) => !stakingLedger.claimedRewards.some((c) => era.eq(c)));
+
+ if (eras.length) {
+ validatorEras.push({ eras, stashId });
+ }
+ }
+ });
+ }
+
+ setFiltered({ filteredEras, validatorEras });
+ }
+ }, [allEras, maxEras, ownValidators]);
+
+ useEffect((): void => {
+ mountedRef.current && stakerRewards && !ownValidators && setState(
+ getRewards(stakerRewards)
);
- }, [allEras, maxEras]);
+ }, [mountedRef, ownValidators, stakerRewards]);
useEffect((): void => {
- mountedRef.current && available && setState(
- getRewards(available)
+ mountedRef && erasPoints && erasRewards && ownValidators && setState(
+ getValRewards(validatorEras, erasPoints, erasRewards)
);
- }, [available, mountedRef]);
+ }, [erasPoints, erasRewards, mountedRef, ownValidators, validatorEras]);
return state;
}