Limit operator exposure by default (#4036)

This commit is contained in:
Jaco Greeff 2020-11-17 12:45:21 +01:00 committed by GitHub
parent e432067ace
commit cdb7a255d7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 59 additions and 16 deletions

View File

@ -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<Props> | 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]);

View File

@ -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<Props> {
const { t } = useTranslation();
const { api } = useApi();
@ -72,6 +100,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
const [selected, setSelected] = useState<string[]>([]);
const [nameFilter, setNameFilter] = useState<string>('');
const [withElected, setWithElected] = useState(false);
const [withGroup, setWithGroup] = useState(true);
const [withIdentity, setWithIdentity] = useState(false);
const [{ sortBy, sortFromMax }, setSortBy] = useState<SortState>({ sortBy: 'rankOverall', sortFromMax: true });
const [sorted, setSorted] = useState<number[] | undefined>();
@ -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}
>
<Toggle
className='staking--buttonToggle'
label={t<string>('limit single operator exposure')}
onChange={setWithGroup}
value={withGroup}
/>
<Toggle
className='staking--buttonToggle'
label={t<string>('limit to elected')}
@ -171,7 +200,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
</Filtering>
</div>
)
), [calcWith, setCalcWith, nameFilter, sorted, t, withElected, withIdentity]);
), [calcWith, setCalcWith, nameFilter, sorted, t, withElected, withGroup, withIdentity]);
return (
<div className={className}>

View File

@ -54,6 +54,7 @@ export interface ValidatorInfo extends ValidatorInfoRank {
isNominating: boolean;
key: string;
numNominators: number;
parentId: string | null;
rewardPayout: BN;
rewardSplit: BN;
validatorPayment: BN;

View File

@ -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,