Rework rewards with new staking (#2338)

* Rework rewards with new staking

* Bump api

* Bump API

* Disable buttons on no controller
This commit is contained in:
Jaco Greeff 2020-03-08 15:07:33 +01:00 committed by GitHub
parent 3d635a1e69
commit 95471f42c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 429 additions and 294 deletions

View File

@ -8,11 +8,10 @@
"packages/*"
],
"resolutions": {
"@polkadot/api": "^1.6.0-beta.5",
"@polkadot/api-contract": "^1.6.0-beta.5",
"@polkadot/api": "^1.6.0-beta.9",
"@polkadot/api-contract": "^1.6.0-beta.9",
"@polkadot/keyring": "^2.6.1",
"@polkadot/typegen": "^1.6.0-beta.5",
"@polkadot/types": "^1.6.0-beta.5",
"@polkadot/types": "^1.6.0-beta.9",
"@polkadot/util": "^2.6.1",
"@polkadot/util-crypto": "^2.6.1",
"babel-core": "^7.0.0-bridge.0",
@ -31,7 +30,7 @@
"postinstall": "polkadot-dev-yarn-only",
"test": "polkadot-dev-run-test packages/page-claims/src",
"vanitygen": "node packages/page-accounts/scripts/vanitygen.js",
"start": "cd packages/apps && webpack --config webpack.config.js"
"start": "yarn clean && cd packages/apps && webpack --config webpack.config.js"
},
"devDependencies": {
"@babel/core": "^7.8.7",

View File

@ -12,6 +12,6 @@
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.8.7",
"@polkadot/api-contract": "^1.6.0-beta.5"
"@polkadot/api-contract": "^1.6.0-beta.9"
}
}

View File

