feat(2356): use validator performance score from previous epoch (#2365)
* feat(1913): validator table column heading mouseovers * feat(2089): stake needed for promotion tooltip * feat(2089): normalised voting power tooltip * feat(2089): total stake tooltip * feat(2089): total stake tooltip for standby-pending-validators-table * feat(2089): total penalties tooltip * feat(2089): tooltip colour tweakage * feat(2089): unit tests for the shared validator data functions * feat(2089): removed unused import from tooltip.tsx * Update apps/token/src/routes/staking/home/validator-tables/standby-pending-validators-table.tsx Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * feat(2089): tweaks from PR comments * feat(2356): display previous epoch validator performance score instead of current epoch * feat(2356): added condition to ensure no divide by zero errors Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com>
This commit is contained in:
parent
e653ad328f
commit
05b07c2bfe
@ -339,4 +339,4 @@ export function useProposalLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<P
|
||||
}
|
||||
export type ProposalQueryHookResult = ReturnType<typeof useProposalQuery>;
|
||||
export type ProposalLazyQueryHookResult = ReturnType<typeof useProposalLazyQuery>;
|
||||
export type ProposalQueryResult = Apollo.QueryResult<ProposalQuery, ProposalQueryVariables>;
|
||||
export type ProposalQueryResult = Apollo.QueryResult<ProposalQuery, ProposalQueryVariables>;
|
||||
|
@ -8,6 +8,9 @@ query PreviousEpoch($epochId: ID) {
|
||||
rewardScore {
|
||||
rawValidatorScore
|
||||
}
|
||||
rankingScore {
|
||||
performanceScore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export type PreviousEpochQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string } | null } } | null> | null } | null } };
|
||||
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string } | null, rankingScore: { __typename?: 'RankingScore', performanceScore: string } } } | null> | null } | null } };
|
||||
|
||||
|
||||
export const PreviousEpochDocument = gql`
|
||||
@ -22,6 +22,9 @@ export const PreviousEpochDocument = gql`
|
||||
rewardScore {
|
||||
rawValidatorScore
|
||||
}
|
||||
rankingScore {
|
||||
performanceScore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +81,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.9998677767864936',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -89,6 +92,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.3',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -97,6 +103,9 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.35',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.999629748500531',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
|
@ -7,11 +7,11 @@ import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
getFormattedPerformanceScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getRawValidatorScore,
|
||||
getTotalPenalties,
|
||||
getUnnormalisedVotingPower,
|
||||
} from '../../shared';
|
||||
@ -146,13 +146,15 @@ export const ConsensusValidatorsTable = ({
|
||||
stakedByDelegates,
|
||||
stakedByOperator,
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore, votingPower, performanceScore },
|
||||
rankingScore: { stakeScore, votingPower },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
}) => {
|
||||
const validatorScore = getRawValidatorScore(previousEpochData, id);
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
validatorScore,
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
);
|
||||
@ -171,7 +173,7 @@ export const ConsensusValidatorsTable = ({
|
||||
[ValidatorFields.NORMALISED_VOTING_POWER]:
|
||||
getNormalisedVotingPower(votingPower),
|
||||
[ValidatorFields.UNNORMALISED_VOTING_POWER]:
|
||||
getUnnormalisedVotingPower(validatorScore),
|
||||
getUnnormalisedVotingPower(rawValidatorScore),
|
||||
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
|
||||
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
|
||||
toBigNum(stakedByDelegates, decimals),
|
||||
@ -191,7 +193,7 @@ export const ConsensusValidatorsTable = ({
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
validatorScore,
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
|
@ -1,7 +1,10 @@
|
||||
import { getRawValidatorScore, getTotalPenalties } from '../../shared';
|
||||
import {
|
||||
getLastEpochScoreAndPerformance,
|
||||
getTotalPenalties,
|
||||
} from '../../shared';
|
||||
import { stakedTotalPercentage } from './shared';
|
||||
|
||||
const mockPreviousEpochData = {
|
||||
const MOCK_PREVIOUS_EPOCH = {
|
||||
epoch: {
|
||||
id: '1',
|
||||
validatorsConnection: {
|
||||
@ -12,6 +15,9 @@ const mockPreviousEpochData = {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -29,7 +35,8 @@ describe('totalPenalties', () => {
|
||||
it('should return the correct penalty based on arbitrary values, test 1', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getRawValidatorScore(mockPreviousEpochData, '0x123'),
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.1',
|
||||
'5000',
|
||||
'100000'
|
||||
@ -40,7 +47,8 @@ describe('totalPenalties', () => {
|
||||
it('should return the correct penalty based on lower performance score than first test', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getRawValidatorScore(mockPreviousEpochData, '0x123'),
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.05',
|
||||
'5000',
|
||||
'100000'
|
||||
@ -51,7 +59,8 @@ describe('totalPenalties', () => {
|
||||
it('should return the correct penalty based on higher amount of stake than other tests (great penalty due to anti-whaling)', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getRawValidatorScore(mockPreviousEpochData, '0x123'),
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.1',
|
||||
'5000',
|
||||
'5500'
|
||||
|
@ -6,10 +6,10 @@ import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
getFormattedPerformanceScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getRawValidatorScore,
|
||||
getTotalPenalties,
|
||||
} from '../../shared';
|
||||
import {
|
||||
@ -58,19 +58,21 @@ export const StandbyPendingValidatorsTable = ({
|
||||
stakedByDelegates,
|
||||
stakedByOperator,
|
||||
stakedTotal,
|
||||
rankingScore: { stakeScore, performanceScore },
|
||||
rankingScore: { stakeScore },
|
||||
pendingStake,
|
||||
}) => {
|
||||
const validatorScore = getRawValidatorScore(previousEpochData, id);
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
validatorScore,
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
);
|
||||
let individualStakeNeededForPromotion,
|
||||
individualStakeNeededForPromotionDescription;
|
||||
|
||||
if (stakeNeededForPromotion) {
|
||||
if (stakeNeededForPromotion && performanceScore) {
|
||||
const stakedTotalBigNum = new BigNumber(stakedTotal);
|
||||
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
|
||||
const performanceScoreBigNum = new BigNumber(performanceScore);
|
||||
@ -131,7 +133,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
||||
getRawValidatorScore(previousEpochData, id),
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedTotal,
|
||||
totalStake
|
||||
|
@ -17,11 +17,11 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import { SubHeading } from '../../../components/heading';
|
||||
import {
|
||||
getFormattedPerformanceScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getOverstakedAmount,
|
||||
getOverstakingPenalty,
|
||||
getPerformancePenalty,
|
||||
getRawValidatorScore,
|
||||
getTotalPenalties,
|
||||
getUnnormalisedVotingPower,
|
||||
} from '../shared';
|
||||
@ -73,10 +73,11 @@ export const ValidatorTable = ({
|
||||
|
||||
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
|
||||
|
||||
const validatorScore = getRawValidatorScore(previousEpochData, node.id);
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, node.id);
|
||||
|
||||
const overstakedAmount = getOverstakedAmount(
|
||||
validatorScore,
|
||||
rawValidatorScore,
|
||||
stakedTotal,
|
||||
node.stakedTotal
|
||||
);
|
||||
@ -87,8 +88,8 @@ export const ValidatorTable = ({
|
||||
: stakedOnNode.dividedBy(total).times(100).dp(2).toString() + '%';
|
||||
|
||||
const totalPenaltiesAmount = getTotalPenalties(
|
||||
validatorScore,
|
||||
node.rankingScore.performanceScore,
|
||||
rawValidatorScore,
|
||||
performanceScore,
|
||||
stakedOnNode.toString(),
|
||||
total.toString()
|
||||
);
|
||||
@ -219,16 +220,12 @@ export const ValidatorTable = ({
|
||||
<KeyValueTableRow>
|
||||
<span>{t('PERFORMANCE SCORE')}</span>
|
||||
<span>
|
||||
{getFormattedPerformanceScore(
|
||||
node.rankingScore.performanceScore
|
||||
).toString()}
|
||||
{getFormattedPerformanceScore(performanceScore).toString()}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
<span>{t('PERFORMANCE PENALITY')}</span>
|
||||
<span>
|
||||
{getPerformancePenalty(node.rankingScore.performanceScore)}
|
||||
</span>
|
||||
<span>{getPerformancePenalty(performanceScore)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<span>
|
||||
@ -246,7 +243,7 @@ export const ValidatorTable = ({
|
||||
<KeyValueTable data-testid="validator-table-voting-power">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('UNNORMALISED VOTING POWER')}</span>
|
||||
<span>{getUnnormalisedVotingPower(validatorScore)}</span>
|
||||
<span>{getUnnormalisedVotingPower(rawValidatorScore)}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow noBorder={true}>
|
||||
<span>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import {
|
||||
getRawValidatorScore,
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getUnnormalisedVotingPower,
|
||||
getOverstakingPenalty,
|
||||
@ -10,7 +10,7 @@ import {
|
||||
getTotalPenalties,
|
||||
} from './shared';
|
||||
|
||||
describe('getRawValidatorScore', () => {
|
||||
describe('getLastEpochScoreAndPerformance', () => {
|
||||
const mockPreviousEpochData = {
|
||||
epoch: {
|
||||
id: '123',
|
||||
@ -22,6 +22,9 @@ describe('getRawValidatorScore', () => {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -30,6 +33,9 @@ describe('getRawValidatorScore', () => {
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.35',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.85',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@ -37,13 +43,19 @@ describe('getRawValidatorScore', () => {
|
||||
},
|
||||
};
|
||||
|
||||
it('should return the rawValidatorScore for the given validator id', () => {
|
||||
expect(getRawValidatorScore(mockPreviousEpochData, '0x123')).toEqual(
|
||||
'0.25'
|
||||
);
|
||||
expect(getRawValidatorScore(mockPreviousEpochData, '0x234')).toEqual(
|
||||
'0.35'
|
||||
);
|
||||
it("should return last epoch's performance and raw validator score for the given validator id", () => {
|
||||
expect(
|
||||
getLastEpochScoreAndPerformance(mockPreviousEpochData, '0x123')
|
||||
).toEqual({
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
});
|
||||
expect(
|
||||
getLastEpochScoreAndPerformance(mockPreviousEpochData, '0x234')
|
||||
).toEqual({
|
||||
rawValidatorScore: '0.35',
|
||||
performanceScore: '0.85',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -5,16 +5,18 @@ import {
|
||||
import type { PreviousEpochQuery } from './__generated___/PreviousEpoch';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
|
||||
export const getRawValidatorScore = (
|
||||
export const getLastEpochScoreAndPerformance = (
|
||||
previousEpochData: PreviousEpochQuery | undefined,
|
||||
id: string
|
||||
) => {
|
||||
return previousEpochData
|
||||
? removePaginationWrapper(
|
||||
previousEpochData.epoch?.validatorsConnection?.edges
|
||||
).find((validator) => validator?.id === id)?.rewardScore
|
||||
?.rawValidatorScore
|
||||
: null;
|
||||
const validator = removePaginationWrapper(
|
||||
previousEpochData?.epoch?.validatorsConnection?.edges
|
||||
).find((validator) => validator?.id === id);
|
||||
|
||||
return {
|
||||
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
|
||||
performanceScore: validator?.rankingScore?.performanceScore,
|
||||
};
|
||||
};
|
||||
|
||||
export const getNormalisedVotingPower = (votingPower: string) =>
|
||||
@ -27,10 +29,12 @@ export const getUnnormalisedVotingPower = (
|
||||
? formatNumberPercentage(new BigNumber(validatorScore).times(100), 2)
|
||||
: null;
|
||||
|
||||
export const getFormattedPerformanceScore = (performanceScore: string) =>
|
||||
new BigNumber(performanceScore).dp(2);
|
||||
export const getFormattedPerformanceScore = (performanceScore?: string) =>
|
||||
performanceScore
|
||||
? new BigNumber(performanceScore).dp(2)
|
||||
: new BigNumber(0).dp(2);
|
||||
|
||||
export const getPerformancePenalty = (performanceScore: string) =>
|
||||
export const getPerformancePenalty = (performanceScore?: string) =>
|
||||
formatNumberPercentage(
|
||||
new BigNumber(1)
|
||||
.minus(getFormattedPerformanceScore(performanceScore))
|
||||
@ -64,19 +68,22 @@ export const getOverstakingPenalty = (
|
||||
|
||||
export const getTotalPenalties = (
|
||||
rawValidatorScore: string | null | undefined,
|
||||
performanceScore: string,
|
||||
performanceScore: string | undefined,
|
||||
stakedOnNode: string,
|
||||
totalStake: string
|
||||
) => {
|
||||
const calc = rawValidatorScore
|
||||
? new BigNumber(1).minus(
|
||||
new BigNumber(performanceScore)
|
||||
.times(new BigNumber(rawValidatorScore))
|
||||
.dividedBy(
|
||||
new BigNumber(stakedOnNode).dividedBy(new BigNumber(totalStake))
|
||||
)
|
||||
)
|
||||
: new BigNumber(0);
|
||||
const calc =
|
||||
rawValidatorScore &&
|
||||
performanceScore &&
|
||||
new BigNumber(totalStake).isGreaterThan(0)
|
||||
? new BigNumber(1).minus(
|
||||
new BigNumber(performanceScore)
|
||||
.times(new BigNumber(rawValidatorScore))
|
||||
.dividedBy(
|
||||
new BigNumber(stakedOnNode).dividedBy(new BigNumber(totalStake))
|
||||
)
|
||||
)
|
||||
: new BigNumber(0);
|
||||
|
||||
return formatNumberPercentage(
|
||||
calc.isPositive() ? calc.times(100) : new BigNumber(0),
|
||||
|
Loading…
Reference in New Issue
Block a user