Fix historic blocks produced display (#1966)
This commit is contained in:
parent
05e2a668b0
commit
50e44f2f2f
@ -3,19 +3,19 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { I18nProps } from '@polkadot/react-components/types';
|
||||
import { Balance, Hash, Exposure, SessionIndex } from '@polkadot/types/interfaces';
|
||||
import { SessionRewards, Slash } from '@polkadot/react-hooks/types';
|
||||
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 { trackStream, useApi } from '@polkadot/react-hooks';
|
||||
import { u32 } from '@polkadot/types';
|
||||
import { useApi } from '@polkadot/react-hooks';
|
||||
import { formatBalance, formatNumber } from '@polkadot/util';
|
||||
|
||||
import translate from '../translate';
|
||||
import useBlockCounts from '../useBlockCounts';
|
||||
|
||||
interface Props extends I18nProps {
|
||||
className?: string;
|
||||
@ -92,11 +92,7 @@ function extractEraSlash (validatorId: string, slashes: Slash[]): BN {
|
||||
|
||||
function Validator ({ className, sessionRewards, t, validatorId }: Props): React.ReactElement<Props> {
|
||||
const { api } = useApi();
|
||||
// FIXME There is something seriously wrong in these two with "any" horrors
|
||||
const blockCounts = trackStream<u32[]>(api.query.imOnline?.authoredBlocks?.multi as any, [sessionRewards, validatorId], {
|
||||
paramMap: ([sessionRewards, validatorId]: [SessionRewards[], string]): any =>
|
||||
[sessionRewards.map(({ sessionIndex }): [SessionIndex, string] => [sessionIndex, validatorId])]
|
||||
});
|
||||
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 }>({
|
||||
|
||||
@ -3,8 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { DerivedFees, DerivedBalances, DerivedHeartbeats, DerivedStakingOverview } from '@polkadot/api-derive/types';
|
||||
import { BlockNumber } from '@polkadot/types/interfaces';
|
||||
import { SessionRewards } from '@polkadot/react-hooks/types';
|
||||
import { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
export type Nominators = Record<string, string[]>;
|
||||
|
||||
@ -29,3 +28,19 @@ export interface CalculateBalanceProps {
|
||||
export type AccountFilter = 'all' | 'controller' | 'session' | 'stash' | 'unbonded';
|
||||
|
||||
export type ValidatorFilter = 'all' | 'hasNominators' | 'noNominators' | 'hasWarnings' | 'noWarnings' | 'iNominated' | 'nextSet';
|
||||
|
||||
export interface Slash {
|
||||
accountId: AccountId;
|
||||
amount: Balance;
|
||||
}
|
||||
|
||||
export interface SessionRewards {
|
||||
blockHash: Hash;
|
||||
blockNumber: BlockNumber;
|
||||
isEventsEmpty: boolean;
|
||||
parentHash: Hash;
|
||||
reward: Balance;
|
||||
sessionIndex: SessionIndex;
|
||||
slashes: Slash[];
|
||||
treasury: Balance;
|
||||
}
|
||||
|
||||
38
packages/app-staking/src/useBlockCounts.tsx
Normal file
38
packages/app-staking/src/useBlockCounts.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2017-2019 @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 { SessionIndex } from '@polkadot/types/interfaces';
|
||||
import { SessionRewards } from './types';
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useApi, trackStream } from '@polkadot/react-hooks';
|
||||
import { u32 } from '@polkadot/types';
|
||||
|
||||
export default function useBlockCounts (accountId: string, sessionRewards: SessionRewards[]): u32[] {
|
||||
const { api } = useApi();
|
||||
const [counts, setCounts] = useState<u32[]>([]);
|
||||
const [historic, setHistoric] = useState<u32[]>([]);
|
||||
const sessionIndex = trackStream<SessionIndex>(api.query.session.currentIndex, []);
|
||||
const current = trackStream<u32>(api.query.imOnline?.authoredBlocks, [sessionIndex, accountId]);
|
||||
|
||||
useEffect((): void => {
|
||||
if (api.query.imOnline?.authoredBlocks && sessionRewards && sessionRewards.length) {
|
||||
const filtered = sessionRewards.filter(({ sessionIndex }): boolean => sessionIndex.gtn(0));
|
||||
|
||||
if (filtered.length) {
|
||||
Promise
|
||||
.all(filtered.map(({ parentHash, sessionIndex }): Promise<u32> =>
|
||||
api.query.imOnline.authoredBlocks.at(parentHash, sessionIndex.subn(1), accountId) as Promise<u32>
|
||||
))
|
||||
.then(setHistoric);
|
||||
}
|
||||
}
|
||||
}, [accountId, sessionRewards]);
|
||||
|
||||
useEffect((): void => {
|
||||
setCounts([...historic, current || api.createType('u32')]);
|
||||
}, [current, historic]);
|
||||
|
||||
return counts;
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
// of the Apache-2.0 license. See the LICENSE file for details.
|
||||
|
||||
import { Balance, EventRecord, Hash, Header, StorageChangeSet } from '@polkadot/types/interfaces';
|
||||
import { Slash, SessionRewards } from '@polkadot/react-hooks/types';
|
||||
import { Slash, SessionRewards } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { useEffect, useState } from 'react';
|
||||
@ -22,9 +22,11 @@ interface Serialized {
|
||||
blockHash: string;
|
||||
blockNumber: string;
|
||||
isEventsEmpty: boolean;
|
||||
parentHash: string;
|
||||
reward: string;
|
||||
sessionIndex: string;
|
||||
slashes: SerializedSlash[];
|
||||
treasury: string;
|
||||
}
|
||||
|
||||
const MAX_BLOCKS = 2500;
|
||||
@ -34,17 +36,20 @@ function fromJSON (sessions: Serialized[]): SessionRewards[] {
|
||||
let keepAll = true;
|
||||
|
||||
return sessions
|
||||
.map(({ blockHash, blockNumber, isEventsEmpty, reward, sessionIndex, slashes }): SessionRewards => ({
|
||||
.map(({ blockHash, blockNumber, isEventsEmpty, parentHash, reward, sessionIndex, slashes, treasury }): SessionRewards => ({
|
||||
blockHash: createType(registry, 'Hash', blockHash),
|
||||
blockNumber: createType(registry, 'BlockNumber', blockNumber),
|
||||
isEventsEmpty,
|
||||
parentHash: createType(registry, 'Hash', parentHash),
|
||||
reward: createType(registry, 'Balance', reward),
|
||||
sessionIndex: createType(registry, 'SessionIndex', sessionIndex),
|
||||
slashes: slashes.map(({ accountId, amount }): Slash => ({
|
||||
accountId: createType(registry, 'AccountId', accountId),
|
||||
amount: createType(registry, 'Balance', amount)
|
||||
}))
|
||||
})),
|
||||
treasury: createType(registry, 'Balance', treasury)
|
||||
}))
|
||||
.filter(({ parentHash }): boolean => !parentHash.isEmpty)
|
||||
.filter(({ isEventsEmpty }): boolean => {
|
||||
if (!isEventsEmpty) {
|
||||
// we first see if we have some data up to this point (we may not, i.e. non-archive)
|
||||
@ -59,16 +64,18 @@ function fromJSON (sessions: Serialized[]): SessionRewards[] {
|
||||
}
|
||||
|
||||
function toJSON (sessions: SessionRewards[], maxSessions: number): Serialized[] {
|
||||
return sessions.map(({ blockHash, blockNumber, isEventsEmpty, reward, sessionIndex, slashes }): Serialized => ({
|
||||
return sessions.map(({ blockHash, blockNumber, isEventsEmpty, parentHash, reward, sessionIndex, slashes, treasury }): Serialized => ({
|
||||
blockHash: blockHash.toHex(),
|
||||
blockNumber: blockNumber.toHex(),
|
||||
isEventsEmpty,
|
||||
parentHash: parentHash.toHex(),
|
||||
reward: reward.toHex(),
|
||||
sessionIndex: sessionIndex.toHex(),
|
||||
slashes: slashes.map(({ accountId, amount }): SerializedSlash => ({
|
||||
accountId: accountId.toString(),
|
||||
amount: amount.toHex()
|
||||
}))
|
||||
})),
|
||||
treasury: treasury.toHex()
|
||||
})).slice(-maxSessions);
|
||||
}
|
||||
|
||||
@ -108,10 +115,10 @@ async function loadSome (api: ApiPromise, fromHash: Hash, toHash: Hash): Promise
|
||||
amount: amount as any
|
||||
}))
|
||||
);
|
||||
const rewards: (Balance | undefined)[] = events.map((info): Balance | undefined => {
|
||||
const rewards: [Balance | undefined, Balance | undefined][] = events.map((info): [Balance | undefined, Balance | undefined] => {
|
||||
const rewards = info.filter(({ event: { method } }): boolean => method === 'Reward');
|
||||
|
||||
return rewards[0]?.event?.data[0] as Balance;
|
||||
return [rewards[0]?.event?.data[0] as Balance, rewards[0]?.event?.data[1] as Balance];
|
||||
});
|
||||
|
||||
// For old v1, the query results have empty spots (subsequently fixed in v2),
|
||||
@ -122,11 +129,13 @@ async function loadSome (api: ApiPromise, fromHash: Hash, toHash: Hash): Promise
|
||||
blockHash: headers[index].hash,
|
||||
blockNumber: headers[index].number.unwrap(),
|
||||
isEventsEmpty: events[index].length === 0,
|
||||
reward: rewards[index] || createType(registry, 'Balance'),
|
||||
parentHash: headers[index].parentHash,
|
||||
reward: rewards[index][0] || createType(registry, 'Balance'),
|
||||
sessionIndex: createType(registry, 'SessionIndex', u8aToU8a(
|
||||
value.isSome ? value.unwrap() : new Uint8Array([])
|
||||
)),
|
||||
slashes: slashes[index]
|
||||
slashes: slashes[index],
|
||||
treasury: rewards[index][1] || createType(registry, 'Balance')
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import { Codec } from '@polkadot/types/types';
|
||||
import { Options, Param, Params } from './types';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { isUndefined } from '@polkadot/util';
|
||||
|
||||
import { dummyPromise, extractParams, transformIdentity } from './util';
|
||||
|
||||
@ -20,6 +21,11 @@ interface TrackFn {
|
||||
(a: Param, b: Param, cb: TrackFnCallback): TrackFnResult;
|
||||
(a: Param, cb: TrackFnCallback): TrackFnResult;
|
||||
(cb: TrackFnCallback): TrackFnResult;
|
||||
meta?: {
|
||||
type: {
|
||||
isDoubleMap: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// tracks a stream, typically an api.* call (derive, rpc, query) that
|
||||
@ -35,10 +41,12 @@ export default function trackStream <T> (fn: TrackFn | undefined, params: Params
|
||||
tracker.current.subscriber = dummyPromise;
|
||||
};
|
||||
const _subscribe = (params: Params): void => {
|
||||
const validParams = params.filter((p): boolean => !isUndefined(p));
|
||||
|
||||
_unsubscribe();
|
||||
|
||||
setImmediate((): void => {
|
||||
tracker.current.subscriber = fn
|
||||
tracker.current.subscriber = fn && (!fn.meta || !fn.meta.type?.isDoubleMap || validParams.length === 2)
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-ignore
|
||||
// @ts-ignore We tried to get the typings right, close but no cigar...
|
||||
? fn(...params, (value: any): void => setValue(transform(value)))
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
// Copyright 2017-2019 @polkadot/react-components 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 { AccountId, Balance, BlockNumber, Hash, SessionIndex } from '@polkadot/types/interfaces';
|
||||
|
||||
export interface Slash {
|
||||
accountId: AccountId;
|
||||
amount: Balance;
|
||||
}
|
||||
|
||||
export interface SessionRewards {
|
||||
blockHash: Hash;
|
||||
blockNumber: BlockNumber;
|
||||
isEventsEmpty: boolean;
|
||||
reward: Balance;
|
||||
sessionIndex: SessionIndex;
|
||||
slashes: Slash[];
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user