@ -140,7 +140,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, staki
useEffect((): void => {
if (stakingRewardsAll && stakingAccount?.stakingLedger?.lastReward) {
const lastClaim = stakingAccount.stakingLedger.lastReward.unwrapOr(new BN(-1));
const stakingRewards = stakingRewardsAll.filter(({ era }): boolean => era.gt(lastClaim));
const stakingRewards = stakingRewardsAll.filter(({ era, isEmpty }): boolean => !isEmpty && era.gt(lastClaim));
setStakingRewards([
stakingRewards,
@ -306,6 +306,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, staki
? (
<TxButton
accountId={controllerId}
isDisabled={!isOwnController}
isNegative
label={
isStashNominating
@ -322,6 +323,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, staki
{(!sessionIds.length || hexSessionIdNext === '0x')
? (
<Button
isDisabled={!isOwnController}
isPrimary
key='set'
onClick={toggleSetSession}
@ -331,6 +333,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, staki
)
: (
<Button
isDisabled={!isOwnController}
isPrimary
key='validate'
onClick={toggleValidate}
@ -341,6 +344,7 @@ function Account ({ allStashes, className, isOwnStash, next, onUpdateType, staki
}
<Button.Or key='nominate.or' />
<Button
isDisabled={!isOwnController}
isPrimary
key='nominate'
onClick={toggleNominate}

View File

@ -2,19 +2,16 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Balance, Hash, Exposure } from '@polkadot/types/interfaces';
import { SessionRewards, Slash } from '../types';
import { DeriveStakerReward } from '@polkadot/api-derive/types';
import { SessionRewards } from '../types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { Chart, Columar, Column } from '@polkadot/react-components';
import { toShortAddress } from '@polkadot/react-components/util';
import { getHistoric } from '@polkadot/react-api/util';
import { useApi } from '@polkadot/react-hooks';
import { formatBalance, formatNumber } from '@polkadot/util';
import { useApi, useCall } from '@polkadot/react-hooks';
import { formatBalance } from '@polkadot/util';
import { useTranslation } from '../translate';
import useBlockCounts from '../useBlockCounts';
interface Props {
className?: string;
@ -26,244 +23,105 @@ type LineDataEntry = (BN | number)[];
type LineData = LineDataEntry[];
interface SplitEntry {
colors: string[];
label: string;
tooltip: string;
value: number;
interface BlockChart {
blockChart: LineData;
blockLabels: string[];
}
type SplitData = SplitEntry[];
interface StakeChart {
stakeChart: LineData;
stakeLabels: string[];
}
const COLORS_MINE = ['#ff8c00'];
const COLORS_OTHER = ['#acacac'];
const COLORS_REWARD = ['#8c2200', '#008c22', '#acacac'];
// const COLORS_MINE = ['#ff8c00'];
// const COLORS_OTHER = ['#acacac'];
// const COLORS_REWARD = ['#8c2200', '#008c22', '#acacac'];
const COLORS_BLOCKS = [undefined, '#acacac'];
function balanceToNumber (amount: BN, divisor: BN): number {
return amount.muln(1000).div(divisor).toNumber() / 1000;
}
function extractStake (values: [Hash, Exposure][], divisor: BN): LineData {
return [
values.map(([, { total }]): number =>
balanceToNumber(total.unwrap(), divisor))
// exposures.map(({ own }): number =>
// balanceToNumber(own.unwrap(), divisor)),
// exposures.map(({ others }): number =>
// balanceToNumber(
// others.reduce((total, { value }): number =>
// total.add(value.unwrap()), new BN(0)
// ), divisor))
];
function extractBlocks (validatorId: string, rewards: DeriveStakerReward[] = []): BlockChart {
const blockLabels: string[] = [];
const avgSet: number[] = [];
const idxSet: number[] = [];
let total = 0;
rewards.forEach(({ era, validators }, index): void => {
const points = validators[validatorId]?.points.toNumber() || 0;
total += points;
blockLabels.push(era.toHuman());
avgSet.push(Math.ceil(total / (index + 1) * 100) / 100);
idxSet.push(points);
});
return {
blockChart: [idxSet, avgSet],
blockLabels
};
}
function extractSplit (values: [Hash, Exposure][], validatorId: string): SplitData | null {
if (!values.length) {
return null;
}
function extractStake (validatorId: string, divisor: BN, rewards: DeriveStakerReward[] = []): StakeChart {
const stakeLabels: string[] = [];
const stakeChart: LineDataEntry = [];
const last = values[values.length - 1][1];
const total = last.total.unwrap();
rewards.forEach(({ era, validators }): void => {
stakeLabels.push(era.toHuman());
stakeChart.push(
balanceToNumber(validators[validatorId]?.exposure.total.toBn() || new BN(0), divisor)
);
});
if (total.eqn(0)) {
return null;
}
const currency = formatBalance.getDefaults().unit;
return [{ accountId: validatorId, isOwn: true, value: last.own.unwrap() }]
.concat(last.others.map(({ who, value }): { accountId: string; isOwn: boolean; value: Balance } => ({
accountId: who.toString(), isOwn: false, value: value.unwrap()
})))
.sort((a, b): number => b.value.cmp(a.value))
.map(({ accountId, isOwn, value }): SplitEntry => {
const label = toShortAddress(accountId);
const percentage = value.muln(10000).div(total).toNumber() / 100;
const tooltip = `${formatBalance(value, { forceUnit: '-', withSi: false })} ${currency} (${percentage.toFixed(2)}%)`;
return {
colors: isOwn ? COLORS_MINE : COLORS_OTHER,
label,
tooltip,
value: percentage
};
});
return {
stakeChart: [stakeChart],
stakeLabels
};
}
function extractEraSlash (validatorId: string, slashes: Slash[]): BN {
return slashes.reduce((total: BN, { accountId, amount }): BN => {
return accountId.eq(validatorId)
? total.sub(amount)
: total;
}, new BN(0));
}
export default function Validator ({ className, sessionRewards, validatorId }: Props): React.ReactElement<Props> {
export default function Validator ({ className, validatorId }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const blockCounts = useBlockCounts(validatorId, sessionRewards);
const [blocksLabels, setBlocksLabels] = useState<string[]>([]);
const [blocksChart, setBlocksChart] = useState<LineData | null>(null);
const [{ currency, divisor }] = useState<{ currency: string; divisor: BN }>({
const [{ blockChart, blockLabels }, setBlockChart] = useState<BlockChart>({ blockChart: [], blockLabels: [] });
const [{ stakeChart, stakeLabels }, setStakeChart] = useState<StakeChart>({ stakeChart: [], stakeLabels: [] });
const stakerRewards = useCall<DeriveStakerReward[]>(api.derive.staking.stakerRewards as any, [validatorId, true]);
const { currency, divisor } = useMemo((): { currency: string; divisor: BN } => ({
currency: formatBalance.getDefaults().unit,
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
});
const [{ rewardsChart, rewardsLabels }, setRewardsInfo] = useState<{ rewardsChart: LineData | null; rewardsLabels: string[] }>({ rewardsChart: null, rewardsLabels: [] });
const [{ splitChart, splitMax }, setSplitInfo] = useState<{ splitChart: SplitData | null; splitMax: number }>({ splitChart: null, splitMax: 100 });
const [{ stakeChart, stakeLabels }, setStakeInfo] = useState<{ stakeChart: LineData | null; stakeLabels: string[]}>({ stakeChart: null, stakeLabels: [] });
}), [formatBalance]);
useEffect((): void => {
if (!splitChart) {
const hashes = sessionRewards.map(({ blockHash }): Hash => blockHash);
const stakeLabels = sessionRewards.map(({ sessionIndex }): string => formatNumber(sessionIndex));
api.isReady.then(async (): Promise<void> => {
const values = await getHistoric<Exposure>(api.query.staking.stakers.at, [validatorId], hashes);
const stakeChart = extractStake(values, divisor);
const splitChart = extractSplit(values, validatorId);
const splitMax = splitChart ? Math.min(Math.ceil(splitChart[0].value), 100) : 100;
setStakeInfo({ stakeChart, stakeLabels });
setSplitInfo({ splitChart, splitMax });
});
}
}, [sessionRewards, splitChart]);
useEffect((): void => {
if (blockCounts) {
const rewardsLabels: string[] = [];
const rewardsChart: LineData = [[], [], []];
let total = new BN(0);
let lastRewardIndex = 0;
let rewardCount = 0;
// we only work from the second position, the first deemed incomplete
sessionRewards.forEach(({ blockNumber, reward, sessionIndex, slashes }, index): void => {
// we are trying to find the first index where rewards are allocated, this is our start
// since we only want complete eras, this means we drop some at the start
if (lastRewardIndex === 0) {
if (index && reward.gtn(0)) {
lastRewardIndex = index;
}
return;
}
// slash is extracted from the available slashes
const neg = extractEraSlash(validatorId, slashes);
// start of a new session, use the counts for the previous
const totalBlocks = blockCounts
.filter((count, countIndex): boolean =>
!!count && countIndex >= lastRewardIndex && countIndex < index)
.reduce((total, count): BN => total.add(count), new BN(0));
// calculate the rewards based on our total share
const pos = reward
.mul(totalBlocks)
.div(blockNumber.sub(sessionRewards[lastRewardIndex].blockNumber));
// add this to the total
total = total.add(neg).add(pos);
rewardCount++;
// if we have a reward here, set the reward index for the next iteration
if (reward.gtn(0)) {
lastRewardIndex = index;
}
// this shows the start of the new era, however rewards are for previous
rewardsLabels.push(formatNumber(sessionIndex.subn(1)));
// calculate and format to 3 decimals
rewardsChart[0].push(balanceToNumber(neg, divisor));
rewardsChart[1].push(balanceToNumber(pos, divisor));
rewardsChart[2].push(balanceToNumber(total.divn(rewardCount), divisor));
});
setRewardsInfo({ rewardsChart, rewardsLabels });
}
}, [blockCounts, sessionRewards, validatorId]);
useEffect((): void => {
setBlocksLabels(
sessionRewards.map(({ sessionIndex }): string => formatNumber(sessionIndex))
);
}, [sessionRewards]);
useEffect((): void => {
if (blockCounts) {
const avgSet: number[] = [];
const idxSet: BN[] = [];
blockCounts.reduce((total: BN, value, index): BN => {
total = total.add(value);
avgSet.push(total.muln(100).divn(index + 1).toNumber() / 100);
idxSet.push(value);
return total;
}, new BN(0));
setBlocksChart([idxSet, avgSet]);
}
}, [blockCounts]);
setBlockChart(extractBlocks(validatorId, stakerRewards));
setStakeChart(extractStake(validatorId, divisor, stakerRewards));
}, [stakerRewards, validatorId]);
return (
<Columar className={className}>
<Column emptyText={t('Loading block data')}>
{(rewardsChart || blocksChart) && (
<>
{blocksChart && (
<div className='staking--Chart'>
<h1>{t('blocks produced')}</h1>
<Chart.Line
colors={COLORS_BLOCKS}
labels={blocksLabels}
legends={[t('blocks'), t('average')]}
values={blocksChart}
/>
</div>
)}
{rewardsChart && (
<div className='staking--Chart'>
<h1>{t('rewards & slashes')}</h1>
<Chart.Line
colors={COLORS_REWARD}
labels={rewardsLabels}
legends={[t('{{currency}} slashed', { replace: { currency } }), t('{{currency}} rewards (est.)', { replace: { currency } }), t('{{currency}} average', { replace: { currency } })]}
values={rewardsChart}
/>
</div>
)}
</>
{!!blockChart.length && (
<div className='staking--Chart'>
<h1>{t('blocks produced')}</h1>
<Chart.Line
colors={COLORS_BLOCKS}
labels={blockLabels}
legends={[t('blocks'), t('average')]}
values={blockChart}
/>
</div>
)}
</Column>
<Column emptyText={t('Loading staker data')}>
{(stakeChart || splitChart) && (
<>
{stakeChart && stakeChart[0].length !== 0 && (
<div className='staking--Chart'>
<h1>{t('elected stake')}</h1>
<Chart.Line
labels={stakeLabels}
legends={[t('{{currency}} total', { replace: { currency } }), t('{{currency}} own', { replace: { currency } }), t('{{currency}} other', { replace: { currency } })]}
values={stakeChart}
/>
</div>
)}
{splitChart && (
<div className='staking--Chart'>
<h1>{t('staker percentages')}</h1>
<Chart.HorizBar
aspectRatio={2}
max={splitMax}
values={splitChart}
/>
</div>
)}
</>
{stakeChart && !!stakeChart[0]?.length && (
<div className='staking--Chart'>
<h1>{t('elected stake')}</h1>
<Chart.Line
labels={stakeLabels}
legends={[t('{{currency}} total', { replace: { currency } }), t('{{currency}} own', { replace: { currency } }), t('{{currency}} other', { replace: { currency } })]}
values={stakeChart}
/>
</div>
)}
</Column>
</Columar>

View File

@ -0,0 +1,271 @@
// Copyright 2017-2020 @polkadot/app-staking authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Balance, Hash, Exposure } from '@polkadot/types/interfaces';
import { SessionRewards, Slash } from '../types';
import BN from 'bn.js';
import React, { useEffect, useState } from 'react';
import { Chart, Columar, Column } from '@polkadot/react-components';
import { toShortAddress } from '@polkadot/react-components/util';
import { getHistoric } from '@polkadot/react-api/util';
import { useApi } from '@polkadot/react-hooks';
import { formatBalance, formatNumber } from '@polkadot/util';
import { useTranslation } from '../translate';
import useBlockCounts from '../useBlockCounts';
interface Props {
className?: string;
sessionRewards: SessionRewards[];
validatorId: string;
}
type LineDataEntry = (BN | number)[];
type LineData = LineDataEntry[];
interface SplitEntry {
colors: string[];
label: string;
tooltip: string;
value: number;
}
type SplitData = SplitEntry[];
const COLORS_MINE = ['#ff8c00'];
const COLORS_OTHER = ['#acacac'];
const COLORS_REWARD = ['#8c2200', '#008c22', '#acacac'];
const COLORS_BLOCKS = [undefined, '#acacac'];
function balanceToNumber (amount: BN, divisor: BN): number {
return amount.muln(1000).div(divisor).toNumber() / 1000;
}
function extractStake (values: [Hash, Exposure][], divisor: BN): LineData {
return [
values.map(([, { total }]): number =>
balanceToNumber(total.unwrap(), divisor))
// exposures.map(({ own }): number =>
// balanceToNumber(own.unwrap(), divisor)),
// exposures.map(({ others }): number =>
// balanceToNumber(
// others.reduce((total, { value }): number =>
// total.add(value.unwrap()), new BN(0)
// ), divisor))
];
}
function extractSplit (values: [Hash, Exposure][], validatorId: string): SplitData | null {
if (!values.length) {
return null;
}
const last = values[values.length - 1][1];
const total = last.total.unwrap();
if (total.eqn(0)) {
return null;
}
const currency = formatBalance.getDefaults().unit;
return [{ accountId: validatorId, isOwn: true, value: last.own.unwrap() }]
.concat(last.others.map(({ who, value }): { accountId: string; isOwn: boolean; value: Balance } => ({
accountId: who.toString(), isOwn: false, value: value.unwrap()
})))
.sort((a, b): number => b.value.cmp(a.value))
.map(({ accountId, isOwn, value }): SplitEntry => {
const label = toShortAddress(accountId);
const percentage = value.muln(10000).div(total).toNumber() / 100;
const tooltip = `${formatBalance(value, { forceUnit: '-', withSi: false })} ${currency} (${percentage.toFixed(2)}%)`;
return {
colors: isOwn ? COLORS_MINE : COLORS_OTHER,
label,
tooltip,
value: percentage
};
});
}
function extractEraSlash (validatorId: string, slashes: Slash[]): BN {
return slashes.reduce((total: BN, { accountId, amount }): BN => {
return accountId.eq(validatorId)
? total.sub(amount)
: total;
}, new BN(0));
}
export default function Validator ({ className, sessionRewards, validatorId }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const blockCounts = useBlockCounts(validatorId, sessionRewards);
const [blocksLabels, setBlocksLabels] = useState<string[]>([]);
const [blocksChart, setBlocksChart] = useState<LineData | null>(null);
const [{ currency, divisor }] = useState<{ currency: string; divisor: BN }>({
currency: formatBalance.getDefaults().unit,
divisor: new BN('1'.padEnd(formatBalance.getDefaults().decimals + 1, '0'))
});
const [{ rewardsChart, rewardsLabels }, setRewardsInfo] = useState<{ rewardsChart: LineData | null; rewardsLabels: string[] }>({ rewardsChart: null, rewardsLabels: [] });
const [{ splitChart, splitMax }, setSplitInfo] = useState<{ splitChart: SplitData | null; splitMax: number }>({ splitChart: null, splitMax: 100 });
const [{ stakeChart, stakeLabels }, setStakeInfo] = useState<{ stakeChart: LineData | null; stakeLabels: string[]}>({ stakeChart: null, stakeLabels: [] });
useEffect((): void => {
if (!splitChart) {
const hashes = sessionRewards.map(({ blockHash }): Hash => blockHash);
const stakeLabels = sessionRewards.map(({ sessionIndex }): string => formatNumber(sessionIndex));
api.isReady.then(async (): Promise<void> => {
const values = await getHistoric<Exposure>(api.query.staking.stakers.at, [validatorId], hashes);
const stakeChart = extractStake(values, divisor);
const splitChart = extractSplit(values, validatorId);
const splitMax = splitChart ? Math.min(Math.ceil(splitChart[0].value), 100) : 100;
setStakeInfo({ stakeChart, stakeLabels });
setSplitInfo({ splitChart, splitMax });
});
}
}, [sessionRewards, splitChart]);
useEffect((): void => {
if (blockCounts) {
const rewardsLabels: string[] = [];
const rewardsChart: LineData = [[], [], []];
let total = new BN(0);
let lastRewardIndex = 0;
let rewardCount = 0;
// we only work from the second position, the first deemed incomplete
sessionRewards.forEach(({ blockNumber, reward, sessionIndex, slashes }, index): void => {
// we are trying to find the first index where rewards are allocated, this is our start
// since we only want complete eras, this means we drop some at the start
if (lastRewardIndex === 0) {
if (index && reward.gtn(0)) {
lastRewardIndex = index;
}
return;
}
// slash is extracted from the available slashes
const neg = extractEraSlash(validatorId, slashes);
// start of a new session, use the counts for the previous
const totalBlocks = blockCounts
.filter((count, countIndex): boolean =>
!!count && countIndex >= lastRewardIndex && countIndex < index)
.reduce((total, count): BN => total.add(count), new BN(0));
// calculate the rewards based on our total share
const pos = reward
.mul(totalBlocks)
.div(blockNumber.sub(sessionRewards[lastRewardIndex].blockNumber));
// add this to the total
total = total.add(neg).add(pos);
rewardCount++;
// if we have a reward here, set the reward index for the next iteration
if (reward.gtn(0)) {
lastRewardIndex = index;
}
// this shows the start of the new era, however rewards are for previous
rewardsLabels.push(formatNumber(sessionIndex.subn(1)));
// calculate and format to 3 decimals
rewardsChart[0].push(balanceToNumber(neg, divisor));
rewardsChart[1].push(balanceToNumber(pos, divisor));
rewardsChart[2].push(balanceToNumber(total.divn(rewardCount), divisor));
});
setRewardsInfo({ rewardsChart, rewardsLabels });
}
}, [blockCounts, sessionRewards, validatorId]);
useEffect((): void => {
setBlocksLabels(
sessionRewards.map(({ sessionIndex }): string => formatNumber(sessionIndex))
);
}, [sessionRewards]);
useEffect((): void => {
if (blockCounts) {
const avgSet: number[] = [];
const idxSet: BN[] = [];
blockCounts.reduce((total: BN, value, index): BN => {
total = total.add(value);
avgSet.push(total.muln(100).divn(index + 1).toNumber() / 100);
idxSet.push(value);
return total;
}, new BN(0));
setBlocksChart([idxSet, avgSet]);
}
}, [blockCounts]);
return (
<Columar className={className}>
<Column emptyText={t('Loading block data')}>
{(rewardsChart || blocksChart) && (
<>
{blocksChart && (
<div className='staking--Chart'>
<h1>{t('blocks produced')}</h1>
<Chart.Line
colors={COLORS_BLOCKS}
labels={blocksLabels}
legends={[t('blocks'), t('average')]}
values={blocksChart}
/>
</div>
)}
{rewardsChart && (
<div className='staking--Chart'>
<h1>{t('rewards & slashes')}</h1>
<Chart.Line
colors={COLORS_REWARD}
labels={rewardsLabels}
legends={[t('{{currency}} slashed', { replace: { currency } }), t('{{currency}} rewards (est.)', { replace: { currency } }), t('{{currency}} average', { replace: { currency } })]}
values={rewardsChart}
/>
</div>
)}
</>
)}
</Column>
<Column emptyText={t('Loading staker data')}>
{(stakeChart || splitChart) && (
<>
{stakeChart && stakeChart[0].length !== 0 && (
<div className='staking--Chart'>
<h1>{t('elected stake')}</h1>
<Chart.Line
labels={stakeLabels}
legends={[t('{{currency}} total', { replace: { currency } }), t('{{currency}} own', { replace: { currency } }), t('{{currency}} other', { replace: { currency } })]}
values={stakeChart}
/>
</div>
)}
{splitChart && (
<div className='staking--Chart'>
<h1>{t('staker percentages')}</h1>
<Chart.HorizBar
aspectRatio={2}
max={splitMax}
values={splitChart}
/>
</div>
)}
</>
)}
</Column>
</Columar>
);
}

View File

@ -42,8 +42,10 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
const recentlyOnline = useCall<DerivedHeartbeats>(api.derive.imOnline?.receivedHeartbeats, []);
const stakingOverview = useCall<DerivedStakingOverview>(api.derive.staking.overview, []);
const sessionRewards = useSessionRewards(MAX_SESSIONS);
const hasQueries = hasAccounts && !!(api.query.imOnline?.authoredBlocks);
const [nominators, dispatchNominators] = useReducer(reduceNominators, [] as string[]);
const hasQueries = useMemo((): boolean => {
return hasAccounts && !!(api.query.imOnline?.authoredBlocks) && !!(api.query.staking.activeEra);
}, [api, hasAccounts]);
const items = useMemo(() => [
{
isRoot: true,
@ -68,6 +70,13 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
text: t('Validator stats')
}
], [t]);
const hiddenTabs = useMemo((): string[] => {
return hasAccounts
? hasQueries
? []
: ['query']
: ['actions', 'query'];
}, [hasAccounts, hasQueries]);
useEffect((): void => {
stakingOverview && setNext(
@ -81,13 +90,7 @@ function StakingApp ({ basePath, className }: Props): React.ReactElement<Props>
<header>
<Tabs
basePath={basePath}
hidden={
hasAccounts
? hasQueries
? []
: ['query']
: ['actions', 'query']
}
hidden={hiddenTabs}
items={items}
/>
</header>

View File

@ -29,7 +29,7 @@ interface Serialized {
treasury: string;
}
const MAX_BLOCKS = 2500;
const MAX_BLOCKS = 250;
function fromJSON (sessions: Serialized[]): SessionRewards[] {
let keepAll = false;

View File

@ -31,7 +31,7 @@
"homepage": "https://github.com/polkadot-js/ui/tree/master/packages/ui-reactive#readme",
"dependencies": {
"@babel/runtime": "^7.8.7",
"@polkadot/api": "^1.6.0-beta.5",
"@polkadot/api": "^1.6.0-beta.9",
"@polkadot/extension-dapp": "^0.22.1",
"rxjs-compat": "^6.5.4"
}

View File

@ -42,11 +42,11 @@ const alphaColor = (hexColor: string): string =>
ChartJs.helpers.color(hexColor).alpha(0.65).rgbString();
function calculateOptions (colors: (string | undefined)[] = [], legends: string[], labels: string[], values: (number | BN)[][], jsonValues: string): State {
const chartData = values.reduce((config, values, index): Config => {
const chartData = values.reduce((chartData, values, index): Config => {
const color = colors[index] || alphaColor(COLORS[index]);
const data = values.map((value): number => BN.isBN(value) ? value.toNumber() : value);
config.datasets.push({
chartData.datasets.push({
data,
fill: false,
label: legends[index],
@ -55,7 +55,7 @@ function calculateOptions (colors: (string | undefined)[] = [], legends: string[
hoverBackgroundColor: color
});
return config;
return chartData;
}, {
labels,
datasets: [] as Dataset[]

112
yarn.lock
View File

@ -2765,56 +2765,56 @@ __metadata:
languageName: node
linkType: hard
"@polkadot/api-contract@npm:^1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/api-contract@npm:1.6.0-beta.5"
"@polkadot/api-contract@npm:^1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/api-contract@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/api": 1.6.0-beta.5
"@polkadot/rpc-core": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.5
"@polkadot/api": 1.6.0-beta.9
"@polkadot/rpc-core": 1.6.0-beta.9
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
bn.js: ^5.1.1
rxjs: ^6.5.4
checksum: 2/aa349fb472512755aaee16fc9ef7045e2d900c1bc5aeb8598c9baf489be2305161bd1581568b543a2b2fcd2c0ec5431206b242cc5f927cd2a2848a583509aaf9
checksum: 2/a0fd7ab797bd736ba381de7df8ba30baa991f3ff319fb3c71e52cd34a1e9b8e4f83d65247e17c43636581825952815b3d85c40016db495c5a31b7c2005a2ab84
languageName: node
linkType: hard
"@polkadot/api-derive@npm:1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/api-derive@npm:1.6.0-beta.5"
"@polkadot/api-derive@npm:1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/api-derive@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/api": 1.6.0-beta.5
"@polkadot/rpc-core": 1.6.0-beta.5
"@polkadot/rpc-provider": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.5
"@polkadot/api": 1.6.0-beta.9
"@polkadot/rpc-core": 1.6.0-beta.9
"@polkadot/rpc-provider": 1.6.0-beta.9
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
"@polkadot/util-crypto": ^2.6.1
bn.js: ^5.1.1
memoizee: ^0.4.14
rxjs: ^6.5.4
checksum: 2/8a481dc392e28f453ae018507bd5c8b38a58ec6d355bcfb08470028aeb3b68fc30324e73247a13da733088822c8eba5900d78877a75e3222a93d3b5c53556d33
checksum: 2/86de96be7ff53fc660ea058f2704abab510425dd0539378984f222e79e06d144a260b9d1564c3d436c94422697d9146d8ffab38e1761a7ae32a570662e3bfa0e
languageName: node
linkType: hard
"@polkadot/api@npm:^1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/api@npm:1.6.0-beta.5"
"@polkadot/api@npm:^1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/api@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/api-derive": 1.6.0-beta.5
"@polkadot/api-derive": 1.6.0-beta.9
"@polkadot/keyring": ^2.6.1
"@polkadot/metadata": 1.6.0-beta.5
"@polkadot/rpc-core": 1.6.0-beta.5
"@polkadot/rpc-provider": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.5
"@polkadot/metadata": 1.6.0-beta.9
"@polkadot/rpc-core": 1.6.0-beta.9
"@polkadot/rpc-provider": 1.6.0-beta.9
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
"@polkadot/util-crypto": ^2.6.1
bn.js: ^5.1.1
eventemitter3: ^4.0.0
rxjs: ^6.5.4
checksum: 2/fe4c1a1b30c1c861e846bf328f57ff5180c1dceef464e21bb89d80ecdcb29b15833ddfd40874dc24eeee7579bc8b6b5f22b3b095f9d9a2bc75a99f99d85ff175
checksum: 2/101414b97ac24417963bd11ea3095664101ecee87a6299beddadf51ff495ae32c7baf0c9cd78b8cc6c2b035f3b3921d1a1cb1826aa5b35a0a8474069ae9ebfaa
languageName: node
linkType: hard
@ -2864,7 +2864,7 @@ __metadata:
resolution: "@polkadot/app-contracts@workspace:packages/page-contracts"
dependencies:
"@babel/runtime": ^7.8.7
"@polkadot/api-contract": ^1.6.0-beta.5
"@polkadot/api-contract": ^1.6.0-beta.9
languageName: unknown
linkType: soft
@ -3176,14 +3176,14 @@ __metadata:
languageName: node
linkType: hard
"@polkadot/jsonrpc@npm:1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/jsonrpc@npm:1.6.0-beta.5"
"@polkadot/jsonrpc@npm:1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/jsonrpc@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/types": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
checksum: 2/9c43500d6c33f2115ed864a9c4f8a573e5229d5aae864525adbdecd6cfba172f7ce8ec106eef20cb765ba52b87c75da0cf58b689c56c863924d5f4df318c9d8e
checksum: 2/ad2117f07fe6ba3d53dc59032ecba1a2b75746c4683e78e9f181111176af5b32eca8cf3e5b336e89a6cf8058536060b0c90beab50759eca96f3a21367927df25
languageName: node
linkType: hard
@ -3198,16 +3198,16 @@ __metadata:
languageName: node
linkType: hard
"@polkadot/metadata@npm:1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/metadata@npm:1.6.0-beta.5"
"@polkadot/metadata@npm:1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/metadata@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/types": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
"@polkadot/util-crypto": ^2.6.1
bn.js: ^5.1.1
checksum: 2/c51af8cbe2c509e6631913964e122cb7f5e1a94c754c098a94692fface0611af26e7c2b55f8de834ca1c07532a4ce64a7a151527ca8d5d06415fcadded6c1ebc
checksum: 2/d711ea8ae5b484b43b5c837ccf92b676571461f039d521dfc4a57e6ae55e8fb1ee9fa93fcaa9b32b4a713da5ef75af764bb89a01856d4743a511699f10903678
languageName: node
linkType: hard
@ -3216,7 +3216,7 @@ __metadata:
resolution: "@polkadot/react-api@workspace:packages/react-api"
dependencies:
"@babel/runtime": ^7.8.7
"@polkadot/api": ^1.6.0-beta.5
"@polkadot/api": ^1.6.0-beta.9
"@polkadot/extension-dapp": ^0.22.1
rxjs-compat: ^6.5.4
languageName: unknown
@ -3328,37 +3328,37 @@ __metadata:
languageName: unknown
linkType: soft
"@polkadot/rpc-core@npm:1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/rpc-core@npm:1.6.0-beta.5"
"@polkadot/rpc-core@npm:1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/rpc-core@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/jsonrpc": 1.6.0-beta.5
"@polkadot/metadata": 1.6.0-beta.5
"@polkadot/rpc-provider": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.5
"@polkadot/jsonrpc": 1.6.0-beta.9
"@polkadot/metadata": 1.6.0-beta.9
"@polkadot/rpc-provider": 1.6.0-beta.9
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
memoizee: ^0.4.14
rxjs: ^6.5.4
checksum: 2/966450e7d01d724139da72d1e01c2713d84f2f315886bbd0b392f481118aee2e74882e4fae1d3820e678ea19f3979997e26a60e88953dc5ad271020882299e56
checksum: 2/a51f9a41eb779aee8706e3779bcd5aa594e253210da86da9515f74c4a25ab09b57cf391f5291c90724d1ac414de15d554786999644d1cb639c3aeac4c1cca6ea
languageName: node
linkType: hard
"@polkadot/rpc-provider@npm:1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/rpc-provider@npm:1.6.0-beta.5"
"@polkadot/rpc-provider@npm:1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/rpc-provider@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/jsonrpc": 1.6.0-beta.5
"@polkadot/metadata": 1.6.0-beta.5
"@polkadot/types": 1.6.0-beta.5
"@polkadot/jsonrpc": 1.6.0-beta.9
"@polkadot/metadata": 1.6.0-beta.9
"@polkadot/types": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
"@polkadot/util-crypto": ^2.6.1
bn.js: ^5.1.1
eventemitter3: ^4.0.0
isomorphic-fetch: ^2.2.1
websocket: ^1.0.31
checksum: 2/4a6cc6a6c44191e852ff8ff95c2b395b80cba4ba9345cd506f976afa9e6e772f977d2a62486d66ac31a2df77d75c7401e73f7391f2731b0e5fb755d105fca871
checksum: 2/12d670baa6bf60925024a7ffb5b41947e0318c0334fb6ae4e4e3eaa23c611579292014f6c973ff95639917ec9e5b6a4be39cff885b9686a1dc2fffeb6435b009
languageName: node
linkType: hard
@ -3371,19 +3371,19 @@ __metadata:
languageName: node
linkType: hard
"@polkadot/types@npm:^1.6.0-beta.5":
version: 1.6.0-beta.5
resolution: "@polkadot/types@npm:1.6.0-beta.5"
"@polkadot/types@npm:^1.6.0-beta.9":
version: 1.6.0-beta.9
resolution: "@polkadot/types@npm:1.6.0-beta.9"
dependencies:
"@babel/runtime": ^7.8.4
"@polkadot/metadata": 1.6.0-beta.5
"@polkadot/metadata": 1.6.0-beta.9
"@polkadot/util": ^2.6.1
"@polkadot/util-crypto": ^2.6.1
"@types/bn.js": ^4.11.6
bn.js: ^5.1.1
memoizee: ^0.4.14
rxjs: ^6.5.4
checksum: 2/99fbf723afe86772c4d61ef1793ef874334979f9653a5c7ef0b8270f70d73dcd9dfdcd95c9884a2130a26a6d696dccdb96da39cbe4a959ed89decb8ebd1c90b7
checksum: 2/2adecefd6479d673ad6e731757094d13cd20c1867d254348c5c7ee2a1198caa37fbc32d867816c47371031dbe11277b15ff1b62b9e8e1a5b3e4adc3c9aaaf8d7
languageName: node
linkType: hard