Remove auto validator defaults (#3306)
* Remove auto validator defaults * Adjust * Additional cleanups * Disable nominate on no target validators * Remove unused next from targets
This commit is contained in:
parent
5cafda8bfc
commit
ce49da0029
@ -15,15 +15,13 @@ import NominatePartial from '../partials/Nominate';
|
||||
interface Props {
|
||||
className?: string;
|
||||
controllerId: string;
|
||||
next?: string[];
|
||||
nominating?: string[];
|
||||
onClose: () => void;
|
||||
stashId: string;
|
||||
targets: SortedTargets;
|
||||
validators?: string[];
|
||||
}
|
||||
|
||||
function Nominate ({ className = '', controllerId, next, nominating, onClose, stashId, targets, validators }: Props): React.ReactElement<Props> | null {
|
||||
function Nominate ({ className = '', controllerId, nominating, onClose, stashId, targets }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const [{ nominateTx }, setTx] = useState<NominateInfo>({});
|
||||
|
||||
@ -37,12 +35,10 @@ function Nominate ({ className = '', controllerId, next, nominating, onClose, st
|
||||
<NominatePartial
|
||||
className='nominatePartial'
|
||||
controllerId={controllerId}
|
||||
next={next}
|
||||
nominating={nominating}
|
||||
onChange={setTx}
|
||||
stashId={stashId}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
withSenders
|
||||
/>
|
||||
</Modal.Content>
|
||||
|
||||
@ -38,7 +38,7 @@ interface Props {
|
||||
validators?: string[];
|
||||
}
|
||||
|
||||
function Account ({ allSlashes, className = '', info: { controllerId, destination, destinationId, hexSessionIdNext, hexSessionIdQueue, isLoading, isOwnController, isOwnStash, isStashNominating, isStashValidating, nominating, sessionIds, stakingLedger, stashId }, isDisabled, next, targets, validators }: Props): React.ReactElement<Props> {
|
||||
function Account ({ allSlashes, className = '', info: { controllerId, destination, destinationId, hexSessionIdNext, hexSessionIdQueue, isLoading, isOwnController, isOwnStash, isStashNominating, isStashValidating, nominating, sessionIds, stakingLedger, stashId }, isDisabled, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { queueExtrinsic } = useContext(StatusContext);
|
||||
@ -133,12 +133,10 @@ function Account ({ allSlashes, className = '', info: { controllerId, destinatio
|
||||
{isNominateOpen && controllerId && (
|
||||
<Nominate
|
||||
controllerId={controllerId}
|
||||
next={next}
|
||||
nominating={nominating}
|
||||
onClose={toggleNominate}
|
||||
stashId={stashId}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
/>
|
||||
)}
|
||||
{isSetControllerOpen && controllerId && (
|
||||
@ -326,7 +324,7 @@ function Account ({ allSlashes, className = '', info: { controllerId, destinatio
|
||||
}
|
||||
{isStashNominating &&
|
||||
<Menu.Item
|
||||
disabled={!isOwnController}
|
||||
disabled={!isOwnController || !targets.validators?.length}
|
||||
onClick={toggleNominate}
|
||||
>
|
||||
{t<string>('Set nominees')}
|
||||
|
||||
@ -16,15 +16,13 @@ import NominatePartial from './partials/Nominate';
|
||||
|
||||
interface Props {
|
||||
isInElection?: boolean;
|
||||
next?: string[];
|
||||
targets?: SortedTargets;
|
||||
validators?: string[];
|
||||
targets: SortedTargets;
|
||||
}
|
||||
|
||||
const EMPTY_NOMS: string[] = [];
|
||||
const NUM_STEPS = 2;
|
||||
|
||||
function NewNominator ({ isInElection, next, targets, validators }: Props): React.ReactElement<Props> {
|
||||
function NewNominator ({ isInElection, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [isVisible, toggleVisible] = useToggle();
|
||||
@ -57,7 +55,7 @@ function NewNominator ({ isInElection, next, targets, validators }: Props): Reac
|
||||
<>
|
||||
<Button
|
||||
icon='plus'
|
||||
isDisabled={isDisabled}
|
||||
isDisabled={isDisabled || !targets.validators?.length}
|
||||
key='new-nominator'
|
||||
label={t<string>('Nominator')}
|
||||
onClick={_toggle}
|
||||
@ -79,12 +77,10 @@ function NewNominator ({ isInElection, next, targets, validators }: Props): Reac
|
||||
{controllerId && stashId && step === 2 && (
|
||||
<NominatePartial
|
||||
controllerId={controllerId}
|
||||
next={next}
|
||||
nominating={EMPTY_NOMS}
|
||||
onChange={setNominateInfo}
|
||||
stashId={stashId}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
/>
|
||||
)}
|
||||
</Modal.Content>
|
||||
|
||||
@ -48,7 +48,7 @@ function extractState (ownStashes: StakerState[]): State {
|
||||
};
|
||||
}
|
||||
|
||||
function Actions ({ className = '', isInElection, next, ownStashes, targets, validators }: Props): React.ReactElement<Props> {
|
||||
function Actions ({ className = '', isInElection, ownStashes, targets }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const allSlashes = useAvailableSlashes();
|
||||
const [{ bondedTotal, foundStashes }, setState] = useState<State>({});
|
||||
@ -80,9 +80,7 @@ function Actions ({ className = '', isInElection, next, ownStashes, targets, val
|
||||
<Button.Group>
|
||||
<NewNominator
|
||||
isInElection={isInElection}
|
||||
next={next}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
/>
|
||||
<NewValidator isInElection={isInElection} />
|
||||
<NewStash />
|
||||
@ -99,9 +97,7 @@ function Actions ({ className = '', isInElection, next, ownStashes, targets, val
|
||||
info={info}
|
||||
isDisabled={isInElection}
|
||||
key={info.stashId}
|
||||
next={next}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
|
||||
@ -5,142 +5,40 @@
|
||||
import { NominateInfo } from './types';
|
||||
import { SortedTargets } from '../../types';
|
||||
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { AddressMini, InputAddress, InputAddressMulti, Modal, Static, Toggle } from '@polkadot/react-components';
|
||||
import { getParentAccount } from '@polkadot/react-components/AccountName';
|
||||
import { InputAddress, InputAddressMulti, Modal } from '@polkadot/react-components';
|
||||
import { useApi, useFavorites } from '@polkadot/react-hooks';
|
||||
import { randomAsNumber } from '@polkadot/util-crypto';
|
||||
|
||||
import { MAX_NOMINATIONS, MAX_PAYOUTS, STORE_FAVS_BASE } from '../../constants';
|
||||
import { MAX_NOMINATIONS, STORE_FAVS_BASE } from '../../constants';
|
||||
import { useTranslation } from '../../translate';
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
controllerId: string;
|
||||
next?: string[];
|
||||
nominating?: string[];
|
||||
onChange: (info: NominateInfo) => void;
|
||||
stashId: string;
|
||||
targets: SortedTargets;
|
||||
validators: string[];
|
||||
withSenders?: boolean;
|
||||
}
|
||||
|
||||
interface Selected {
|
||||
isAutoSelect: boolean;
|
||||
selected: string[];
|
||||
}
|
||||
|
||||
function autoPick (targets: SortedTargets): string[] {
|
||||
const parents: string[] = [];
|
||||
const positions: number[] = [];
|
||||
|
||||
return (targets.validators || []).reduce((result: string[], { isElected, isFavorite, key, numNominators, rewardPayout }): string[] => {
|
||||
if (result.length < MAX_NOMINATIONS) {
|
||||
if (numNominators && (numNominators < MAX_PAYOUTS) && (isElected || isFavorite) && !rewardPayout.isZero()) {
|
||||
const parent = getParentAccount(key);
|
||||
let shouldInclude = isFavorite;
|
||||
|
||||
if (!shouldInclude) {
|
||||
const noParent = !parents.includes(key) && (!parent || !parents.includes(parent));
|
||||
|
||||
if (noParent) {
|
||||
shouldInclude = true;
|
||||
} else {
|
||||
const parentIndex = parent
|
||||
? parents.indexOf(parent)
|
||||
: -1;
|
||||
const keyIndex = parents.indexOf(key);
|
||||
const index = keyIndex !== -1
|
||||
? keyIndex
|
||||
: parentIndex;
|
||||
|
||||
// 66.6% chance to be replaced by a later one
|
||||
if (index !== -1 && ((randomAsNumber() % 300) > 100)) {
|
||||
result[positions[index]] = key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (shouldInclude) {
|
||||
positions[parents.length] = result.length;
|
||||
parents.push(key);
|
||||
|
||||
if (parent) {
|
||||
positions[parents.length] = result.length;
|
||||
parents.push(parent);
|
||||
}
|
||||
|
||||
result.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
function initialPick (targets: SortedTargets): Selected {
|
||||
const selected = targets.validators?.length
|
||||
? autoPick(targets)
|
||||
: [];
|
||||
|
||||
return {
|
||||
isAutoSelect: selected.length !== 0,
|
||||
selected
|
||||
};
|
||||
}
|
||||
|
||||
function Nominate ({ className = '', controllerId, next, nominating, onChange, stashId, targets, validators, withSenders }: Props): React.ReactElement<Props> {
|
||||
function Nominate ({ className = '', controllerId, nominating, onChange, stashId, targets: { validatorIds = [] }, withSenders }: Props): React.ReactElement<Props> {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const [favorites] = useFavorites(STORE_FAVS_BASE);
|
||||
const [{ isAutoSelect, selected }, setSelected] = useState<Selected>(
|
||||
// nominating?.length
|
||||
// ? { isAutoSelect: false, selected: nominating }
|
||||
// : initialPick(targets)
|
||||
initialPick(targets)
|
||||
);
|
||||
const [selected, setSelected] = useState<string[]>(nominating || []);
|
||||
const [available] = useState<string[]>((): string[] => {
|
||||
const shortlist = [
|
||||
// ensure that the favorite is included in the list of stashes
|
||||
...favorites.filter((acc) => (validators || []).includes(acc) || (next || []).includes(acc)),
|
||||
...favorites.filter((acc) => validatorIds.includes(acc)),
|
||||
// make sure the nominee is not in our favorites already
|
||||
...(nominating || []).filter((acc) => !favorites.includes(acc))
|
||||
];
|
||||
|
||||
return shortlist
|
||||
.concat(...(validators || []).filter((acc) => !shortlist.includes(acc)))
|
||||
.concat(...(next || []).filter((acc) => !shortlist.includes(acc)));
|
||||
.concat(...(validatorIds.filter((acc) => !shortlist.includes(acc))));
|
||||
});
|
||||
const [autoSelected, setAutoSelected] = useState<string[]>([]);
|
||||
|
||||
const _setSelected = useCallback(
|
||||
(selected: string[]) => setSelected(({ isAutoSelect }: Selected) => ({
|
||||
isAutoSelect,
|
||||
selected
|
||||
})),
|
||||
[]
|
||||
);
|
||||
|
||||
const _toggleAutoSelect = useCallback(
|
||||
(isAutoSelect: boolean) => setSelected(() => ({
|
||||
isAutoSelect,
|
||||
selected: isAutoSelect
|
||||
? autoSelected
|
||||
: []
|
||||
})),
|
||||
[autoSelected]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
setAutoSelected(
|
||||
targets.validators?.length
|
||||
? autoPick(targets)
|
||||
: []
|
||||
);
|
||||
}, [targets]);
|
||||
|
||||
useEffect((): void => {
|
||||
onChange({
|
||||
@ -173,53 +71,18 @@ function Nominate ({ className = '', controllerId, next, nominating, onChange, s
|
||||
)}
|
||||
<Modal.Columns>
|
||||
<Modal.Column>
|
||||
{isAutoSelect
|
||||
? (
|
||||
<>
|
||||
<Static
|
||||
label={t<string>('auto-selected targets for nomination')}
|
||||
value={
|
||||
selected.map((validatorId) => (
|
||||
<AddressMini
|
||||
className='addressStatic'
|
||||
isHighlight={favorites.includes(validatorId)}
|
||||
key={validatorId}
|
||||
value={validatorId}
|
||||
/>
|
||||
))
|
||||
}
|
||||
/>
|
||||
<article className='warning'>{t<string>('The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for the validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.')}</article>
|
||||
</>
|
||||
)
|
||||
: (
|
||||
<InputAddressMulti
|
||||
available={available}
|
||||
availableLabel={t<string>('candidate accounts')}
|
||||
defaultValue={nominating}
|
||||
help={t<string>('Filter available candidates based on name, address or short account index.')}
|
||||
maxCount={MAX_NOMINATIONS}
|
||||
onChange={_setSelected}
|
||||
valueLabel={t<string>('nominated accounts')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{autoSelected.length !== 0 && (
|
||||
<Toggle
|
||||
className='auto--toggle'
|
||||
isDisabled={!targets.validators?.length}
|
||||
label={
|
||||
isAutoSelect
|
||||
? t<string>('Use an automatic selection of the currently most profitable validators')
|
||||
: t<string>('Select targets manually (no auto-selection based on current profitability)')
|
||||
}
|
||||
onChange={_toggleAutoSelect}
|
||||
value={isAutoSelect}
|
||||
/>
|
||||
)}
|
||||
<InputAddressMulti
|
||||
available={available}
|
||||
availableLabel={t<string>('candidate accounts')}
|
||||
defaultValue={nominating}
|
||||
help={t<string>('Filter available candidates based on name, address or short account index.')}
|
||||
maxCount={MAX_NOMINATIONS}
|
||||
onChange={setSelected}
|
||||
valueLabel={t<string>('nominated accounts')}
|
||||
/>
|
||||
</Modal.Column>
|
||||
<Modal.Column>
|
||||
<p>{t<string>('Nominators can be selected automatically based on the current on-chain conditions or supplied manually as selected from the list of all currently available validators. In both cases, your favorites appear for the selection.')}</p>
|
||||
<p>{t<string>('Nominators can be selected manually from the list of all currently available validators. If you have set favorites these will appear for the selection.')}</p>
|
||||
<p>{t<string>('Once transmitted the new selection will only take effect in 2 eras since the selection criteria for the next era was done at the end of the previous era. Until then, the nominations will show as inactive.')}</p>
|
||||
</Modal.Column>
|
||||
</Modal.Columns>
|
||||
|
||||
@ -23,7 +23,6 @@ import useOwnNominators from './useOwnNominators';
|
||||
interface Props {
|
||||
className?: string;
|
||||
isInElection: boolean;
|
||||
next?: string[];
|
||||
ownStashes?: StakerState[];
|
||||
stakingOverview?: DeriveStakingOverview;
|
||||
targets: SortedTargets;
|
||||
@ -182,11 +181,11 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { calcWit
|
||||
<Button
|
||||
icon='check'
|
||||
isDisabled={!validators?.length || !ownNominators?.length}
|
||||
label={t<string>('Select best')}
|
||||
label={t<string>('Most profitable')}
|
||||
onClick={_selectProfitable}
|
||||
/>
|
||||
<Nominate
|
||||
isDisabled={isInElection}
|
||||
isDisabled={isInElection || !validators?.length}
|
||||
ownNominators={ownNominators}
|
||||
targets={selected}
|
||||
/>
|
||||
|
||||
@ -36,7 +36,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
const { api } = useApi();
|
||||
const { hasAccounts } = useAccounts();
|
||||
const { pathname } = useLocation();
|
||||
const [{ next, validators }, setValidators] = useState<Validators>({});
|
||||
const [{ next }, setValidators] = useState<Validators>({});
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const allStashes = useStashIds();
|
||||
const ownStashes = useOwnStashInfos();
|
||||
@ -123,7 +123,6 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
<Route path={`${basePath}/targets`}>
|
||||
<Targets
|
||||
isInElection={isInElection}
|
||||
next={next}
|
||||
ownStashes={ownStashes}
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
@ -144,10 +143,8 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
<Actions
|
||||
className={pathname === `${basePath}/actions` ? '' : 'staking--hidden'}
|
||||
isInElection={isInElection}
|
||||
next={next}
|
||||
ownStashes={ownStashes}
|
||||
targets={targets}
|
||||
validators={validators}
|
||||
/>
|
||||
<Overview
|
||||
className={basePath === pathname ? '' : 'staking--hidden'}
|
||||
|
||||
@ -67,4 +67,5 @@ export interface SortedTargets {
|
||||
setCalcWith: (amount?: BN) => void;
|
||||
totalStaked?: BN;
|
||||
validators?: ValidatorInfo[];
|
||||
validatorIds?: string[];
|
||||
}
|
||||
|
||||
@ -139,8 +139,9 @@ function extractInfo (allAccounts: string[], amount: BN = baseBalance(), elected
|
||||
const [elected, nominators, totalStaked] = extractSingle(allAccounts, amount, electedDerive, favorites, perValidatorReward, true);
|
||||
const [waiting] = extractSingle(allAccounts, amount, waitingDerive, favorites, perValidatorReward, false);
|
||||
const validators = sortValidators(elected.concat(waiting));
|
||||
const validatorIds = validators.map(({ accountId }) => accountId.toString());
|
||||
|
||||
return { nominators, totalStaked, validators };
|
||||
return { nominators, totalStaked, validatorIds, validators };
|
||||
}
|
||||
|
||||
export default function useSortedTargets (favorites: string[]): SortedTargets {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user