Fix/1546 staking requests (#1747)

* fix: bad data flow

* chore: generate types

* chore: move component to where it is used

* chore: move things top the routes where they are actually used

* chore: more moving of files

* chore: generate types

* chore: replace old type files with new

* test: fix tests
This commit is contained in:
Dexter Edwards 2022-10-14 15:59:00 +01:00 committed by GitHub
parent 144b4bd847
commit 8ecb4430cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 214 additions and 170 deletions

View File

@ -0,0 +1,45 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: AddSignerBundle
// ====================================================
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles_edges_node {
__typename: "ERC20MultiSigSignerAddedBundle";
/**
* The ethereum address of the signer to be added
*/
newSigner: string;
/**
* The nonce used in the signing operation
*/
nonce: string;
/**
* The bundle of signatures from current validators to sign in the new signer
*/
signatures: string;
}
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles_edges {
__typename: "ERC20MultiSigSignerAddedBundleEdge";
node: AddSignerBundle_erc20MultiSigSignerAddedBundles_edges_node;
}
export interface AddSignerBundle_erc20MultiSigSignerAddedBundles {
__typename: "ERC20MultiSigSignerAddedConnection";
edges: (AddSignerBundle_erc20MultiSigSignerAddedBundles_edges | null)[] | null;
}
export interface AddSignerBundle {
/**
* Get the signature bundle to add a particular validator to the signer list of the multisig contract
*/
erc20MultiSigSignerAddedBundles: AddSignerBundle_erc20MultiSigSignerAddedBundles;
}
export interface AddSignerBundleVariables {
nodeId: string;
}

View File

@ -0,0 +1,48 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: RemoveSignerBundle
// ====================================================
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges_node {
__typename: "ERC20MultiSigSignerRemovedBundle";
/**
* The ethereum address of the signer to be removed
*/
oldSigner: string;
/**
* The nonce used in the signing operation
*/
nonce: string;
/**
* The bundle of signatures from current validators to sign in the new signer
*/
signatures: string;
}
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges {
__typename: "ERC20MultiSigSignerRemovedBundleEdge";
node: RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges_node;
}
export interface RemoveSignerBundle_erc20MultiSigSignerRemovedBundles {
__typename: "ERC20MultiSigSignerRemovedConnection";
/**
* The list of signer bundles for that validator
*/
edges: (RemoveSignerBundle_erc20MultiSigSignerRemovedBundles_edges | null)[] | null;
}
export interface RemoveSignerBundle {
/**
* Get the signatures bundle to remove a particular validator from signer list of the multisig contract
*/
erc20MultiSigSignerRemovedBundles: RemoveSignerBundle_erc20MultiSigSignerRemovedBundles;
}
export interface RemoveSignerBundleVariables {
nodeId: string;
}

View File

@ -0,0 +1 @@
export { ConnectToVega } from './connect-to-vega';

View File

@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import { ConnectToVega } from '../../routes/staking/connect-to-vega';
import { ConnectToVega } from '../connect-to-vega/connect-to-vega';
export const ConnectedVegaKey = ({ pubKey }: { pubKey: string | null }) => {
const { t } = useTranslation();

View File

@ -1,7 +1,7 @@
import { formatDistanceToNow } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { formatNumber } from '../../../../lib/format-number';
import { ConnectToVega } from '../../../staking/connect-to-vega';
import { ConnectToVega } from '../../../../components/connect-to-vega/connect-to-vega';
import { useVoteInformation } from '../../hooks';
import { CurrentProposalStatus } from '../current-proposal-status';
import { useUserVote } from './use-user-vote';

View File

@ -77,21 +77,14 @@ const LazyStakingDisassociate = React.lazy(
const LazyStakingIndex = React.lazy(
() =>
import(
/* webpackChunkName: "route-staking-index", webpackPrefetch: true */ './staking/staking'
/* webpackChunkName: "route-staking-index", webpackPrefetch: true */ './staking/home'
)
);
const LazyStakingNode = React.lazy(
() =>
import(
/* webpackChunkName: "route-staking-node", webpackPrefetch: true */ './staking/staking-node'
)
);
const LazyStakingNodes = React.lazy(
() =>
import(
/* webpackChunkName: "route-staking-nodes", webpackPrefetch: true */ './staking/staking-nodes-container'
/* webpackChunkName: "route-staking-node", webpackPrefetch: true */ './staking/node'
)
);
@ -227,11 +220,7 @@ const routerConfig = [
{ path: ':node', element: <LazyStakingNode /> },
{
index: true,
element: (
<LazyStakingNodes>
{({ data }) => <LazyStakingIndex data={data} />}
</LazyStakingNodes>
),
element: <LazyStakingIndex />,
},
],
},

View File

@ -1,5 +1,5 @@
import { useEthereumConfig } from '@vegaprotocol/web3';
import { StakingWalletsContainer } from '../components/staking-wallets-container/staking-wallets-container';
import { StakingWalletsContainer } from './components/staking-wallets-container/staking-wallets-container';
import { AssociatePage } from './associate-page';
import { AssociatePageNoVega } from './associate-page-no-vega';

View File

@ -1,7 +1,7 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ConnectToVega } from '../connect-to-vega';
import { ConnectToVega } from '../../../components/connect-to-vega/connect-to-vega';
export const AssociatePageNoVega = () => {
const { t } = useTranslation();

View File

@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react';
import type { useWeb3React } from '@web3-react/core';
import { StakingWalletsContainer } from './staking-wallets-container';
jest.mock('../../connect-to-vega', () => ({
jest.mock('../../../../../components/connect-to-vega', () => ({
ConnectToVega: () => <div data-testid="connect-to-vega" />,
}));

View File

@ -1,9 +1,9 @@
import { useWeb3React } from '@web3-react/core';
import { useTranslation } from 'react-i18next';
import { EthConnectPrompt } from '../../../../components/eth-connect-prompt';
import { EthConnectPrompt } from '../../../../../components/eth-connect-prompt';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ConnectToVega } from '../../connect-to-vega';
import { ConnectToVega } from '../../../../../components/connect-to-vega';
export const StakingWalletsContainer = ({
children,

View File

@ -9,6 +9,34 @@ import { ValidatorStatus } from "@vegaprotocol/types";
// GraphQL query operation: Nodes
// ====================================================
export interface Nodes_epoch_timestamps {
__typename: "EpochTimestamps";
/**
* RFC3339 timestamp - Vega time of epoch start, null if not started
*/
start: string | null;
/**
* RFC3339 timestamp - Vega time of epoch end, null if not ended
*/
end: string | null;
/**
* RFC3339 timestamp - Vega time of epoch expiry
*/
expiry: string | null;
}
export interface Nodes_epoch {
__typename: "Epoch";
/**
* Numeric sequence number used to identify the epoch
*/
id: string;
/**
* Timestamps for start and end of epochs
*/
timestamps: Nodes_epoch_timestamps;
}
export interface Nodes_nodes_rankingScore {
__typename: "RankingScore";
/**
@ -80,6 +108,10 @@ export interface Nodes_nodeData {
}
export interface Nodes {
/**
* Get data for a specific epoch, if ID omitted it gets the current epoch. If the string is 'next', fetch the next epoch
*/
epoch: Nodes_epoch;
/**
* All known network nodes
*/

View File

@ -0,0 +1 @@
export { Staking as default } from './staking';

View File

@ -8,7 +8,7 @@ import type { Nodes_nodes } from './__generated__/Nodes';
import type { PartialDeep } from 'type-fest';
import { ValidatorStatus } from '@vegaprotocol/types';
jest.mock('../../components/epoch-countdown', () => ({
jest.mock('../../../components/epoch-countdown', () => ({
EpochCountdown: () => <div data-testid="epoch-info"></div>,
}));
@ -87,6 +87,16 @@ const MOCK_NODES = {
uptime: 1560.266845703125,
__typename: 'NodeData',
},
epoch: {
__typename: 'Epoch',
id: '1',
timestamps: {
__typename: 'EpochTimestamps',
start: new Date(0).toISOString(),
end: '',
expiry: new Date(1000 * 60 * 60 * 24).toISOString(),
},
},
};
const renderNodeList = (data = MOCK_NODES) => {
@ -100,18 +110,7 @@ const renderNodeList = (data = MOCK_NODES) => {
},
]}
>
<NodeList
epoch={{
__typename: 'Epoch',
id: '1',
timestamps: {
__typename: 'EpochTimestamps',
start: new Date(0).toISOString(),
end: '',
expiry: new Date(1000 * 60 * 60 * 24).toISOString(),
},
}}
/>
<NodeList />
</MockedProvider>
</MemoryRouter>
);
@ -186,6 +185,16 @@ describe('Nodes list', () => {
uptime: 1560.266845703125,
__typename: 'NodeData',
},
epoch: {
__typename: 'Epoch',
id: '1',
timestamps: {
__typename: 'EpochTimestamps',
start: new Date(0).toISOString(),
end: '',
expiry: new Date(1000 * 60 * 60 * 24).toISOString(),
},
},
};
renderNodeList(MOCK_NODE);

View File

@ -8,12 +8,11 @@ import {
import type { AgGridReact } from 'ag-grid-react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { EpochCountdown } from '../../components/epoch-countdown';
import { BigNumber } from '../../lib/bignumber';
import { EpochCountdown } from '../../../components/epoch-countdown';
import { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '@vegaprotocol/react-helpers';
import { ValidatorStatus } from '@vegaprotocol/types';
import type { Nodes } from './__generated__/Nodes';
import type { Staking_epoch } from './__generated__/Staking';
import type { ColDef } from 'ag-grid-community';
const VALIDATOR = 'validator';
@ -29,6 +28,14 @@ const VOTING_POWER = 'votingPower';
export const NODES_QUERY = gql`
query Nodes {
epoch {
id
timestamps {
start
end
expiry
}
}
nodes {
avatarUrl
id
@ -53,10 +60,6 @@ export const NODES_QUERY = gql`
}
`;
interface NodeListProps {
epoch: Staking_epoch | undefined;
}
interface ValidatorRendererProps {
data: { validator: { avatarUrl: string; name: string } };
}
@ -103,7 +106,7 @@ const nodeListGridStyles = `
}
`;
export const NodeList = ({ epoch }: NodeListProps) => {
export const NodeList = () => {
const { t } = useTranslation();
// errorPolicy due to vegaprotocol/vega issue 5898
const { data, error, loading, refetch } = useQuery<Nodes>(NODES_QUERY, {
@ -114,9 +117,9 @@ export const NodeList = ({ epoch }: NodeListProps) => {
useEffect(() => {
const epochInterval = setInterval(() => {
if (!epoch?.timestamps.expiry) return;
if (!data?.epoch.timestamps.expiry) return;
const now = Date.now();
const expiry = new Date(epoch.timestamps.expiry).getTime();
const expiry = new Date(data.epoch.timestamps.expiry).getTime();
if (now > expiry) {
refetch();
@ -127,7 +130,7 @@ export const NodeList = ({ epoch }: NodeListProps) => {
return () => {
clearInterval(epochInterval);
};
}, [epoch?.timestamps.expiry, refetch]);
}, [data?.epoch.timestamps.expiry, refetch]);
const nodes = useMemo(() => {
if (!data?.nodes) return [];
@ -344,15 +347,17 @@ export const NodeList = ({ epoch }: NodeListProps) => {
return (
<AsyncRenderer loading={loading} error={error} data={nodes}>
{epoch && epoch.timestamps.start && epoch.timestamps.expiry && (
<div className="mb-8">
<EpochCountdown
id={epoch.id}
startDate={new Date(epoch.timestamps.start)}
endDate={new Date(epoch.timestamps.expiry)}
/>
</div>
)}
{data?.epoch &&
data.epoch.timestamps.start &&
data?.epoch.timestamps.expiry && (
<div className="mb-8">
<EpochCountdown
id={data.epoch.id}
startDate={new Date(data.epoch.timestamps.start)}
endDate={new Date(data.epoch.timestamps.expiry)}
/>
</div>
)}
<NodeListTable ref={gridRef} />
</AsyncRenderer>
);

View File

@ -6,11 +6,10 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import { Links } from '../../config';
import { Links } from '../../../config';
import { NodeList } from './node-list';
import type { Staking as StakingQueryResult } from './__generated__/Staking';
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
export const Staking = () => {
const { t } = useTranslation();
return (
@ -44,10 +43,8 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
</section>
<section>
<h2 className="text-2xl uppercase">{t('Validator nodes')}</h2>
<NodeList data-testid="node-list" epoch={data?.epoch} />
<NodeList data-testid="node-list" />
</section>
</>
);
};
export default Staking;

View File

@ -0,0 +1 @@
export { Node as default } from './node';

View File

@ -2,24 +2,16 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'react-router-dom';
import { EpochCountdown } from '../../components/epoch-countdown';
import { BigNumber } from '../../lib/bignumber';
import { EpochCountdown } from '../../../components/epoch-countdown';
import { BigNumber } from '../../../lib/bignumber';
import type { Staking as StakingQueryResult } from './__generated__/Staking';
import { ConnectToVega } from './connect-to-vega';
import { ConnectToVega } from '../../../components/connect-to-vega/connect-to-vega';
import { StakingForm } from './staking-form';
import { ValidatorTable } from './validator-table';
import { YourStake } from './your-stake';
import StakingNodesContainer from './staking-nodes-container';
import NodeContainer from './nodes-container';
import { useVegaWallet } from '@vegaprotocol/wallet';
export const StakingNodeContainer = () => {
return (
<StakingNodesContainer>
{({ data }) => <StakingNode data={data} />}
</StakingNodesContainer>
);
};
interface StakingNodeProps {
data?: StakingQueryResult;
}
@ -28,7 +20,6 @@ export const StakingNode = ({ data }: StakingNodeProps) => {
const { pubKey: vegaKey } = useVegaWallet();
const { node } = useParams<{ node: string }>();
const { t } = useTranslation();
const nodeInfo = React.useMemo(() => {
return data?.nodes?.find(({ id }) => id === node);
}, [node, data]);
@ -137,4 +128,8 @@ export const StakingNode = ({ data }: StakingNodeProps) => {
);
};
export default StakingNodeContainer;
export const Node = () => {
return (
<NodeContainer>{({ data }) => <StakingNode data={data} />}</NodeContainer>
);
};

View File

@ -4,9 +4,10 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { SplashLoader } from '../../components/splash-loader';
import { SplashLoader } from '../../../components/splash-loader';
import type { Staking as StakingQueryResult } from './__generated__/Staking';
// TODO should only request a single node. When migrating from deprecated APIs we should address this.
export const STAKING_QUERY = gql`
query Staking($partyId: ID!) {
party(id: $partyId) {
@ -74,7 +75,7 @@ export const STAKING_QUERY = gql`
const RPC_ERROR = 'rpc error: code = NotFound desc = NotFound error';
export const StakingNodesContainer = ({
export const NodeContainer = ({
children,
}: {
children: ({ data }: { data?: StakingQueryResult }) => React.ReactElement;
@ -128,4 +129,4 @@ export const StakingNodesContainer = ({
return children({ data });
};
export default StakingNodesContainer;
export default NodeContainer;

View File

@ -2,9 +2,9 @@ import React from 'react';
import * as Sentry from '@sentry/react';
import { Button, Callout, Intent, Loader } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { useAppState } from '../../contexts/app-state/app-state-context';
import { BigNumber } from '../../lib/bignumber';
import { removeDecimal } from '../../lib/decimals';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../lib/bignumber';
import { removeDecimal } from '../../../lib/decimals';
import type { UndelegateSubmissionBody } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';

View File

@ -1,7 +1,7 @@
import { Callout, Intent } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { Link } from 'react-router-dom';
import Routes from '../routes';
import Routes from '../../routes';
import type { StakeAction } from './staking-form';
import { Actions, RemoveType } from './staking-form';

View File

@ -4,11 +4,11 @@ import React from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { TokenInput } from '../../components/token-input';
import { useAppState } from '../../contexts/app-state/app-state-context';
import { useSearchParams } from '../../hooks/use-search-params';
import { BigNumber } from '../../lib/bignumber';
import { addDecimal, removeDecimal } from '../../lib/decimals';
import { TokenInput } from '../../../components/token-input';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import { useSearchParams } from '../../../hooks/use-search-params';
import { BigNumber } from '../../../lib/bignumber';
import { addDecimal, removeDecimal } from '../../../lib/decimals';
import type {
PartyDelegations,
PartyDelegationsVariables,

View File

@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/environment';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../lib/format-number';
import { BigNumber } from '../../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import type { Staking_nodes } from './__generated__/Staking';
const ValidatorTableCell = ({

View File

@ -1,8 +1,8 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
import { formatNumber } from '../../lib/format-number';
import type { BigNumber } from '../../lib/bignumber';
import { formatNumber } from '../../../lib/format-number';
import type { BigNumber } from '../../../lib/bignumber';
export interface YourStakeProps {
stakeThisEpoch: BigNumber;

View File

@ -1,80 +0,0 @@
import { Schema as Types } from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string } };
export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type BlockTimeSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null };
export const StatisticsDocument = gql`
query Statistics {
statistics {
chainId
blockHeight
}
}
`;
/**
* __useStatisticsQuery__
*
* To run a query within a React component, call `useStatisticsQuery` and pass it any options that fit your needs.
* When your component renders, `useStatisticsQuery` 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 } = useStatisticsQuery({
* variables: {
* },
* });
*/
export function useStatisticsQuery(baseOptions?: Apollo.QueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export function useStatisticsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export type StatisticsQueryHookResult = ReturnType<typeof useStatisticsQuery>;
export type StatisticsLazyQueryHookResult = ReturnType<typeof useStatisticsLazyQuery>;
export type StatisticsQueryResult = Apollo.QueryResult<StatisticsQuery, StatisticsQueryVariables>;
export const BlockTimeDocument = gql`
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
}
`;
/**
* __useBlockTimeSubscription__
*
* To run a query within a React component, call `useBlockTimeSubscription` and pass it any options that fit your needs.
* When your component renders, `useBlockTimeSubscription` 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 subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useBlockTimeSubscription({
* variables: {
* },
* });
*/
export function useBlockTimeSubscription(baseOptions?: Apollo.SubscriptionHookOptions<BlockTimeSubscription, BlockTimeSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<BlockTimeSubscription, BlockTimeSubscriptionVariables>(BlockTimeDocument, options);
}
export type BlockTimeSubscriptionHookResult = ReturnType<typeof useBlockTimeSubscription>;
export type BlockTimeSubscriptionResult = Apollo.SubscriptionResult<BlockTimeSubscription>;