fix(governance): penalties calculation (#3850)
This commit is contained in:
parent
fd338c7400
commit
ff3519279d
@ -5,12 +5,22 @@ query PreviousEpoch($epochId: ID) {
|
|||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
stakedTotal
|
||||||
rewardScore {
|
rewardScore {
|
||||||
rawValidatorScore
|
rawValidatorScore
|
||||||
performanceScore
|
performanceScore
|
||||||
|
multisigScore
|
||||||
|
validatorScore
|
||||||
|
normalisedScore
|
||||||
|
validatorStatus
|
||||||
}
|
}
|
||||||
rankingScore {
|
rankingScore {
|
||||||
|
status
|
||||||
|
previousStatus
|
||||||
|
rankingScore
|
||||||
stakeScore
|
stakeScore
|
||||||
|
performanceScore
|
||||||
|
votingPower
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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, performanceScore: string } | null, rankingScore: { __typename?: 'RankingScore', stakeScore: string } } } | 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, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
|
||||||
|
|
||||||
|
|
||||||
export const PreviousEpochDocument = gql`
|
export const PreviousEpochDocument = gql`
|
||||||
@ -19,12 +19,22 @@ export const PreviousEpochDocument = gql`
|
|||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
|
stakedTotal
|
||||||
rewardScore {
|
rewardScore {
|
||||||
rawValidatorScore
|
rawValidatorScore
|
||||||
performanceScore
|
performanceScore
|
||||||
|
multisigScore
|
||||||
|
validatorScore
|
||||||
|
normalisedScore
|
||||||
|
validatorStatus
|
||||||
}
|
}
|
||||||
rankingScore {
|
rankingScore {
|
||||||
|
status
|
||||||
|
previousStatus
|
||||||
|
rankingScore
|
||||||
stakeScore
|
stakeScore
|
||||||
|
performanceScore
|
||||||
|
votingPower
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,36 +79,72 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
|
|||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||||
|
stakedTotal: '14182454495731682635157',
|
||||||
rewardScore: {
|
rewardScore: {
|
||||||
rawValidatorScore: '0.25',
|
rawValidatorScore: '0.25',
|
||||||
performanceScore: '0.9998677767864936',
|
performanceScore: '0.9998677767864936',
|
||||||
|
multisigScore: '',
|
||||||
|
validatorScore: '',
|
||||||
|
normalisedScore: '',
|
||||||
|
validatorStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
},
|
},
|
||||||
rankingScore: {
|
rankingScore: {
|
||||||
stakeScore: '0.2499583402766206',
|
stakeScore: '0.2499583402766206',
|
||||||
|
performanceScore: '0.9998677767864936',
|
||||||
|
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
previousStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
rankingScore: '',
|
||||||
|
votingPower: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
|
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
|
||||||
|
stakedTotal: '9618711883996159534058',
|
||||||
rewardScore: {
|
rewardScore: {
|
||||||
rawValidatorScore: '0.3',
|
rawValidatorScore: '0.3',
|
||||||
performanceScore: '1',
|
performanceScore: '1',
|
||||||
|
multisigScore: '',
|
||||||
|
validatorScore: '0.31067',
|
||||||
|
normalisedScore: '',
|
||||||
|
validatorStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
},
|
},
|
||||||
rankingScore: {
|
rankingScore: {
|
||||||
stakeScore: '0.25',
|
stakeScore: '0.25',
|
||||||
|
performanceScore: '0.9998677767864936',
|
||||||
|
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
previousStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
rankingScore: '',
|
||||||
|
votingPower: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
|
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
|
||||||
|
stakedTotal: '4041343338923442976709',
|
||||||
rewardScore: {
|
rewardScore: {
|
||||||
rawValidatorScore: '0.35',
|
rawValidatorScore: '0.35',
|
||||||
performanceScore: '0.999629748500531',
|
performanceScore: '0.999629748500531',
|
||||||
|
multisigScore: '',
|
||||||
|
validatorScore: '',
|
||||||
|
normalisedScore: '',
|
||||||
|
validatorStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
},
|
},
|
||||||
rankingScore: {
|
rankingScore: {
|
||||||
stakeScore: '0.2312',
|
stakeScore: '0.2312',
|
||||||
|
performanceScore: '0.9998677767864936',
|
||||||
|
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
previousStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
rankingScore: '',
|
||||||
|
votingPower: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -7,12 +7,12 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
|||||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
import { BigNumber } from '../../../../lib/bignumber';
|
import { BigNumber } from '../../../../lib/bignumber';
|
||||||
import {
|
import {
|
||||||
|
calculateOverallPenalty,
|
||||||
|
calculateOverstakedPenalty,
|
||||||
|
calculatesPerformancePenalty,
|
||||||
getFormattedPerformanceScore,
|
getFormattedPerformanceScore,
|
||||||
getLastEpochScoreAndPerformance,
|
getLastEpochScoreAndPerformance,
|
||||||
getNormalisedVotingPower,
|
getNormalisedVotingPower,
|
||||||
getOverstakingPenalty,
|
|
||||||
getPerformancePenalty,
|
|
||||||
getTotalPenalties,
|
|
||||||
getUnnormalisedVotingPower,
|
getUnnormalisedVotingPower,
|
||||||
} from '../../shared';
|
} from '../../shared';
|
||||||
import {
|
import {
|
||||||
@ -32,6 +32,7 @@ import type { ValidatorsTableProps } from './shared';
|
|||||||
import {
|
import {
|
||||||
formatNumber,
|
formatNumber,
|
||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
|
removePaginationWrapper,
|
||||||
toBigNum,
|
toBigNum,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { VALIDATOR_LOGO_MAP } from './logo-map';
|
import { VALIDATOR_LOGO_MAP } from './logo-map';
|
||||||
@ -136,6 +137,10 @@ export const ConsensusValidatorsTable = ({
|
|||||||
[totalStake]
|
[totalStake]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const allNodesInPreviousEpoch = removePaginationWrapper(
|
||||||
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
|
);
|
||||||
|
|
||||||
const nodes = useMemo(() => {
|
const nodes = useMemo(() => {
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
let canonisedNodes = data
|
let canonisedNodes = data
|
||||||
@ -160,7 +165,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
stakedByDelegates,
|
stakedByDelegates,
|
||||||
stakedByOperator,
|
stakedByOperator,
|
||||||
stakedTotal,
|
stakedTotal,
|
||||||
rankingScore: { stakeScore, votingPower },
|
rankingScore: { stakeScore, votingPower, performanceScore },
|
||||||
pendingStake,
|
pendingStake,
|
||||||
stakedTotalRanking,
|
stakedTotalRanking,
|
||||||
stakedByUser,
|
stakedByUser,
|
||||||
@ -172,11 +177,8 @@ export const ConsensusValidatorsTable = ({
|
|||||||
: avatarUrl
|
: avatarUrl
|
||||||
? avatarUrl
|
? avatarUrl
|
||||||
: null;
|
: null;
|
||||||
const {
|
const { rawValidatorScore: previousEpochValidatorScore } =
|
||||||
rawValidatorScore: previousEpochValidatorScore,
|
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||||
performanceScore: previousEpochPerformanceScore,
|
|
||||||
stakeScore: previousEpochStakeScore,
|
|
||||||
} = getLastEpochScoreAndPerformance(previousEpochData, id);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
@ -199,21 +201,19 @@ export const ConsensusValidatorsTable = ({
|
|||||||
toBigNum(stakedByOperator, decimals),
|
toBigNum(stakedByOperator, decimals),
|
||||||
2
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
|
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||||
previousEpochPerformanceScore
|
getFormattedPerformanceScore(performanceScore).toString(),
|
||||||
).toString(),
|
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
|
||||||
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
|
calculatesPerformancePenalty(performanceScore),
|
||||||
previousEpochPerformanceScore
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||||
previousEpochValidatorScore,
|
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||||
previousEpochStakeScore
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||||
previousEpochValidatorScore,
|
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||||
previousEpochPerformanceScore,
|
2
|
||||||
stakedTotal,
|
|
||||||
totalStake
|
|
||||||
),
|
),
|
||||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||||
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
||||||
@ -328,12 +328,12 @@ export const ConsensusValidatorsTable = ({
|
|||||||
...remaining,
|
...remaining,
|
||||||
];
|
];
|
||||||
}, [
|
}, [
|
||||||
|
allNodesInPreviousEpoch,
|
||||||
data,
|
data,
|
||||||
decimals,
|
decimals,
|
||||||
hideTopThird,
|
hideTopThird,
|
||||||
previousEpochData,
|
previousEpochData,
|
||||||
thirdOfTotalStake,
|
thirdOfTotalStake,
|
||||||
totalStake,
|
|
||||||
validatorsView,
|
validatorsView,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -5,15 +5,14 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
|||||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
import { BigNumber } from '../../../../lib/bignumber';
|
import { BigNumber } from '../../../../lib/bignumber';
|
||||||
import {
|
import {
|
||||||
|
calculatesPerformancePenalty,
|
||||||
|
calculateOverallPenalty,
|
||||||
|
calculateOverstakedPenalty,
|
||||||
getFormattedPerformanceScore,
|
getFormattedPerformanceScore,
|
||||||
getLastEpochScoreAndPerformance,
|
getLastEpochScoreAndPerformance,
|
||||||
getOverstakingPenalty,
|
|
||||||
getPerformancePenalty,
|
|
||||||
getTotalPenalties,
|
|
||||||
} from '../../shared';
|
} from '../../shared';
|
||||||
import {
|
import {
|
||||||
defaultColDef,
|
defaultColDef,
|
||||||
StakeNeededForPromotionRenderer,
|
|
||||||
stakedTotalPercentage,
|
stakedTotalPercentage,
|
||||||
ValidatorFields,
|
ValidatorFields,
|
||||||
ValidatorRenderer,
|
ValidatorRenderer,
|
||||||
@ -28,6 +27,7 @@ import type { ValidatorsTableProps } from './shared';
|
|||||||
import {
|
import {
|
||||||
formatNumber,
|
formatNumber,
|
||||||
formatNumberPercentage,
|
formatNumberPercentage,
|
||||||
|
removePaginationWrapper,
|
||||||
toBigNum,
|
toBigNum,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
|
|
||||||
@ -52,6 +52,10 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
|
|
||||||
const gridRef = useRef<AgGridReact | null>(null);
|
const gridRef = useRef<AgGridReact | null>(null);
|
||||||
|
|
||||||
|
const allNodesInPreviousEpoch = removePaginationWrapper(
|
||||||
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
|
);
|
||||||
|
|
||||||
let nodes = useMemo(() => {
|
let nodes = useMemo(() => {
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
|
|
||||||
@ -77,18 +81,15 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
stakedByDelegates,
|
stakedByDelegates,
|
||||||
stakedByOperator,
|
stakedByOperator,
|
||||||
stakedTotal,
|
stakedTotal,
|
||||||
rankingScore: { stakeScore },
|
rankingScore: { stakeScore, performanceScore },
|
||||||
pendingStake,
|
pendingStake,
|
||||||
stakedTotalRanking,
|
stakedTotalRanking,
|
||||||
stakedByUser,
|
stakedByUser,
|
||||||
pendingUserStake,
|
pendingUserStake,
|
||||||
userStakeShare,
|
userStakeShare,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const { performanceScore: previousEpochPerformanceScore } =
|
||||||
rawValidatorScore: previousEpochValidatorScore,
|
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||||
performanceScore: previousEpochPerformanceScore,
|
|
||||||
stakeScore: previousEpochStakeScore,
|
|
||||||
} = getLastEpochScoreAndPerformance(previousEpochData, id);
|
|
||||||
|
|
||||||
let individualStakeNeededForPromotion,
|
let individualStakeNeededForPromotion,
|
||||||
individualStakeNeededForPromotionDescription;
|
individualStakeNeededForPromotionDescription;
|
||||||
@ -144,21 +145,19 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
toBigNum(stakedByOperator, decimals),
|
toBigNum(stakedByOperator, decimals),
|
||||||
2
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
|
[ValidatorFields.PERFORMANCE_SCORE]:
|
||||||
previousEpochPerformanceScore
|
getFormattedPerformanceScore(performanceScore).toString(),
|
||||||
).toString(),
|
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
|
||||||
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
|
calculatesPerformancePenalty(performanceScore),
|
||||||
previousEpochPerformanceScore
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
|
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||||
previousEpochValidatorScore,
|
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||||
previousEpochStakeScore
|
2
|
||||||
),
|
),
|
||||||
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
|
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||||
previousEpochValidatorScore,
|
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||||
previousEpochPerformanceScore,
|
2
|
||||||
stakedTotal,
|
|
||||||
totalStake
|
|
||||||
),
|
),
|
||||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||||
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
||||||
@ -172,13 +171,13 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}, [
|
}, [
|
||||||
|
allNodesInPreviousEpoch,
|
||||||
data,
|
data,
|
||||||
decimals,
|
decimals,
|
||||||
previousEpochData,
|
previousEpochData,
|
||||||
stakeNeededForPromotion,
|
stakeNeededForPromotion,
|
||||||
stakeNeededForPromotionDescription,
|
stakeNeededForPromotionDescription,
|
||||||
t,
|
t,
|
||||||
totalStake,
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (validatorsView === 'myStake') {
|
if (validatorsView === 'myStake') {
|
||||||
@ -226,21 +225,21 @@ export const StandbyPendingValidatorsTable = ({
|
|||||||
cellRenderer: StakeShareRenderer,
|
cellRenderer: StakeShareRenderer,
|
||||||
width: 100,
|
width: 100,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
|
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
|
||||||
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
|
// headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
|
||||||
headerTooltip: t(stakeNeededForPromotionDescription, {
|
// headerTooltip: t(stakeNeededForPromotionDescription, {
|
||||||
prefix: t('The'),
|
// prefix: t('The'),
|
||||||
}),
|
// }),
|
||||||
cellRenderer: StakeNeededForPromotionRenderer,
|
// cellRenderer: StakeNeededForPromotionRenderer,
|
||||||
width: 210,
|
// width: 210,
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
field: ValidatorFields.TOTAL_PENALTIES,
|
field: ValidatorFields.TOTAL_PENALTIES,
|
||||||
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
|
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
|
||||||
headerTooltip: t('TotalPenaltiesDescription').toString(),
|
headerTooltip: t('TotalPenaltiesDescription').toString(),
|
||||||
cellRenderer: TotalPenaltiesRenderer,
|
cellRenderer: TotalPenaltiesRenderer,
|
||||||
width: 120,
|
width: 120 + 210,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
DocsLinks,
|
DocsLinks,
|
||||||
ExternalLinks,
|
ExternalLinks,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import {
|
||||||
|
formatNumberPercentage,
|
||||||
|
removePaginationWrapper,
|
||||||
|
toBigNum,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
Link as UTLink,
|
Link as UTLink,
|
||||||
@ -24,11 +28,11 @@ import { SubHeading } from '../../../components/heading';
|
|||||||
import {
|
import {
|
||||||
getLastEpochScoreAndPerformance,
|
getLastEpochScoreAndPerformance,
|
||||||
getNormalisedVotingPower,
|
getNormalisedVotingPower,
|
||||||
getOverstakingPenalty,
|
|
||||||
getPerformancePenalty,
|
|
||||||
getTotalPenalties,
|
|
||||||
getUnnormalisedVotingPower,
|
getUnnormalisedVotingPower,
|
||||||
getStakePercentage,
|
getStakePercentage,
|
||||||
|
calculatesPerformancePenalty,
|
||||||
|
calculateOverstakedPenalty,
|
||||||
|
calculateOverallPenalty,
|
||||||
} from '../shared';
|
} from '../shared';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
||||||
@ -78,17 +82,27 @@ export const ValidatorTable = ({
|
|||||||
|
|
||||||
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
|
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
|
||||||
|
|
||||||
const { rawValidatorScore, performanceScore, stakeScore } =
|
const { rawValidatorScore } = getLastEpochScoreAndPerformance(
|
||||||
getLastEpochScoreAndPerformance(previousEpochData, node.id);
|
previousEpochData,
|
||||||
|
node.id
|
||||||
|
);
|
||||||
|
|
||||||
const stakePercentage = getStakePercentage(total, stakedOnNode);
|
const stakePercentage = getStakePercentage(total, stakedOnNode);
|
||||||
|
|
||||||
const totalPenaltiesAmount = getTotalPenalties(
|
const penalties = useMemo(() => {
|
||||||
rawValidatorScore,
|
const allNodesInPreviousEpoch = removePaginationWrapper(
|
||||||
performanceScore,
|
previousEpochData?.epoch.validatorsConnection?.edges
|
||||||
stakedOnNode.toString(),
|
|
||||||
total.toString()
|
|
||||||
);
|
);
|
||||||
|
return {
|
||||||
|
// current epoch
|
||||||
|
performance: calculatesPerformancePenalty(
|
||||||
|
node.rankingScore.performanceScore
|
||||||
|
),
|
||||||
|
// previous epoch
|
||||||
|
overstaked: calculateOverstakedPenalty(node.id, allNodesInPreviousEpoch),
|
||||||
|
overall: calculateOverallPenalty(node.id, allNodesInPreviousEpoch),
|
||||||
|
};
|
||||||
|
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -242,7 +256,7 @@ export const ValidatorTable = ({
|
|||||||
|
|
||||||
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
||||||
<span data-testid="overstaking-penalty">
|
<span data-testid="overstaking-penalty">
|
||||||
{getOverstakingPenalty(rawValidatorScore, stakeScore)}
|
{formatNumberPercentage(penalties.overstaked, 2)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
@ -251,7 +265,7 @@ export const ValidatorTable = ({
|
|||||||
|
|
||||||
<Tooltip description={t('PerformancePenaltyDescription')}>
|
<Tooltip description={t('PerformancePenaltyDescription')}>
|
||||||
<span data-testid="performance-penalty">
|
<span data-testid="performance-penalty">
|
||||||
{getPerformancePenalty(performanceScore)}
|
{formatNumberPercentage(penalties.performance, 2)}
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
@ -260,7 +274,7 @@ export const ValidatorTable = ({
|
|||||||
<strong>{t('TOTAL PENALTIES')}</strong>
|
<strong>{t('TOTAL PENALTIES')}</strong>
|
||||||
</span>
|
</span>
|
||||||
<span data-testid="total-penalties">
|
<span data-testid="total-penalties">
|
||||||
<strong>{totalPenaltiesAmount}</strong>
|
<strong>{formatNumberPercentage(penalties.overall, 2)}</strong>
|
||||||
</span>
|
</span>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
</KeyValueTable>
|
</KeyValueTable>
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
getTotalPenalties,
|
getTotalPenalties,
|
||||||
getStakePercentage,
|
getStakePercentage,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
|
||||||
describe('getLastEpochScoreAndPerformance', () => {
|
describe('getLastEpochScoreAndPerformance', () => {
|
||||||
const mockPreviousEpochData = {
|
const mockPreviousEpochData = {
|
||||||
@ -19,24 +20,48 @@ describe('getLastEpochScoreAndPerformance', () => {
|
|||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '0x123',
|
id: '0x123',
|
||||||
|
stakedTotal: '',
|
||||||
rewardScore: {
|
rewardScore: {
|
||||||
rawValidatorScore: '0.25',
|
rawValidatorScore: '0.25',
|
||||||
performanceScore: '0.75',
|
performanceScore: '0.75',
|
||||||
|
multisigScore: '',
|
||||||
|
validatorScore: '',
|
||||||
|
normalisedScore: '',
|
||||||
|
validatorStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
},
|
},
|
||||||
rankingScore: {
|
rankingScore: {
|
||||||
stakeScore: '0.25',
|
stakeScore: '0.25',
|
||||||
|
performanceScore: '0.75',
|
||||||
|
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
previousStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
rankingScore: '',
|
||||||
|
votingPower: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
id: '0x234',
|
id: '0x234',
|
||||||
|
stakedTotal: '',
|
||||||
rewardScore: {
|
rewardScore: {
|
||||||
rawValidatorScore: '0.35',
|
rawValidatorScore: '0.35',
|
||||||
performanceScore: '0.85',
|
performanceScore: '0.85',
|
||||||
|
multisigScore: '',
|
||||||
|
validatorScore: '',
|
||||||
|
normalisedScore: '',
|
||||||
|
validatorStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
},
|
},
|
||||||
rankingScore: {
|
rankingScore: {
|
||||||
stakeScore: '0.25',
|
stakeScore: '0.25',
|
||||||
|
performanceScore: '0.85',
|
||||||
|
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
previousStatus:
|
||||||
|
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||||
|
rankingScore: '',
|
||||||
|
votingPower: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,94 @@ import {
|
|||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
|
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
|
import type { LastArrayElement } from 'type-fest';
|
||||||
|
|
||||||
|
type Node = NonNullable<
|
||||||
|
LastArrayElement<
|
||||||
|
NonNullable<
|
||||||
|
NonNullable<PreviousEpochQuery['epoch']['validatorsConnection']>['edges']
|
||||||
|
>
|
||||||
|
>
|
||||||
|
>['node'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates theoretical stake score for a given node
|
||||||
|
* @param nodeId Id of a node for which a score is calculated
|
||||||
|
* @param nodes A collection of all nodes
|
||||||
|
* @returns Theoretical stake score for given node based on the staked total
|
||||||
|
* of all node of the same type (status)
|
||||||
|
*/
|
||||||
|
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||||
|
const node = nodes.find((n) => n.id === nodeId);
|
||||||
|
if (!node) {
|
||||||
|
return new BigNumber(0);
|
||||||
|
}
|
||||||
|
const all = nodes
|
||||||
|
.filter((n) => n.rankingScore.status === node.rankingScore.status)
|
||||||
|
.map((n) => new BigNumber(n.stakedTotal));
|
||||||
|
const sumOfSameType = all.reduce((acc, a) => acc.plus(a), new BigNumber(0));
|
||||||
|
if (sumOfSameType.isZero()) {
|
||||||
|
return new BigNumber(0);
|
||||||
|
}
|
||||||
|
return new BigNumber(node.stakedTotal).dividedBy(sumOfSameType);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates overall penalty for a given node
|
||||||
|
* @param nodeId Id of a node for which a penalty is calculated
|
||||||
|
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||||
|
* @returns %
|
||||||
|
*/
|
||||||
|
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||||
|
const node = nodes.find((n) => n.id === nodeId);
|
||||||
|
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||||
|
if (!node || tts.isZero()) {
|
||||||
|
return new BigNumber(0);
|
||||||
|
}
|
||||||
|
const penalty = new BigNumber(1)
|
||||||
|
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
|
||||||
|
.times(100);
|
||||||
|
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates over-staked penalty for a given node
|
||||||
|
* @param nodeId Id of a node for which a penalty is calculated
|
||||||
|
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||||
|
* @returns %
|
||||||
|
*/
|
||||||
|
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||||
|
const node = nodes.find((n) => n.id === nodeId);
|
||||||
|
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||||
|
if (!node || tts.isZero()) {
|
||||||
|
return new BigNumber(0);
|
||||||
|
}
|
||||||
|
const penalty = new BigNumber(1)
|
||||||
|
.minus(
|
||||||
|
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
|
||||||
|
)
|
||||||
|
.times(100);
|
||||||
|
console.log(
|
||||||
|
nodeId,
|
||||||
|
new BigNumber(node.rewardScore?.rawValidatorScore || 0).toString(),
|
||||||
|
tts.toString(),
|
||||||
|
new BigNumber(node.rewardScore?.rawValidatorScore || 0)
|
||||||
|
.dividedBy(tts)
|
||||||
|
.toString()
|
||||||
|
);
|
||||||
|
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates performance penalty based on the given performance score.
|
||||||
|
* @returns %
|
||||||
|
*/
|
||||||
|
export const calculatesPerformancePenalty = (performanceScore: string) => {
|
||||||
|
const penalty = new BigNumber(1)
|
||||||
|
.minus(new BigNumber(performanceScore))
|
||||||
|
.times(100);
|
||||||
|
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||||
|
};
|
||||||
|
|
||||||
export const getLastEpochScoreAndPerformance = (
|
export const getLastEpochScoreAndPerformance = (
|
||||||
previousEpochData: PreviousEpochQuery | undefined,
|
previousEpochData: PreviousEpochQuery | undefined,
|
||||||
@ -15,7 +103,7 @@ export const getLastEpochScoreAndPerformance = (
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
|
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
|
||||||
performanceScore: validator?.rewardScore?.performanceScore,
|
performanceScore: validator?.rankingScore?.performanceScore,
|
||||||
stakeScore: validator?.rankingScore?.stakeScore,
|
stakeScore: validator?.rankingScore?.stakeScore,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user