Staking details query optimizations (#3618)
* Staking details query optimizations * Map lookups * Extras
This commit is contained in:
parent
5faf9cba25
commit
ca4b0b9cca
@ -17,6 +17,7 @@ Changes:
|
||||
|
||||
- Add individual era-slash cancels on staking slashes page
|
||||
- Adjust staking slashes with per-era selector
|
||||
- Staking list details now fully shared with targets (query optimizations)
|
||||
- Adjust max payout batch size (40 txs for 64 payees) based on staking payout constant
|
||||
- Add "copy seed" button to align with the extension UX on new accounts
|
||||
- Combine confirmation/backup of imports/new account into the modals (no double-modal)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import { Badge, Icon } from '@polkadot/react-components';
|
||||
|
||||
@ -11,7 +12,7 @@ interface Props {
|
||||
isElected: boolean;
|
||||
isMain?: boolean;
|
||||
numNominators?: number;
|
||||
onlineCount?: false | number;
|
||||
onlineCount?: false | BN;
|
||||
onlineMessage?: boolean;
|
||||
}
|
||||
|
||||
@ -32,7 +33,7 @@ function Status ({ isElected, isMain, numNominators, onlineCount, onlineMessage
|
||||
? (
|
||||
<Badge
|
||||
color='green'
|
||||
info={onlineCount || <Icon icon='envelope' />}
|
||||
info={onlineCount ? onlineCount.toNumber() : <Icon icon='envelope' />}
|
||||
/>
|
||||
)
|
||||
: <Badge color='transparent' />
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Balance, EraIndex, SlashingSpans } from '@polkadot/types/interfaces';
|
||||
import { DeriveAccountInfo, DeriveStakingQuery } from '@polkadot/api-derive/types';
|
||||
import { Balance, EraIndex, SlashingSpans, ValidatorPrefs } from '@polkadot/types/interfaces';
|
||||
import { DeriveAccountInfo } from '@polkadot/api-derive/types';
|
||||
import { ValidatorInfo } from '../../types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
@ -29,10 +30,11 @@ interface Props {
|
||||
isMain?: boolean;
|
||||
lastBlock?: string;
|
||||
nominatedBy?: [string, EraIndex, number][];
|
||||
onlineCount?: false | number;
|
||||
onlineCount?: false | BN;
|
||||
onlineMessage?: boolean;
|
||||
points?: string;
|
||||
toggleFavorite: (accountId: string) => void;
|
||||
validatorInfo?: ValidatorInfo;
|
||||
withIdentity: boolean;
|
||||
}
|
||||
|
||||
@ -44,9 +46,7 @@ interface StakingState {
|
||||
stakeOwn?: BN;
|
||||
}
|
||||
|
||||
const PERBILL_PERCENT = 10_000_000;
|
||||
|
||||
function expandInfo ({ exposure, validatorPrefs }: DeriveStakingQuery): StakingState {
|
||||
function expandInfo ({ exposure, validatorPrefs }: ValidatorInfo): StakingState {
|
||||
let nominators: [string, Balance][] = [];
|
||||
let stakeTotal: BN | undefined;
|
||||
let stakeOther: BN | undefined;
|
||||
@ -59,12 +59,10 @@ function expandInfo ({ exposure, validatorPrefs }: DeriveStakingQuery): StakingS
|
||||
stakeOther = stakeTotal.sub(stakeOwn);
|
||||
}
|
||||
|
||||
const commission = validatorPrefs?.commission?.unwrap();
|
||||
const commission = (validatorPrefs as ValidatorPrefs)?.commission?.unwrap();
|
||||
|
||||
return {
|
||||
commission: commission
|
||||
? `${(commission.toNumber() / PERBILL_PERCENT).toFixed(2)}%`
|
||||
: undefined,
|
||||
commission: commission?.toHuman(),
|
||||
nominators,
|
||||
stakeOther,
|
||||
stakeOwn,
|
||||
@ -80,18 +78,17 @@ function useAddressCalls (api: ApiPromise, address: string, isMain?: boolean) {
|
||||
const params = useMemo(() => [address], [address]);
|
||||
const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, params);
|
||||
const slashingSpans = useCall<SlashingSpans | null>(!isMain && api.query.staking.slashingSpans, params, transformSlashes);
|
||||
const stakingInfo = useCall<DeriveStakingQuery>(api.derive.staking.query, params);
|
||||
|
||||
return { accountInfo, slashingSpans, stakingInfo };
|
||||
return { accountInfo, slashingSpans };
|
||||
}
|
||||
|
||||
function Address ({ address, className = '', filterName, hasQueries, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, withIdentity }: Props): React.ReactElement<Props> | null {
|
||||
function Address ({ address, className = '', filterName, hasQueries, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, validatorInfo, withIdentity }: Props): React.ReactElement<Props> | null {
|
||||
const { api } = useApi();
|
||||
const { accountInfo, slashingSpans, stakingInfo } = useAddressCalls(api, address, isMain);
|
||||
const { accountInfo, slashingSpans } = useAddressCalls(api, address, isMain);
|
||||
|
||||
const { commission, nominators, stakeOther, stakeOwn } = useMemo(
|
||||
() => stakingInfo ? expandInfo(stakingInfo) : { nominators: [] },
|
||||
[stakingInfo]
|
||||
() => validatorInfo ? expandInfo(validatorInfo) : { nominators: [] },
|
||||
[validatorInfo]
|
||||
);
|
||||
|
||||
const isVisible = useMemo(
|
||||
@ -121,7 +118,7 @@ function Address ({ address, className = '', filterName, hasQueries, isElected,
|
||||
<Status
|
||||
isElected={isElected}
|
||||
isMain={isMain}
|
||||
numNominators={nominatedBy?.length || nominators.length}
|
||||
numNominators={(nominatedBy || nominators).length}
|
||||
onlineCount={onlineCount}
|
||||
onlineMessage={onlineMessage}
|
||||
/>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
import { DeriveHeartbeats, DeriveStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { AccountId, EraIndex, Nominations } from '@polkadot/types/interfaces';
|
||||
import { Authors } from '@polkadot/react-query/BlockAuthors';
|
||||
import { SortedTargets, ValidatorInfo } from '../types';
|
||||
|
||||
import React, { useCallback, useContext, useMemo, useRef, useState } from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
@ -23,6 +24,7 @@ interface Props {
|
||||
next?: string[];
|
||||
setNominators?: (nominators: string[]) => void;
|
||||
stakingOverview?: DeriveStakingOverview;
|
||||
targets: SortedTargets;
|
||||
toggleFavorite: (address: string) => void;
|
||||
}
|
||||
|
||||
@ -91,7 +93,7 @@ function extractNominators (nominations: [StorageKey, Option<Nominations>][]): R
|
||||
}, {});
|
||||
}
|
||||
|
||||
function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOverview, toggleFavorite }: Props): React.ReactElement<Props> | null {
|
||||
function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOverview, targets, toggleFavorite }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const { byAuthor, eraPoints } = useContext(isIntentions ? EmptyAuthorsContext : BlockAuthorsContext);
|
||||
@ -108,6 +110,15 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
|
||||
[favorites, next, stakingOverview]
|
||||
);
|
||||
|
||||
const infoMap = useMemo(
|
||||
() => (targets?.validators || []).reduce((result: Record<string, ValidatorInfo>, info): Record<string, ValidatorInfo> => {
|
||||
result[info.accountId.toString()] = info;
|
||||
|
||||
return result;
|
||||
}, {}),
|
||||
[targets]
|
||||
);
|
||||
|
||||
const nominatedBy = useMemo(
|
||||
() => nominators ? extractNominators(nominators) : null,
|
||||
[nominators]
|
||||
@ -145,14 +156,15 @@ function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOvervi
|
||||
key={address}
|
||||
lastBlock={byAuthor[address]}
|
||||
nominatedBy={nominatedBy ? (nominatedBy[address] || []) : undefined}
|
||||
onlineCount={recentlyOnline?.[address]?.blockCount.toNumber()}
|
||||
onlineCount={recentlyOnline?.[address]?.blockCount}
|
||||
onlineMessage={recentlyOnline?.[address]?.hasMessage}
|
||||
points={eraPoints[address]}
|
||||
toggleFavorite={toggleFavorite}
|
||||
validatorInfo={infoMap[address]}
|
||||
withIdentity={withIdentity}
|
||||
/>
|
||||
)),
|
||||
[byAuthor, eraPoints, hasQueries, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, withIdentity]
|
||||
[byAuthor, eraPoints, hasQueries, infoMap, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, withIdentity]
|
||||
);
|
||||
|
||||
return isIntentions
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DeriveStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { SortedTargets } from '../types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
@ -15,10 +16,11 @@ interface Props {
|
||||
isIntentions?: boolean;
|
||||
next?: string[];
|
||||
stakingOverview?: DeriveStakingOverview;
|
||||
targets: SortedTargets;
|
||||
toggleFavorite: (address: string) => void;
|
||||
}
|
||||
|
||||
function Overview ({ className = '', favorites, hasQueries, isIntentions, next, stakingOverview, toggleFavorite }: Props): React.ReactElement<Props> {
|
||||
function Overview ({ className = '', favorites, hasQueries, isIntentions, next, stakingOverview, targets, toggleFavorite }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<div className={`staking--Overview ${className}`}>
|
||||
<CurrentList
|
||||
@ -27,6 +29,7 @@ function Overview ({ className = '', favorites, hasQueries, isIntentions, next,
|
||||
isIntentions={isIntentions}
|
||||
next={next}
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -155,6 +155,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
isIntentions
|
||||
next={next}
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
</Route>
|
||||
@ -171,6 +172,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
hasQueries={hasQueries}
|
||||
next={next}
|
||||
stakingOverview={stakingOverview}
|
||||
targets={targets}
|
||||
toggleFavorite={toggleFavorite}
|
||||
/>
|
||||
</main>
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
|
||||
import { AccountId, Balance, BlockNumber, Exposure, Hash, SessionIndex, ValidatorPrefs, ValidatorPrefsTo196 } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
@ -46,6 +46,7 @@ export interface ValidatorInfo extends ValidatorInfoRank {
|
||||
bondShare: number;
|
||||
bondTotal: Balance;
|
||||
commissionPer: number;
|
||||
exposure: Exposure;
|
||||
hasIdentity: boolean;
|
||||
isActive: boolean;
|
||||
isCommission: boolean;
|
||||
@ -57,6 +58,7 @@ export interface ValidatorInfo extends ValidatorInfoRank {
|
||||
rewardPayout: BN;
|
||||
rewardSplit: BN;
|
||||
validatorPayment: BN;
|
||||
validatorPrefs?: ValidatorPrefs | ValidatorPrefsTo196;
|
||||
}
|
||||
|
||||
export type TargetSortBy = keyof ValidatorInfoRank;
|
||||
|
||||
@ -65,14 +65,8 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
}
|
||||
|
||||
function extractSingle (allAccounts: string[], amount: BN = baseBalance(), { info }: DeriveStakingElected | DeriveStakingWaiting, favorites: string[], perValidatorReward: BN, isElected: boolean): [ValidatorInfo[], string[]] {
|
||||
const defaultExposure = {
|
||||
others: registry.createType('Vec<IndividualExposure>'),
|
||||
own: registry.createType('Compact<Balance>'),
|
||||
total: registry.createType('Compact<Balance>')
|
||||
};
|
||||
const defaultPrefs = {
|
||||
commission: registry.createType('Compact<Perbill>')
|
||||
};
|
||||
const defaultExposure = registry.createType('Exposure');
|
||||
const defaultPrefs = registry.createType('ValidatorPrefs');
|
||||
const nominators: Record<string, boolean> = {};
|
||||
const list = info.map(({ accountId, exposure: _exposure, stakingLedger, validatorPrefs }): ValidatorInfo => {
|
||||
const exposure = _exposure || defaultExposure;
|
||||
@ -108,6 +102,7 @@ function extractSingle (allAccounts: string[], amount: BN = baseBalance(), { inf
|
||||
bondShare: 0,
|
||||
bondTotal,
|
||||
commissionPer: (((prefs as ValidatorPrefs).commission?.unwrap() || BN_ZERO).toNumber() / 10_000_000),
|
||||
exposure,
|
||||
hasIdentity: false,
|
||||
isActive: !skipRewards,
|
||||
isCommission: !!(prefs as ValidatorPrefs).commission,
|
||||
@ -126,7 +121,8 @@ function extractSingle (allAccounts: string[], amount: BN = baseBalance(), { inf
|
||||
rankReward: 0,
|
||||
rewardPayout: skipRewards ? BN_ZERO : rewardPayout,
|
||||
rewardSplit,
|
||||
validatorPayment
|
||||
validatorPayment,
|
||||
validatorPrefs: prefs
|
||||
};
|
||||
});
|
||||
|
||||
@ -146,14 +142,6 @@ function extractInfo (allAccounts: string[], amount: BN = baseBalance(), elected
|
||||
const totalStaked = activeTotals.reduce((total: BN, value) => total.iadd(value), new BN(0));
|
||||
const avgStaked = totalStaked.divn(activeTotals.length);
|
||||
|
||||
// median
|
||||
// const midIndex = Math.floor(activeTotals.length / 2);
|
||||
// const avgStaked = activeTotals.length
|
||||
// ? activeTotals.length % 2
|
||||
// ? activeTotals[midIndex]
|
||||
// : activeTotals[midIndex - 1].add(activeTotals[midIndex]).divn(2)
|
||||
// : BN_ZERO;
|
||||
|
||||
return { avgStaked, lowStaked: activeTotals[0] || BN_ZERO, nominators, totalStaked, validatorIds, validators };
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user