diff --git a/CHANGELOG.md b/CHANGELOG.md index ef044158cf..28dbc66ca0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/packages/page-staking/src/Overview/Address/Status.tsx b/packages/page-staking/src/Overview/Address/Status.tsx index 04106df6aa..41040f4a01 100644 --- a/packages/page-staking/src/Overview/Address/Status.tsx +++ b/packages/page-staking/src/Overview/Address/Status.tsx @@ -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 ? ( } + info={onlineCount ? onlineCount.toNumber() : } /> ) : diff --git a/packages/page-staking/src/Overview/Address/index.tsx b/packages/page-staking/src/Overview/Address/index.tsx index 8763e4c9db..62b169137c 100644 --- a/packages/page-staking/src/Overview/Address/index.tsx +++ b/packages/page-staking/src/Overview/Address/index.tsx @@ -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(api.derive.accounts.info, params); const slashingSpans = useCall(!isMain && api.query.staking.slashingSpans, params, transformSlashes); - const stakingInfo = useCall(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 | null { +function Address ({ address, className = '', filterName, hasQueries, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, validatorInfo, withIdentity }: Props): React.ReactElement | 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, diff --git a/packages/page-staking/src/Overview/CurrentList.tsx b/packages/page-staking/src/Overview/CurrentList.tsx index f7620af607..bd4a1b7c5a 100644 --- a/packages/page-staking/src/Overview/CurrentList.tsx +++ b/packages/page-staking/src/Overview/CurrentList.tsx @@ -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][]): R }, {}); } -function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOverview, toggleFavorite }: Props): React.ReactElement | null { +function CurrentList ({ favorites, hasQueries, isIntentions, next, stakingOverview, targets, toggleFavorite }: Props): React.ReactElement | 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, info): Record => { + 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 diff --git a/packages/page-staking/src/Overview/index.tsx b/packages/page-staking/src/Overview/index.tsx index 624b925558..28e7161ee9 100644 --- a/packages/page-staking/src/Overview/index.tsx +++ b/packages/page-staking/src/Overview/index.tsx @@ -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 { +function Overview ({ className = '', favorites, hasQueries, isIntentions, next, stakingOverview, targets, toggleFavorite }: Props): React.ReactElement { return (
diff --git a/packages/page-staking/src/index.tsx b/packages/page-staking/src/index.tsx index 3e112cf52f..428d6c2e98 100644 --- a/packages/page-staking/src/index.tsx +++ b/packages/page-staking/src/index.tsx @@ -155,6 +155,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement @@ -171,6 +172,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement diff --git a/packages/page-staking/src/types.ts b/packages/page-staking/src/types.ts index 71e9631671..9faa26b277 100644 --- a/packages/page-staking/src/types.ts +++ b/packages/page-staking/src/types.ts @@ -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; diff --git a/packages/page-staking/src/useSortedTargets.ts b/packages/page-staking/src/useSortedTargets.ts index 2cab234521..7037e00fc0 100644 --- a/packages/page-staking/src/useSortedTargets.ts +++ b/packages/page-staking/src/useSortedTargets.ts @@ -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'), - own: registry.createType('Compact'), - total: registry.createType('Compact') - }; - const defaultPrefs = { - commission: registry.createType('Compact') - }; + const defaultExposure = registry.createType('Exposure'); + const defaultPrefs = registry.createType('ValidatorPrefs'); const nominators: Record = {}; 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 }; }