diff --git a/packages/page-staking/src/Targets/Validator.tsx b/packages/page-staking/src/Targets/Validator.tsx index 272566ea57..1344fafbcf 100644 --- a/packages/page-staking/src/Targets/Validator.tsx +++ b/packages/page-staking/src/Targets/Validator.tsx @@ -32,13 +32,22 @@ interface Props { withIdentity: boolean; } -function checkIdentity (api: ApiPromise, accountInfo: DeriveAccountInfo): boolean { +function checkIdentity (api: ApiPromise, accountInfo: DeriveAccountInfo): [boolean, string | null] { let hasIdentity = false; + let parentId: string | null = null; const { accountId, identity, nickname } = accountInfo; + if (accountId) { + parentId = accountId.toString(); + } + if (api.query.identity && api.query.identity.identityOf) { hasIdentity = !!(identity?.display && identity.display.toString()); + + if (identity.parent) { + parentId = identity.parent.toString(); + } } else if (nickname) { hasIdentity = !!nickname.toString(); } @@ -49,7 +58,7 @@ function checkIdentity (api: ApiPromise, accountInfo: DeriveAccountInfo): boolea hasIdentity = !!account?.meta?.name; } - return hasIdentity; + return [hasIdentity, parentId]; } function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSelected, toggleFavorite, toggleSelected, withElected, withIdentity }: Props): React.ReactElement | null { @@ -59,7 +68,10 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe useEffect((): void => { if (accountInfo) { - info.hasIdentity = checkIdentity(api, accountInfo); + const [hasIdentity, parentId] = checkIdentity(api, accountInfo); + + info.hasIdentity = hasIdentity; + info.parentId = parentId; } }, [api, accountInfo, info]); diff --git a/packages/page-staking/src/Targets/index.tsx b/packages/page-staking/src/Targets/index.tsx index c594c8225d..b382619852 100644 --- a/packages/page-staking/src/Targets/index.tsx +++ b/packages/page-staking/src/Targets/index.tsx @@ -5,6 +5,7 @@ import { DeriveStakingOverview } from '@polkadot/api-derive/types'; import { StakerState } from '@polkadot/react-hooks/types'; import { SortedTargets, TargetSortBy, ValidatorInfo } from '../types'; +import BN from 'bn.js'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; import { Button, Icon, InputBalance, Table, Toggle } from '@polkadot/react-components'; @@ -28,6 +29,11 @@ interface Props { toggleFavorite: (address: string) => void; } +interface Flags { + withIdentity: boolean; + withGroup: boolean; +} + interface SortState { sortBy: TargetSortBy; sortFromMax: boolean; @@ -64,6 +70,28 @@ function extractNominees (ownNominators: StakerState[] = []): string[] { return myNominees; } +function selectProfitable (maxPaid: BN, list: ValidatorInfo[], { withGroup, withIdentity }: Flags): string[] { + const parentIds: (string | null)[] = []; + const result: string[] = []; + + for (let i = 0; i < list.length && result.length < MAX_NOMINATIONS; i++) { + const { hasIdentity, isElected, isFavorite, key, numNominators, parentId, rewardPayout } = list[i]; + + if ( + (!withIdentity || hasIdentity) && + (isElected || isFavorite) && + !rewardPayout.isZero() && + (!maxPaid || maxPaid.gtn(numNominators)) && + (!withGroup || (isFavorite || !parentIds.includes(parentId))) + ) { + result.push(key); + parentId && parentIds.push(parentId); + } + } + + return result; +} + function Targets ({ className = '', isInElection, ownStashes, targets: { avgStaked, calcWith, lastReward, lowStaked, nominators, setCalcWith, totalStaked, validators }, toggleFavorite }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); @@ -72,6 +100,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak const [selected, setSelected] = useState([]); const [nameFilter, setNameFilter] = useState(''); const [withElected, setWithElected] = useState(false); + const [withGroup, setWithGroup] = useState(true); const [withIdentity, setWithIdentity] = useState(false); const [{ sortBy, sortFromMax }, setSortBy] = useState({ sortBy: 'rankOverall', sortFromMax: true }); const [sorted, setSorted] = useState(); @@ -108,18 +137,12 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak ); const _selectProfitable = useCallback( - () => setSelected((): string[] => { - const max = api.consts.staking?.maxNominatorRewardedPerValidator; - - return (validators || []).reduce((result: string[], { hasIdentity, isElected, isFavorite, key, numNominators, rewardPayout }): string[] => { - if ((result.length < MAX_NOMINATIONS) && (hasIdentity || !withIdentity) && (isElected || isFavorite) && !rewardPayout.isZero() && (!max || max.gtn(numNominators))) { - result.push(key); - } - - return result; - }, []); - }), - [api, validators, withIdentity] + () => setSelected(selectProfitable( + api.consts.staking.maxNominatorRewardedPerValidator, + validators || [], + { withGroup, withIdentity } + )), + [api, validators, withGroup, withIdentity] ); const labelsRef = useRef({ @@ -162,6 +185,12 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak setWithIdentity={setWithIdentity} withIdentity={withIdentity} > + ('limit single operator exposure')} + onChange={setWithGroup} + value={withGroup} + /> ('limit to elected')} @@ -171,7 +200,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak ) - ), [calcWith, setCalcWith, nameFilter, sorted, t, withElected, withIdentity]); + ), [calcWith, setCalcWith, nameFilter, sorted, t, withElected, withGroup, withIdentity]); return (
diff --git a/packages/page-staking/src/types.ts b/packages/page-staking/src/types.ts index e16e5df223..c43315314a 100644 --- a/packages/page-staking/src/types.ts +++ b/packages/page-staking/src/types.ts @@ -54,6 +54,7 @@ export interface ValidatorInfo extends ValidatorInfoRank { isNominating: boolean; key: string; numNominators: number; + parentId: string | null; rewardPayout: BN; rewardSplit: BN; validatorPayment: BN; diff --git a/packages/page-staking/src/useSortedTargets.ts b/packages/page-staking/src/useSortedTargets.ts index 0bc66cfe8c..f6140da0b9 100644 --- a/packages/page-staking/src/useSortedTargets.ts +++ b/packages/page-staking/src/useSortedTargets.ts @@ -108,6 +108,7 @@ function extractSingle (allAccounts: string[], amount: BN = baseBalance(), { inf isNominating, key, numNominators: (exposure.others || []).length, + parentId: null, rankBondOther: 0, rankBondOwn: 0, rankBondTotal: 0,