Small target opts (incl. non-selected sorting) (#4159)

* Small target opts (incl. non-selected sorting)

* Remove last dangling validatorPayment
This commit is contained in:
Jaco Greeff 2020-12-04 16:21:50 +01:00 committed by GitHub
parent 6bef8aa443
commit f86cc1a689
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 42 additions and 62 deletions

View File

@ -4,7 +4,11 @@
import BN from 'bn.js';
import React, { useMemo } from 'react';
import type { DeriveSessionIndexes } from '@polkadot/api-derive/types';
import type { Option } from '@polkadot/types';
import type { Balance } from '@polkadot/types/interfaces';
import { CardSummary, SummaryBox } from '@polkadot/react-components';
import { useApi, useCall } from '@polkadot/react-hooks';
import { FormatBalance } from '@polkadot/react-query';
import { useTranslation } from '../translate';
@ -12,7 +16,6 @@ import { useTranslation } from '../translate';
interface Props {
avgStaked?: BN;
lowStaked?: BN;
lastReward?: BN;
numNominators?: number;
numValidators?: number;
stakedReturn: number;
@ -20,8 +23,19 @@ interface Props {
totalStaked?: BN;
}
function Summary ({ avgStaked, lastReward, lowStaked, numNominators, numValidators, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement<Props> {
const transformReward = {
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
};
const transformEra = {
transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gtn(0) ? activeEra.subn(1) : undefined
};
function Summary ({ avgStaked, lowStaked, numNominators, numValidators, stakedReturn, totalIssuance, totalStaked }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const lastEra = useCall<BN | undefined>(api.derive.session.indexes, undefined, transformEra);
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], transformReward);
const progressStake = useMemo(
() => totalIssuance && totalStaked && totalStaked.gtn(0)

View File

@ -67,7 +67,7 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
return null;
}
const { accountId, bondOther, bondOwn, bondTotal, commissionPer, isCommission, isElected, isFavorite, key, lastPayout, numNominators, rankOverall, stakedReturnCmp, validatorPayment } = info;
const { accountId, bondOther, bondOwn, bondTotal, commissionPer, isCommission, isElected, isFavorite, key, lastPayout, numNominators, rankOverall, stakedReturnCmp } = info;
return (
<tr>
@ -125,13 +125,7 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
</td>
<td className='number media--1200 no-pad-right'>{numNominators || ''}</td>
<td className='number media--1200 no-pad-left'>{nominatedBy.length || ''}</td>
<td className='number media--1100'>
{
isCommission
? `${commissionPer.toFixed(2)}%`
: <FormatBalance value={validatorPayment} />
}
</td>
<td className='number media--1100'>{isCommission && `${commissionPer.toFixed(2)}%`}</td>
<td className='number together'>{!bondTotal.isZero() && <FormatBalance value={bondTotal} />}</td>
<td className='number together media--900'>{!bondOwn.isZero() && <FormatBalance value={bondOwn} />}</td>
<td className='number together media--1600'>{!bondOther.isZero() && <FormatBalance value={bondOther} />}</td>

View File

@ -169,7 +169,7 @@ function selectProfitable (list: ValidatorInfo[]): string[] {
return result;
}
function Targets ({ className = '', isInElection, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lastReward, lowStaked, medianComm, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite }: Props): React.ReactElement<Props> {
function Targets ({ className = '', isInElection, ownStashes, targets: { avgStaked, inflation: { stakedReturn }, lowStaked, medianComm, nominators, totalIssuance, totalStaked, validatorIds, validators }, toggleFavorite }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const allSlashes = useAvailableSlashes();
@ -270,7 +270,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
<>{labelsRef.current[header]}<Icon icon={sortBy === header ? (sortFromMax ? 'chevron-down' : 'chevron-up') : 'minus'} /></>,
`${sorted ? `isClickable ${sortBy === header ? 'highlight--border' : ''} number` : 'number'} ${CLASSES[header] || ''}`,
1,
() => _sort(header as 'rankComm')
() => _sort(header as 'rankOverall')
]),
[],
[]
@ -337,7 +337,6 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
<div className={className}>
<Summary
avgStaked={avgStaked}
lastReward={lastReward}
lowStaked={lowStaked}
numNominators={nominators?.length}
numValidators={validators?.length}

View File

@ -38,10 +38,8 @@ interface ValidatorInfoRank {
rankBondOther: number;
rankBondOwn: number;
rankBondTotal: number;
rankComm: number;
rankNumNominators: number;
rankOverall: number;
rankPayment: number;
rankReward: number;
}
@ -63,11 +61,9 @@ export interface ValidatorInfo extends ValidatorInfoRank {
lastPayout?: BN;
numNominators: number;
numRecentPayouts: number;
rewardSplit: BN;
skipRewards: boolean;
stakedReturn: number;
stakedReturnCmp: number;
validatorPayment: BN;
validatorPrefs?: ValidatorPrefs | ValidatorPrefsTo196;
}
@ -77,7 +73,6 @@ export interface SortedTargets {
avgStaked?: BN;
electedIds?: string[];
inflation: Inflation;
lastReward?: BN;
lowStaked?: BN;
medianComm: number;
nominators?: string[];

View File

@ -6,8 +6,6 @@ import { useMemo } from 'react';
import type { ApiPromise } from '@polkadot/api';
import type { DeriveSessionInfo, DeriveStakingElected, DeriveStakingWaiting } from '@polkadot/api-derive/types';
import type { Option } from '@polkadot/types';
import type { Balance, ValidatorPrefsTo196 } from '@polkadot/types/interfaces';
import { registry } from '@polkadot/react-api';
import { calcInflation, useAccounts, useApi, useCall } from '@polkadot/react-hooks';
import { BN_ONE, BN_ZERO } from '@polkadot/util';
@ -18,11 +16,9 @@ interface LastEra {
activeEra: BN;
eraLength: BN;
lastEra: BN;
lastEraEnd: BN;
sessionLength: BN;
}
const PERBILL = new BN(1_000_000_000);
const EMPTY_PARTIAL = {};
function mapIndex (mapBy: TargetSortBy): (info: ValidatorInfo, index: number) => ValidatorInfo {
@ -62,24 +58,18 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
.map(mapIndex('rankBondOwn'))
.sort((a, b) => b.bondTotal.cmp(a.bondTotal))
.map(mapIndex('rankBondTotal'))
.sort((a, b) => b.validatorPayment.cmp(a.validatorPayment))
.map(mapIndex('rankPayment'))
// .sort((a, b) => b.validatorPayment.cmp(a.validatorPayment))
// .map(mapIndex('rankPayment'))
.sort((a, b) => a.stakedReturnCmp - b.stakedReturnCmp)
.map(mapIndex('rankReward'))
// ignored, not used atm
// .sort((a, b) => b.numNominators - a.numNominators)
// .map(mapIndex('rankNumNominators'))
.sort((a, b): number => {
const cmp = b.stakedReturnCmp - a.stakedReturnCmp;
return cmp !== 0
? cmp
: a.rankReward === b.rankReward
? a.rankPayment === b.rankPayment
? b.rankBondTotal - a.rankBondTotal
: b.rankPayment - a.rankPayment
: b.rankReward - a.rankReward;
})
.sort((a, b) =>
(b.stakedReturnCmp - a.stakedReturnCmp) ||
(a.commissionPer - b.commissionPer) ||
(b.rankBondTotal - a.rankBondTotal)
)
.map(mapIndex('rankOverall'))
.sort((a, b) =>
a.isFavorite === b.isFavorite
@ -88,7 +78,7 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
);
}
function extractSingle (allAccounts: string[], derive: DeriveStakingElected | DeriveStakingWaiting, favorites: string[], perValidatorReward: BN, { activeEra, eraLength, lastEra, sessionLength }: LastEra, historyDepth?: BN): [ValidatorInfo[], string[]] {
function extractSingle (allAccounts: string[], derive: DeriveStakingElected | DeriveStakingWaiting, favorites: string[], { activeEra, eraLength, lastEra, sessionLength }: LastEra, historyDepth?: BN): [ValidatorInfo[], string[]] {
const nominators: Record<string, boolean> = {};
const emptyExposure = registry.createType('Exposure');
const earliestEra = historyDepth && lastEra.sub(historyDepth).addn(1);
@ -102,9 +92,6 @@ function extractSingle (allAccounts: string[], derive: DeriveStakingElected | De
bondTotal = bondOwn = stakingLedger.total.unwrap();
}
const validatorPayment = (validatorPrefs as unknown as ValidatorPrefsTo196).validatorPayment
? (validatorPrefs as unknown as ValidatorPrefsTo196).validatorPayment.unwrap() as BN
: validatorPrefs.commission.unwrap().mul(perValidatorReward).div(PERBILL);
const key = accountId.toString();
const lastEraPayout = !lastEra.isZero()
? stakingLedger.claimedRewards[stakingLedger.claimedRewards.length - 1]
@ -148,16 +135,12 @@ function extractSingle (allAccounts: string[], derive: DeriveStakingElected | De
rankBondOther: 0,
rankBondOwn: 0,
rankBondTotal: 0,
rankComm: 0,
rankNumNominators: 0,
rankOverall: 0,
rankPayment: 0,
rankReward: 0,
rewardSplit: perValidatorReward.sub(validatorPayment),
skipRewards,
stakedReturn: 0,
stakedReturnCmp: 0,
validatorPayment,
validatorPrefs
};
});
@ -165,10 +148,9 @@ function extractSingle (allAccounts: string[], derive: DeriveStakingElected | De
return [list, Object.keys(nominators)];
}
function extractInfo (api: ApiPromise, allAccounts: string[], electedDerive: DeriveStakingElected, waitingDerive: DeriveStakingWaiting, favorites: string[], totalIssuance: BN, lastEraInfo: LastEra, lastReward: BN, historyDepth?: BN): Partial<SortedTargets> {
const perValidatorReward = lastReward.divn(electedDerive.info.length);
const [elected, nominators] = extractSingle(allAccounts, electedDerive, favorites, perValidatorReward, lastEraInfo, historyDepth);
const [waiting] = extractSingle(allAccounts, waitingDerive, favorites, perValidatorReward, lastEraInfo);
function extractInfo (api: ApiPromise, allAccounts: string[], electedDerive: DeriveStakingElected, waitingDerive: DeriveStakingWaiting, favorites: string[], totalIssuance: BN, lastEraInfo: LastEra, historyDepth?: BN): Partial<SortedTargets> {
const [elected, nominators] = extractSingle(allAccounts, electedDerive, favorites, lastEraInfo, historyDepth);
const [waiting] = extractSingle(allAccounts, waitingDerive, favorites, lastEraInfo);
const activeTotals = elected
.filter(({ isActive }) => isActive)
.map(({ bondTotal }) => bondTotal)
@ -215,19 +197,14 @@ function extractInfo (api: ApiPromise, allAccounts: string[], electedDerive: Der
}
const transformEra = {
transform: ({ activeEra, activeEraStart, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
transform: ({ activeEra, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
activeEra,
eraLength,
lastEra: activeEra.gtn(0) ? activeEra.subn(1) : BN_ZERO,
lastEraEnd: activeEra.gtn(0) && activeEraStart.isSome ? activeEraStart.unwrap() : BN_ZERO,
lastEra: activeEra.isZero() ? BN_ZERO : activeEra.subn(1),
sessionLength
})
};
const transformReward = {
transform: (optBalance: Option<Balance>) => optBalance.unwrapOrDefault()
};
export default function useSortedTargets (favorites: string[]): SortedTargets {
const { api } = useApi();
const { allAccounts } = useAccounts();
@ -236,14 +213,13 @@ export default function useSortedTargets (favorites: string[]): SortedTargets {
const electedInfo = useCall<DeriveStakingElected>(api.derive.staking.electedInfo);
const waitingInfo = useCall<DeriveStakingWaiting>(api.derive.staking.waitingInfo);
const lastEraInfo = useCall<LastEra>(api.derive.session.info, undefined, transformEra);
const lastReward = useCall<BN>(lastEraInfo && api.query.staking.erasValidatorReward, [lastEraInfo?.lastEra], transformReward);
const partial = useMemo(
() => electedInfo && lastEraInfo && lastReward && totalIssuance && waitingInfo
? extractInfo(api, allAccounts, electedInfo, waitingInfo, favorites, totalIssuance, lastEraInfo, lastReward, historyDepth)
() => electedInfo && lastEraInfo && totalIssuance && waitingInfo
? extractInfo(api, allAccounts, electedInfo, waitingInfo, favorites, totalIssuance, lastEraInfo, historyDepth)
: EMPTY_PARTIAL,
[api, allAccounts, electedInfo, favorites, historyDepth, lastEraInfo, lastReward, totalIssuance, waitingInfo]
[api, allAccounts, electedInfo, favorites, historyDepth, lastEraInfo, totalIssuance, waitingInfo]
);
return { inflation: { inflation: 0, stakedReturn: 0 }, medianComm: 0, ...partial, lastReward };
return { inflation: { inflation: 0, stakedReturn: 0 }, medianComm: 0, ...partial };
}

View File

@ -183,7 +183,9 @@ function AccountName ({ children, className = '', defaultName, label, onClick, o
}
export default React.memo(styled(AccountName)(({ theme }: ThemeProps) => `
align-items: center;
border: 1px dotted transparent;
display: inline-flex;
vertical-align: middle;
white-space: nowrap;
@ -198,6 +200,8 @@ export default React.memo(styled(AccountName)(({ theme }: ThemeProps) => `
width: 100%;
.name {
align-items: center;
display: inline-flex;
font-weight: 400 !important;
filter: grayscale(100%);
line-height: 1;

View File

@ -42,8 +42,7 @@ function AddressSmall ({ children, className = '', defaultName, onClickName, ove
export default React.memo(styled(AddressSmall)`
white-space: nowrap;
.ui--IdentityIcon,
.ui--AccountName {
.ui--IdentityIcon{
vertical-align: middle;
}
@ -52,7 +51,6 @@ export default React.memo(styled(AddressSmall)`
}
.ui--AccountName {
display: inline-block;
max-width: 26rem;
overflow: hidden;