Oversubscriptions by size index (#3573)

This commit is contained in:
Jaco Greeff 2020-08-30 14:25:20 +02:00 committed by GitHub
parent 095caddd29
commit d78770a094
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 38 additions and 16 deletions

View File

@ -4,7 +4,9 @@
import React from 'react';
import { AddressMini, Expander } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { MAX_NOM_PAYOUTS } from '../../constants';
import { useTranslation } from '../../translate';
import useInactives from '../useInactives';
@ -15,13 +17,16 @@ interface Props {
function ListNominees ({ nominating, stashId }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { nomsActive, nomsChilled, nomsInactive, nomsOver, nomsWaiting } = useInactives(stashId, nominating);
const max = (api.consts.staking?.maxNominatorRewardedPerValidator || MAX_NOM_PAYOUTS).toString();
return (
<>
{nomsOver && nomsOver.length !== 0 && (
<Expander
help={t<string>('The validators that are over-subscribed, only the top 64 backing stake will be rewarded.')}
help={t<string>('These validators are active but only the top {{max}} nominators by backing stake will be receiving rewards. The nominating stash is not one of those to be rewarded in the current era.', { replace: max })}
summary={t<string>('Oversubscribed nominations ({{count}})', { replace: { count: nomsOver.length } })}
>
{nomsOver.map((nomineeId, index): React.ReactNode => (

View File

@ -6,9 +6,12 @@ import { DeriveSessionIndexes } from '@polkadot/api-derive/types';
import { EraIndex, Exposure, Nominations, SlashingSpans } from '@polkadot/types/interfaces';
import { useEffect, useState } from 'react';
import { ApiPromise } from '@polkadot/api';
import { useApi, useCall, useIsMountedRef } from '@polkadot/react-hooks';
import { Option } from '@polkadot/types';
import { MAX_NOM_PAYOUTS } from '../constants';
interface Inactives {
nomsActive?: string[];
nomsChilled?: string[];
@ -17,7 +20,9 @@ interface Inactives {
nomsWaiting?: string[];
}
function extractState (stashId: string, slashes: Option<SlashingSpans>[], nominees: string[], activeEra: EraIndex, submittedIn: EraIndex, exposures: Exposure[]): Inactives {
function extractState (api: ApiPromise, stashId: string, slashes: Option<SlashingSpans>[], nominees: string[], activeEra: EraIndex, submittedIn: EraIndex, exposures: Exposure[]): Inactives {
const max = (api.consts.staking?.maxNominatorRewardedPerValidator || MAX_NOM_PAYOUTS);
// chilled
const nomsChilled = nominees.filter((_, index): boolean => {
if (slashes[index].isNone) {
@ -31,10 +36,11 @@ function extractState (stashId: string, slashes: Option<SlashingSpans>[], nomine
// all nominations that are oversubscribed
const nomsOver = exposures
.map((exposure, index) =>
exposure.others.length > 64
? nominees[index]
: null
.map(({ others }) => others.sort((a, b) => b.value.unwrap().cmp(a.value.unwrap())))
.map((others, index) =>
max.gtn(others.map(({ who }) => who.toString()).indexOf(stashId))
? null
: nominees[index]
)
.filter((nominee): nominee is string => !!nominee && !nomsChilled.includes(nominee));
@ -98,7 +104,7 @@ export default function useInactives (stashId: string, nominees?: string[]): Ina
const slashes = exposuresAndSpans.slice(nominees.length) as Option<SlashingSpans>[];
mountedRef.current && setState(
extractState(stashId, slashes, nominees, indexes.activeEra, optNominators.unwrapOrDefault().submittedIn, exposures)
extractState(api, stashId, slashes, nominees, indexes.activeEra, optNominators.unwrapOrDefault().submittedIn, exposures)
);
}
)

View File

@ -5,7 +5,9 @@
import React from 'react';
import styled from 'styled-components';
import { Badge, Icon } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { MAX_NOM_PAYOUTS } from './constants';
import { useTranslation } from './translate';
interface Props {
@ -14,6 +16,7 @@ interface Props {
function Ledgend ({ className }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
return (
<div className={className}>
@ -48,7 +51,7 @@ function Ledgend ({ className }: Props): React.ReactElement<Props> {
<span>
<Badge
color='red'
info='64'
info={(api.consts.staking?.maxNominatorRewardedPerValidator || MAX_NOM_PAYOUTS).toString()}
/>
{t('Oversubscribed')}
</span>

View File

@ -4,22 +4,27 @@
import React from 'react';
import { Badge } from '@polkadot/react-components';
import { useApi } from '@polkadot/react-hooks';
import { MAX_PAYOUTS } from './constants';
import { MAX_NOM_PAYOUTS } from './constants';
interface Props {
numNominators?: number;
}
function MaxBadge ({ numNominators }: Props): React.ReactElement<Props> | null {
if (!numNominators || (numNominators < MAX_PAYOUTS)) {
const { api } = useApi();
const max = (api.consts.staking?.maxNominatorRewardedPerValidator || MAX_NOM_PAYOUTS);
if (!numNominators || max.gtn(numNominators)) {
return null;
}
return (
<Badge
color='red'
info='64'
info={max.toString()}
/>
);
}

View File

@ -9,11 +9,11 @@ import { SortedTargets, TargetSortBy, ValidatorInfo } from '../types';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Button, Icon, InputBalance, Table, Toggle } from '@polkadot/react-components';
import { useAvailableSlashes } from '@polkadot/react-hooks';
import { useApi, useAvailableSlashes } from '@polkadot/react-hooks';
import ElectionBanner from '../ElectionBanner';
import Filtering from '../Filtering';
import { MAX_NOMINATIONS } from '../constants';
import { MAX_NOMINATIONS, MAX_NOM_PAYOUTS } from '../constants';
import { useTranslation } from '../translate';
import Nominate from './Nominate';
import Summary from './Summary';
@ -67,6 +67,7 @@ function extractNominees (ownNominators: StakerState[] = []): string[] {
function Targets ({ className = '', isInElection, ownStashes, targets: { avgStaked, calcWith, lastReward, lowStaked, nominators, setCalcWith, totalStaked, validators }, toggleFavorite }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const allSlashes = useAvailableSlashes();
const ownNominators = useOwnNominators(ownStashes);
const [selected, setSelected] = useState<string[]>([]);
@ -110,14 +111,14 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
const _selectProfitable = useCallback(
() => setSelected(
(validators || []).reduce((result: string[], { hasIdentity, isElected, isFavorite, key, numNominators, rewardPayout }): string[] => {
if ((numNominators < 64) && (result.length < MAX_NOMINATIONS) && (hasIdentity || !withIdentity) && (isElected || isFavorite) && !rewardPayout.isZero()) {
if ((api.consts.staking?.maxNominatorRewardedPerValidator || MAX_NOM_PAYOUTS).gtn(numNominators) && (result.length < MAX_NOMINATIONS) && (hasIdentity || !withIdentity) && (isElected || isFavorite) && !rewardPayout.isZero()) {
result.push(key);
}
return result;
}, [])
),
[validators, withIdentity]
[api, validators, withIdentity]
);
const labels = useMemo(

View File

@ -2,8 +2,10 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import BN from 'bn.js';
export const MAX_NOMINATIONS = 16;
export const MAX_PAYOUTS = 64;
export const MAX_NOM_PAYOUTS = new BN(64);
export const STORE_FAVS_BASE = 'staking:favorites';