Human-readable payout (target) descriptions (#4107)

This commit is contained in:
Jaco Greeff 2020-11-30 00:47:24 +01:00 committed by GitHub
parent 9f0cc4c09b
commit 118cc51dc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 52 additions and 18 deletions

View File

@ -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'>
{

View File

@ -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] || ''}`,

View File

@ -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 };

View File

@ -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]