Adjust (some) .reduce/.map into loops (staking) (#7711)

This commit is contained in:
Jaco 2022-06-09 09:32:44 +02:00 committed by GitHub
parent a37634d604
commit bd97c28ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 173 additions and 134 deletions

View File

@ -33,42 +33,40 @@ function queryAddress (address: string): void {
window.location.hash = `/staking/query/${address}`;
}
function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSelected, nominatedBy = [], toggleFavorite, toggleSelected }: Props): React.ReactElement<Props> | null {
function Validator ({ allSlashes, canSelect, filterName, info: { accountId, bondOther, bondOwn, bondTotal, commissionPer, isBlocking, isElected, isFavorite, key, lastPayout, numNominators, rankOverall, stakedReturnCmp }, isNominated, isSelected, nominatedBy = [], toggleFavorite, toggleSelected }: Props): React.ReactElement<Props> | null {
const { t } = useTranslation();
const { api } = useApi();
const accountInfo = useDeriveAccountInfo(info.accountId);
const [,, time] = useBlockTime(info.lastPayout);
const accountInfo = useDeriveAccountInfo(accountId);
const [,, time] = useBlockTime(lastPayout);
const isVisible = useMemo(
() => accountInfo
? checkVisibility(api, info.key, accountInfo, filterName)
? checkVisibility(api, key, accountInfo, filterName)
: true,
[accountInfo, api, filterName, info]
[accountInfo, api, filterName, key]
);
const slashes = useMemo(
() => (allSlashes || [])
.map(([era, all]) => ({ era, slashes: all.filter(({ validator }) => validator.eq(info.accountId)) }))
.map(([era, all]) => ({ era, slashes: all.filter(({ validator }) => validator.eq(accountId)) }))
.filter(({ slashes }) => slashes.length),
[allSlashes, info]
[allSlashes, accountId]
);
const _onQueryStats = useCallback(
() => queryAddress(info.key),
[info.key]
() => queryAddress(key),
[key]
);
const _toggleSelected = useCallback(
() => toggleSelected(info.key),
[info.key, toggleSelected]
() => toggleSelected(key),
[key, toggleSelected]
);
if (!isVisible) {
return null;
}
const { accountId, bondOther, bondOwn, bondTotal, commissionPer, isBlocking, isElected, isFavorite, key, lastPayout, numNominators, rankOverall, stakedReturnCmp } = info;
return (
<tr>
<td className='badge together'>

View File

@ -62,7 +62,7 @@ const CLASSES: Record<string, string> = {
rankBondOwn: 'media--900'
};
const MAX_CAP_PERCENT = 100; // 75 if only using numNominators
const MAX_COMM_PERCENT = 20; // -1 for median
const MAX_COMM_PERCENT = 10; // -1 for median
const MAX_DAYS = 7;
const SORT_KEYS = ['rankBondTotal', 'rankBondOwn', 'rankBondOther', 'rankOverall'];
@ -95,7 +95,7 @@ function applyFilter (validators: ValidatorInfo[], medianComm: number, allIdenti
(!withPayout || !isBabe || (!!lastPayout && daysPayout.gte(lastPayout))) &&
(!withoutComm || (
MAX_COMM_PERCENT > 0
? (commissionPer < MAX_COMM_PERCENT)
? (commissionPer <= MAX_COMM_PERCENT)
: (!medianComm || (commissionPer <= medianComm)))
) &&
(!withoutOver || !maxPaid || maxPaid.muln(MAX_CAP_PERCENT).div(BN_HUNDRED).gten(nomCount))
@ -143,13 +143,15 @@ function sort (sortBy: TargetSortBy, sortFromMax: boolean, validators: Validator
// Use slice to create new array, so that sorting triggers component render
return validators
.slice(0)
.sort((a, b) => sortFromMax
? a[sortBy] - b[sortBy]
: b[sortBy] - a[sortBy]
.sort((a, b) =>
sortFromMax
? a[sortBy] - b[sortBy]
: b[sortBy] - a[sortBy]
)
.sort((a, b) => a.isFavorite === b.isFavorite
? 0
: (a.isFavorite ? -1 : 1)
.sort((a, b) =>
a.isFavorite === b.isFavorite
? 0
: (a.isFavorite ? -1 : 1)
);
}
@ -319,8 +321,8 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
className='staking--buttonToggle'
label={
MAX_COMM_PERCENT > 0
? t<string>('comm. < {{maxComm}}%', { replace: { maxComm: MAX_COMM_PERCENT } })
: t<string>('comm. < median')
? t<string>('comm. <= {{maxComm}}%', { replace: { maxComm: MAX_COMM_PERCENT } })
: t<string>('comm. <= median')
}
onChange={setToggle.withoutComm}
value={toggles.withoutComm}
@ -357,6 +359,7 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
const displayList = isQueryFiltered
? validators
: sorted;
const canSelect = selected.length < maxNominations;
return (
<div className={className}>
@ -402,7 +405,7 @@ function Targets ({ className = '', isInElection, nominatedBy, ownStashes, targe
{displayList && displayList.map((info): React.ReactNode =>
<Validator
allSlashes={allSlashes}
canSelect={selected.length < maxNominations}
canSelect={canSelect}
filterName={nameFilter}
info={info}
isNominated={myNominees.includes(info.key)}

View File

@ -9,7 +9,10 @@ import { createNamedHook } from '@polkadot/react-hooks';
function useOwnNominatorsImpl (ownStashes?: StakerState[]): StakerState[] | undefined {
return useMemo(
() => ownStashes && ownStashes.filter(({ isOwnController, isStashValidating }) => isOwnController && !isStashValidating),
() => ownStashes && ownStashes.filter(({ isOwnController, isStashValidating }) =>
isOwnController &&
!isStashValidating
),
[ownStashes]
);
}

View File

@ -25,10 +25,10 @@ function extractFunction (all: string[]): null | [number, () => React.ReactNode[
return all.length
? [
all.length,
() => all.map((who): React.ReactNode =>
() => all.map((value): React.ReactNode =>
<AddressMini
key={who}
value={who}
key={value}
value={value}
/>
)
]
@ -38,7 +38,10 @@ function extractFunction (all: string[]): null | [number, () => React.ReactNode[
function extractChilled (nominators: NominatedByType[] = [], slashingSpans?: SlashingSpans | null): Chilled {
const chilled = slashingSpans
? nominators
.filter(({ submittedIn }) => !slashingSpans.lastNonzeroSlash.isZero() && slashingSpans.lastNonzeroSlash.gte(submittedIn))
.filter(({ submittedIn }) =>
!slashingSpans.lastNonzeroSlash.isZero() &&
slashingSpans.lastNonzeroSlash.gte(submittedIn)
)
.map(({ nominatorId }) => nominatorId)
: [];

View File

@ -31,6 +31,16 @@ function extractFunction (all: NominatorValue[]): null | [number, () => React.Re
: null;
}
function sumValue (all: { value: BN }[]): BN {
const total = new BN(0);
for (let i = 0; i < all.length; i++) {
total.iadd(all[i].value);
}
return total;
}
function extractTotals (maxPaid: BN | undefined, nominators: NominatorValue[], stakeOther?: BN): [null | [number, () => React.ReactNode[]], BN, null | [number, () => React.ReactNode[]], BN] {
const sorted = nominators.sort((a, b) => b.value.cmp(a.value));
@ -40,9 +50,9 @@ function extractTotals (maxPaid: BN | undefined, nominators: NominatorValue[], s
const max = maxPaid.toNumber();
const rewarded = sorted.slice(0, max);
const rewardedTotal = rewarded.reduce((total, { value }) => total.iadd(value), new BN(0));
const rewardedTotal = sumValue(rewarded);
const unrewarded = sorted.slice(max);
const unrewardedTotal = unrewarded.reduce((total, { value }) => total.iadd(value), new BN(0));
const unrewardedTotal = sumValue(unrewarded);
return [extractFunction(rewarded), rewardedTotal, extractFunction(unrewarded), unrewardedTotal];
}

View File

@ -57,7 +57,10 @@ function expandInfo ({ exposure, validatorPrefs }: ValidatorInfo, minCommission?
let stakeOwn: BN | undefined;
if (exposure && exposure.total) {
nominators = exposure.others.map(({ value, who }) => ({ nominatorId: who.toString(), value: value.unwrap() }));
nominators = exposure.others.map(({ value, who }) => ({
nominatorId: who.toString(),
value: value.unwrap()
}));
stakeTotal = exposure.total?.unwrap() || BN_ZERO;
stakeOwn = exposure.own.unwrap();
stakeOther = stakeTotal.sub(stakeOwn);

View File

@ -71,7 +71,13 @@ function filterAccounts (isOwn: boolean, accounts: string[] = [], ownStashIds: s
}
function accountsToString (accounts: AccountId[]): string[] {
return accounts.map((a) => a.toString());
const result = new Array<string>(accounts.length);
for (let i = 0; i < accounts.length; i++) {
result[i] = accounts[i].toString();
}
return result;
}
function getFiltered (isOwn: boolean, stakingOverview: DeriveStakingOverview | undefined, favorites: string[], next?: string[], ownStashIds?: string[]): Filtered {
@ -90,6 +96,18 @@ function getFiltered (isOwn: boolean, stakingOverview: DeriveStakingOverview | u
};
}
function mapValidators (infos: ValidatorInfo[]): Record<string, ValidatorInfo> {
const result: Record<string, ValidatorInfo> = {};
for (let i = 0; i < infos.length; i++) {
const info = infos[i];
result[info.key] = info;
}
return result;
}
const DEFAULT_PARAS = {};
function CurrentList ({ className, favorites, hasQueries, isIntentions, isOwn, minCommission, nominatedBy, ownStashIds, paraValidators = DEFAULT_PARAS, recentlyOnline, stakingOverview, targets, toggleFavorite }: Props): React.ReactElement<Props> | null {
@ -116,11 +134,7 @@ function CurrentList ({ className, favorites, hasQueries, isIntentions, isOwn, m
);
const infoMap = useMemo(
() => targets.validators && targets.validators.reduce<Record<string, ValidatorInfo>>((result, info) => {
result[info.key] = info;
return result;
}, {}),
() => targets.validators && mapValidators(targets.validators),
[targets]
);

View File

@ -9,11 +9,13 @@ type Result = Record<string, DeriveHasIdentity>;
const OPT_CALL = {
transform: ([[validatorIds], hasIdentities]: [[string[]], DeriveHasIdentity[]]): Record<string, DeriveHasIdentity> => {
return validatorIds.reduce((result: Record<string, DeriveHasIdentity>, validatorId, index): Record<string, DeriveHasIdentity> => {
result[validatorId] = hasIdentities[index];
const result: Record<string, DeriveHasIdentity> = {};
return result;
}, {});
for (let i = 0; i < validatorIds.length; i++) {
result[validatorIds[i]] = hasIdentities[i];
}
return result;
},
withParamsTransform: true
};

View File

@ -10,25 +10,28 @@ import { useMemo } from 'react';
import { createNamedHook, useApi, useCall } from '@polkadot/react-hooks';
function extractNominators (nominations: [StorageKey, Option<Nominations>][]): NominatedByMap {
return nominations.reduce((mapped: NominatedByMap, [key, optNoms]) => {
const mapped: NominatedByMap = {};
for (let i = 0; i < nominations.length; i++) {
const [key, optNoms] = nominations[i];
if (optNoms.isSome && key.args.length) {
const nominatorId = key.args[0].toString();
const { submittedIn, targets } = optNoms.unwrap();
targets.forEach((_validatorId, index): void => {
const validatorId = _validatorId.toString();
const info = { index: index + 1, nominatorId, submittedIn };
for (let j = 0; j < targets.length; j++) {
const validatorId = targets[j].toString();
if (!mapped[validatorId]) {
mapped[validatorId] = [info];
} else {
mapped[validatorId].push(info);
mapped[validatorId] = [];
}
});
}
return mapped;
}, {});
mapped[validatorId].push({ index: j + 1, nominatorId, submittedIn });
}
}
}
return mapped;
}
function useNominationsImpl (isActive = true): NominatedByMap | undefined {

View File

@ -12,10 +12,12 @@ import { createNamedHook, useAccounts, useApi, useCall } from '@polkadot/react-h
import { createAccounts } from './usePoolAccounts';
const OPT_MULTI = {
transform: ([[ids], opts]: [[string[]], Option<PalletNominationPoolsPoolMember>[]]): OwnPoolBase[] =>
ids.reduce((pools: OwnPoolBase[], accountId, index): OwnPoolBase[] => {
if (opts[index].isSome) {
const member = opts[index].unwrap();
transform: ([[ids], opts]: [[string[]], Option<PalletNominationPoolsPoolMember>[]]): OwnPoolBase[] => {
const pools: OwnPoolBase[] = [];
for (let i = 0; i < ids.length; i++) {
if (opts[i].isSome) {
const member = opts[i].unwrap();
let entry = pools.find(({ poolId }) => poolId.eq(member.poolId));
if (!entry) {
@ -24,11 +26,12 @@ const OPT_MULTI = {
pools.push(entry);
}
entry.members[accountId] = member;
entry.members[ids[i]] = member;
}
}
return pools;
}, []),
return pools;
},
withParamsTransform: true
};

View File

@ -34,6 +34,35 @@ const EMPTY_PARTIAL: Partial<SortedTargets> = {};
const DEFAULT_FLAGS_ELECTED = { withController: true, withExposure: true, withPrefs: true };
const DEFAULT_FLAGS_WAITING = { withController: true, withPrefs: true };
const OPT_ERA = {
transform: ({ activeEra, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
activeEra,
eraLength,
lastEra: activeEra.isZero()
? BN_ZERO
: activeEra.sub(BN_ONE),
sessionLength
})
};
const OPT_MULTI = {
defaultValue: {},
transform: ([historyDepth, counterForNominators, counterForValidators, optMaxNominatorsCount, optMaxValidatorsCount, minNominatorBond, minValidatorBond, totalIssuance]: [BN, BN?, BN?, Option<u32>?, Option<u32>?, BN?, BN?, BN?]): MultiResult => ({
counterForNominators,
counterForValidators,
historyDepth,
maxNominatorsCount: optMaxNominatorsCount && optMaxNominatorsCount.isSome
? optMaxNominatorsCount.unwrap()
: undefined,
maxValidatorsCount: optMaxValidatorsCount && optMaxValidatorsCount.isSome
? optMaxValidatorsCount.unwrap()
: undefined,
minNominatorBond,
minValidatorBond,
totalIssuance
})
};
function mapIndex (mapBy: TargetSortBy): (info: ValidatorInfo, index: number) => ValidatorInfo {
return (info, index): ValidatorInfo => {
info[mapBy] = index + 1;
@ -50,19 +79,17 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
const existing: string[] = [];
return list
.filter((a): boolean => {
const s = a.accountId.toString();
.filter(({ accountId }): boolean => {
const key = accountId.toString();
if (!existing.includes(s)) {
existing.push(s);
if (existing.includes(key)) {
return false;
} else {
existing.push(key);
return true;
}
return false;
})
// .filter((a) => a.bondTotal.gtn(0))
// ignored, not used atm
// .sort((a, b) => b.commissionPer - a.commissionPer)
// .map(mapIndex('rankComm'))
.sort((a, b) => b.bondOther.cmp(a.bondOther))
@ -75,7 +102,6 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
// .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) =>
@ -95,12 +121,22 @@ function extractSingle (api: ApiPromise, allAccounts: string[], derive: DeriveSt
const nominators: Record<string, BN> = {};
const emptyExposure = api.createType('Exposure');
const earliestEra = historyDepth && lastEra.sub(historyDepth).iadd(BN_ONE);
const list = derive.info.map(({ accountId, exposure = emptyExposure, stakingLedger, validatorPrefs }): ValidatorInfo => {
const list = new Array<ValidatorInfo>(derive.info.length);
for (let i = 0; i < derive.info.length; i++) {
const { accountId, exposure = emptyExposure, stakingLedger, validatorPrefs } = derive.info[i];
// some overrides (e.g. Darwinia Crab) does not have the own/total field in Exposure
let [bondOwn, bondTotal] = exposure.total
? [exposure.own.unwrap(), exposure.total.unwrap()]
: [BN_ZERO, BN_ZERO];
if (bondTotal.isZero()) {
bondTotal = bondOwn = stakingLedger.total?.unwrap() || BN_ZERO;
}
const skipRewards = bondTotal.isZero();
// some overrides (e.g. Darwinia Crab) does not have the value field in IndividualExposure
const minNominated = (exposure.others || []).reduce((min: BN, { value = api.createType('Compact<Balance>') }): BN => {
const actual = value.unwrap();
@ -110,25 +146,12 @@ function extractSingle (api: ApiPromise, allAccounts: string[], derive: DeriveSt
: min;
}, BN_ZERO);
if (bondTotal.isZero()) {
bondTotal = bondOwn = stakingLedger.total?.unwrap() || BN_ZERO;
}
const key = accountId.toString();
const lastEraPayout = !lastEra.isZero()
? stakingLedger.claimedRewards[stakingLedger.claimedRewards.length - 1]
: undefined;
// only use if it is more recent than historyDepth
let lastPayout: BN | undefined = earliestEra && lastEraPayout && lastEraPayout.gt(earliestEra)
? lastEraPayout
: undefined;
if (lastPayout && !sessionLength.eq(BN_ONE)) {
lastPayout = lastEra.sub(lastPayout).mul(eraLength);
}
return {
list[i] = {
accountId,
bondOther: bondTotal.sub(bondOwn),
bondOwn,
@ -149,7 +172,10 @@ function extractSingle (api: ApiPromise, allAccounts: string[], derive: DeriveSt
}, allAccounts.includes(key)),
key,
knownLength: activeEra.sub(stakingLedger.claimedRewards[0] || activeEra),
lastPayout,
// only use if it is more recent than historyDepth
lastPayout: earliestEra && lastEraPayout && lastEraPayout.gt(earliestEra) && !sessionLength.eq(BN_ONE)
? lastEra.sub(lastEraPayout).mul(eraLength)
: undefined,
minNominated,
numNominators: (exposure.others || []).length,
numRecentPayouts: earliestEra
@ -167,7 +193,7 @@ function extractSingle (api: ApiPromise, allAccounts: string[], derive: DeriveSt
validatorPrefs,
withReturns
};
});
}
return [list, nominators];
}
@ -245,35 +271,6 @@ function extractBaseInfo (api: ApiPromise, allAccounts: string[], electedDerive:
};
}
const OPT_ERA = {
transform: ({ activeEra, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
activeEra,
eraLength,
lastEra: activeEra.isZero()
? BN_ZERO
: activeEra.sub(BN_ONE),
sessionLength
})
};
const OPT_MULTI = {
defaultValue: {},
transform: ([historyDepth, counterForNominators, counterForValidators, optMaxNominatorsCount, optMaxValidatorsCount, minNominatorBond, minValidatorBond, totalIssuance]: [BN, BN?, BN?, Option<u32>?, Option<u32>?, BN?, BN?, BN?]): MultiResult => ({
counterForNominators,
counterForValidators,
historyDepth,
maxNominatorsCount: optMaxNominatorsCount && optMaxNominatorsCount.isSome
? optMaxNominatorsCount.unwrap()
: undefined,
maxValidatorsCount: optMaxValidatorsCount && optMaxValidatorsCount.isSome
? optMaxValidatorsCount.unwrap()
: undefined,
minNominatorBond,
minValidatorBond,
totalIssuance
})
};
function useSortedTargetsImpl (favorites: string[], withLedger: boolean): SortedTargets {
const { api } = useApi();
const { allAccounts } = useAccounts();
@ -300,26 +297,26 @@ function useSortedTargetsImpl (favorites: string[], withLedger: boolean): Sorted
const inflation = useInflation(baseInfo?.totalStaked);
const partial = useMemo(
() => inflation && inflation.stakedReturn
? addReturns(inflation, baseInfo)
: baseInfo,
[baseInfo, inflation]
return useMemo(
(): SortedTargets => ({
counterForNominators,
counterForValidators,
historyDepth,
inflation,
maxNominatorsCount,
maxValidatorsCount,
medianComm: 0,
minNominated: BN_ZERO,
minNominatorBond,
minValidatorBond,
...(
inflation && inflation.stakedReturn
? addReturns(inflation, baseInfo)
: baseInfo
)
}),
[baseInfo, counterForNominators, counterForValidators, historyDepth, inflation, maxNominatorsCount, maxValidatorsCount, minNominatorBond, minValidatorBond]
);
return {
counterForNominators,
counterForValidators,
historyDepth,
inflation,
maxNominatorsCount,
maxValidatorsCount,
medianComm: 0,
minNominated: BN_ZERO,
minNominatorBond,
minValidatorBond,
...partial
};
}
export default createNamedHook('useSortedTargets', useSortedTargetsImpl);