Compare commits

...
Author SHA1 Message Date
asiaznik ff0b3b3733 fix(governance): penalties calculation 2023-05-19 15:53:24 +02:00
Edd 5c0588887c fix(explorer): fix footer links (#3825) 2023-05-19 10:37:59 +01:00
daro-maj 089a7daac7 test(trading): e2e test for select network and nodes - 0006-NETW (#3828) 2023-05-19 10:37:44 +01:00
Mikołaj Młodzikowski 02b6251b1a feat(ci): support mainnet branching strategy (#3832) 2023-05-19 10:38:36 +02:00
Bartłomiej Głownia 9ce8907861 feat(trading): do not show oracle banner when data is loading (#3821) 2023-05-19 10:01:22 +02:00
dexturr 3be9126906 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-19 06:10:00 +00:00
dexturr 77316092d1 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-19 00:15:57 +00:00
Dexter Edwards fca229e62a feat(governance): use rest to expose proposal data (#3827) 2023-05-18 22:43:32 +01:00
dexturr 162b167860 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 18:07:36 +00:00
41 changed files with 1438 additions and 294 deletions
+6 -4
View File
@@ -62,17 +62,19 @@ jobs:
- name: Define variables
run: |
envName=''
domain="vega.rocks"
if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community"
fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
fi
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
+1
View File
@@ -14,6 +14,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
# App flags
NX_EXPLORER_ASSETS=1
@@ -48,11 +48,13 @@ export const Footer = () => {
</Link>
</div>
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
{ENV.addresses.feedback ? (
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
) : null}
</div>
</footer>
);
@@ -62,9 +64,5 @@ const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
return (
<Link href={'https://' + nodeUrl} target="_blank">
{nodeUrl}
</Link>
);
return <span className="cursor-default">{nodeUrl}</span>;
};
+1
View File
@@ -17,6 +17,7 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -15,6 +15,7 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -17,6 +17,7 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -11,3 +11,4 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
+1
View File
@@ -12,3 +12,4 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
+1
View File
@@ -8,3 +8,4 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
+1
View File
@@ -12,3 +12,4 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
+1
View File
@@ -9,3 +9,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
+1
View File
@@ -62,6 +62,7 @@ export const ENV = {
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
rest: windowOrDefault('NX_VEGA_REST_URL'),
flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
@@ -43,13 +43,17 @@ jest.mock('../list-asset', () => ({
it('Renders with data-testid', async () => {
const proposal = generateProposal();
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
expect(await screen.findByTestId('proposal')).toBeInTheDocument();
});
it('renders each section', async () => {
const proposal = generateProposal();
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
@@ -76,6 +80,8 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
},
},
});
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
});
@@ -24,9 +24,11 @@ export enum ProposalType {
}
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
}
export const Proposal = ({ proposal }: ProposalProps) => {
export const Proposal = ({ proposal, restData }: ProposalProps) => {
const { params, loading, error } = useNetworkParams([
NetworkParams.governance_proposal_market_minVoterBalance,
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
@@ -104,7 +106,7 @@ export const Proposal = ({ proposal }: ProposalProps) => {
</div>
<div className="mb-6">
<ProposalJson proposal={proposal} />
<ProposalJson proposal={restData?.data?.proposal} />
</div>
<div className="mb-10">
@@ -5,9 +5,15 @@ import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useProposalQuery } from './__generated__/Proposal';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
const {
state: { loading: restLoading, error: restError, data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
console.log(restLoading, restError, restData);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -23,7 +29,7 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer loading={loading} error={error} data={data}>
{data?.proposal ? (
<Proposal proposal={data.proposal} />
<Proposal proposal={data.proposal} restData={restData} />
) : (
<ProposalNotFound />
)}
@@ -5,12 +5,22 @@ query PreviousEpoch($epochId: ID) {
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
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`
@@ -19,12 +19,22 @@ export const PreviousEpochDocument = gql`
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
}
@@ -79,36 +79,72 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
{
node: {
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
stakedTotal: '14182454495731682635157',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.9998677767864936',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.2499583402766206',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
stakedTotal: '9618711883996159534058',
rewardScore: {
rawValidatorScore: '0.3',
performanceScore: '1',
multisigScore: '',
validatorScore: '0.31067',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
stakedTotal: '4041343338923442976709',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.999629748500531',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
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 { BigNumber } from '../../../../lib/bignumber';
import {
calculateOverallPenalty,
calculateOverstakedPenalty,
calculatesPerformancePenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
} from '../../shared';
import {
@@ -32,6 +32,7 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
@@ -136,6 +137,10 @@ export const ConsensusValidatorsTable = ({
[totalStake]
);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
const nodes = useMemo(() => {
if (!data) return [];
let canonisedNodes = data
@@ -160,7 +165,7 @@ export const ConsensusValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore, votingPower },
rankingScore: { stakeScore, votingPower, performanceScore },
pendingStake,
stakedTotalRanking,
stakedByUser,
@@ -172,11 +177,8 @@ export const ConsensusValidatorsTable = ({
: avatarUrl
? avatarUrl
: null;
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
return {
id,
@@ -199,21 +201,19 @@ export const ConsensusValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
),
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
),
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -328,12 +328,12 @@ export const ConsensusValidatorsTable = ({
...remaining,
];
}, [
allNodesInPreviousEpoch,
data,
decimals,
hideTopThird,
previousEpochData,
thirdOfTotalStake,
totalStake,
validatorsView,
]);
@@ -5,15 +5,14 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
calculatesPerformancePenalty,
calculateOverallPenalty,
calculateOverstakedPenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
} from '../../shared';
import {
defaultColDef,
StakeNeededForPromotionRenderer,
stakedTotalPercentage,
ValidatorFields,
ValidatorRenderer,
@@ -28,6 +27,7 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
@@ -52,6 +52,10 @@ export const StandbyPendingValidatorsTable = ({
const gridRef = useRef<AgGridReact | null>(null);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
let nodes = useMemo(() => {
if (!data) return [];
@@ -77,18 +81,15 @@ export const StandbyPendingValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore },
rankingScore: { stakeScore, performanceScore },
pendingStake,
stakedTotalRanking,
stakedByUser,
pendingUserStake,
userStakeShare,
}) => {
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
const { performanceScore: previousEpochPerformanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
let individualStakeNeededForPromotion,
individualStakeNeededForPromotionDescription;
@@ -144,21 +145,19 @@ export const StandbyPendingValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
),
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
),
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -172,13 +171,13 @@ export const StandbyPendingValidatorsTable = ({
}
);
}, [
allNodesInPreviousEpoch,
data,
decimals,
previousEpochData,
stakeNeededForPromotion,
stakeNeededForPromotionDescription,
t,
totalStake,
]);
if (validatorsView === 'myStake') {
@@ -226,21 +225,21 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: StakeShareRenderer,
width: 100,
},
{
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
headerTooltip: t(stakeNeededForPromotionDescription, {
prefix: t('The'),
}),
cellRenderer: StakeNeededForPromotionRenderer,
width: 210,
},
// {
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
// headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
// headerTooltip: t(stakeNeededForPromotionDescription, {
// prefix: t('The'),
// }),
// cellRenderer: StakeNeededForPromotionRenderer,
// width: 210,
// },
{
field: ValidatorFields.TOTAL_PENALTIES,
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
headerTooltip: t('TotalPenaltiesDescription').toString(),
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 {
useEnvironment,
DocsLinks,
ExternalLinks,
} from '@vegaprotocol/environment';
import { toBigNum } from '@vegaprotocol/utils';
import {
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import {
Link as UTLink,
@@ -24,11 +28,11 @@ import { SubHeading } from '../../../components/heading';
import {
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
getStakePercentage,
calculatesPerformancePenalty,
calculateOverstakedPenalty,
calculateOverallPenalty,
} from '../shared';
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
@@ -78,17 +82,27 @@ export const ValidatorTable = ({
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
const { rawValidatorScore, performanceScore, stakeScore } =
getLastEpochScoreAndPerformance(previousEpochData, node.id);
const { rawValidatorScore } = getLastEpochScoreAndPerformance(
previousEpochData,
node.id
);
const stakePercentage = getStakePercentage(total, stakedOnNode);
const totalPenaltiesAmount = getTotalPenalties(
rawValidatorScore,
performanceScore,
stakedOnNode.toString(),
total.toString()
);
const penalties = useMemo(() => {
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
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 (
<>
@@ -242,7 +256,7 @@ export const ValidatorTable = ({
<Tooltip description={t('OverstakedPenaltyDescription')}>
<span data-testid="overstaking-penalty">
{getOverstakingPenalty(rawValidatorScore, stakeScore)}
{formatNumberPercentage(penalties.overstaked, 2)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -251,7 +265,7 @@ export const ValidatorTable = ({
<Tooltip description={t('PerformancePenaltyDescription')}>
<span data-testid="performance-penalty">
{getPerformancePenalty(performanceScore)}
{formatNumberPercentage(penalties.performance, 2)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -260,7 +274,7 @@ export const ValidatorTable = ({
<strong>{t('TOTAL PENALTIES')}</strong>
</span>
<span data-testid="total-penalties">
<strong>{totalPenaltiesAmount}</strong>
<strong>{formatNumberPercentage(penalties.overall, 2)}</strong>
</span>
</KeyValueTableRow>
</KeyValueTable>
@@ -9,6 +9,7 @@ import {
getTotalPenalties,
getStakePercentage,
} from './shared';
import * as Schema from '@vegaprotocol/types';
describe('getLastEpochScoreAndPerformance', () => {
const mockPreviousEpochData = {
@@ -19,24 +20,48 @@ describe('getLastEpochScoreAndPerformance', () => {
{
node: {
id: '0x123',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.75',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.75',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '0x234',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.85',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.85',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
+89 -1
View File
@@ -4,6 +4,94 @@ import {
} from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
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 = (
previousEpochData: PreviousEpochQuery | undefined,
@@ -15,7 +103,7 @@ export const getLastEpochScoreAndPerformance = (
return {
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
performanceScore: validator?.rewardScore?.performanceScore,
performanceScore: validator?.rankingScore?.performanceScore,
stakeScore: validator?.rankingScore?.stakeScore,
};
};
File diff suppressed because it is too large Load Diff
@@ -194,6 +194,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
});
it('shows node health', function () {
// 0006-NETW-010
const market = this.market;
cy.visit(`/#/markets/${market.id}`);
cy.getByTestId('node-health')
@@ -0,0 +1,111 @@
import { closeWelcomeDialog } from '../support/helpers';
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
describe('home', { tags: '@regression' }, () => {
before(() => {
cy.clearLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
});
describe('footer', () => {
it('shows current block height', () => {
closeWelcomeDialog();
// 0006-NETW-004
// 0006-NETW-005
// 0006-NETW-008
// 0006-NETW-009
// 0006-NETW-011
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(3000);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Warning delay ( >3 sec)');
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(1);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational', { timeout: 10000 })
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
it('shows node switcher details', () => {
// 0006-NETW-012
// 0006-NETW-013
// 0006-NETW-014
// 0006-NETW-015
// 0006-NETW-016
cy.getByTestId(nodeHealth).click();
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
cy.getByTestId(dialogContent).should(
'contain.text',
'This app will only work on CUSTOM. Select a node to connect to.'
);
cy.getByTestId('node')
.first()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', 'Response time')
.next()
.should('contain.text', 'Block')
.next()
.should('contain.text', 'Subscription');
cy.getByTestId('custom-row').should('contain.text', 'Other');
cy.getByTestId('dialog-close').click();
});
it('switch to other node', () => {
// 0006-NETW-017
// 0006-NETW-018
// 0006-NETW-019
// 0006-NETW-020
cy.getByTestId(nodeHealth).click();
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
cy.getByTestId('connect').should('be.disabled');
cy.get("input[placeholder='https://']")
.focus()
.type(new URL(Cypress.env('VEGA_URL')).origin + '/graphql');
cy.getByTestId('connect').click();
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational');
});
});
describe('Network switcher', () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
});
// 0006-NETW-002
// 0006-NETW-003
it('switch to fairground network', () => {
cy.getByTestId('network-switcher').click();
cy.getByTestId('network-item').contains('Fairground testnet').click();
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
});
});
});
+3 -16
View File
@@ -4,6 +4,7 @@ import { marketsDataQuery } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const selectMarketOverlay = 'select-market-list';
const dialogContent = 'dialog-content';
const generateProposal = (code: string): ProposalListFieldsFragment => ({
__typename: 'Proposal',
@@ -288,7 +289,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-1');
cy.getByTestId('dialog-content').should('not.exist');
cy.getByTestId(dialogContent).should('not.exist');
});
});
@@ -301,22 +302,8 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.getByTestId('dialog-content').should('not.exist');
cy.getByTestId(dialogContent).should('not.exist');
});
});
});
describe('footer', () => {
it('shows current block height', () => {
cy.visit('/');
cy.getByTestId('node-health')
.children()
.first()
.should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
});
});
@@ -107,6 +107,7 @@ export const NetworkSwitcher = ({
onOpenChange={handleOpen}
trigger={
<DropdownMenuTrigger
data-testid="network-switcher"
ref={menuRef}
className={classNames(
'flex justify-between items-center text-sm text-vega-dark-600 dark:text-vega-light-600 py-1 px-2 rounded border border-vega-dark-200 whitespace-nowrap dark:hover:bg-vega-dark-500 hover:bg-vega-light-500',
@@ -5,7 +5,7 @@ import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { marketDataProvider } from '../../market-data-provider';
import { totalFeesPercentage } from '../../market-utils';
import { Dialog, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
formatNumber,
@@ -25,12 +25,9 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { useEnvironment } from '@vegaprotocol/environment';
import type { Provider } from '../../oracle-schema';
import {
OracleBasicProfile,
OracleProfileTitle,
OracleFullProfile,
} from '../../components';
import { useOracleProofs, useOracleMarkets } from '../../hooks';
import { OracleBasicProfile } from '../../components/oracle-basic-profile';
import { useOracleProofs } from '../../hooks';
import { OracleDialog } from '../oracle-dialog/oracle-dialog';
import { useDataProvider } from '@vegaprotocol/data-provider';
type PanelProps = Pick<
@@ -613,34 +610,6 @@ const NoOracleProof = ({
);
};
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
const OracleProfile = (props: {
provider: Provider;
dataSourceSpecId: string;
@@ -143,7 +143,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
@@ -164,7 +164,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
@@ -1,8 +1,10 @@
import { useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@blueprintjs/icons';
import { getMatchingOracleProvider, useOracleProofs } from '../../hooks';
import type { Market } from '../../markets-provider';
import { getVerifiedStatusIcon } from '../oracle-basic-profile';
export const OracleStatus = ({
dataSourceSpecForSettlementData,
@@ -23,22 +25,15 @@ export const OracleStatus = ({
dataSourceSpecForTradingTermination.data,
providers
);
if (
(settlementDataProvider &&
settlementDataProvider.oracle.status !== 'GOOD') ||
(tradingTerminationDataProvider &&
tradingTerminationDataProvider.oracle.status !== 'GOOD')
) {
return (
<span
className="ml-1"
role="img"
aria-label={t('oracle status not healthy')}
>
</span>
);
let maliciousOracleProvider = null;
if (settlementDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = settlementDataProvider;
} else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = tradingTerminationDataProvider;
}
if (!maliciousOracleProvider) return null;
const { icon } = getVerifiedStatusIcon(maliciousOracleProvider);
return <Icon size={3} name={icon as IconName} className="ml-1" />;
}
return null;
}, [
@@ -6,27 +6,13 @@ import {
NotificationBanner,
ButtonLink,
} from '@vegaprotocol/ui-toolkit';
import { OracleDialog } from '../market-info';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
import { OracleDialog } from '../oracle-dialog';
import { oracleStatuses } from './oracle-statuses';
export const OracleBanner = ({ marketId }: { marketId: string }) => {
const [open, onChange] = useState(false);
const settlementOracle = useMarketOracle(marketId);
const tradingTerminationOracle = useMarketOracle(
const { data: settlementOracle } = useMarketOracle(marketId);
const { data: tradingTerminationOracle } = useMarketOracle(
marketId,
'dataSourceSpecForTradingTermination'
);
@@ -36,15 +22,7 @@ export const OracleBanner = ({ marketId }: { marketId: string }) => {
} else if (tradingTerminationOracle?.provider.oracle.status !== 'GOOD') {
maliciousOracle = tradingTerminationOracle;
}
if (!maliciousOracle) return null;
if (!settlementOracle && !tradingTerminationOracle) {
return (
<NotificationBanner intent={Intent.Primary}>
<div>{t('There is no oracle for this market.')} </div>
</NotificationBanner>
);
}
const { provider } = maliciousOracle;
return (
@@ -0,0 +1,16 @@
import { t } from '@vegaprotocol/i18n';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
@@ -0,0 +1 @@
export * from './oracle-dialog';
@@ -0,0 +1,35 @@
import { Dialog } from '@vegaprotocol/ui-toolkit';
import {
OracleProfileTitle,
OracleFullProfile,
} from '../../components/oracle-full-profile';
import { useOracleMarkets } from '../../hooks';
import type { Provider } from '../../oracle-schema';
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
@@ -1,2 +1 @@
export * from './oracle-full-profile.stories';
export * from './oracle-full-profile';
@@ -9,7 +9,7 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { oracleStatuses } from '../oracle-banner';
import { oracleStatuses } from '../oracle-banner/oracle-statuses';
import type { IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
@@ -1,6 +1,6 @@
import { renderHook } from '@testing-library/react';
import { useMarketOracle } from './use-market-oracle';
import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo';
import type { MarketFieldsFragment } from '../__generated__/markets';
import type { Provider } from '../oracle-schema';
const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL';
@@ -10,43 +10,42 @@ const key = 'key';
const dataSourceSpecId = 'dataSourceSpecId';
const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL }));
const mockDataProvider = jest.fn<
{ data: MarketInfoQuery['market'] },
unknown[]
>(() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal',
const mockMarket = jest.fn<{ data: MarketFieldsFragment | null }, unknown[]>(
() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
sourceType: {
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
__typename: 'DataSourceDefinitionExternal',
sourceType: {
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
},
},
},
{
signer: {
__typename: 'PubKey',
key,
{
signer: {
__typename: 'PubKey',
key,
},
},
},
],
],
},
},
},
},
},
},
},
},
} as MarketInfoQuery['market'],
}));
} as MarketFieldsFragment,
})
);
const mockOracleProofs = jest.fn<{ data?: Provider[] }, unknown[]>(() => ({}));
@@ -54,9 +53,8 @@ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: jest.fn((args) => mockEnvironment()),
}));
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn((args) => mockDataProvider()),
jest.mock('../markets-provider', () => ({
useMarket: jest.fn((args) => mockMarket()),
}));
jest.mock('./use-oracle-proofs', () => ({
@@ -66,15 +64,15 @@ jest.mock('./use-oracle-proofs', () => ({
const marketId = 'marketId';
describe('useMarketOracle', () => {
it('returns undefined if no market info present', () => {
mockDataProvider.mockReturnValueOnce({ data: null });
mockMarket.mockReturnValueOnce({ data: null });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current).toBeUndefined();
expect(result.current?.data).toBeUndefined();
});
it('returns undefined if no oracle proofs present', () => {
mockOracleProofs.mockReturnValueOnce({ data: undefined });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current).toBeUndefined();
expect(result.current?.data).toBeUndefined();
});
it('returns oracle matched by eth_address', () => {
@@ -102,8 +100,8 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
});
it('returns oracle matching by public_key', () => {
@@ -131,7 +129,7 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
});
});
+21 -14
View File
@@ -1,7 +1,7 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useOracleProofs } from './use-oracle-proofs';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoProvider } from '../components/market-info/market-info-data-provider';
import { useMarket } from '../markets-provider';
import { useMemo } from 'react';
import type { Provider } from '../oracle-schema';
import type { DataSourceSpecFragment } from '../__generated__/OracleMarketsSpec';
@@ -41,23 +41,30 @@ export const useMarketOracle = (
dataSourceType:
| 'dataSourceSpecForSettlementData'
| 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData'
) => {
): {
data?: {
provider: NonNullable<ReturnType<typeof getMatchingOracleProvider>>;
dataSourceSpecId: string;
};
loading?: boolean;
} => {
const { ORACLE_PROOFS_URL } = useEnvironment();
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
});
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
const { data: market, loading: marketLoading } = useMarket(marketId);
const { data: providers, loading: providersLoading } =
useOracleProofs(ORACLE_PROOFS_URL);
return useMemo(() => {
if (!providers || !marketInfo) {
return undefined;
if (marketLoading || providersLoading) {
return { loading: true };
}
if (!providers || !market) {
return { data: undefined };
}
const dataSourceSpec =
marketInfo.tradableInstrument.instrument.product[dataSourceType];
market.tradableInstrument.instrument.product[dataSourceType];
const provider = getMatchingOracleProvider(dataSourceSpec.data, providers);
if (provider) {
return { provider, dataSourceSpecId: dataSourceSpec.id };
return { data: { provider, dataSourceSpecId: dataSourceSpec.id } };
}
return undefined;
}, [marketInfo, dataSourceType, providers]);
return { data: undefined };
}, [market, dataSourceType, providers, marketLoading, providersLoading]);
};
+20 -2
View File
@@ -64,26 +64,44 @@ export const createMarketFragment = (
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'oracleId',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'oracleId',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
@@ -1,4 +1,5 @@
import classNames from 'classnames';
import { useMemo } from 'react';
import Highlighter from 'react-syntax-highlighter';
export const SyntaxHighlighter = ({
@@ -8,6 +9,13 @@ export const SyntaxHighlighter = ({
data: unknown;
size?: 'smaller' | 'default';
}) => {
const parsedData = useMemo(() => {
try {
return JSON.stringify(data, null, ' ');
} catch (e) {
return 'Unable to parse data';
}
}, [data]);
return (
<div
className={classNames('syntax-highlighter-wrapper', {
@@ -15,7 +23,7 @@ export const SyntaxHighlighter = ({
})}
>
<Highlighter language="json" useInlineStyles={false}>
{JSON.stringify(data, null, ' ')}
{parsedData}
</Highlighter>
</div>
);