Slash breakdown (#3315)
This commit is contained in:
parent
0d631759ae
commit
597dfd0a44
57
packages/page-staking/src/Slashes/Row.tsx
Normal file
57
packages/page-staking/src/Slashes/Row.tsx
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 { Slash } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { AddressMini, AddressSmall } from '@polkadot/react-components';
|
||||
import { FormatBalance } from '@polkadot/react-query';
|
||||
import { formatNumber } from '@polkadot/util';
|
||||
|
||||
interface Props {
|
||||
slash: Slash;
|
||||
withEra: boolean;
|
||||
}
|
||||
|
||||
function Row ({ slash: { era, slash: { others, own, payout, reporters, validator }, total }, withEra }: Props): React.ReactElement<Props> {
|
||||
return (
|
||||
<tr>
|
||||
<td className='number'>
|
||||
<h1>{withEra && formatNumber(era)}</h1>
|
||||
</td>
|
||||
<td className='address'>
|
||||
<AddressSmall value={validator} />
|
||||
</td>
|
||||
<td className='number together'>
|
||||
<FormatBalance value={own} />
|
||||
</td>
|
||||
<td className='number together'>
|
||||
<FormatBalance value={total} />
|
||||
</td>
|
||||
<td className='address all'>
|
||||
{others.map(([accountId, balance], index): React.ReactNode => (
|
||||
<AddressMini
|
||||
balance={balance}
|
||||
key={index}
|
||||
value={accountId}
|
||||
withBalance
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
<td className='address'>
|
||||
{reporters.map((reporter, index): React.ReactNode => (
|
||||
<AddressMini
|
||||
key={index}
|
||||
value={reporter}
|
||||
/>
|
||||
))}
|
||||
</td>
|
||||
<td className='number together'>
|
||||
<FormatBalance value={payout} />
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Row);
|
||||
61
packages/page-staking/src/Slashes/index.tsx
Normal file
61
packages/page-staking/src/Slashes/index.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
// 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 { UnappliedSlash } from '@polkadot/types/interfaces';
|
||||
import { Slash } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Table } from '@polkadot/react-components';
|
||||
|
||||
import { useTranslation } from '../translate';
|
||||
import Row from './Row';
|
||||
|
||||
interface Props {
|
||||
slashes: [BN, UnappliedSlash[]][];
|
||||
}
|
||||
|
||||
function Slashes ({ slashes }: Props): React.ReactElement<Props> | null {
|
||||
const { t } = useTranslation();
|
||||
const header = useMemo(() => [
|
||||
[t('unapplied slashes'), 'start', 2],
|
||||
[t('own')],
|
||||
[t('total')],
|
||||
[t('nominators'), 'start'],
|
||||
[t('reporters'), 'start'],
|
||||
[t('payout')]
|
||||
], [t]);
|
||||
const rows = useMemo((): Slash[] => {
|
||||
return slashes
|
||||
.reduce((rows: Slash[], [era, slashes]): Slash[] => {
|
||||
return slashes.reduce((rows: Slash[], slash): Slash[] => {
|
||||
const total = slash.others.reduce((total: BN, [, value]): BN => {
|
||||
return total.add(value);
|
||||
}, slash.own);
|
||||
|
||||
rows.push({ era, slash, total });
|
||||
|
||||
return rows;
|
||||
}, rows);
|
||||
}, [])
|
||||
.sort((a, b) => b.era.cmp(a.era));
|
||||
}, [slashes]);
|
||||
|
||||
return (
|
||||
<Table
|
||||
empty={t<string>('There an no unapplied/pending slashes')}
|
||||
header={header}
|
||||
>
|
||||
{rows.map((slash, index): React.ReactNode => (
|
||||
<Row
|
||||
key={`${index}-${slash.era.toString()}`}
|
||||
slash={slash}
|
||||
withEra={index === 0 || !slash.era.eq(rows[index - 1].era)}
|
||||
/>
|
||||
))}
|
||||
</Table>
|
||||
);
|
||||
}
|
||||
|
||||
export default React.memo(Slashes);
|
||||
13
packages/page-staking/src/Slashes/types.ts
Normal file
13
packages/page-staking/src/Slashes/types.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// 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 { UnappliedSlash } from '@polkadot/types/interfaces';
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
export interface Slash {
|
||||
era: BN;
|
||||
slash: UnappliedSlash;
|
||||
total: BN;
|
||||
}
|
||||
@ -12,7 +12,7 @@ import { useLocation } from 'react-router-dom';
|
||||
import styled from 'styled-components';
|
||||
import { HelpOverlay } from '@polkadot/react-components';
|
||||
import Tabs from '@polkadot/react-components/Tabs';
|
||||
import { useAccounts, useApi, useCall, useFavorites, useOwnStashInfos, useStashIds } from '@polkadot/react-hooks';
|
||||
import { useAccounts, useApi, useAvailableSlashes, useCall, useFavorites, useOwnStashInfos, useStashIds } from '@polkadot/react-hooks';
|
||||
import { isFunction } from '@polkadot/util';
|
||||
|
||||
import basicMd from './md/basic.md';
|
||||
@ -21,6 +21,7 @@ import Overview from './Overview';
|
||||
import Payouts from './Payouts';
|
||||
import Query from './Query';
|
||||
import Summary from './Overview/Summary';
|
||||
import Slashes from './Slashes';
|
||||
import Targets from './Targets';
|
||||
import { STORE_FAVS_BASE } from './constants';
|
||||
import { useTranslation } from './translate';
|
||||
@ -40,6 +41,7 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
const [favorites, toggleFavorite] = useFavorites(STORE_FAVS_BASE);
|
||||
const allStashes = useStashIds();
|
||||
const ownStashes = useOwnStashInfos();
|
||||
const slashes = useAvailableSlashes();
|
||||
const targets = useSortedTargets(favorites);
|
||||
const stakingOverview = useCall<DeriveStakingOverview>(api.derive.staking.overview, []);
|
||||
const isInElection = useCall<boolean>(api.query.staking?.eraElectionStatus, [], {
|
||||
@ -74,6 +76,10 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
name: 'waiting',
|
||||
text: t<string>('Waiting')
|
||||
},
|
||||
{
|
||||
name: 'slashes',
|
||||
text: t<string>('Slashes')
|
||||
},
|
||||
{
|
||||
hasParams: true,
|
||||
name: 'query',
|
||||
@ -81,13 +87,18 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
}
|
||||
].filter((q): q is { name: string; text: string } => !!q), [api, t]);
|
||||
const hiddenTabs = useMemo(
|
||||
(): string[] =>
|
||||
!hasAccounts
|
||||
(): string[] => {
|
||||
const base = !hasAccounts
|
||||
? ['actions', 'payouts', 'query']
|
||||
: !hasQueries
|
||||
? ['returns', 'query']
|
||||
: [],
|
||||
[hasAccounts, hasQueries]
|
||||
: [];
|
||||
|
||||
return slashes.length
|
||||
? base
|
||||
: base.concat('slashes');
|
||||
},
|
||||
[hasAccounts, hasQueries, slashes]
|
||||
);
|
||||
|
||||
useEffect((): void => {
|
||||
@ -120,6 +131,9 @@ function StakingApp ({ basePath, className = '' }: Props): React.ReactElement<Pr
|
||||
<Route path={[`${basePath}/query/:value`, `${basePath}/query`]}>
|
||||
<Query />
|
||||
</Route>
|
||||
<Route path={`${basePath}/slashes`}>
|
||||
<Slashes slashes={slashes} />
|
||||
</Route>
|
||||
<Route path={`${basePath}/targets`}>
|
||||
<Targets
|
||||
isInElection={isInElection}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user