Human-readable payout (target) descriptions (#4107)
This commit is contained in:
parent
9f0cc4c09b
commit
118cc51dc3
@ -10,7 +10,7 @@ import React, { useCallback, useMemo } from 'react';
|
||||
import { AddressSmall, Badge, Checkbox, Icon } from '@polkadot/react-components';
|
||||
import { checkVisibility } from '@polkadot/react-components/util';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { useApi, useBlockTime, useCall } from '@polkadot/react-hooks';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
import MaxBadge from '../MaxBadge';
|
||||
@ -32,6 +32,7 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
|
||||
const { t } = useTranslation();
|
||||
const { api } = useApi();
|
||||
const accountInfo = useCall<DeriveAccountInfo>(api.derive.accounts.info, [info.accountId]);
|
||||
const [,, time] = useBlockTime(info.lastPayout);
|
||||
|
||||
const isVisible = useMemo(
|
||||
() => accountInfo
|
||||
@ -108,7 +109,17 @@ function Validator ({ allSlashes, canSelect, filterName, info, isNominated, isSe
|
||||
<td className='address all'>
|
||||
<AddressSmall value={accountId} />
|
||||
</td>
|
||||
<td className='number media--1400'>{lastPayout && formatNumber(lastPayout)}</td>
|
||||
<td className='number media--1400'>
|
||||
{lastPayout && (
|
||||
api.consts.babe
|
||||
? time.days
|
||||
? time.days === 1
|
||||
? t('yesterday')
|
||||
: t('{{days}} days', { replace: { days: time.days } })
|
||||
: t('recently')
|
||||
: formatNumber(lastPayout)
|
||||
)}
|
||||
</td>
|
||||
<td className='number media--1200'>{numNominators || ''}</td>
|
||||
<td className='number'>
|
||||
{
|
||||
|
||||
@ -221,7 +221,7 @@ function Targets ({ className = '', isInElection, ownStashes, targets: { avgStak
|
||||
|
||||
const header = useMemo(() => [
|
||||
[t('validators'), 'start', 3],
|
||||
[t('paid'), 'media--1400'],
|
||||
[t('payout'), 'media--1500'],
|
||||
...(SORT_KEYS as (keyof typeof labelsRef.current)[]).map((header) => [
|
||||
<>{labelsRef.current[header]}<Icon icon={sortBy === header ? (sortFromMax ? 'chevron-down' : 'chevron-up') : 'minus'} /></>,
|
||||
`${sorted ? `isClickable ${sortBy === header ? 'highlight--border' : ''} number` : 'number'} ${CLASSES[header] || ''}`,
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { ApiPromise } from '@polkadot/api';
|
||||
import type { DeriveSessionIndexes, DeriveStakingElected, DeriveStakingWaiting } from '@polkadot/api-derive/types';
|
||||
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 type { SortedTargets, TargetSortBy, ValidatorInfo } from './types';
|
||||
@ -13,6 +13,13 @@ import { registry } from '@polkadot/react-api';
|
||||
import { calcInflation, useAccounts, useApi, useCall } from '@polkadot/react-hooks';
|
||||
import { BN_ONE, BN_ZERO } from '@polkadot/util';
|
||||
|
||||
interface LastEra {
|
||||
eraLength: BN;
|
||||
lastEra: BN;
|
||||
lastEraEnd: BN;
|
||||
sessionLength: BN;
|
||||
}
|
||||
|
||||
const PERBILL = new BN(1_000_000_000);
|
||||
const EMPTY_PARTIAL = {};
|
||||
|
||||
@ -77,7 +84,7 @@ function sortValidators (list: ValidatorInfo[]): ValidatorInfo[] {
|
||||
);
|
||||
}
|
||||
|
||||
function extractSingle (allAccounts: string[], derive: DeriveStakingElected | DeriveStakingWaiting, favorites: string[], perValidatorReward: BN): [ValidatorInfo[], string[]] {
|
||||
function extractSingle (allAccounts: string[], derive: DeriveStakingElected | DeriveStakingWaiting, favorites: string[], perValidatorReward: BN, { eraLength, lastEra, sessionLength }: LastEra): [ValidatorInfo[], string[]] {
|
||||
const nominators: Record<string, boolean> = {};
|
||||
const emptyExposure = registry.createType('Exposure');
|
||||
const info = derive.info;
|
||||
@ -104,7 +111,14 @@ function extractSingle (allAccounts: string[], derive: DeriveStakingElected | De
|
||||
return isNominating || allAccounts.includes(nominator);
|
||||
}, allAccounts.includes(key));
|
||||
const isElected = !isWaitingDerive(derive) && derive.nextElected.some((e) => e.eq(accountId));
|
||||
const lastPayout = stakingLedger.claimedRewards[stakingLedger.claimedRewards.length - 1];
|
||||
const lastEraPayout = !lastEra.isZero()
|
||||
? stakingLedger.claimedRewards[stakingLedger.claimedRewards.length - 1]
|
||||
: undefined;
|
||||
let lastPayout: BN | undefined = lastEraPayout;
|
||||
|
||||
if (lastPayout && !sessionLength.eq(BN_ONE)) {
|
||||
lastPayout = lastEra.sub(lastPayout).mul(eraLength);
|
||||
}
|
||||
|
||||
return {
|
||||
accountId,
|
||||
@ -142,10 +156,10 @@ 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, lastReward = BN_ONE): Partial<SortedTargets> {
|
||||
function extractInfo (api: ApiPromise, allAccounts: string[], electedDerive: DeriveStakingElected, waitingDerive: DeriveStakingWaiting, favorites: string[], totalIssuance: BN, lastEraInfo: LastEra, lastReward: BN): Partial<SortedTargets> {
|
||||
const perValidatorReward = lastReward.divn(electedDerive.info.length);
|
||||
const [elected, nominators] = extractSingle(allAccounts, electedDerive, favorites, perValidatorReward);
|
||||
const [waiting] = extractSingle(allAccounts, waitingDerive, favorites, perValidatorReward);
|
||||
const [elected, nominators] = extractSingle(allAccounts, electedDerive, favorites, perValidatorReward, lastEraInfo);
|
||||
const [waiting] = extractSingle(allAccounts, waitingDerive, favorites, perValidatorReward, lastEraInfo);
|
||||
const activeTotals = elected
|
||||
.filter(({ isActive }) => isActive)
|
||||
.map(({ bondTotal }) => bondTotal)
|
||||
@ -177,7 +191,12 @@ function extractInfo (api: ApiPromise, allAccounts: string[], electedDerive: Der
|
||||
}
|
||||
|
||||
const transformEra = {
|
||||
transform: ({ activeEra }: DeriveSessionIndexes) => activeEra.gtn(0) ? activeEra.subn(1) : BN_ZERO
|
||||
transform: ({ activeEra, activeEraStart, eraLength, sessionLength }: DeriveSessionInfo): LastEra => ({
|
||||
eraLength,
|
||||
lastEra: activeEra.gtn(0) ? activeEra.subn(1) : BN_ZERO,
|
||||
lastEraEnd: activeEra.gtn(0) && activeEraStart.isSome ? activeEraStart.unwrap() : BN_ZERO,
|
||||
sessionLength
|
||||
})
|
||||
};
|
||||
|
||||
const transformReward = {
|
||||
@ -190,14 +209,14 @@ export default function useSortedTargets (favorites: string[]): SortedTargets {
|
||||
const totalIssuance = useCall<BN>(api.query.balances.totalIssuance);
|
||||
const electedInfo = useCall<DeriveStakingElected>(api.derive.staking.electedInfo);
|
||||
const waitingInfo = useCall<DeriveStakingWaiting>(api.derive.staking.waitingInfo);
|
||||
const lastEra = useCall<BN>(api.derive.session.indexes, undefined, transformEra);
|
||||
const lastReward = useCall<BN>(lastEra && api.query.staking.erasValidatorReward, [lastEra], transformReward);
|
||||
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 && totalIssuance && waitingInfo
|
||||
? extractInfo(api, allAccounts, electedInfo, waitingInfo, favorites, totalIssuance, lastReward)
|
||||
() => electedInfo && lastEraInfo && lastReward && totalIssuance && waitingInfo
|
||||
? extractInfo(api, allAccounts, electedInfo, waitingInfo, favorites, totalIssuance, lastEraInfo, lastReward)
|
||||
: EMPTY_PARTIAL,
|
||||
[api, allAccounts, electedInfo, favorites, lastReward, totalIssuance, waitingInfo]
|
||||
[api, allAccounts, electedInfo, favorites, lastEraInfo, lastReward, totalIssuance, waitingInfo]
|
||||
);
|
||||
|
||||
return { inflation: { inflation: 0, stakedReturn: 0 }, ...partial, lastReward };
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// Copyright 2017-2020 @polkadot/app-democracy authors & contributors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import type { Time } from '@polkadot/util/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useMemo } from 'react';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
@ -8,7 +10,7 @@ import { BN_ONE, extractTime } from '@polkadot/util';
|
||||
|
||||
import { useTranslation } from './translate';
|
||||
|
||||
type Result = [number, string];
|
||||
type Result = [number, string, Time];
|
||||
|
||||
const DEFAULT_TIME = new BN(6000);
|
||||
|
||||
@ -24,7 +26,8 @@ export function useBlockTime (blocks = BN_ONE): Result {
|
||||
api.consts.timestamp?.minimumPeriod.muln(2) ||
|
||||
DEFAULT_TIME
|
||||
);
|
||||
const { days, hours, minutes, seconds } = extractTime(blockTime.mul(blocks).toNumber());
|
||||
const time = extractTime(blockTime.mul(blocks).toNumber());
|
||||
const { days, hours, minutes, seconds } = time;
|
||||
const timeStr = [
|
||||
days ? (days > 1) ? t<string>('{{days}} days', { replace: { days } }) : t<string>('1 day') : null,
|
||||
hours ? (hours > 1) ? t<string>('{{hours}} hrs', { replace: { hours } }) : t<string>('1 hr') : null,
|
||||
@ -37,7 +40,8 @@ export function useBlockTime (blocks = BN_ONE): Result {
|
||||
|
||||
return [
|
||||
blockTime.toNumber(),
|
||||
timeStr
|
||||
timeStr,
|
||||
time
|
||||
];
|
||||
},
|
||||
[api, blocks, t]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user