Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08f21eeee4 |
@@ -74,7 +74,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake', function () {
|
||||
cy.getByTestId('total-stake')
|
||||
cy.get('[col-id="stake"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($stake) => {
|
||||
cy.wrap($stake).should('not.be.empty');
|
||||
@@ -82,7 +82,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake tooltip', function () {
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
cy.get('[col-id="stake"] > div > span > span').first().realHover();
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
@@ -96,15 +96,17 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.getByTestId('normalised-voting-power')
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power tooltip', function () {
|
||||
cy.getByTestId('normalised-voting-power').first().realHover();
|
||||
it('Should be able to see validator voting power tooltip', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
.first()
|
||||
.realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
@@ -116,7 +118,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
// 2002-SINC-018
|
||||
it('Should be able to see validator total penalties', function () {
|
||||
cy.getByTestId('total-penalty')
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($penalties) => {
|
||||
cy.wrap($penalties).should('contain.text', '0%');
|
||||
@@ -124,7 +126,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator penalties tooltip', function () {
|
||||
cy.getByTestId('total-penalty').realHover();
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
@@ -138,7 +140,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator pending stake', function () {
|
||||
cy.getByTestId('total-pending-stake')
|
||||
cy.get('[col-id="pendingStake"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($pendingStake) => {
|
||||
cy.wrap($pendingStake).should('contain.text', '0.00');
|
||||
|
||||
@@ -14,8 +14,8 @@ const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
|
||||
const associateContractRadioButton = '[data-testid="associate-radio-contract"]';
|
||||
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
|
||||
const stakeValidatorListPendingStake = '[col-id="pendingStake"]';
|
||||
const stakeValidatorListTotalStake = 'total-stake';
|
||||
const stakeValidatorListTotalShare = 'total-stake-share';
|
||||
const stakeValidatorListTotalStake = '[col-id="stake"] > div > span';
|
||||
const stakeValidatorListTotalShare = '[col-id="stakeShare"] > div > span';
|
||||
const stakeValidatorListName = '[col-id="validator"]';
|
||||
const vegaKeySelector = '#vega-key-selector';
|
||||
const dialogCloseButton = '[data-testid="dialog-close"]';
|
||||
@@ -185,11 +185,11 @@ export function validateValidatorListTotalStakeAndShare(
|
||||
cy.contains('Loading...', epochTimeout).should('not.exist');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.get(`[row-id="${positionOnList}"]`).within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
|
||||
cy.get(stakeValidatorListTotalStake, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalStake
|
||||
);
|
||||
cy.getByTestId(stakeValidatorListTotalShare, epochTimeout).should(
|
||||
cy.get(stakeValidatorListTotalShare, epochTimeout).should(
|
||||
'have.text',
|
||||
expectedTotalShare
|
||||
);
|
||||
|
||||
@@ -602,14 +602,11 @@
|
||||
"noValidators": "No validators",
|
||||
"validator": "Validator",
|
||||
"stake": "Stake",
|
||||
"myStake": "My stake",
|
||||
"stakeShare": "Stake share",
|
||||
"stakedByOperator": "Staked by operator",
|
||||
"stakedByDelegates": "Staked by delegates",
|
||||
"stakedByMe": "Staked by me",
|
||||
"totalStake": "Total stake",
|
||||
"pendingStake": "Pending stake",
|
||||
"myPendingStake": "My pending stake",
|
||||
"totalPenalties": "Total penalties",
|
||||
"noPenaltyDataFromLastEpoch": "No penalty data from last epoch",
|
||||
"stakeNeededForPromotion": "Stake needed for promotion",
|
||||
|
||||
@@ -12,14 +12,14 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useNodesQuery } from '../staking/home/__generated___/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
import { getNotRejectedProposals } from '../proposals/proposals/proposals-container';
|
||||
import { Heading } from '../../components/heading';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { RouteChildProps } from '..';
|
||||
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
|
||||
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
|
||||
import type { NodesFragmentFragment } from '../staking/home/__generated___/Nodes';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
|
||||
@@ -16,8 +16,12 @@ import orderBy from 'lodash/orderBy';
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[(p) => new Date(p?.terms?.closingDatetime).getTime(), (p) => p.id],
|
||||
['desc', 'desc']
|
||||
[
|
||||
(p) => new Date(p?.terms?.enactmentDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered.
|
||||
(p) => new Date(p?.terms?.closingDatetime).getTime(),
|
||||
(p) => p.id,
|
||||
],
|
||||
['desc', 'desc', 'desc']
|
||||
);
|
||||
|
||||
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
|
||||
|
||||
@@ -104,7 +104,7 @@ export const RewardsPage = () => {
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="grid xl:grid-cols-[1fr_auto] gap-12 items-center mb-8">
|
||||
<section className="grid xl:grid-cols-2 gap-12 items-center mb-8">
|
||||
<div>
|
||||
<SubHeading title={t('rewardsAndFeesReceived')} />
|
||||
<p>
|
||||
@@ -114,7 +114,7 @@ export const RewardsPage = () => {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="w-[440px]">
|
||||
<div className="max-w-[600px]">
|
||||
<Toggle
|
||||
name="epoch-reward-view-toggle"
|
||||
toggles={[
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type PreviousEpochQueryVariables = Types.Exact<{
|
||||
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
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`
|
||||
query PreviousEpoch($epochId: ID) {
|
||||
epoch(id: $epochId) {
|
||||
id
|
||||
validatorsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
rewardScore {
|
||||
rawValidatorScore
|
||||
}
|
||||
rankingScore {
|
||||
performanceScore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __usePreviousEpochQuery__
|
||||
*
|
||||
* To run a query within a React component, call `usePreviousEpochQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `usePreviousEpochQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = usePreviousEpochQuery({
|
||||
* variables: {
|
||||
* epochId: // value for 'epochId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function usePreviousEpochQuery(
|
||||
baseOptions?: Apollo.QueryHookOptions<
|
||||
PreviousEpochQuery,
|
||||
PreviousEpochQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<PreviousEpochQuery, PreviousEpochQueryVariables>(
|
||||
PreviousEpochDocument,
|
||||
options
|
||||
);
|
||||
}
|
||||
export function usePreviousEpochLazyQuery(
|
||||
baseOptions?: Apollo.LazyQueryHookOptions<
|
||||
PreviousEpochQuery,
|
||||
PreviousEpochQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useLazyQuery<PreviousEpochQuery, PreviousEpochQueryVariables>(
|
||||
PreviousEpochDocument,
|
||||
options
|
||||
);
|
||||
}
|
||||
export type PreviousEpochQueryHookResult = ReturnType<
|
||||
typeof usePreviousEpochQuery
|
||||
>;
|
||||
export type PreviousEpochLazyQueryHookResult = ReturnType<
|
||||
typeof usePreviousEpochLazyQuery
|
||||
>;
|
||||
export type PreviousEpochQueryResult = Apollo.QueryResult<
|
||||
PreviousEpochQuery,
|
||||
PreviousEpochQueryVariables
|
||||
>;
|
||||
@@ -0,0 +1,114 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type LinkingsFieldsFragment = {
|
||||
__typename?: 'StakeLinking';
|
||||
id: string;
|
||||
txHash: string;
|
||||
status: Types.StakeLinkingStatus;
|
||||
};
|
||||
|
||||
export type PartyStakeLinkingsQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
export type PartyStakeLinkingsQuery = {
|
||||
__typename?: 'Query';
|
||||
party?: {
|
||||
__typename?: 'Party';
|
||||
id: string;
|
||||
stakingSummary: {
|
||||
__typename?: 'StakingSummary';
|
||||
linkings: {
|
||||
__typename?: 'StakesConnection';
|
||||
edges?: Array<{
|
||||
__typename?: 'StakeLinkingEdge';
|
||||
node: {
|
||||
__typename?: 'StakeLinking';
|
||||
id: string;
|
||||
txHash: string;
|
||||
status: Types.StakeLinkingStatus;
|
||||
};
|
||||
} | null> | null;
|
||||
};
|
||||
};
|
||||
} | null;
|
||||
};
|
||||
|
||||
export const LinkingsFieldsFragmentDoc = gql`
|
||||
fragment LinkingsFields on StakeLinking {
|
||||
id
|
||||
txHash
|
||||
status
|
||||
}
|
||||
`;
|
||||
export const PartyStakeLinkingsDocument = gql`
|
||||
query PartyStakeLinkings($partyId: ID!) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
stakingSummary {
|
||||
linkings {
|
||||
edges {
|
||||
node {
|
||||
...LinkingsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${LinkingsFieldsFragmentDoc}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __usePartyStakeLinkingsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `usePartyStakeLinkingsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `usePartyStakeLinkingsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = usePartyStakeLinkingsQuery({
|
||||
* variables: {
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function usePartyStakeLinkingsQuery(
|
||||
baseOptions: Apollo.QueryHookOptions<
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables
|
||||
>(PartyStakeLinkingsDocument, options);
|
||||
}
|
||||
export function usePartyStakeLinkingsLazyQuery(
|
||||
baseOptions?: Apollo.LazyQueryHookOptions<
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useLazyQuery<
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables
|
||||
>(PartyStakeLinkingsDocument, options);
|
||||
}
|
||||
export type PartyStakeLinkingsQueryHookResult = ReturnType<
|
||||
typeof usePartyStakeLinkingsQuery
|
||||
>;
|
||||
export type PartyStakeLinkingsLazyQueryHookResult = ReturnType<
|
||||
typeof usePartyStakeLinkingsLazyQuery
|
||||
>;
|
||||
export type PartyStakeLinkingsQueryResult = Apollo.QueryResult<
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables
|
||||
>;
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
} from '../../../hooks/transaction-reducer';
|
||||
import Routes from '../../routes';
|
||||
import { truncateMiddle } from '../../../lib/truncate-middle';
|
||||
import type { LinkingsFieldsFragment } from './__generated__/PartyStakeLinkings';
|
||||
import type { LinkingsFieldsFragment } from './__generated___/PartyStakeLinkings';
|
||||
|
||||
export const AssociateTransaction = ({
|
||||
amount,
|
||||
|
||||
@@ -15,8 +15,8 @@ import type {
|
||||
LinkingsFieldsFragment,
|
||||
PartyStakeLinkingsQuery,
|
||||
PartyStakeLinkingsQueryVariables,
|
||||
} from './__generated__/PartyStakeLinkings';
|
||||
import { PartyStakeLinkingsDocument } from './__generated__/PartyStakeLinkings';
|
||||
} from './__generated___/PartyStakeLinkings';
|
||||
import { PartyStakeLinkingsDocument } from './__generated___/PartyStakeLinkings';
|
||||
|
||||
export const useAddStake = (
|
||||
address: string,
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type NodesFragmentFragment = {
|
||||
__typename?: 'Node';
|
||||
avatarUrl?: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
pubkey: string;
|
||||
stakedByOperator: string;
|
||||
stakedByDelegates: string;
|
||||
stakedTotal: string;
|
||||
pendingStake: string;
|
||||
rankingScore: {
|
||||
__typename?: 'RankingScore';
|
||||
rankingScore: string;
|
||||
stakeScore: string;
|
||||
performanceScore: string;
|
||||
votingPower: string;
|
||||
status: Types.ValidatorStatus;
|
||||
};
|
||||
};
|
||||
|
||||
export type NodesQueryVariables = Types.Exact<{ [key: string]: never }>;
|
||||
|
||||
export type NodesQuery = {
|
||||
__typename?: 'Query';
|
||||
epoch: {
|
||||
__typename?: 'Epoch';
|
||||
id: string;
|
||||
timestamps: {
|
||||
__typename?: 'EpochTimestamps';
|
||||
start?: any | null;
|
||||
end?: any | null;
|
||||
expiry?: any | null;
|
||||
};
|
||||
};
|
||||
nodesConnection: {
|
||||
__typename?: 'NodesConnection';
|
||||
edges?: Array<{
|
||||
__typename?: 'NodeEdge';
|
||||
node: {
|
||||
__typename?: 'Node';
|
||||
avatarUrl?: string | null;
|
||||
id: string;
|
||||
name: string;
|
||||
pubkey: string;
|
||||
stakedByOperator: string;
|
||||
stakedByDelegates: string;
|
||||
stakedTotal: string;
|
||||
pendingStake: string;
|
||||
rankingScore: {
|
||||
__typename?: 'RankingScore';
|
||||
rankingScore: string;
|
||||
stakeScore: string;
|
||||
performanceScore: string;
|
||||
votingPower: string;
|
||||
status: Types.ValidatorStatus;
|
||||
};
|
||||
};
|
||||
} | null> | null;
|
||||
};
|
||||
nodeData?: { __typename?: 'NodeData'; stakedTotal: string } | null;
|
||||
};
|
||||
|
||||
export const NodesFragmentFragmentDoc = gql`
|
||||
fragment NodesFragment on Node {
|
||||
avatarUrl
|
||||
id
|
||||
name
|
||||
pubkey
|
||||
stakedByOperator
|
||||
stakedByDelegates
|
||||
stakedTotal
|
||||
pendingStake
|
||||
rankingScore {
|
||||
rankingScore
|
||||
stakeScore
|
||||
performanceScore
|
||||
votingPower
|
||||
status
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const NodesDocument = gql`
|
||||
query Nodes {
|
||||
epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
nodesConnection {
|
||||
edges {
|
||||
node {
|
||||
...NodesFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
nodeData {
|
||||
stakedTotal
|
||||
}
|
||||
}
|
||||
${NodesFragmentFragmentDoc}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useNodesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useNodesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useNodesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useNodesQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useNodesQuery(
|
||||
baseOptions?: Apollo.QueryHookOptions<NodesQuery, NodesQueryVariables>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<NodesQuery, NodesQueryVariables>(
|
||||
NodesDocument,
|
||||
options
|
||||
);
|
||||
}
|
||||
export function useNodesLazyQuery(
|
||||
baseOptions?: Apollo.LazyQueryHookOptions<NodesQuery, NodesQueryVariables>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useLazyQuery<NodesQuery, NodesQueryVariables>(
|
||||
NodesDocument,
|
||||
options
|
||||
);
|
||||
}
|
||||
export type NodesQueryHookResult = ReturnType<typeof useNodesQuery>;
|
||||
export type NodesLazyQueryHookResult = ReturnType<typeof useNodesLazyQuery>;
|
||||
export type NodesQueryResult = Apollo.QueryResult<
|
||||
NodesQuery,
|
||||
NodesQueryVariables
|
||||
>;
|
||||
@@ -1,53 +1,36 @@
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { EpochCountdown } from '../../../components/epoch-countdown';
|
||||
import { useNodesQuery } from './__generated__/Nodes';
|
||||
import { useStakingQuery } from '../__generated__/Staking';
|
||||
import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import { useNodesQuery } from './__generated___/Nodes';
|
||||
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
import { ValidatorTables } from './validator-tables';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
export const EpochData = () => {
|
||||
// errorPolicy due to vegaprotocol/vega issue 5898
|
||||
const { pubKey } = useVegaWallet();
|
||||
const {
|
||||
data: nodesData,
|
||||
error: nodesError,
|
||||
loading: nodesLoading,
|
||||
refetch,
|
||||
} = useNodesQuery();
|
||||
const { data: userStakingData } = useStakingQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
},
|
||||
});
|
||||
const { data, error, loading, refetch } = useNodesQuery();
|
||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||
variables: {
|
||||
epochId: (Number(nodesData?.epoch.id) - 1).toString(),
|
||||
epochId: (Number(data?.epoch.id) - 1).toString(),
|
||||
},
|
||||
skip: !nodesData?.epoch.id,
|
||||
skip: !data?.epoch.id,
|
||||
});
|
||||
|
||||
useRefreshAfterEpoch(nodesData?.epoch.timestamps.expiry, refetch);
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={nodesLoading} error={nodesError} data={nodesData}>
|
||||
{nodesData?.epoch &&
|
||||
nodesData.epoch.timestamps.start &&
|
||||
nodesData?.epoch.timestamps.expiry && (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
{data?.epoch &&
|
||||
data.epoch.timestamps.start &&
|
||||
data?.epoch.timestamps.expiry && (
|
||||
<div className="mb-10">
|
||||
<EpochCountdown
|
||||
id={nodesData.epoch.id}
|
||||
startDate={new Date(nodesData.epoch.timestamps.start)}
|
||||
endDate={new Date(nodesData.epoch.timestamps.expiry)}
|
||||
id={data.epoch.id}
|
||||
startDate={new Date(data.epoch.timestamps.start)}
|
||||
endDate={new Date(data.epoch.timestamps.expiry)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ValidatorTables
|
||||
nodesData={nodesData}
|
||||
userStakingData={userStakingData}
|
||||
previousEpochData={previousEpochData}
|
||||
/>
|
||||
<ValidatorTables data={data} previousEpochData={previousEpochData} />
|
||||
</AsyncRenderer>
|
||||
);
|
||||
};
|
||||
|
||||
+5
-8
@@ -3,15 +3,14 @@ import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ConsensusValidatorsTable } from './consensus-validators-table';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { NodesDocument } from '../__generated__/Nodes';
|
||||
import { PreviousEpochDocument } from '../../__generated__/PreviousEpoch';
|
||||
import { NodesDocument } from '../__generated___/Nodes';
|
||||
import { PreviousEpochDocument } from '../../__generated___/PreviousEpoch';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { NodesFragmentFragment } from '../__generated__/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
|
||||
import type { ValidatorsView } from './validator-tables';
|
||||
import type { NodesFragmentFragment } from '../__generated___/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
|
||||
|
||||
const nodeFactory = (
|
||||
overrides?: PartialDeep<NodesFragmentFragment>
|
||||
@@ -143,8 +142,7 @@ const MOCK_TOTAL_STAKE = '28832590188747439203824';
|
||||
|
||||
const renderValidatorsTable = (
|
||||
data = MOCK_NODES,
|
||||
previousEpochData = MOCK_PREVIOUS_EPOCH,
|
||||
validatorsView: ValidatorsView = 'all'
|
||||
previousEpochData = MOCK_PREVIOUS_EPOCH
|
||||
) => {
|
||||
return render(
|
||||
<AppStateProvider initialState={{ decimals: 18 }}>
|
||||
@@ -154,7 +152,6 @@ const renderValidatorsTable = (
|
||||
data={data}
|
||||
previousEpochData={previousEpochData}
|
||||
totalStake={MOCK_TOTAL_STAKE}
|
||||
validatorsView={validatorsView}
|
||||
/>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
|
||||
+20
-52
@@ -19,9 +19,7 @@ import {
|
||||
import {
|
||||
defaultColDef,
|
||||
NODE_LIST_GRID_STYLES,
|
||||
PendingStakeRenderer,
|
||||
stakedTotalPercentage,
|
||||
StakeShareRenderer,
|
||||
TotalPenaltiesRenderer,
|
||||
TotalStakeRenderer,
|
||||
ValidatorFields,
|
||||
@@ -56,9 +54,6 @@ interface CanonisedConsensusNodeProps {
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: string;
|
||||
[ValidatorFields.TOTAL_PENALTIES]: string;
|
||||
[ValidatorFields.PENDING_STAKE]: string;
|
||||
[ValidatorFields.STAKED_BY_USER]: string | undefined;
|
||||
[ValidatorFields.PENDING_USER_STAKE]: string | undefined;
|
||||
[ValidatorFields.USER_STAKE_SHARE]: string | undefined;
|
||||
}
|
||||
|
||||
const getRowHeight = (params: RowHeightParams) => {
|
||||
@@ -66,7 +61,7 @@ const getRowHeight = (params: RowHeightParams) => {
|
||||
// Note: this value will change if the height of the top third cell renderer changes
|
||||
return 138;
|
||||
}
|
||||
return 68;
|
||||
return 52;
|
||||
};
|
||||
|
||||
const TopThirdCellRenderer = (
|
||||
@@ -122,7 +117,6 @@ export const ConsensusValidatorsTable = ({
|
||||
data,
|
||||
previousEpochData,
|
||||
totalStake,
|
||||
validatorsView,
|
||||
}: ValidatorsTableProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -135,7 +129,7 @@ export const ConsensusValidatorsTable = ({
|
||||
|
||||
const nodes = useMemo(() => {
|
||||
if (!data || !previousEpochData) return [];
|
||||
let canonisedNodes = data
|
||||
const canonisedNodes = data
|
||||
.sort((a, b) => {
|
||||
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
|
||||
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
|
||||
@@ -160,9 +154,6 @@ export const ConsensusValidatorsTable = ({
|
||||
rankingScore: { stakeScore, votingPower },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
stakedByUser,
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
}) => {
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
@@ -210,28 +201,12 @@ export const ConsensusValidatorsTable = ({
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
||||
? formatNumber(toBigNum(stakedByUser, decimals), 2)
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? stakedTotalPercentage(userStakeShare)
|
||||
: undefined,
|
||||
decimals,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
if (validatorsView === 'myStake') {
|
||||
canonisedNodes = canonisedNodes.filter(
|
||||
(node) => node[ValidatorFields.STAKED_BY_USER] !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
canonisedNodes.length < 3 ||
|
||||
!hideTopThird ||
|
||||
validatorsView === 'myStake'
|
||||
) {
|
||||
if (canonisedNodes.length < 3 || !hideTopThird) {
|
||||
return canonisedNodes;
|
||||
}
|
||||
|
||||
@@ -316,14 +291,7 @@ export const ConsensusValidatorsTable = ({
|
||||
},
|
||||
...remaining,
|
||||
];
|
||||
}, [
|
||||
data,
|
||||
decimals,
|
||||
hideTopThird,
|
||||
previousEpochData,
|
||||
totalStake,
|
||||
validatorsView,
|
||||
]);
|
||||
}, [data, decimals, hideTopThird, previousEpochData, totalStake]);
|
||||
|
||||
const ConsensusTable = forwardRef<AgGridReact>((_, gridRef) => {
|
||||
const colDefs = useMemo<ColDef[]>(
|
||||
@@ -343,7 +311,7 @@ export const ConsensusValidatorsTable = ({
|
||||
return a > b ? 1 : -1;
|
||||
},
|
||||
pinned: 'left',
|
||||
width: 260,
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE,
|
||||
@@ -352,20 +320,6 @@ export const ConsensusValidatorsTable = ({
|
||||
cellRenderer: TotalStakeRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
|
||||
headerTooltip: t('PendingStakeDescription').toString(),
|
||||
cellRenderer: PendingStakeRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE_SHARE,
|
||||
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
|
||||
headerTooltip: t('StakeShareDescription').toString(),
|
||||
cellRenderer: StakeShareRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.NORMALISED_VOTING_POWER,
|
||||
headerName: t(ValidatorFields.NORMALISED_VOTING_POWER).toString(),
|
||||
@@ -374,6 +328,12 @@ export const ConsensusValidatorsTable = ({
|
||||
width: 200,
|
||||
sort: 'desc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE_SHARE,
|
||||
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
|
||||
headerTooltip: t('StakeShareDescription').toString(),
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.TOTAL_PENALTIES,
|
||||
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
|
||||
@@ -381,6 +341,14 @@ export const ConsensusValidatorsTable = ({
|
||||
cellRenderer: TotalPenaltiesRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
|
||||
headerTooltip: t('PendingStakeDescription').toString(),
|
||||
valueFormatter: ({ value }) =>
|
||||
formatNumber(toBigNum(value, decimals), 2),
|
||||
width: 110,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
getLastEpochScoreAndPerformance,
|
||||
getTotalPenalties,
|
||||
} from '../../shared';
|
||||
import { stakedTotalPercentage } from './shared';
|
||||
|
||||
const MOCK_PREVIOUS_EPOCH = {
|
||||
epoch: {
|
||||
id: '1',
|
||||
validatorsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '0x123',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
},
|
||||
rankingScore: {
|
||||
performanceScore: '0.75',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('stakedTotalPercentage', () => {
|
||||
it('should return the correct percentage as a string, 2dp', () => {
|
||||
expect(stakedTotalPercentage('1.2345')).toBe('123.45%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('totalPenalties', () => {
|
||||
it('should return the correct penalty based on arbitrary values, test 1', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.1',
|
||||
'5000',
|
||||
'100000'
|
||||
)
|
||||
).toBe('50.00%');
|
||||
});
|
||||
|
||||
it('should return the correct penalty based on lower performance score than first test', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.05',
|
||||
'5000',
|
||||
'100000'
|
||||
)
|
||||
).toBe('75.00%');
|
||||
});
|
||||
|
||||
it('should return the correct penalty based on higher amount of stake than other tests (great penalty due to anti-whaling)', () => {
|
||||
expect(
|
||||
getTotalPenalties(
|
||||
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
|
||||
.rawValidatorScore,
|
||||
'0.1',
|
||||
'5000',
|
||||
'5500'
|
||||
)
|
||||
).toBe('97.25%');
|
||||
});
|
||||
});
|
||||
@@ -10,12 +10,9 @@ import {
|
||||
Tooltip,
|
||||
TooltipCellComponent,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import type { NodesFragmentFragment } from '../__generated__/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
|
||||
import type { NodesFragmentFragment } from '../__generated___/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
|
||||
import type { ValidatorsView } from './validator-tables';
|
||||
|
||||
export enum ValidatorFields {
|
||||
RANKING_INDEX = 'rankingIndex',
|
||||
@@ -34,49 +31,12 @@ export enum ValidatorFields {
|
||||
PERFORMANCE_PENALTY = 'performancePenalty',
|
||||
OVERSTAKED_AMOUNT = 'overstakedAmount',
|
||||
OVERSTAKING_PENALTY = 'overstakingPenalty',
|
||||
// the following are additional fields added to the validator object displaying user data
|
||||
STAKED_BY_USER = 'stakedByUser',
|
||||
PENDING_USER_STAKE = 'pendingUserStake',
|
||||
USER_STAKE_SHARE = 'userStakeShare',
|
||||
}
|
||||
|
||||
export const addUserDataToValidator = (
|
||||
validator: NodesFragmentFragment,
|
||||
currentEpochUserStaking: StakingDelegationFieldsFragment | undefined,
|
||||
nextEpochUserStaking: StakingDelegationFieldsFragment | undefined,
|
||||
currentUserStakeAvailable: string
|
||||
) => {
|
||||
return {
|
||||
...validator,
|
||||
[ValidatorFields.STAKED_BY_USER]:
|
||||
currentEpochUserStaking && Number(currentEpochUserStaking?.amount) > 0
|
||||
? currentEpochUserStaking.amount
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: nextEpochUserStaking
|
||||
? new BigNumber(nextEpochUserStaking?.amount)
|
||||
.minus(new BigNumber(currentEpochUserStaking?.amount || 0))
|
||||
.toString()
|
||||
: undefined,
|
||||
[ValidatorFields.USER_STAKE_SHARE]:
|
||||
currentEpochUserStaking && Number(currentEpochUserStaking.amount) > 0
|
||||
? new BigNumber(currentEpochUserStaking.amount).dividedBy(
|
||||
new BigNumber(currentUserStakeAvailable)
|
||||
)
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export type ValidatorWithUserData = NodesFragmentFragment & {
|
||||
stakedByUser?: string;
|
||||
pendingUserStake?: string;
|
||||
userStakeShare?: string;
|
||||
};
|
||||
|
||||
export interface ValidatorsTableProps {
|
||||
data: ValidatorWithUserData[] | undefined;
|
||||
data: NodesFragmentFragment[] | undefined;
|
||||
previousEpochData: PreviousEpochQuery | undefined;
|
||||
totalStake: string;
|
||||
validatorsView: ValidatorsView;
|
||||
}
|
||||
|
||||
// Custom styling to account for the scrollbar. This is needed because the
|
||||
@@ -98,23 +58,19 @@ export const defaultColDef = {
|
||||
resizable: true,
|
||||
autoHeight: true,
|
||||
comparator: (a: string, b: string) => parseFloat(a) - parseFloat(b),
|
||||
cellStyle: { margin: '10px 0', padding: '0 12px' },
|
||||
tooltipComponent: TooltipCellComponent,
|
||||
cellStyle: { display: 'flex', alignItems: 'center', padding: '0 10px' },
|
||||
};
|
||||
|
||||
interface ValidatorRendererProps {
|
||||
data: {
|
||||
id: string;
|
||||
validator: { avatarUrl: string; name: string };
|
||||
stakedByUser: string | undefined;
|
||||
};
|
||||
data: { id: string; validator: { avatarUrl: string; name: string } };
|
||||
}
|
||||
|
||||
export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { avatarUrl, name } = data.validator;
|
||||
return (
|
||||
<div className="w-[238px] grid grid-cols-[1fr_auto] gap-2 items-center">
|
||||
<div className="grid grid-cols-[1fr_auto] gap-2 items-center">
|
||||
<span className="flex overflow-hidden">
|
||||
{avatarUrl && (
|
||||
<img
|
||||
@@ -127,19 +83,9 @@ export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
|
||||
<span>{name}</span>
|
||||
</span>
|
||||
<Link to={data.id}>
|
||||
{data.stakedByUser ? (
|
||||
<Button
|
||||
data-testid="my-stake-btn"
|
||||
size="sm"
|
||||
className="text-vega-green border-vega-green"
|
||||
>
|
||||
{t('myStake')}
|
||||
</Button>
|
||||
) : (
|
||||
<Button data-testid="stake-btn" size="sm" fill={true}>
|
||||
{t('Stake')}
|
||||
</Button>
|
||||
)}
|
||||
<Button size="sm" fill={true}>
|
||||
{t('Stake')}
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
@@ -156,14 +102,8 @@ export const StakeNeededForPromotionRenderer = ({
|
||||
data,
|
||||
}: StakeNeededForPromotionRendererProps) => {
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
<span data-testid="stake-needed-for-promotion-tooltip">
|
||||
{data.stakeNeededForPromotionDescription}
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<span data-testid="stake-needed-for-promotion">
|
||||
<Tooltip description={data.stakeNeededForPromotionDescription}>
|
||||
<span>
|
||||
{data.stakeNeededForPromotion &&
|
||||
formatNumber(data.stakeNeededForPromotion, 2)}
|
||||
</span>
|
||||
@@ -194,59 +134,7 @@ export const VotingPowerRenderer = ({ data }: VotingPowerRendererProps) => {
|
||||
</>
|
||||
}
|
||||
>
|
||||
<span data-testid="normalised-voting-power">
|
||||
{data.normalisedVotingPower}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface PendingStakeRendererProps {
|
||||
data: {
|
||||
pendingStake: string;
|
||||
pendingUserStake: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export const PendingStakeRenderer = ({ data }: PendingStakeRendererProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
appState: { decimals },
|
||||
} = useAppState();
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div data-testid="pending-stake-tooltip">
|
||||
{t('pendingStake')}:{' '}
|
||||
{formatNumber(toBigNum(data.pendingStake, decimals), decimals)}
|
||||
</div>
|
||||
{data.pendingUserStake && (
|
||||
<div
|
||||
className="text-vega-green border-t border-t-vega-dark-200 mt-1.5 pt-1"
|
||||
data-testid="pending-user-stake-tooltip"
|
||||
>
|
||||
{t('myPendingStake')}:{' '}
|
||||
{formatNumber(
|
||||
toBigNum(data.pendingUserStake, decimals),
|
||||
decimals
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{data.pendingUserStake && data.pendingStake !== '0' && (
|
||||
<span data-testid="pending-user-stake" className="text-vega-green">
|
||||
{formatNumber(toBigNum(data.pendingUserStake, decimals), 2)}
|
||||
</span>
|
||||
)}
|
||||
<span data-testid="total-pending-stake">
|
||||
{formatNumber(toBigNum(data.pendingStake, decimals), 2)}
|
||||
</span>
|
||||
</div>
|
||||
<span>{data.normalisedVotingPower}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -256,7 +144,6 @@ interface TotalStakeRendererProps {
|
||||
stake: string;
|
||||
stakedByDelegates: string;
|
||||
stakedByOperator: string;
|
||||
stakedByUser: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -278,52 +165,18 @@ export const TotalStakeRenderer = ({ data }: TotalStakeRendererProps) => {
|
||||
<div data-testid="staked-delegates-tooltip">
|
||||
{t('stakedByDelegates')}: {data.stakedByDelegates.toString()}
|
||||
</div>
|
||||
<div className="font-bold" data-testid="total-staked-tooltip">
|
||||
{t('totalStake')}: {formattedStake}
|
||||
<div data-testid="total-staked-tooltip">
|
||||
{t('totalStake')}:{' '}
|
||||
<span className="font-bold">{formattedStake}</span>
|
||||
</div>
|
||||
{data.stakedByUser && (
|
||||
<div
|
||||
className="text-vega-green border-t border-t-vega-dark-200 mt-1.5 pt-1"
|
||||
data-testid="staked-by-user-tooltip"
|
||||
>
|
||||
{t('stakedByMe')}: {data.stakedByUser}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{data.stakedByUser && (
|
||||
<span data-testid="user-stake" className="text-vega-green">
|
||||
{data.stakedByUser}
|
||||
</span>
|
||||
)}
|
||||
<span data-testid="total-stake">{formattedStake}</span>
|
||||
</div>
|
||||
<span>{formattedStake}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
interface StakeShareRendererProps {
|
||||
data: {
|
||||
stakeShare: string;
|
||||
userStakeShare: string | undefined;
|
||||
};
|
||||
}
|
||||
|
||||
export const StakeShareRenderer = ({ data }: StakeShareRendererProps) => {
|
||||
return (
|
||||
<div className="flex flex-col">
|
||||
{data.userStakeShare && (
|
||||
<span data-testid="user-stake-share" className="text-vega-green">
|
||||
{data.userStakeShare}
|
||||
</span>
|
||||
)}
|
||||
<span data-testid="total-stake-share">{data.stakeShare}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface TotalPenaltiesRendererProps {
|
||||
data: {
|
||||
performanceScore: string;
|
||||
@@ -356,7 +209,7 @@ export const TotalPenaltiesRenderer = ({
|
||||
</>
|
||||
}
|
||||
>
|
||||
<span data-testid="total-penalty">{data.totalPenalties}</span>
|
||||
<span>{data.totalPenalties}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
+17
-36
@@ -21,8 +21,6 @@ import {
|
||||
ValidatorRenderer,
|
||||
TotalPenaltiesRenderer,
|
||||
TotalStakeRenderer,
|
||||
StakeShareRenderer,
|
||||
PendingStakeRenderer,
|
||||
} from './shared';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
@@ -40,7 +38,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
totalStake,
|
||||
stakeNeededForPromotion,
|
||||
stakeNeededForPromotionDescription,
|
||||
validatorsView,
|
||||
}: StandbyPendingValidatorsTableProps) => {
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -50,7 +47,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
|
||||
let nodes = useMemo(() => {
|
||||
const nodes = useMemo(() => {
|
||||
if (!data) return [];
|
||||
|
||||
return data
|
||||
@@ -78,9 +75,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
rankingScore: { stakeScore },
|
||||
pendingStake,
|
||||
votingPowerRanking,
|
||||
stakedByUser,
|
||||
pendingUserStake,
|
||||
userStakeShare,
|
||||
}) => {
|
||||
const { rawValidatorScore, performanceScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
@@ -158,13 +152,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
totalStake
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
[ValidatorFields.STAKED_BY_USER]: stakedByUser
|
||||
? formatNumber(toBigNum(stakedByUser, decimals), 2)
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? stakedTotalPercentage(userStakeShare)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -178,12 +165,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
totalStake,
|
||||
]);
|
||||
|
||||
if (validatorsView === 'myStake') {
|
||||
nodes = nodes.filter(
|
||||
(node) => node[ValidatorFields.STAKED_BY_USER] !== undefined
|
||||
);
|
||||
}
|
||||
|
||||
const StandbyPendingTable = forwardRef<AgGridReact>((_, gridRef) => {
|
||||
const colDefs = useMemo<ColDef[]>(
|
||||
() => [
|
||||
@@ -199,7 +180,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
cellRenderer: ValidatorRenderer,
|
||||
comparator: ({ name: a }, { name: b }) => Math.sign(a - b),
|
||||
pinned: 'left',
|
||||
width: 260,
|
||||
width: 240,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE,
|
||||
@@ -208,20 +189,6 @@ export const StandbyPendingValidatorsTable = ({
|
||||
cellRenderer: TotalStakeRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
|
||||
headerTooltip: t('PendingStakeDescription').toString(),
|
||||
cellRenderer: PendingStakeRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE_SHARE,
|
||||
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
|
||||
headerTooltip: t('StakeShareDescription').toString(),
|
||||
cellRenderer: StakeShareRenderer,
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
|
||||
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
|
||||
@@ -232,6 +199,12 @@ export const StandbyPendingValidatorsTable = ({
|
||||
width: 210,
|
||||
sort: 'asc',
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.STAKE_SHARE,
|
||||
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
|
||||
headerTooltip: t('StakeShareDescription').toString(),
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.TOTAL_PENALTIES,
|
||||
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
|
||||
@@ -239,6 +212,14 @@ export const StandbyPendingValidatorsTable = ({
|
||||
cellRenderer: TotalPenaltiesRenderer,
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
field: ValidatorFields.PENDING_STAKE,
|
||||
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
|
||||
headerTooltip: t('PendingStakeDescription').toString(),
|
||||
valueFormatter: ({ value }) =>
|
||||
formatNumber(toBigNum(value, decimals), 2),
|
||||
width: 110,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
@@ -249,7 +230,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
domLayout="autoHeight"
|
||||
style={{ width: '100%' }}
|
||||
customThemeParams={NODE_LIST_GRID_STYLES}
|
||||
rowHeight={68}
|
||||
rowHeight={52}
|
||||
defaultColDef={defaultColDef}
|
||||
tooltipShowDelay={0}
|
||||
animateRows={true}
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { ConsensusValidatorsTable } from './consensus-validators-table';
|
||||
import { StandbyPendingValidatorsTable } from './standby-pending-validators-table';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
import {
|
||||
createDocsLinks,
|
||||
removePaginationWrapper,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { Link as UTLink, Toggle } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
import { Link as UTLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { addUserDataToValidator } from './shared';
|
||||
import { ConsensusValidatorsTable } from './consensus-validators-table';
|
||||
import { StandbyPendingValidatorsTable } from './standby-pending-validators-table';
|
||||
import type { NodesQuery, NodesFragmentFragment } from '../__generated__/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
|
||||
import type { StakingQuery } from '../../__generated__/Staking';
|
||||
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
|
||||
import type { ValidatorWithUserData } from './shared';
|
||||
import type {
|
||||
NodesQuery,
|
||||
NodesFragmentFragment,
|
||||
} from '../__generated___/Nodes';
|
||||
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export interface ValidatorsTableProps {
|
||||
nodesData: NodesQuery | undefined;
|
||||
userStakingData: StakingQuery | undefined;
|
||||
data: NodesQuery | undefined;
|
||||
previousEpochData: PreviousEpochQuery | undefined;
|
||||
}
|
||||
|
||||
@@ -34,11 +31,8 @@ interface SortedValidatorsProps {
|
||||
pendingValidators: NodesFragmentFragment[];
|
||||
}
|
||||
|
||||
export type ValidatorsView = 'all' | 'myStake';
|
||||
|
||||
export const ValidatorTables = ({
|
||||
nodesData,
|
||||
userStakingData,
|
||||
data,
|
||||
previousEpochData,
|
||||
}: ValidatorsTableProps) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -46,69 +40,25 @@ export const ValidatorTables = ({
|
||||
const {
|
||||
appState: { decimals },
|
||||
} = useAppState();
|
||||
|
||||
const [validatorsView, setValidatorsView] = useState<ValidatorsView>('all');
|
||||
const totalStake = useMemo(
|
||||
() => nodesData?.nodeData?.stakedTotal || '0',
|
||||
[nodesData?.nodeData?.stakedTotal]
|
||||
() => data?.nodeData?.stakedTotal || '0',
|
||||
[data?.nodeData?.stakedTotal]
|
||||
);
|
||||
const epochId = useMemo(() => nodesData?.epoch.id, [nodesData?.epoch.id]);
|
||||
const currentUserStakeAvailable = useMemo(
|
||||
() => userStakingData?.party?.stakingSummary.currentStakeAvailable || '0',
|
||||
[userStakingData?.party?.stakingSummary.currentStakeAvailable]
|
||||
);
|
||||
|
||||
let stakeNeededForPromotion = undefined;
|
||||
let delegations: StakingDelegationFieldsFragment[] | undefined = undefined;
|
||||
|
||||
if (userStakingData) {
|
||||
delegations = removePaginationWrapper(
|
||||
userStakingData?.party?.delegationsConnection?.edges
|
||||
);
|
||||
}
|
||||
|
||||
const { consensusValidators, standbyValidators, pendingValidators } = useMemo(
|
||||
() =>
|
||||
removePaginationWrapper(nodesData?.nodesConnection.edges).reduce(
|
||||
removePaginationWrapper(data?.nodesConnection.edges).reduce(
|
||||
(acc: SortedValidatorsProps, validator) => {
|
||||
const validatorId = validator.id;
|
||||
const currentDelegation = delegations?.find(
|
||||
(d) => d.node.id === validatorId && d.epoch === Number(epochId)
|
||||
);
|
||||
const nextDelegation = delegations?.find(
|
||||
(d) => d.node.id === validatorId && d.epoch === Number(epochId) + 1
|
||||
);
|
||||
|
||||
switch (validator.rankingScore?.status) {
|
||||
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT:
|
||||
acc.consensusValidators.push(
|
||||
addUserDataToValidator(
|
||||
validator,
|
||||
currentDelegation,
|
||||
nextDelegation,
|
||||
currentUserStakeAvailable
|
||||
)
|
||||
);
|
||||
acc.consensusValidators.push(validator);
|
||||
break;
|
||||
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ:
|
||||
acc.standbyValidators.push(
|
||||
addUserDataToValidator(
|
||||
validator,
|
||||
currentDelegation,
|
||||
nextDelegation,
|
||||
currentUserStakeAvailable
|
||||
)
|
||||
);
|
||||
acc.standbyValidators.push(validator);
|
||||
break;
|
||||
case Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_PENDING:
|
||||
acc.pendingValidators.push(
|
||||
addUserDataToValidator(
|
||||
validator,
|
||||
currentDelegation,
|
||||
nextDelegation,
|
||||
currentUserStakeAvailable
|
||||
)
|
||||
);
|
||||
acc.pendingValidators.push(validator);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
@@ -118,12 +68,7 @@ export const ValidatorTables = ({
|
||||
pendingValidators: [],
|
||||
}
|
||||
),
|
||||
[
|
||||
currentUserStakeAvailable,
|
||||
delegations,
|
||||
epochId,
|
||||
nodesData?.nodesConnection.edges,
|
||||
]
|
||||
[data?.nodesConnection.edges]
|
||||
);
|
||||
|
||||
if (
|
||||
@@ -131,7 +76,7 @@ export const ValidatorTables = ({
|
||||
(standbyValidators.length || pendingValidators.length)
|
||||
) {
|
||||
const lowestRankingConsensusScore = consensusValidators.reduce(
|
||||
(lowest: ValidatorWithUserData, validator: ValidatorWithUserData) => {
|
||||
(lowest: NodesFragmentFragment, validator: NodesFragmentFragment) => {
|
||||
if (
|
||||
Number(validator.rankingScore.rankingScore) <
|
||||
Number(lowest.rankingScore.rankingScore)
|
||||
@@ -153,42 +98,19 @@ export const ValidatorTables = ({
|
||||
).toString();
|
||||
}
|
||||
return (
|
||||
<section data-testid="validator-tables">
|
||||
<div className="grid w-full justify-end">
|
||||
<div className="w-[400px]">
|
||||
<Toggle
|
||||
name="validators-view-toggle"
|
||||
toggles={[
|
||||
{
|
||||
label: t('ALL VALIDATORS'),
|
||||
value: 'all',
|
||||
},
|
||||
{
|
||||
label: t('STAKED BY ME'),
|
||||
value: 'myStake',
|
||||
},
|
||||
]}
|
||||
checkedValue={validatorsView}
|
||||
onChange={(e) =>
|
||||
setValidatorsView(e.target.value as ValidatorsView)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-testid="validator-tables">
|
||||
{consensusValidators.length > 0 && (
|
||||
<div className="mb-10">
|
||||
<>
|
||||
<SubHeading title={t('status-tendermint')} />
|
||||
<ConsensusValidatorsTable
|
||||
data={consensusValidators}
|
||||
previousEpochData={previousEpochData}
|
||||
totalStake={totalStake}
|
||||
validatorsView={validatorsView}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{standbyValidators.length > 0 && (
|
||||
<div className="mb-10">
|
||||
<>
|
||||
<SubHeading title={t('status-ersatz')} />
|
||||
<p>
|
||||
<Trans
|
||||
@@ -204,9 +126,8 @@ export const ValidatorTables = ({
|
||||
totalStake={totalStake}
|
||||
stakeNeededForPromotion={stakeNeededForPromotion}
|
||||
stakeNeededForPromotionDescription="StakeNeededForPromotionStandbyDescription"
|
||||
validatorsView={validatorsView}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{pendingValidators.length > 0 && (
|
||||
<>
|
||||
@@ -234,10 +155,9 @@ export const ValidatorTables = ({
|
||||
totalStake={totalStake}
|
||||
stakeNeededForPromotion={stakeNeededForPromotion}
|
||||
stakeNeededForPromotionDescription="StakeNeededForPromotionCandidateDescription"
|
||||
validatorsView={validatorsView}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
+5
-12
@@ -23,17 +23,6 @@ fragment StakingNodeFields on Node {
|
||||
}
|
||||
}
|
||||
|
||||
fragment StakingDelegationFields on Delegation {
|
||||
amount
|
||||
epoch
|
||||
node {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
id
|
||||
@@ -43,7 +32,11 @@ query Staking($partyId: ID!, $delegationsPagination: Pagination) {
|
||||
delegationsConnection(pagination: $delegationsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...StakingDelegationFields
|
||||
amount
|
||||
epoch
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-18
@@ -5,15 +5,13 @@ import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type StakingNodeFieldsFragment = { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } };
|
||||
|
||||
export type StakingDelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string }, party: { __typename?: 'Party', id: string } };
|
||||
|
||||
export type StakingQueryVariables = Types.Exact<{
|
||||
partyId: Types.Scalars['ID'];
|
||||
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
}>;
|
||||
|
||||
|
||||
export type StakingQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string }, party: { __typename?: 'Party', id: string } } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } }, nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } } } | null> | null }, nodeData?: { __typename?: 'NodeData', stakedTotal: string, totalNodes: number, inactiveNodes: number, uptime: number } | null };
|
||||
export type StakingQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string } } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } }, nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } } } | null> | null }, nodeData?: { __typename?: 'NodeData', stakedTotal: string, totalNodes: number, inactiveNodes: number, uptime: number } | null };
|
||||
|
||||
export const StakingNodeFieldsFragmentDoc = gql`
|
||||
fragment StakingNodeFields on Node {
|
||||
@@ -41,18 +39,6 @@ export const StakingNodeFieldsFragmentDoc = gql`
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StakingDelegationFieldsFragmentDoc = gql`
|
||||
fragment StakingDelegationFields on Delegation {
|
||||
amount
|
||||
epoch
|
||||
node {
|
||||
id
|
||||
}
|
||||
party {
|
||||
id
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const StakingDocument = gql`
|
||||
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
@@ -63,7 +49,11 @@ export const StakingDocument = gql`
|
||||
delegationsConnection(pagination: $delegationsPagination) {
|
||||
edges {
|
||||
node {
|
||||
...StakingDelegationFields
|
||||
amount
|
||||
epoch
|
||||
node {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,8 +80,7 @@ export const StakingDocument = gql`
|
||||
uptime
|
||||
}
|
||||
}
|
||||
${StakingDelegationFieldsFragmentDoc}
|
||||
${StakingNodeFieldsFragmentDoc}`;
|
||||
${StakingNodeFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useStakingQuery__
|
||||
@@ -20,8 +20,8 @@ import NodeContainer from './nodes-container';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { Heading, SubHeading } from '../../../components/heading';
|
||||
import Routes from '../../routes';
|
||||
import type { StakingQuery } from '../__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import type { StakingQuery } from './__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
|
||||
interface StakingNodeProps {
|
||||
data?: StakingQuery;
|
||||
@@ -116,6 +116,13 @@ export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
|
||||
t('validatorTitle', { nodeName: t('validatorTitleFallback') })
|
||||
}
|
||||
/>
|
||||
<section className="mb-4">
|
||||
<ValidatorTable
|
||||
node={nodeInfo}
|
||||
stakedTotal={addDecimal(data?.nodeData?.stakedTotal || '0', decimals)}
|
||||
previousEpochData={previousEpochData}
|
||||
/>
|
||||
</section>
|
||||
{data?.epoch.timestamps.start && data?.epoch.timestamps.expiry && (
|
||||
<section className="mb-10">
|
||||
<EpochCountdown
|
||||
@@ -150,13 +157,6 @@ export const StakingNode = ({ data, previousEpochData }: StakingNodeProps) => {
|
||||
<ConnectToVega />
|
||||
</>
|
||||
)}
|
||||
<section className="mb-4">
|
||||
<ValidatorTable
|
||||
node={nodeInfo}
|
||||
stakedTotal={addDecimal(data?.nodeData?.stakedTotal || '0', decimals)}
|
||||
previousEpochData={previousEpochData}
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -4,11 +4,11 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { useStakingQuery } from '../__generated__/Staking';
|
||||
import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import { useStakingQuery } from './__generated__/Staking';
|
||||
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
import type { ReactElement } from 'react';
|
||||
import type { StakingQuery } from '../__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import type { StakingQuery } from './__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
|
||||
// TODO should only request a single node. When migrating from deprecated APIs we should address this.
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@ import {
|
||||
getStakePercentage,
|
||||
} from '../shared';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import type { StakingNodeFieldsFragment } from './__generated__/Staking';
|
||||
import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
|
||||
const statuses = {
|
||||
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
||||
@@ -111,7 +111,7 @@ export const ValidatorTable = ({
|
||||
|
||||
<div className="my-12" data-testid="validator-table">
|
||||
<SubHeading title={t('profile')} />
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<RoundedWrapper>
|
||||
<KeyValueTable data-testid="validator-table-profile">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('id')}</span>
|
||||
@@ -156,7 +156,7 @@ export const ValidatorTable = ({
|
||||
</div>
|
||||
|
||||
<SubHeading title={t('ADDRESS')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<RoundedWrapper marginBottomLarge={true}>
|
||||
<KeyValueTable data-testid="validator-table-address">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('VEGA ADDRESS / PUBLIC KEY')}</span>
|
||||
@@ -187,7 +187,7 @@ export const ValidatorTable = ({
|
||||
</RoundedWrapper>
|
||||
|
||||
<SubHeading title={t('STAKE')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<RoundedWrapper marginBottomLarge={true}>
|
||||
<KeyValueTable data-testid="validator-table-stake">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('STAKED BY OPERATOR')}</span>
|
||||
@@ -238,7 +238,7 @@ export const ValidatorTable = ({
|
||||
</RoundedWrapper>
|
||||
|
||||
<SubHeading title={t('PENALTIES')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<RoundedWrapper marginBottomLarge={true}>
|
||||
<KeyValueTable data-testid="validator-table-penalties">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('OVERSTAKED PENALTY')}</span>
|
||||
@@ -270,7 +270,7 @@ export const ValidatorTable = ({
|
||||
</RoundedWrapper>
|
||||
|
||||
<SubHeading title={t('VOTING POWER')} />
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
<RoundedWrapper marginBottomLarge={true}>
|
||||
<KeyValueTable data-testid="validator-table-voting-power">
|
||||
<KeyValueTableRow>
|
||||
<span>{t('UNNORMALISED VOTING POWER')}</span>
|
||||
|
||||
@@ -23,7 +23,7 @@ export const YourStake = ({
|
||||
return (
|
||||
<div data-testid="your-stake">
|
||||
<SubHeading title={t('Your stake')} />
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<RoundedWrapper>
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
{t('Your Stake On Node (This Epoch)')}
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
formatNumberPercentage,
|
||||
removePaginationWrapper,
|
||||
} from '@vegaprotocol/utils';
|
||||
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
|
||||
import type { PreviousEpochQuery } from './__generated___/PreviousEpoch';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
|
||||
export const getLastEpochScoreAndPerformance = (
|
||||
|
||||
@@ -529,8 +529,8 @@
|
||||
"tranche_start": "2023-03-06T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-06T00:00:00.000Z",
|
||||
"total_added": "14099",
|
||||
"total_removed": "165.10784124668",
|
||||
"locked_amount": "3863.2439127837516484",
|
||||
"total_removed": "143.78419653368",
|
||||
"locked_amount": "4432.17309438470708546",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "30",
|
||||
@@ -1299,11 +1299,6 @@
|
||||
"user": "0x47e6217C1667e2B1fD177Ab8fD89037bCEC7dc6C",
|
||||
"tx": "0xc68a9a71f6a8a296e8c90bc748307ce197c444e3ca18129a189b7b462a60eceb"
|
||||
},
|
||||
{
|
||||
"amount": "21.323644713",
|
||||
"user": "0x6A228Fffda4D1B3C773b9ce38DA43592581b36FC",
|
||||
"tx": "0x5a8d0a823b77eb011079663279e0f2eaf0bd8773114dd6d5bfe65c2ab47680e6"
|
||||
},
|
||||
{
|
||||
"amount": "14.85013776876",
|
||||
"user": "0x6F32AA5A6198329c16e438512F992a0548C856f9",
|
||||
@@ -1731,17 +1726,10 @@
|
||||
"tx": "0x20d505a766d3d8e376509ed8003a5b9f35fd0524f0979a5a57c7d3714d01d3e5"
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "21.323644713",
|
||||
"user": "0x6A228Fffda4D1B3C773b9ce38DA43592581b36FC",
|
||||
"tranche_id": 53,
|
||||
"tx": "0x5a8d0a823b77eb011079663279e0f2eaf0bd8773114dd6d5bfe65c2ab47680e6"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "30",
|
||||
"withdrawn_tokens": "21.323644713",
|
||||
"remaining_tokens": "8.676355287"
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "30"
|
||||
},
|
||||
{
|
||||
"address": "0x259fBDdeE4e686242AAdFC7fc856947DF15Acc59",
|
||||
@@ -3318,7 +3306,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "86666.297",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "59715.277169199897517339",
|
||||
"locked_amount": "60012.2994098061907759131",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "86666.297",
|
||||
@@ -3384,7 +3372,7 @@
|
||||
"tranche_end": "2023-06-01T00:00:00.000Z",
|
||||
"total_added": "2500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "885.91015466015475",
|
||||
"locked_amount": "903.093203093203175",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2500",
|
||||
@@ -3505,7 +3493,7 @@
|
||||
"tranche_end": "2023-09-01T00:00:00.000Z",
|
||||
"total_added": "17500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "14883.964875201289",
|
||||
"locked_amount": "15002.9388083735895",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -3771,8 +3759,8 @@
|
||||
"tranche_start": "2023-02-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-08-01T00:00:00.000Z",
|
||||
"total_added": "37500",
|
||||
"total_removed": "4574.865235275",
|
||||
"locked_amount": "26000.1918354818925",
|
||||
"total_removed": "4492.78075095",
|
||||
"locked_amount": "26259.36157151626875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -3791,11 +3779,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x9b76b93c59157b6740b91496e48037b239a6ca6db5758804aba9894e02a7eb69"
|
||||
},
|
||||
{
|
||||
"amount": "82.084484325",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xe021c767a9587b28a6772b57666d72122e5d869c477596e246a4365ee6b7ce43"
|
||||
},
|
||||
{
|
||||
"amount": "92.587476975",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -3910,12 +3893,6 @@
|
||||
"tranche_id": 34,
|
||||
"tx": "0x9b76b93c59157b6740b91496e48037b239a6ca6db5758804aba9894e02a7eb69"
|
||||
},
|
||||
{
|
||||
"amount": "82.084484325",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 34,
|
||||
"tx": "0xe021c767a9587b28a6772b57666d72122e5d869c477596e246a4365ee6b7ce43"
|
||||
},
|
||||
{
|
||||
"amount": "92.587476975",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -4026,8 +4003,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "2299.752052575",
|
||||
"remaining_tokens": "5200.247947425"
|
||||
"withdrawn_tokens": "2217.66756825",
|
||||
"remaining_tokens": "5282.33243175"
|
||||
},
|
||||
{
|
||||
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
|
||||
@@ -4059,7 +4036,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "59660.7944924472825704535",
|
||||
"locked_amount": "59957.545737631794657195",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -4125,7 +4102,7 @@
|
||||
"tranche_end": "2023-09-03T00:00:00.000Z",
|
||||
"total_added": "62600",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "27182.851040081174176",
|
||||
"locked_amount": "27397.393404363265956",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10000",
|
||||
@@ -4318,7 +4295,7 @@
|
||||
"tranche_end": "2023-09-17T00:00:00.000Z",
|
||||
"total_added": "5000",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "2362.93505834601735",
|
||||
"locked_amount": "2380.07102993404385",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "5000",
|
||||
@@ -4529,7 +4506,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "97499.58",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "1743.88336560594536108964",
|
||||
"locked_amount": "2034.9686935030549769988",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "97499.58",
|
||||
@@ -4562,7 +4539,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "135173.4239508",
|
||||
"total_removed": "98230.390980249184455396",
|
||||
"locked_amount": "2383.5874918801371561646812876",
|
||||
"locked_amount": "2781.4508813301073482881800752",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "135173.4239508",
|
||||
@@ -4608,7 +4585,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "32499.86",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "733.62161665551011862912",
|
||||
"locked_amount": "856.07618732708597453098",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "32499.86",
|
||||
@@ -4641,7 +4618,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "10833.29",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "238.78671732570820440756",
|
||||
"locked_amount": "278.6444918082786955163",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10833.29",
|
||||
@@ -4674,7 +4651,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "22749.93",
|
||||
"total_removed": "4720.860935375",
|
||||
"locked_amount": "892.6380814136124106908",
|
||||
"locked_amount": "1041.6353445026172644364",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "6500",
|
||||
@@ -4825,8 +4802,8 @@
|
||||
"tranche_start": "2022-11-01T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "6111.900993675",
|
||||
"locked_amount": "4163.6510128913445",
|
||||
"total_removed": "6029.8222644",
|
||||
"locked_amount": "4319.15285451197025",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "7500",
|
||||
@@ -4845,11 +4822,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x4fa181368c0dc03d6e81dfb94e9b00edc7a4b5b44ea1354dcd03db1e58a060a6"
|
||||
},
|
||||
{
|
||||
"amount": "82.078729275",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x2ab0e85bffb0894e741265c4efb962d5f09831002daa4acc92ab87f72917bfe3"
|
||||
},
|
||||
{
|
||||
"amount": "92.593232025",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5019,12 +4991,6 @@
|
||||
"tranche_id": 33,
|
||||
"tx": "0x4fa181368c0dc03d6e81dfb94e9b00edc7a4b5b44ea1354dcd03db1e58a060a6"
|
||||
},
|
||||
{
|
||||
"amount": "82.078729275",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 33,
|
||||
"tx": "0x2ab0e85bffb0894e741265c4efb962d5f09831002daa4acc92ab87f72917bfe3"
|
||||
},
|
||||
{
|
||||
"amount": "92.593232025",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -5207,8 +5173,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "7500",
|
||||
"withdrawn_tokens": "6111.900993675",
|
||||
"remaining_tokens": "1388.099006325"
|
||||
"withdrawn_tokens": "6029.8222644",
|
||||
"remaining_tokens": "1470.1777356"
|
||||
},
|
||||
{
|
||||
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
|
||||
@@ -5233,7 +5199,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "928642.9598472029154",
|
||||
"locked_amount": "348093.622641410450169508",
|
||||
"locked_amount": "354742.134161907705277922",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -38924,7 +38890,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "700133.348465855088393",
|
||||
"locked_amount": "559400.02940181935737278844",
|
||||
"locked_amount": "569616.476842965740083581504",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -40305,8 +40271,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15870102.715470999700000001",
|
||||
"total_removed": "812130.55546472109935452",
|
||||
"locked_amount": "7283284.17298492326254788501911774103514463",
|
||||
"total_removed": "811190.94573738377022952",
|
||||
"locked_amount": "7319511.0396526062849853116896723115788051",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -40815,11 +40781,6 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x58aa740f4eaf17c0c60c22587b34db0dfd670c968d3affb614fee9b917f0b53c"
|
||||
},
|
||||
{
|
||||
"amount": "939.609727337329125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xa13046dbc8f011b7647728acbd7b9c6a67fb3869efc0e9e48c9590300345570a"
|
||||
},
|
||||
{
|
||||
"amount": "545.4204142199725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -42814,12 +42775,6 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0x58aa740f4eaf17c0c60c22587b34db0dfd670c968d3affb614fee9b917f0b53c"
|
||||
},
|
||||
{
|
||||
"amount": "939.609727337329125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0xa13046dbc8f011b7647728acbd7b9c6a67fb3869efc0e9e48c9590300345570a"
|
||||
},
|
||||
{
|
||||
"amount": "545.4204142199725",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -44196,8 +44151,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "259998.8875",
|
||||
"withdrawn_tokens": "140674.575062959993",
|
||||
"remaining_tokens": "119324.312437040007"
|
||||
"withdrawn_tokens": "139734.965335622663875",
|
||||
"remaining_tokens": "120263.922164377336125"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -46542,8 +46497,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "5782575.169630789834713996",
|
||||
"locked_amount": "1002436.218000846412796734272837213",
|
||||
"total_removed": "5781272.031584311406488996",
|
||||
"locked_amount": "1035880.629997723569255699501954567",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -46762,11 +46717,6 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xab861da5aece3737a3d6b9d8ac61fd11f864aefa410287e00357497f1c859081"
|
||||
},
|
||||
{
|
||||
"amount": "1303.138046478428225",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x5c9bcf8195dc2bf6d36af74095f45cc5f8289d7239e4205d74f06419b042d648"
|
||||
},
|
||||
{
|
||||
"amount": "755.482067130393772",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -49903,12 +49853,6 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0xab861da5aece3737a3d6b9d8ac61fd11f864aefa410287e00357497f1c859081"
|
||||
},
|
||||
{
|
||||
"amount": "1303.138046478428225",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x5c9bcf8195dc2bf6d36af74095f45cc5f8289d7239e4205d74f06419b042d648"
|
||||
},
|
||||
{
|
||||
"amount": "755.482067130393772",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -52587,8 +52531,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "334458.02309051305859075",
|
||||
"remaining_tokens": "24665.44648448694140925"
|
||||
"withdrawn_tokens": "333154.88504403463036575",
|
||||
"remaining_tokens": "25968.58453096536963425"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@@ -53986,7 +53930,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "3381706.364737906998719578",
|
||||
"locked_amount": "79165.2088757254297160760025198165",
|
||||
"locked_amount": "92379.298325811440012225988805963",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "552496.6455",
|
||||
@@ -56113,8 +56057,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "38870.3290450237949",
|
||||
"locked_amount": "88640.13232561127483282507204466",
|
||||
"total_removed": "38818.1373783577949",
|
||||
"locked_amount": "90258.98682236420185873334500254",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -62743,11 +62687,6 @@
|
||||
"user": "0xF5037DDA4A660d67560200f45380FF8364e35540",
|
||||
"tx": "0xffc827a0984694ac0243552d0fa4267513471426f6ec34596fd57cfd9d9390e7"
|
||||
},
|
||||
{
|
||||
"amount": "52.191666666",
|
||||
"user": "0xdc040917f80aC350460e55467e4B6c52Fc5e220E",
|
||||
"tx": "0xd1ded90c18e54cae27c885ec52af8db6260e3eebfd23eb61092de4ad4d20c301"
|
||||
},
|
||||
{
|
||||
"amount": "154.812094114",
|
||||
"user": "0x27e2254A2A8c9c9D1321E5Fe64cAD88e7A4f0ba7",
|
||||
@@ -83995,12 +83934,6 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "52.191666666",
|
||||
"user": "0xdc040917f80aC350460e55467e4B6c52Fc5e220E",
|
||||
"tranche_id": 5,
|
||||
"tx": "0xd1ded90c18e54cae27c885ec52af8db6260e3eebfd23eb61092de4ad4d20c301"
|
||||
},
|
||||
{
|
||||
"amount": "20.281506849",
|
||||
"user": "0xdc040917f80aC350460e55467e4B6c52Fc5e220E",
|
||||
@@ -84027,8 +83960,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "300",
|
||||
"withdrawn_tokens": "242.897364915",
|
||||
"remaining_tokens": "57.102635085"
|
||||
"withdrawn_tokens": "190.705698249",
|
||||
"remaining_tokens": "109.294301751"
|
||||
},
|
||||
{
|
||||
"address": "0x8D01053B4f4857Ff453CAeDBf8387Cd57a5a2A2b",
|
||||
|
||||
@@ -19,7 +19,6 @@ CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
||||
CYPRESS_VEGA_ENV=CUSTOM
|
||||
|
||||
@@ -181,15 +181,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
'termination.BTC.value'
|
||||
);
|
||||
|
||||
// check that links to github for oracle proofs are shown
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-proof-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://github.com/vegaprotocol/well-known');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-spec-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/oracles');
|
||||
|
||||
@@ -35,8 +35,6 @@ type MarketPageMockData = {
|
||||
trigger?: Schema.AuctionTrigger;
|
||||
};
|
||||
|
||||
const ORACLE_PUBKEY = Cypress.env('ORACLE_PUBKEY');
|
||||
|
||||
const marketDataOverride = (
|
||||
data: MarketPageMockData
|
||||
): PartialDeep<MarketDataQuery> => ({
|
||||
@@ -98,54 +96,7 @@ const mockTradingPage = (
|
||||
aliasGQLQuery(req, 'Margins', marginsQuery());
|
||||
aliasGQLQuery(req, 'Assets', assetsQuery());
|
||||
aliasGQLQuery(req, 'Asset', assetQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketInfo',
|
||||
marketInfoQuery({
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
dataSourceSpecForSettlementData: {
|
||||
data: {
|
||||
sourceType: {
|
||||
sourceType: {
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
data: {
|
||||
sourceType: {
|
||||
sourceType: {
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(req, 'MarketInfo', marketInfoQuery());
|
||||
aliasGQLQuery(req, 'Trades', tradesQuery());
|
||||
aliasGQLQuery(req, 'Chart', chartQuery());
|
||||
aliasGQLQuery(req, 'Candles', candlesQuery());
|
||||
@@ -176,40 +127,6 @@ export const addMockTradingPage = () => {
|
||||
cy.mockGQL((req) => {
|
||||
mockTradingPage(req, state, tradingMode, trigger);
|
||||
});
|
||||
|
||||
// Prevent request to github, return some dummy content
|
||||
cy.intercept(
|
||||
'GET',
|
||||
/^https:\/\/raw.githubusercontent.com\/vegaprotocol\/well-known/,
|
||||
{
|
||||
body: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet3/vegawallet-stagnet3.toml
|
||||
NX_VEGA_ENV=STAGNET3
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
|
||||
|
||||
@@ -35,8 +35,6 @@ do
|
||||
fi
|
||||
done < $env_vars_file
|
||||
|
||||
rm $env_vars_file
|
||||
|
||||
echo "}" >> $env_file
|
||||
|
||||
# start serving
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { memo, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { memo, useCallback, useEffect, useState } from 'react';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { DealTicketAmount } from './deal-ticket-amount';
|
||||
import { DealTicketButton } from './deal-ticket-button';
|
||||
@@ -75,8 +75,6 @@ export const DealTicket = ({
|
||||
handleSubmit,
|
||||
} = useOrderForm(market.id);
|
||||
|
||||
const lastSubmitTime = useRef(0);
|
||||
|
||||
const asset = market.tradableInstrument.instrument.product.settlementAsset;
|
||||
|
||||
const { accountBalance: marginAccountBalance } = useMarketAccountBalance(
|
||||
@@ -148,10 +146,6 @@ export const DealTicket = ({
|
||||
|
||||
const onSubmit = useCallback(
|
||||
(order: OrderSubmission) => {
|
||||
const now = new Date().getTime();
|
||||
if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) {
|
||||
return;
|
||||
}
|
||||
submit(
|
||||
normalizeOrderSubmission(
|
||||
order,
|
||||
@@ -159,7 +153,6 @@ export const DealTicket = ({
|
||||
market.positionDecimalPlaces
|
||||
)
|
||||
);
|
||||
lastSubmitTime.current = now;
|
||||
},
|
||||
[submit, market.decimalPlaces, market.positionDecimalPlaces]
|
||||
);
|
||||
|
||||
@@ -277,7 +277,6 @@ function compileEnvVars() {
|
||||
),
|
||||
ETH_LOCAL_PROVIDER_URL: process.env['NX_ETH_LOCAL_PROVIDER_URL'],
|
||||
ETH_WALLET_MNEMONIC: process.env['NX_ETH_WALLET_MNEMONIC'],
|
||||
ORACLE_PROOFS_URL: process.env['NX_ORACLE_PROOFS_URL'],
|
||||
VEGA_DOCS_URL: process.env['NX_VEGA_DOCS_URL'],
|
||||
VEGA_EXPLORER_URL: process.env['NX_VEGA_EXPLORER_URL'],
|
||||
VEGA_TOKEN_URL: process.env['NX_VEGA_TOKEN_URL'],
|
||||
|
||||
@@ -20,7 +20,6 @@ const schemaObject = {
|
||||
GIT_COMMIT_HASH: z.optional(z.string()),
|
||||
GIT_ORIGIN_URL: z.optional(z.string()),
|
||||
GITHUB_FEEDBACK_URL: z.optional(z.string()),
|
||||
ORACLE_PROOFS_URL: z.optional(z.string().url()),
|
||||
VEGA_ENV: z.nativeEnum(Networks),
|
||||
VEGA_EXPLORER_URL: z.optional(z.string()),
|
||||
VEGA_TOKEN_URL: z.optional(z.string()),
|
||||
|
||||
@@ -1,34 +1,3 @@
|
||||
fragment DataSource on DataSourceDefinition {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query MarketInfo($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
id
|
||||
@@ -110,15 +79,9 @@ query MarketInfo($marketId: ID!) {
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
data {
|
||||
...DataSource
|
||||
}
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
data {
|
||||
...DataSource
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
|
||||
@@ -3,47 +3,14 @@ import * as Types from '@vegaprotocol/types';
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type DataSourceFragment = { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } };
|
||||
|
||||
export type MarketInfoQueryVariables = Types.Exact<{
|
||||
marketId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', operator: Types.ConditionOperator, value?: string | null } | null> } } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
|
||||
export type MarketInfoQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, lpPriceRange: string, proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any }, openingAuction: { __typename?: 'AuctionDuration', durationSecs: number, volume: number }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', id: string } } } | null> | null } | null, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, priceMonitoringSettings: { __typename?: 'PriceMonitoringSettings', parameters?: { __typename?: 'PriceMonitoringParameters', triggers?: Array<{ __typename?: 'PriceMonitoringTrigger', horizonSecs: number, probability: number, auctionExtensionSecs: number }> | null } | null }, riskFactors?: { __typename?: 'RiskFactor', market: string, short: string, long: string } | null, liquidityMonitoringParameters: { __typename?: 'LiquidityMonitoringParameters', triggeringRatio: string, targetStakeParameters: { __typename?: 'TargetStakeParameters', timeWindow: number, scalingFactor: number } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } }, riskModel: { __typename?: 'LogNormalRiskModel', tau: number, riskAversionParameter: number, params: { __typename?: 'LogNormalModelParams', r: number, sigma: number, mu: number } } | { __typename?: 'SimpleRiskModel', params: { __typename?: 'SimpleRiskModelParams', factorLong: number, factorShort: number } }, marginCalculator?: { __typename?: 'MarginCalculator', scalingFactors: { __typename?: 'ScalingFactors', searchLevel: number, initialMargin: number, collateralRelease: number } } | null } } | null };
|
||||
|
||||
|
||||
export const DataSourceFragmentDoc = gql`
|
||||
fragment DataSource on DataSourceDefinition {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const MarketInfoDocument = gql`
|
||||
query MarketInfo($marketId: ID!) {
|
||||
market(id: $marketId) {
|
||||
@@ -126,15 +93,9 @@ export const MarketInfoDocument = gql`
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
id
|
||||
data {
|
||||
...DataSource
|
||||
}
|
||||
}
|
||||
dataSourceSpecForTradingTermination {
|
||||
id
|
||||
data {
|
||||
...DataSource
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
@@ -170,7 +131,7 @@ export const MarketInfoDocument = gql`
|
||||
}
|
||||
}
|
||||
}
|
||||
${DataSourceFragmentDoc}`;
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useMarketInfoQuery__
|
||||
|
||||
@@ -85,7 +85,7 @@ export const MarketInfoContainer = ({
|
||||
};
|
||||
|
||||
export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const { VEGA_TOKEN_URL, VEGA_EXPLORER_URL } = useEnvironment();
|
||||
const headerClassName = 'uppercase text-lg';
|
||||
|
||||
if (!market) return null;
|
||||
@@ -124,10 +124,6 @@ export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
title: t('Instrument'),
|
||||
content: <InstrumentInfoPanel market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: <OracleInfoPanel market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Settlement asset'),
|
||||
content: <SettlementAssetInfoPanel market={market} />,
|
||||
@@ -181,6 +177,23 @@ export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
title: t('Liquidity price range'),
|
||||
content: <LiquidityPriceRangeInfoPanel market={market} />,
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel market={market}>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
|
||||
>
|
||||
{t('View settlement data oracle specification')}
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination oracle specification')}
|
||||
</ExternalLink>
|
||||
</OracleInfoPanel>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
const marketGovPanels = [
|
||||
@@ -223,17 +236,17 @@ export const Info = ({ market, onSelect }: InfoProps) => {
|
||||
return (
|
||||
<div className="p-4">
|
||||
<div className="mb-8">
|
||||
<h3 className={headerClassName}>{t('Market data')}</h3>
|
||||
<p className={headerClassName}>{t('Market data')}</p>
|
||||
<Accordion panels={marketDataPanels} />
|
||||
</div>
|
||||
<div className="mb-8">
|
||||
<MarketProposalNotification marketId={market.id} />
|
||||
<h3 className={headerClassName}>{t('Market specification')}</h3>
|
||||
<p className={headerClassName}>{t('Market specification')}</p>
|
||||
<Accordion panels={marketSpecPanels} />
|
||||
</div>
|
||||
{VEGA_TOKEN_URL && market.proposal?.id && (
|
||||
<div>
|
||||
<h3 className={headerClassName}>{t('Market governance')}</h3>
|
||||
<p className={headerClassName}>{t('Market governance')}</p>
|
||||
<Accordion panels={marketGovPanels} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -1,188 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import {
|
||||
ConditionOperator,
|
||||
ConditionOperatorMapping,
|
||||
} from '@vegaprotocol/types';
|
||||
import { DataSourceProof } from './market-info-panels';
|
||||
|
||||
describe('DataSourceProof', () => {
|
||||
const ORACLE_PUBKEY =
|
||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f';
|
||||
it('renders correct proof for external data sources', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD' as const,
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key' as const,
|
||||
public_key: ORACLE_PUBKEY,
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message' as const,
|
||||
available: true,
|
||||
type: 'public_key' as const,
|
||||
public_key: ORACLE_PUBKEY,
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(screen.getByRole('link')).toHaveAttribute(
|
||||
'href',
|
||||
props.providers[0].github_link
|
||||
);
|
||||
});
|
||||
|
||||
it('renders message if there are no providers', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for termination')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message if there are no matching proofs', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration' as const,
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer' as const,
|
||||
signer: {
|
||||
__typename: 'PubKey' as const,
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD' as const,
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key' as const,
|
||||
public_key: 'not-the-pubkey',
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message' as const,
|
||||
available: true,
|
||||
type: 'public_key' as const,
|
||||
public_key: 'not-the-pubkey',
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/PubKey-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
type: 'settlementData' as const,
|
||||
};
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(
|
||||
screen.getByText('No oracle proof for settlement data')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders message if no data source on market', () => {
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'Invalid',
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
// @ts-ignore types are invalid
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(screen.getByText('Invalid data source')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders conditions for internal data sources', () => {
|
||||
const condition = {
|
||||
__typename: 'Condition' as const,
|
||||
operator: ConditionOperator.OPERATOR_GREATER_THAN,
|
||||
value: '100',
|
||||
};
|
||||
const props = {
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionInternal' as const,
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfigurationTime' as const,
|
||||
conditions: [condition],
|
||||
},
|
||||
},
|
||||
},
|
||||
providers: [],
|
||||
type: 'termination' as const,
|
||||
};
|
||||
render(<DataSourceProof {...props} />);
|
||||
expect(screen.getByText('Internal conditions')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
`${ConditionOperatorMapping[condition.operator]} ${condition.value}`
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
calcCandleVolume,
|
||||
totalFeesPercentage,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
formatNumber,
|
||||
@@ -21,12 +21,7 @@ import type {
|
||||
MarketInfoWithDataAndCandles,
|
||||
} from './market-info-data-provider';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { DataSourceDefinition, SignerKind } from '@vegaprotocol/types';
|
||||
import { ConditionOperatorMapping } from '@vegaprotocol/types';
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { Provider } from '@vegaprotocol/oracles';
|
||||
import { useOracleProofs } from '@vegaprotocol/oracles';
|
||||
|
||||
type PanelProps = Pick<
|
||||
ComponentProps<typeof MarketInfoTable>,
|
||||
@@ -406,7 +401,7 @@ export const LiquidityPriceRangeInfoPanel = ({
|
||||
market.decimalPlaces
|
||||
)} ${quoteUnit}`,
|
||||
}}
|
||||
/>
|
||||
></MarketInfoTable>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
@@ -415,156 +410,9 @@ export const LiquidityPriceRangeInfoPanel = ({
|
||||
export const OracleInfoPanel = ({
|
||||
market,
|
||||
...props
|
||||
}: MarketInfoProps & PanelProps) => {
|
||||
const product = market.tradableInstrument.instrument.product;
|
||||
const { VEGA_EXPLORER_URL, ORACLE_PROOFS_URL } = useEnvironment();
|
||||
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
|
||||
return (
|
||||
<MarketInfoTable data={product.dataSourceSpecBinding} {...props}>
|
||||
<div
|
||||
className="flex flex-col gap-2 mt-4"
|
||||
data-testid="oracle-proof-links"
|
||||
>
|
||||
<DataSourceProof
|
||||
data={product.dataSourceSpecForSettlementData.data}
|
||||
providers={data}
|
||||
type="settlementData"
|
||||
/>
|
||||
<DataSourceProof
|
||||
data={product.dataSourceSpecForTradingTermination.data}
|
||||
providers={data}
|
||||
type="termination"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2" data-testid="oracle-spec-links">
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForSettlementData.id}`}
|
||||
>
|
||||
{t('View settlement data specification')}
|
||||
</ExternalLink>
|
||||
<ExternalLink
|
||||
href={`${VEGA_EXPLORER_URL}/oracles#${product.dataSourceSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination specification')}
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</MarketInfoTable>
|
||||
);
|
||||
};
|
||||
|
||||
export const DataSourceProof = ({
|
||||
data,
|
||||
providers,
|
||||
type,
|
||||
}: {
|
||||
data: DataSourceDefinition;
|
||||
providers: Provider[] | undefined;
|
||||
type: 'settlementData' | 'termination';
|
||||
}) => {
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
|
||||
if (!providers?.length) {
|
||||
return <NoOracleProof type={type} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
{signers.map(({ signer }, i) => {
|
||||
return (
|
||||
<OracleLink
|
||||
key={i}
|
||||
providers={providers}
|
||||
signer={signer}
|
||||
type={type}
|
||||
index={i}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionInternal') {
|
||||
return (
|
||||
<div>
|
||||
<h3>{t('Internal conditions')}</h3>
|
||||
{data.sourceType.sourceType.conditions.map((condition, i) => {
|
||||
if (!condition) return null;
|
||||
return (
|
||||
<p key={i}>
|
||||
{ConditionOperatorMapping[condition.operator]} {condition.value}
|
||||
</p>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <div>{t('Invalid data source')}</div>;
|
||||
};
|
||||
|
||||
const OracleLink = ({
|
||||
providers,
|
||||
signer,
|
||||
type,
|
||||
index,
|
||||
}: {
|
||||
providers: Provider[];
|
||||
signer: SignerKind;
|
||||
type: 'settlementData' | 'termination';
|
||||
index: number;
|
||||
}) => {
|
||||
const text =
|
||||
type === 'settlementData'
|
||||
? t('View settlement oracle details')
|
||||
: t('View termination oracle details');
|
||||
const textWithCount = index > 0 ? `${text} (${index + 1})` : text;
|
||||
|
||||
const provider = providers.find((p) => {
|
||||
if (signer.__typename === 'PubKey') {
|
||||
if (
|
||||
p.oracle.type === 'public_key' &&
|
||||
p.oracle.public_key === signer.key
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (signer.__typename === 'ETHAddress') {
|
||||
if (
|
||||
p.oracle.type === 'eth_address' &&
|
||||
p.oracle.eth_address === signer.address
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!provider) {
|
||||
return <NoOracleProof type={type} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<p>
|
||||
<ExternalLink href={provider.github_link}>{textWithCount}</ExternalLink>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
const NoOracleProof = ({
|
||||
type,
|
||||
}: {
|
||||
type: 'settlementData' | 'termination';
|
||||
}) => {
|
||||
return (
|
||||
<p>
|
||||
{t(
|
||||
'No oracle proof for %s',
|
||||
type === 'settlementData' ? 'settlement data' : 'termination'
|
||||
)}
|
||||
</p>
|
||||
);
|
||||
};
|
||||
}: MarketInfoProps & PanelProps) => (
|
||||
<MarketInfoTable
|
||||
data={market.tradableInstrument.instrument.product.dataSourceSpecBinding}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -133,44 +133,10 @@ export const marketInfoQuery = (
|
||||
dataSourceSpecForSettlementData: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
__typename: 'DataSourceSpec',
|
||||
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
|
||||
data: {
|
||||
sourceType: {
|
||||
__typename: 'DataSourceDefinitionExternal',
|
||||
sourceType: {
|
||||
__typename: 'DataSourceSpecConfiguration',
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
__typename: 'DataSourceSpecToFutureBinding',
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@nrwl/react/babel",
|
||||
{
|
||||
"runtime": "automatic",
|
||||
"useBuiltIns": "usage"
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
# oracles
|
||||
|
||||
This library was generated with [Nx](https://nx.dev).
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test oracles` to execute the unit tests via [Jest](https://jestjs.io).
|
||||
@@ -1,10 +0,0 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'oracles',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/oracles',
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "@vegaprotocol/oracles",
|
||||
"version": "0.0.1"
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
{
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/oracles/src",
|
||||
"projectType": "library",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/web:rollup",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"options": {
|
||||
"outputPath": "dist/libs/oracles",
|
||||
"tsConfig": "libs/oracles/tsconfig.lib.json",
|
||||
"project": "libs/oracles/package.json",
|
||||
"entryFile": "libs/oracles/src/index.ts",
|
||||
"external": ["react/jsx-runtime"],
|
||||
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
|
||||
"compiler": "babel",
|
||||
"assets": [
|
||||
{
|
||||
"glob": "libs/oracles/README.md",
|
||||
"input": ".",
|
||||
"output": "."
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["libs/oracles/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["coverage/libs/oracles"],
|
||||
"options": {
|
||||
"jestConfig": "libs/oracles/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
export * from './lib/oracle-schema';
|
||||
export * from './lib/use-oracle-proofs';
|
||||
@@ -1,74 +0,0 @@
|
||||
import z from 'zod';
|
||||
|
||||
export type Provider = z.infer<typeof providerSchema>;
|
||||
export type Oracle = z.infer<typeof oracleSchema>;
|
||||
export type Proof = z.infer<typeof proofSchema>;
|
||||
export type Status = z.infer<typeof statusSchema>;
|
||||
|
||||
const statusSchema = z.enum([
|
||||
'UNKNOWN',
|
||||
'GOOD',
|
||||
'SUSPICIOUS',
|
||||
'MALICIOUS',
|
||||
'RETIRED',
|
||||
'COMPROMISED',
|
||||
]);
|
||||
|
||||
const baseProofSchema = z.object({
|
||||
format: z.enum(['url', 'signed_message']),
|
||||
available: z.boolean(),
|
||||
});
|
||||
|
||||
const proofSchema = z.discriminatedUnion('type', [
|
||||
baseProofSchema.extend({
|
||||
type: z.literal('public_key'),
|
||||
public_key: z.string().min(64),
|
||||
message: z.string().min(1),
|
||||
}),
|
||||
baseProofSchema.extend({
|
||||
type: z.literal('eth_address'),
|
||||
eth_address: z.string().min(42),
|
||||
message: z.string().min(1),
|
||||
}),
|
||||
baseProofSchema.extend({
|
||||
type: z.literal('web'),
|
||||
url: z.string().url(),
|
||||
}),
|
||||
baseProofSchema.extend({
|
||||
type: z.literal('github'),
|
||||
url: z.string().url(),
|
||||
}),
|
||||
baseProofSchema.extend({
|
||||
type: z.literal('twitter'),
|
||||
url: z.string().url(),
|
||||
}),
|
||||
]);
|
||||
|
||||
const baseOracleSchema = z.object({
|
||||
status: statusSchema,
|
||||
status_reason: z.string(),
|
||||
first_verified: z.string(),
|
||||
last_verified: z.string(),
|
||||
});
|
||||
|
||||
const oracleSchema = z.discriminatedUnion('type', [
|
||||
baseOracleSchema.extend({
|
||||
type: z.literal('public_key'),
|
||||
public_key: z.string().min(64),
|
||||
}),
|
||||
baseOracleSchema.extend({
|
||||
type: z.literal('eth_address'),
|
||||
eth_address: z.string().min(42),
|
||||
}),
|
||||
]);
|
||||
|
||||
const providerSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
url: z.string().url(),
|
||||
description_markdown: z.string(),
|
||||
oracle: oracleSchema,
|
||||
proofs: z.array(proofSchema),
|
||||
github_link: z.string().url(),
|
||||
});
|
||||
|
||||
export const providersSchema = z.array(providerSchema);
|
||||
@@ -1,135 +0,0 @@
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import type { Provider } from './oracle-schema';
|
||||
import { useOracleProofs, cache, invalidateCache } from './use-oracle-proofs';
|
||||
|
||||
global.fetch = jest.fn();
|
||||
const mockFetch = global.fetch as jest.Mock;
|
||||
|
||||
const createOracleData = (): Provider[] => {
|
||||
return [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key',
|
||||
public_key:
|
||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'url',
|
||||
available: true,
|
||||
type: 'twitter',
|
||||
url: 'https://twitter.com/vegaprotocol/status/956833487230730241',
|
||||
},
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'public_key',
|
||||
public_key:
|
||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link:
|
||||
'https://github.com/vegaprotocol/well-known/blob/feat/add-process-script/oracle-providers/PubKey-69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f.toml',
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
describe('useOracleProofs', () => {
|
||||
const url = 'https://foo.bar.com';
|
||||
const setup = (data: Provider[]) => {
|
||||
mockFetch.mockImplementation(() => {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve(data),
|
||||
});
|
||||
});
|
||||
return renderHook(() => useOracleProofs(url));
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockFetch.mockClear();
|
||||
});
|
||||
|
||||
describe('fetches and caches', () => {
|
||||
it('fetches oracle data', async () => {
|
||||
const data = createOracleData();
|
||||
const { result } = setup(data);
|
||||
|
||||
expect(result.current.data).toBe(undefined);
|
||||
expect(result.current.error).toBe(undefined);
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.data).toEqual(data);
|
||||
expect(result.current.error).toBe(undefined);
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
// check result was cached
|
||||
expect(cache).toEqual({ [url]: data });
|
||||
});
|
||||
|
||||
it('uses cached value if present', () => {
|
||||
const data = createOracleData();
|
||||
const { result } = setup(data);
|
||||
|
||||
expect(result.current.data).toEqual(data);
|
||||
expect(result.current.error).toBe(undefined);
|
||||
expect(result.current.loading).toBe(false);
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
it('handles invalid payload', async () => {
|
||||
invalidateCache();
|
||||
// @ts-ignore enforce invalid result
|
||||
const { result } = setup([{ invalid: 'result' }]);
|
||||
|
||||
expect(result.current.data).toBe(undefined);
|
||||
expect(result.current.error).toBe(undefined);
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.data).toBe(undefined);
|
||||
expect(result.current.error instanceof Error).toBe(true);
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('handles failed to fetch', async () => {
|
||||
invalidateCache();
|
||||
|
||||
mockFetch.mockImplementation(() => {
|
||||
return Promise.reject(new Error('failed to fetch'));
|
||||
});
|
||||
|
||||
const { result } = renderHook(() => useOracleProofs(url));
|
||||
|
||||
expect(result.current.data).toBe(undefined);
|
||||
expect(result.current.error).toBe(undefined);
|
||||
expect(result.current.loading).toBe(true);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.data).toBe(undefined);
|
||||
expect(result.current.error instanceof Error).toBe(true);
|
||||
expect(result.current.error).toEqual(new Error('failed to fetch'));
|
||||
expect(result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(mockFetch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { Provider } from './oracle-schema';
|
||||
import { providersSchema } from './oracle-schema';
|
||||
|
||||
export let cache: {
|
||||
[url: string]: Provider[];
|
||||
} = {};
|
||||
|
||||
export const useOracleProofs = (url?: string) => {
|
||||
const [data, setData] = useState<Provider[] | undefined>(() =>
|
||||
url ? cache[url] : undefined
|
||||
);
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'done'>('idle');
|
||||
const [error, setError] = useState<Error>();
|
||||
|
||||
useEffect(() => {
|
||||
let ignore = false;
|
||||
|
||||
if (!url) return;
|
||||
|
||||
const run = async () => {
|
||||
try {
|
||||
if (cache[url]) {
|
||||
setData(cache[url]);
|
||||
} else {
|
||||
setStatus('loading');
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
|
||||
if (ignore) return;
|
||||
|
||||
const result = providersSchema.parse(json);
|
||||
|
||||
cache[url] = result;
|
||||
setData(result);
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
setError(err);
|
||||
} else {
|
||||
setError(new Error('Something went wrong'));
|
||||
}
|
||||
} finally {
|
||||
setStatus('done');
|
||||
}
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
return () => {
|
||||
ignore = true;
|
||||
};
|
||||
}, [url]);
|
||||
|
||||
return {
|
||||
data,
|
||||
loading: status === 'loading',
|
||||
error,
|
||||
};
|
||||
};
|
||||
|
||||
export const invalidateCache = () => {
|
||||
cache = {};
|
||||
};
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"../../node_modules/@nrwl/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx"
|
||||
],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.jsx",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -24,7 +24,11 @@ export const PositionsManager = ({
|
||||
}: PositionsManagerProps) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const [dataCount, setDataCount] = useState(0);
|
||||
const { data, error, loading, reload } = usePositionsData(partyId, gridRef);
|
||||
const { data, error, loading, reload } = usePositionsData(
|
||||
partyId,
|
||||
gridRef,
|
||||
true
|
||||
);
|
||||
const create = useVegaTransactionStore((store) => store.create);
|
||||
const onClose = ({
|
||||
marketId,
|
||||
@@ -68,7 +72,7 @@ export const PositionsManager = ({
|
||||
});
|
||||
useEffect(() => {
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
}, [data]);
|
||||
}, [data?.length]);
|
||||
const onFilterChanged = useCallback((event: FilterChangedEvent) => {
|
||||
setDataCount(gridRef.current?.api?.getModel().getRowCount() ?? 0);
|
||||
}, []);
|
||||
|
||||
@@ -6,13 +6,13 @@ import { positionsMetricsProvider } from './positions-data-providers';
|
||||
import type { PositionsQueryVariables } from './__generated__/Positions';
|
||||
import { useDataProvider, updateGridData } from '@vegaprotocol/react-helpers';
|
||||
import type { GetRowsParams } from '@vegaprotocol/datagrid';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
|
||||
export const getRowId = ({ data }: { data: Position }) => data.marketId;
|
||||
|
||||
export const usePositionsData = (
|
||||
partyId: string,
|
||||
gridRef: RefObject<AgGridReact>
|
||||
gridRef: RefObject<AgGridReact>,
|
||||
clientSideModel?: boolean
|
||||
) => {
|
||||
const variables = useMemo<PositionsQueryVariables>(
|
||||
() => ({ partyId }),
|
||||
@@ -21,30 +21,9 @@ export const usePositionsData = (
|
||||
const dataRef = useRef<Position[] | null>(null);
|
||||
const update = useCallback(
|
||||
({ data }: { data: Position[] | null }) => {
|
||||
if (gridRef.current?.api?.getModel().getType() === 'infinite') {
|
||||
return updateGridData(dataRef, data, gridRef);
|
||||
}
|
||||
|
||||
const update: Position[] = [];
|
||||
const add: Position[] = [];
|
||||
data?.forEach((d) => {
|
||||
const rowNode = gridRef.current?.api?.getRowNode(d.marketId);
|
||||
if (rowNode) {
|
||||
if (!isEqual(rowNode.data, d)) {
|
||||
update.push(d);
|
||||
}
|
||||
} else {
|
||||
add.push(d);
|
||||
}
|
||||
});
|
||||
gridRef.current?.api?.applyTransaction({
|
||||
update,
|
||||
add,
|
||||
addIndex: 0,
|
||||
});
|
||||
return true;
|
||||
return clientSideModel ? false : updateGridData(dataRef, data, gridRef);
|
||||
},
|
||||
[gridRef]
|
||||
[gridRef, clientSideModel]
|
||||
);
|
||||
const { data, error, loading, reload } = useDataProvider({
|
||||
dataProvider: positionsMetricsProvider,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "@vegaprotocol/react-helpers",
|
||||
"version": "0.1.0"
|
||||
"version": "0.0.1"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
GRAPHQL_SCHEMA_PATH=https://api.n07.testnet.vega.xyz/graphql
|
||||
GRAPHQL_SCHEMA_PATH=https://api.n06.testnet.vega.xyz/graphql
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ConditionOperator } from './__generated__/types';
|
||||
import type {
|
||||
AccountType,
|
||||
AuctionTrigger,
|
||||
@@ -89,7 +88,6 @@ export const DepositStatusMapping: {
|
||||
export const IntervalMapping: {
|
||||
[T in Interval]: string;
|
||||
} = {
|
||||
// @ts-ignore - temporarily suppressing this as it's a valid value
|
||||
INTERVAL_BLOCK: '1 block',
|
||||
INTERVAL_I15M: 'I15M',
|
||||
INTERVAL_I1D: 'I1D',
|
||||
@@ -459,11 +457,3 @@ export const PositionStatusMapping: {
|
||||
POSITION_STATUS_ORDERS_CLOSED: 'Maintained by network',
|
||||
POSITION_STATUS_UNSPECIFIED: 'Normal',
|
||||
};
|
||||
|
||||
export const ConditionOperatorMapping: { [C in ConditionOperator]: string } = {
|
||||
OPERATOR_EQUALS: 'Equals',
|
||||
OPERATOR_GREATER_THAN: 'Greater than',
|
||||
OPERATOR_GREATER_THAN_OR_EQUAL: 'Greater than or equal to',
|
||||
OPERATOR_LESS_THAN: 'Less than',
|
||||
OPERATOR_LESS_THAN_OR_EQUAL: 'Less than or equal to',
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{
|
||||
"name": "@vegaprotocol/ui-toolkit",
|
||||
"version": "0.9.0"
|
||||
"version": "0.8.0"
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
"@vegaprotocol/mock": ["libs/cypress/mock.ts"],
|
||||
"@vegaprotocol/network-info": ["libs/network-info/src/index.ts"],
|
||||
"@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"],
|
||||
"@vegaprotocol/oracles": ["libs/oracles/src/index.ts"],
|
||||
"@vegaprotocol/orders": ["libs/orders/src/index.ts"],
|
||||
"@vegaprotocol/positions": ["libs/positions/src/index.ts"],
|
||||
"@vegaprotocol/proposals": ["libs/proposals/src/index.ts"],
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
"multisig-signer": "apps/multisig-signer",
|
||||
"network-info": "libs/network-info",
|
||||
"network-stats": "libs/network-stats",
|
||||
"oracles": "libs/oracles",
|
||||
"orders": "libs/orders",
|
||||
"positions": "libs/positions",
|
||||
"proposals": "libs/proposals",
|
||||
|
||||
Reference in New Issue
Block a user