Compare commits

..
Author SHA1 Message Date
Dariusz Majcherczyk 17bc4541e9 chore: multi click for toast 2023-01-29 19:51:12 +01:00
Dariusz Majcherczyk 2bd8044a6c chore: reverted proper assertion 2023-01-29 16:03:16 +01:00
Dariusz Majcherczyk b3a82fd6f0 chore: capsule tests refactor and increase deposit AC test coverage 2023-01-29 14:09:43 +01:00
mattrussell36 f22fb56afd chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-29 12:06:12 +00:00
mattrussell36 f2627e6ebf chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-29 06:07:03 +00:00
mattrussell36 24e6a13d93 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-29 00:13:02 +00:00
mattrussell36 b56050ad1c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-28 18:07:37 +00:00
mattrussell36 cff83ed062 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-28 12:06:05 +00:00
mattrussell36 8ecce874b0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-28 06:08:30 +00:00
Bartłomiej Głownia 00e319b3c6 chore: handle not found errors as correct response (#2759) 2023-01-27 18:35:45 -08:00
mattrussell36 05559f3ea0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-28 00:11:14 +00:00
m.ray 4b3b5c322a fix: trades grid colors (#2766) 2023-01-27 18:55:20 +00:00
Bartłomiej Głownia b1280c8285 fix: hide close button if openVolume is zero (#2768) 2023-01-27 18:44:20 +00:00
Edd 1e0e1c7859 feat(explorer): add long text component for hashes and tx viewer (#2765) 2023-01-27 18:37:59 +00:00
mattrussell36 ec2bb81ec8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-27 18:08:36 +00:00
m.ray fe95c6fcbc fix: show markets link & deal ticket validation (#2763) 2023-01-27 18:10:20 +01:00
macqbat 853ec8f69c chore(2351): improve handling wallet errors (#2729) 2023-01-27 15:45:29 +01:00
mattrussell36 b68c090ee5 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-27 12:09:03 +00:00
Matthew Russellandasiaznik cb9b811730 feat(#1791): block height check (#2701)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-01-27 11:31:23 +01:00
Bartłomiej Głownia 98b5260d93 feat: fix positions and margins providers update (#2753) 2023-01-27 09:39:39 +01:00
mattrussell36 613262f7a5 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-27 06:05:57 +00:00
mattrussell36 9ae6f5201c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-27 00:10:54 +00:00
m.ray cfb75fcf8f fix: volume charts position dp (#2756) 2023-01-26 19:36:29 +00:00
mattrussell36 ae6ccdfb52 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-26 18:08:18 +00:00
Radosław Szpiech 9e73c6a462 chore: 2593 trading-e2e refactor - split global.cy (#2754) 2023-01-26 17:50:33 +01:00
Edd 24c017bc2b feat(explorer): add basic votesubmission tx view (#2694) 2023-01-26 14:53:34 +00:00
Edd 3188f23439 fix(explorer): place sensible limits on oracles page queries (#2699) 2023-01-26 14:50:52 +00:00
Edd 33e528c569 fix(explorer): switch decimals used in lp commitment views (#2747) 2023-01-26 14:47:31 +00:00
Radosław Szpiech e10945eeda chore: correct ethereum releated envs in trading-e2e (#2744) 2023-01-26 15:40:49 +01:00
m.ray 47b2a2fdcb fix: volume chart values fixing - position dp (#2749) 2023-01-26 14:21:43 +00:00
m.ray 860938f26c fix: market selected no longer available - add link to all markets (#2748) 2023-01-26 13:46:44 +00:00
Art 5766d84804 fix: select position (#2745) 2023-01-26 12:36:47 +00:00
mattrussell36 6362c022b5 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-26 12:06:16 +00:00
Bartłomiej Głownia a6118c14dd feat: fix EthTxCompletedToastContent (#2741) 2023-01-26 12:00:00 +00:00
Sam Keen d384fb35f1 feat(2396): token delegations pagination (#2575) 2023-01-26 10:20:26 +00:00
Ciaran McGhie 3ed3714e79 fix(#2216): lp-dashboard grab market close from metadata (#2564) 2023-01-26 09:58:39 +00:00
Matthew Russell 3893b26d30 fix: dropdown menu portals (#2740) 2023-01-26 09:52:49 +01:00
mattrussell36 1ba0ad234b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-26 06:07:07 +00:00
mattrussell36 9f6a5aac39 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-26 00:13:06 +00:00
Bartłomiej GłowniaandMatthew Russell 129274a8e6 feat: add seperate transaction toast for completed eth transaction (#2733)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-01-25 12:25:44 -08:00
Bartłomiej Głownia 3fadda15fd chore: remove sentry captureException in marketDepthProvider update (#2713) 2023-01-25 11:58:37 -08:00
m.rayandasiaznik 7ea7edc1e2 feat(#2565): orderbook populate limit (#2690)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-01-25 11:38:26 -08:00
Edd dad953b45b fix(explorer): fix various small lp tx view bugs (#2738) 2023-01-25 18:18:59 +00:00
mattrussell36 ed1363b035 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-01-25 18:08:10 +00:00
Art b2ab4f49d9 feat: rich asset details dropdown (2557) (#2711) 2023-01-25 14:24:53 +00:00
Edd a60379b40f feat(explorer): data submission tx view (#2698) 2023-01-25 13:33:12 +00:00
257 changed files with 4396 additions and 2679 deletions
-1
View File
@@ -68,7 +68,6 @@ jobs:
working-directory: frontend-monorepo
env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
CYPRESS_grepTags: ${{ inputs.tags }}
+2 -1
View File
@@ -6,4 +6,5 @@ NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https:
NX_VEGA_ENV=DEVNET
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
+1
View File
@@ -7,3 +7,4 @@ NX_VEGA_ENV=MAINNET
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.vega.xyz
+1
View File
@@ -7,6 +7,7 @@ NX_VEGA_ENV=MIRROR
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.xyz/rest/
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://mainnet-mirror.token.vega.xyz
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -9,3 +9,4 @@ NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https:
NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://sandbox.token.vega.xyz
+1
View File
@@ -11,3 +11,4 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://stagnet1.token.vega.xyz
+2 -1
View File
@@ -4,4 +4,5 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_ENV=STAGNET3
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
NX_VEGA_GOVERNANCE_URL=https://stagnet3.token.vega.xyz
+1
View File
@@ -10,3 +10,4 @@ NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_NETWORKS={}
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.fairground.wtf
+1 -1
View File
@@ -70,7 +70,7 @@
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.66.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.67.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
@@ -4,6 +4,7 @@ import { useExplorerAssetQuery } from './__generated__/Asset';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
@@ -27,7 +28,7 @@ const AssetLink = ({ id, ...props }: AssetLinkProps) => {
return (
<Link className="underline" {...props} to={`/${Routes.ASSETS}#${id}`}>
{label}
<Hash text={label} />
</Link>
);
};
@@ -3,6 +3,7 @@ import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type BlockLinkProps = Partial<ComponentProps<typeof Link>> & {
height: string;
@@ -11,7 +12,7 @@ export type BlockLinkProps = Partial<ComponentProps<typeof Link>> & {
const BlockLink = ({ height, ...props }: BlockLinkProps) => {
return (
<Link className="underline" {...props} to={`/${Routes.BLOCKS}/${height}`}>
{height}
<Hash text={height} />
</Link>
);
};
@@ -1,6 +1,7 @@
import React from 'react';
import { DATA_SOURCES } from '../../../config';
import Hash from '../hash';
export enum EthExplorerLinkTypes {
block = 'block',
@@ -27,7 +28,7 @@ export const EthExplorerLink = ({
{...props}
href={link}
>
{id}
<Hash text={id} />
</a>
);
};
@@ -0,0 +1,18 @@
export type HashProps = {
text: string;
};
/**
* A simple component that ensures long text things like hashes
* are broken when they need to wrap. This will remove the need
* for a lot of the overflow scrolling that currently exists.
*/
const Hash = ({ text }: HashProps) => {
return (
<code className="break-all font-mono" style={{ wordWrap: 'break-word' }}>
{text}
</code>
);
};
export default Hash;
@@ -8,6 +8,9 @@ query ExplorerMarket($id: ID!) {
product {
... on Future {
quoteName
settlementAsset {
decimals
}
}
}
}
@@ -8,7 +8,7 @@ export type ExplorerMarketQueryVariables = Types.Exact<{
}>;
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null };
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export const ExplorerMarketDocument = gql`
@@ -22,6 +22,9 @@ export const ExplorerMarketDocument = gql`
product {
... on Future {
quoteName
settlementAsset {
decimals
}
}
}
}
@@ -1,13 +1,14 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { useExplorerMarketQuery } from './__generated__/Market';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import Hash from '../hash';
export type MarketLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
showMarketName?: boolean;
};
/**
@@ -15,7 +16,11 @@ export type MarketLinkProps = Partial<ComponentProps<typeof Link>> & {
* with a link to the markets list. If the name does not come back
* it will use the ID instead
*/
const MarketLink = ({ id, ...props }: MarketLinkProps) => {
const MarketLink = ({
id,
showMarketName = true,
...props
}: MarketLinkProps) => {
const { data, error, loading } = useExplorerMarketQuery({
variables: { id },
});
@@ -31,17 +36,31 @@ const MarketLink = ({ id, ...props }: MarketLinkProps) => {
<span role="img" aria-label="Unknown market" className="img">
&nbsp;{t('Invalid market')}
</span>
&nbsp;{id}
&nbsp;
<Hash text={id} />
</div>
);
}
}
return (
<Link className="underline" {...props} to={`/${Routes.MARKETS}#${id}`}>
{label}
</Link>
);
if (showMarketName) {
return (
<Link
className="underline"
{...props}
to={`/${Routes.MARKETS}#${id}`}
title={id}
>
{label}
</Link>
);
} else {
return (
<Link className="underline" {...props} to={`/${Routes.MARKETS}#${id}`}>
<Hash text={id} />
</Link>
);
}
};
export default MarketLink;
@@ -4,6 +4,7 @@ import { useExplorerNodeQuery } from './__generated__/Node';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type NodeLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
@@ -22,7 +23,7 @@ const NodeLink = ({ id, ...props }: NodeLinkProps) => {
return (
<Link className="underline" {...props} to={`/${Routes.VALIDATORS}#${id}`}>
<code>{label}</code>
<Hash text={label} />
</Link>
);
};
@@ -0,0 +1,23 @@
import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
};
const OracleLink = ({ id, ...props }: OracleLinkProps) => {
return (
<Link
className="underline font-mono"
{...props}
to={`/${Routes.ORACLES}/${id}`}
>
<Hash text={id} />
</Link>
);
};
export default OracleLink;
@@ -2,6 +2,7 @@ import { Routes } from '../../../routes/route-names';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
export type PartyLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
@@ -14,7 +15,7 @@ const PartyLink = ({ id, ...props }: PartyLinkProps) => {
{...props}
to={`/${Routes.PARTIES}/${id}`}
>
{id}
<Hash text={id} />
</Link>
);
};
@@ -0,0 +1,9 @@
query ExplorerProposal($id: ID!) {
proposal(id: $id) {
id
rationale {
title
description
}
}
}
@@ -0,0 +1,52 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerProposalQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
export const ExplorerProposalDocument = gql`
query ExplorerProposal($id: ID!) {
proposal(id: $id) {
id
rationale {
title
description
}
}
}
`;
/**
* __useExplorerProposalQuery__
*
* To run a query within a React component, call `useExplorerProposalQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerProposalQuery` 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 } = useExplorerProposalQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerProposalQuery(baseOptions: Apollo.QueryHookOptions<ExplorerProposalQuery, ExplorerProposalQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerProposalQuery, ExplorerProposalQueryVariables>(ExplorerProposalDocument, options);
}
export function useExplorerProposalLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerProposalQuery, ExplorerProposalQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerProposalQuery, ExplorerProposalQueryVariables>(ExplorerProposalDocument, options);
}
export type ExplorerProposalQueryHookResult = ReturnType<typeof useExplorerProposalQuery>;
export type ExplorerProposalLazyQueryHookResult = ReturnType<typeof useExplorerProposalLazyQuery>;
export type ExplorerProposalQueryResult = Apollo.QueryResult<ExplorerProposalQuery, ExplorerProposalQueryVariables>;
@@ -0,0 +1,82 @@
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import ProposalLink from './proposal-link';
import { ExplorerProposalDocument } from './__generated__/Proposal';
import { GraphQLError } from 'graphql';
function renderComponent(id: string, mocks: MockedResponse[]) {
return (
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter>
<ProposalLink id={id} />
</MemoryRouter>
</MockedProvider>
);
}
describe('Proposal link component', () => {
it('Renders the ID at first', () => {
const res = render(renderComponent('123', []));
expect(res.getByText('123')).toBeInTheDocument();
});
it('Renders the ID on error', async () => {
const mock = {
request: {
query: ExplorerProposalDocument,
variables: {
id: '456',
},
},
result: {
errors: [new GraphQLError('No such proposal')],
},
};
const res = render(renderComponent('456', [mock]));
// The ID
expect(res.getByText('456')).toBeInTheDocument();
});
it('Renders the proposal title when the query returns a result', async () => {
const mock = {
request: {
query: ExplorerProposalDocument,
variables: {
id: '123',
},
},
result: {
data: {
proposal: {
id: '123',
rationale: {
title: 'test-title',
description: 'test description',
},
},
},
},
};
const res = render(renderComponent('123', [mock]));
expect(res.getByText('123')).toBeInTheDocument();
expect(await res.findByText('test-title')).toBeInTheDocument();
});
it('Leaves the proposal id when the market is not found', async () => {
const mock = {
request: {
query: ExplorerProposalDocument,
variables: {
id: '123',
},
},
error: new Error('No such asset'),
};
const res = render(renderComponent('123', [mock]));
expect(await res.findByText('123')).toBeInTheDocument();
});
});
@@ -0,0 +1,28 @@
import { useExplorerProposalQuery } from './__generated__/Proposal';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ENV } from '../../../config/env';
import Hash from '../hash';
export type ProposalLinkProps = {
id: string;
};
/**
* Given a proposal ID, generates an external link over to
* the Governance page for more information
*/
const ProposalLink = ({ id }: ProposalLinkProps) => {
const { data } = useExplorerProposalQuery({
variables: { id },
});
const base = ENV.dataSources.governanceUrl;
const label = data?.proposal?.rationale.title || id;
return (
<ExternalLink href={`${base}/proposals/${id}`}>
<Hash text={label} />
</ExternalLink>
);
};
export default ProposalLink;
@@ -97,6 +97,10 @@ describe('Order TX Summary component', () => {
product: {
__typename: 'Future',
quoteName: 'TEST',
settlementAsset: {
__typeName: 'SettlementAsset',
decimals: 18,
},
},
},
},
@@ -3,61 +3,78 @@ import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import PriceInMarket from './price-in-market';
import type { DecimalSource } from './price-in-market';
import { ExplorerMarketDocument } from '../links/market-link/__generated__/Market';
function renderComponent(
price: string,
marketId: string,
mocks: MockedResponse[]
mocks: MockedResponse[],
decimalSource: DecimalSource = 'MARKET'
) {
return (
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter>
<PriceInMarket marketId={marketId} price={price} />
<PriceInMarket
marketId={marketId}
price={price}
decimalSource={decimalSource}
/>
</MemoryRouter>
</MockedProvider>
);
}
const fullMock = {
request: {
query: ExplorerMarketDocument,
variables: {
id: '123',
},
},
result: {
data: {
market: {
id: '123',
decimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
name: 'test dai',
product: {
__typename: 'Future',
quoteName: 'dai',
settlementAsset: {
decimals: 18,
},
},
},
},
},
},
},
};
describe('Price in Market component', () => {
it('Renders the raw price when there is no market data', () => {
const res = render(renderComponent('100', '123', []));
expect(res.getByText('100')).toBeInTheDocument();
});
it('Renders the formatted price when market data is fetched', async () => {
const mock = {
request: {
query: ExplorerMarketDocument,
variables: {
id: '123',
},
},
result: {
data: {
market: {
id: '123',
decimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
name: 'test dai',
product: {
__typename: 'Future',
quoteName: 'dai',
},
},
},
},
},
},
};
const res = render(renderComponent('100', '123', [mock]));
it('Renders the formatted price when market data is fetched, using market decimals by default', async () => {
const res = render(renderComponent('100', '123', [fullMock]));
expect(await res.findByText('1.00')).toBeInTheDocument();
expect(await res.findByText('dai')).toBeInTheDocument();
});
it('Renders the formatted price when market data is fetched, using settlement decimals', async () => {
const res = render(
renderComponent('100', '123', [fullMock], 'SETTLEMENT_ASSET')
);
expect(await res.findByText('0.0000000000000001')).toBeInTheDocument();
expect(await res.findByText('dai')).toBeInTheDocument();
});
it('Leaves the market id when the market is not found', async () => {
const mock = {
request: {
@@ -3,16 +3,23 @@ import isUndefined from 'lodash/isUndefined';
import { useExplorerMarketQuery } from '../links/market-link/__generated__/Market';
import get from 'lodash/get';
export type DecimalSource = 'MARKET' | 'SETTLEMENT_ASSET';
export type PriceInMarketProps = {
marketId: string;
price: string;
decimalSource?: DecimalSource;
};
/**
* Given a market ID and a price it will fetch the market
* and format the price in that market's decimal places.
*/
const PriceInMarket = ({ marketId, price }: PriceInMarketProps) => {
const PriceInMarket = ({
marketId,
price,
decimalSource = 'MARKET',
}: PriceInMarketProps) => {
const { data } = useExplorerMarketQuery({
variables: { id: marketId },
fetchPolicy: 'cache-first',
@@ -20,8 +27,19 @@ const PriceInMarket = ({ marketId, price }: PriceInMarketProps) => {
let label = price;
if (data && data.market?.decimalPlaces) {
label = addDecimalsFormatNumber(price, data.market.decimalPlaces);
if (data) {
if (decimalSource === 'MARKET' && data.market?.decimalPlaces) {
label = addDecimalsFormatNumber(price, data.market.decimalPlaces);
} else if (
decimalSource === 'SETTLEMENT_ASSET' &&
data.market?.tradableInstrument.instrument.product.settlementAsset
) {
label = addDecimalsFormatNumber(
price,
data.market?.tradableInstrument.instrument.product.settlementAsset
.decimals
);
}
}
const suffix = get(
@@ -63,15 +63,21 @@ describe('Chain Event: Builtin asset deposit', () => {
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const partyLink = screen.getByText(`${fullMock.partyId}`);
expect(partyLink).toBeInTheDocument();
expect(partyLink.tagName).toEqual('A');
expect(partyLink.getAttribute('href')).toEqual(
if (!partyLink.parentElement) {
throw new Error('Party link does not exist');
}
expect(partyLink.parentElement.tagName).toEqual('A');
expect(partyLink.parentElement.getAttribute('href')).toEqual(
`/parties/${fullMock.partyId}`
);
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
@@ -69,15 +69,21 @@ describe('Chain Event: Builtin asset withdrawal', () => {
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const partyLink = screen.getByText(`${fullMock.partyId}`);
expect(partyLink).toBeInTheDocument();
expect(partyLink.tagName).toEqual('A');
expect(partyLink.getAttribute('href')).toEqual(
if (!partyLink.parentElement) {
throw new Error('Party link does not exist');
}
expect(partyLink.parentElement.tagName).toEqual('A');
expect(partyLink.parentElement.getAttribute('href')).toEqual(
`/parties/${fullMock.partyId}`
);
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
@@ -60,8 +60,11 @@ describe('Chain Event: ERC20 Asset Delist', () => {
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
@@ -76,14 +76,20 @@ describe('Chain Event: ERC20 Asset limits updated', () => {
expect(screen.getByText(t('Vega asset'))).toBeInTheDocument();
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('ERC20 asset'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.sourceEthereumAddress}`
);
});
@@ -62,14 +62,20 @@ describe('Chain Event: ERC20 Asset List', () => {
expect(screen.getByText(t('Added Vega asset'))).toBeInTheDocument();
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.assetSource}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.assetSource}`
);
});
@@ -62,21 +62,30 @@ describe('Chain Event: ERC20 asset deposit', () => {
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const partyLink = screen.getByText(`${fullMock.targetPartyId}`);
expect(partyLink).toBeInTheDocument();
expect(partyLink.tagName).toEqual('A');
expect(partyLink.getAttribute('href')).toEqual(
if (!partyLink.parentElement) {
throw new Error('Party link does not exist');
}
expect(partyLink.parentElement.tagName).toEqual('A');
expect(partyLink.parentElement.getAttribute('href')).toEqual(
`/parties/${fullMock.targetPartyId}`
);
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.sourceEthereumAddress}`
);
});
@@ -57,14 +57,20 @@ describe('Chain Event: ERC20 asset deposit', () => {
expect(screen.getByText(t('Asset'))).toBeInTheDocument();
const assetLink = screen.getByText(`${fullMock.vegaAssetId}`);
expect(assetLink).toBeInTheDocument();
expect(assetLink.tagName).toEqual('A');
expect(assetLink.getAttribute('href')).toEqual(
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.targetEthereumAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.targetEthereumAddress}`
);
});
@@ -64,14 +64,20 @@ describe('Chain Event: Stake deposit', () => {
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const partyLink = screen.getByText(`${fullMock.vegaPublicKey}`);
expect(partyLink).toBeInTheDocument();
expect(partyLink.tagName).toEqual('A');
expect(partyLink.getAttribute('href')).toEqual(
if (!partyLink.parentElement) {
throw new Error('Party link does not exist');
}
expect(partyLink.parentElement.tagName).toEqual('A');
expect(partyLink.parentElement.getAttribute('href')).toEqual(
`/parties/${fullMock.vegaPublicKey}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.ethereumAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.ethereumAddress}`
);
});
@@ -64,14 +64,20 @@ describe('Chain Event: Stake remove', () => {
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const partyLink = screen.getByText(`${fullMock.vegaPublicKey}`);
expect(partyLink).toBeInTheDocument();
expect(partyLink.tagName).toEqual('A');
expect(partyLink.getAttribute('href')).toEqual(
if (!partyLink.parentElement) {
throw new Error('Party link does not exist');
}
expect(partyLink.parentElement.tagName).toEqual('A');
expect(partyLink.parentElement.getAttribute('href')).toEqual(
`/parties/${fullMock.vegaPublicKey}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.ethereumAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.ethereumAddress}`
);
});
@@ -66,7 +66,10 @@ describe('Chain Event: Stake total supply change', () => {
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.tokenAddress}`);
expect(ethLink.getAttribute('href')).toContain(
if (!ethLink.parentElement) {
throw new Error('ETH link does not exist');
}
expect(ethLink.parentElement.getAttribute('href')).toContain(
`/address/${fullMock.tokenAddress}`
);
});
@@ -2,16 +2,5 @@ query ExplorerSettlementAssetForMarket($id: ID!) {
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
@@ -8,7 +8,7 @@ export type ExplorerSettlementAssetForMarketQueryVariables = Types.Exact<{
}>;
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
export const ExplorerSettlementAssetForMarketDocument = gql`
@@ -16,17 +16,6 @@ export const ExplorerSettlementAssetForMarketDocument = gql`
market(id: $id) {
id
decimalPlaces
tradableInstrument {
instrument {
product {
... on Future {
settlementAsset {
decimals
}
}
}
}
}
}
}
`;
@@ -91,7 +91,7 @@ describe('LiquidityProvisionDetails component', () => {
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('-1')).toBeInTheDocument();
expect(res.getByText('Best Bid')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
expect(res.getByText('40%')).toBeInTheDocument();
});
it('handles a missing offset gracefully (should not happen)', () => {
@@ -125,7 +125,7 @@ describe('LiquidityProvisionDetails component', () => {
);
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
expect(res.getByText('40% (normalised from: 20%)')).toBeInTheDocument();
expect(res.getByText('40%')).toBeInTheDocument();
expect(res.getByText('-')).toBeInTheDocument();
});
});
@@ -67,11 +67,7 @@ export function LiquidityProvisionDetailsRow({
<td className="text-center">
{order.reference ? LiquidityReferenceLabel[order.reference] : '-'}
</td>
<td className="text-center">
{proportion === order.proportion
? `${proportion}%`
: `${proportion}% (normalised from: ${order.proportion}%)`}{' '}
</td>
<td className="text-center">{proportion}%</td>
</TableRow>
);
}
@@ -13,15 +13,6 @@ const decimalsMock: ExplorerSettlementAssetForMarketQuery = {
id: '123',
__typename: 'Market',
decimalPlaces: 5,
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
decimals: 5,
},
},
},
},
},
};
@@ -36,7 +36,7 @@ export function LiquidityProvisionOffset({
}
/**
* Does the work of formatting the number now we have the settlement decimal places.
* Does the work of formatting the number now we have the market decimal places.
* If no market data is assigned (i.e. during loading, or if the market doesn't exist)
* this function will return the unformatted number
*
@@ -49,9 +49,7 @@ export function getFormattedOffset(
offset: string,
data?: ExplorerSettlementAssetForMarketQuery
) {
const decimals =
data?.market?.tradableInstrument.instrument.product.settlementAsset
.decimals;
const decimals = data?.market?.decimalPlaces;
if (!decimals) {
return offset;
@@ -203,7 +203,7 @@ describe('LiquidityProvisionDetails component', () => {
};
const res = renderComponent(mock);
expect(res.getByText('45% (normalised from: 25%)')).toBeInTheDocument();
expect(res.getByText('55% (normalised from: 30%)')).toBeInTheDocument();
expect(res.getByText('45%')).toBeInTheDocument();
expect(res.getByText('55%')).toBeInTheDocument();
});
});
@@ -0,0 +1,45 @@
import { render } from '@testing-library/react';
import { JSONOracleData, parseData } from './data-submission-json-oracle';
describe('JSONOracleData', () => {
it('renders a code block if there is a payload that is base64 encoded', () => {
const p = 'eyJwcmljZXMuRVRIOTkudmFsdWUiOiAiMTMwNTAwMDAwMCJ9';
const screen = render(<JSONOracleData payload={p} />);
expect(screen.getByTestId('json-code')).toBeInTheDocument();
});
it('renders an error if b64 decoding fails', () => {
const p = 'not-encoded';
const screen = render(<JSONOracleData payload={p} />);
expect(
screen.getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
describe('Parse JSON oracle data', () => {
it('returns null for an invalid b64 string', () => {
expect(parseData('🤷')).toBeNull();
});
it('returns null for a string that looks like an object', () => {
expect(parseData('{}')).toBeNull();
});
it('returns null for null', () => {
expect(parseData(null as unknown as string)).toBeNull();
});
it('returns a string for b64 encoded data', () => {
const res = parseData('dGVzdCA9IHRydWU=');
expect(typeof res).toEqual('string');
expect(res).toEqual('test = true');
});
it('returns a string for b64 encoded json, without any extra parsing', () => {
const res = parseData('eyJ0ZXN0Ijp0cnVlfQ==');
expect(typeof res).toEqual('string');
expect(res).toEqual('{"test":true}');
});
});
@@ -0,0 +1,40 @@
import { t } from '@vegaprotocol/react-helpers';
interface JSONOracleDataProps {
payload: string;
}
/**
* Renders a JSON oracle. Given the shape could be any valid JSON,
* this doesn't try to do anything smart - it just renders the message
*/
export const JSONOracleData = ({ payload }: JSONOracleDataProps) => {
const decodedSubmission = parseData(payload);
if (!decodedSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
return (
<section>
<code data-testid="json-code">{decodedSubmission}</code>
</section>
);
};
/**
* Safely parses the JSON Oracle payload
*
* @param payload base64 encoded JSON
* @returns Object or null
*/
export function parseData(payload: string) {
try {
if (!payload || typeof payload !== 'string') {
throw new Error('Not a string');
}
return atob(payload);
} catch (e) {
return null;
}
}
@@ -0,0 +1,58 @@
import { render } from '@testing-library/react';
import { OpenOracleData, parseData } from './data-submission-open-oracle';
describe('OpenOracleData', () => {
it('renders a table if there is data', () => {
const p =
'eyJtZXNzYWdlcyI6IFsiMHgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDgwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA2M2M3ZTMwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNGYxZjM4ZWEwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwNjcwNzI2OTYzNjU3MzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAzNDI1NDQzMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMCJdLCAicHJpY2VzIjogeyJFVEgiOiIxMjMifSwgInNpZ25hdHVyZXMiOiBbIjB4N2U0Nzc2OWEyNzI5NzIwN2Q5ZTIyMmM3YTY2YjBlYzk3Njg4MWY3NmVkYjg5OGFhYzQ5OTRlZWYwOTc4MmI3NGUxMGE5MTJmZWQ1Y2E3NmFlN2U3NzVjOWZiOTBjZGE4ZjhkMDhlNzUyOTE1NzQ2ZTY2OGE1ZWM3OWRmOTZmNmQwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFjIl0sICJ0aW1lc3RhbXAiOiAiMTIzIn0=';
const screen = render(<OpenOracleData payload={p} />);
expect(screen.getByTestId('decoded-payload')).toBeInTheDocument();
expect(screen.getByText('ETH')).toBeInTheDocument();
expect(screen.getByText('123')).toBeInTheDocument();
});
it('renders an error if decoding fails', () => {
const p = 'not-encoded';
const screen = render(<OpenOracleData payload={p} />);
expect(
screen.getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
describe('Parse Open oracle data', () => {
it('returns null for an invalid b64 string', () => {
expect(parseData('🤷')).toBeNull();
});
it('returns null for a string that looks like an object', () => {
expect(parseData('{}')).toBeNull();
});
it('returns null for null', () => {
expect(parseData(null as unknown as string)).toBeNull();
});
it('returns a string for b64 encoded data', () => {
const res = parseData('dGVzdCA9IHRydWU=');
expect(res).toBeNull();
});
it('returns null if the parsed object does not look like te right shape', () => {
// Encoded: {"test":true}
const res = parseData('eyJ0ZXN0Ijp0cnVlfQ==');
expect(res).toBeNull();
});
it('returns an object if the payload parses and looks fine', () => {
// Encoded: {"messages": [], "prices": {}, "signatures": [], "timestamp": "123"}
const res = parseData(
'eyJtZXNzYWdlcyI6IFtdLCAicHJpY2VzIjoge30sICJzaWduYXR1cmVzIjogW10sICJ0aW1lc3RhbXAiOiAiMTIzIn0='
);
expect(typeof res.prices).toEqual('object');
expect(Array.isArray(res.messages)).toBeTruthy();
expect(Array.isArray(res.signatures)).toBeTruthy();
});
});
@@ -0,0 +1,64 @@
import { t } from '@vegaprotocol/react-helpers';
import { NestedDataList } from '../../../nested-data-list';
import { OpenOraclePrices } from './open-oracle/open-oracle-prices';
interface OpenOracleDataProps {
payload: string;
}
/**
* Decodes Open Oracle data based on it's main parts:
* - messages
* - signatures
* - prices
*/
export const OpenOracleData = ({ payload }: OpenOracleDataProps) => {
const decodedSubmission = parseData(payload);
if (!decodedSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const date = decodedSubmission.timestamp;
const formattedDate = new Date(date * 1000).toLocaleString();
return (
<section>
<span>{formattedDate}</span>
<code>
<OpenOraclePrices
prices={decodedSubmission.prices}
messages={decodedSubmission.messages}
signatures={decodedSubmission.signatures}
/>
</code>
<details data-testid="decoded-payload">
<summary>{t('Decoded payload')}</summary>
<NestedDataList data={decodedSubmission} />
</details>
</section>
);
};
/**
* Safely parses the Open Oracle payload
*
* @param payload base64 encoded JSON
* @returns Object or null
*/
export function parseData(payload: string) {
try {
if (!payload || typeof payload !== 'string') {
throw new Error('Not a string');
}
const res = JSON.parse(atob(payload));
if (!res.prices || !res.messages || !res.signatures) {
throw new Error('Not an open oracle format message');
}
return res;
} catch (e) {
return null;
}
}
@@ -0,0 +1,88 @@
import { render } from '@testing-library/react';
import { OpenOraclePrices } from './open-oracle-prices';
import type { Price } from './open-oracle-prices';
describe('OpenOraclePrices', () => {
it('Will not render with no prices', () => {
const p = undefined as unknown as Price;
const m: string[] = [];
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('Will not render with no messages', () => {
const m = undefined as unknown as string[];
const p: Price = {};
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with no signatures', () => {
const s = undefined as unknown as string[];
const m: string[] = [];
const p: Price = {};
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with mismatched prices/signatures', () => {
const p: Price = { ETH: '123' };
const m = ['0x123', '0x456'];
const s: string[] = [];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('will not render with mismatched signatures and messages', () => {
const p: Price = { ETH: '123', BTC: '12' };
const s = ['0xcac', '0xb33f'];
const m = ['0x1204o42c', '0xb3ddd3f', '0x9999'];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.container).toBeEmptyDOMElement();
});
it('renders a table when there are prices and signatures and messages', () => {
const p: Price = { ETH: '123', BTC: '12' };
const s = ['0xcac', '0xb33f'];
const m = ['0x120442c', '0xb3ddd3f'];
const screen = render(
<OpenOraclePrices prices={p} messages={m} signatures={s} />
);
expect(screen.getByTestId('openoracleprices')).toBeInTheDocument();
// Table headings
expect(screen.getByText('Asset')).toBeInTheDocument();
expect(screen.getByText('Price')).toBeInTheDocument();
expect(screen.getByText('Signature')).toBeInTheDocument();
expect(screen.getByText('Message')).toBeInTheDocument();
// Disabled - see vegaprotocol/frontend-monorepo#/2726
// expect(screen.getByText('Signer')).toBeInTheDocument();
// One row per asset
expect(screen.getByText('ETH')).toBeInTheDocument();
expect(screen.getByText('123')).toBeInTheDocument();
expect(screen.getByText('0xcac')).toBeInTheDocument();
expect(screen.getByText('0xb33f')).toBeInTheDocument();
// Does not test signer element for this row
expect(screen.getByText('BTC')).toBeInTheDocument();
expect(screen.getByText('12')).toBeInTheDocument();
expect(screen.getByText('0x120442c')).toBeInTheDocument();
expect(screen.getByText('0xb3ddd3f')).toBeInTheDocument();
// Does not test signer element for this row
});
});
@@ -0,0 +1,132 @@
import { t } from '@vegaprotocol/react-helpers';
import { CopyWithTooltip } from '@vegaprotocol/ui-toolkit';
import { TableRow } from '../../../../table';
import { TruncateInline } from '../../../../truncate/truncate';
import { utils } from 'ethers';
export type Price = {
[key: string]: string;
};
interface OpenOraclePricesProps {
prices: Price;
signatures: string[];
messages: string[];
}
/**
* Open Oracle price table, showing the entries in this
* decoded message
*
* Notes:
* - Signer is derived by recovering the address from the
* signature and the message. This is currently disabled
* as the implementation is incorrect
*/
export function OpenOraclePrices({
prices,
messages,
signatures,
}: OpenOraclePricesProps) {
if (
!prices ||
!messages ||
!signatures ||
Object.keys(prices).length !== messages.length ||
signatures.length !== messages.length
) {
return null;
}
return (
<table data-testid="openoracleprices">
<thead>
<TableRow modifier="bordered">
<th>{t('Asset')}</th>
<th className="text-right">{t('Price')}</th>
<th>{t('Signature')}</th>
<th>{t('Message')}</th>
{/* <th>{t('Signer')}</th> */}
</TableRow>
</thead>
<tbody>
{Object.keys(prices).map((k: string, i: number) => {
if (k && prices[k]) {
const message = messages[i];
const signature = signatures[i];
return (
<OpenOraclePrice
key={`price-${i}`}
asset={k}
value={prices[k]}
message={message}
signature={signature}
/>
);
} else {
return null;
}
})}
</tbody>
</table>
);
}
type OpenOraclePriceProps = {
asset: string;
value: string;
message: string;
signature: string;
};
export function OpenOraclePrice({
asset,
value,
signature,
message,
}: OpenOraclePriceProps) {
// const addr = getAddressFromMessageAndSignature(message, signature);
return (
<TableRow modifier="bordered" key={`${asset}`}>
<td className="px-4">{asset}</td>
<td className="px-4 text-right">{value}</td>
<td className="px-4">
<CopyWithTooltip text={signature}>
<button>
<TruncateInline text={signature} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td>
<td className="px-4">
<CopyWithTooltip text={message}>
<button>
<TruncateInline text={message} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td>
{/*<td className="px-4">
<CopyWithTooltip text={message}>
<button>
<TruncateInline text={addr} startChars={5} endChars={5} />
</button>
</CopyWithTooltip>
</td> */}
</TableRow>
);
}
export function getAddressFromMessageAndSignature(
message: string,
signature: string
) {
try {
const m = utils.hashMessage(utils.arrayify(utils.keccak256(message)));
const s = utils.splitSignature(
signature.slice(0, 130) + signature.slice(-2)
);
return utils.recoverAddress(m, s);
} catch (e) {
return '-';
}
}
@@ -7,6 +7,8 @@ import type { BlockExplorerTransactionResult } from '../../../../routes/types/bl
import type { TendermintBlocksResponse } from '../../../../routes/blocks/tendermint-blocks-response';
import { Time } from '../../../time';
import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
import { TxDataView } from '../../tx-data-view';
import Hash from '../../../links/hash';
interface TxDetailsSharedProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -46,7 +48,7 @@ export const TxDetailsShared = ({
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Hash')}</TableCell>
<TableCell>
<code>{txData.hash}</code>
<Hash text={txData.hash} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -82,6 +84,12 @@ export const TxDetailsShared = ({
<ChainResponseCode code={txData.code} error={txData.error} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell {...sharedHeaderProps}>{t('Transaction')}</TableCell>
<TableCell>
<TxDataView blockData={blockData} txData={txData} />
</TableCell>
</TableRow>
</>
);
};
@@ -0,0 +1,55 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { OpenOracleData } from './oracle-data/data-submission-open-oracle';
import { JSONOracleData } from './oracle-data/data-submission-json-oracle';
export type OracleSubmissionSource =
components['schemas']['OracleDataSubmissionOracleSource'];
interface TxDetailsDataSubmissionProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* Someone cancelled an order
*/
export const TxDetailsDataSubmission = ({
txData,
pubKey,
blockData,
}: TxDetailsDataSubmissionProps) => {
if (!txData || !txData.command.oracleDataSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const type: OracleSubmissionSource =
txData.command.oracleDataSubmission.source;
if (!type) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const payload = txData.command.oracleDataSubmission.payload;
return (
<>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
</TableWithTbody>
{type === 'ORACLE_SOURCE_OPEN_ORACLE' ? (
<OpenOracleData payload={payload} />
) : (
<JSONOracleData payload={payload} />
)}
</>
);
};
@@ -8,10 +8,8 @@ import { TxDetailsHeartbeat } from './tx-hearbeat';
import { TxDetailsGeneric } from './tx-generic';
import { TxDetailsBatch } from './tx-batch';
import { TxDetailsChainEvent } from './tx-chain-event';
import { TxContent } from '../../../routes/txs/id/tx-content';
import { TxDetailsNodeVote } from './tx-node-vote';
import { TxDetailsOrderCancel } from './tx-order-cancel';
import get from 'lodash/get';
import { TxDetailsOrderAmend } from './tx-order-amend';
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
import { TxDetailsDelegate } from './tx-delegation';
@@ -19,6 +17,8 @@ import { TxDetailsUndelegate } from './tx-undelegation';
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
import { TxDetailsDataSubmission } from './tx-data-submission';
import { TxProposalVote } from './tx-proposal-vote';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -44,23 +44,9 @@ export const TxDetailsWrapper = ({
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const raw = get(blockData, `result.block.data.txs[${txData.index}]`);
return (
<div key={`txd-${txData.hash}`}>
<section>{child({ txData, pubKey, blockData })}</section>
<details title={t('Decoded transaction')} className="mt-3">
<summary className="cursor-pointer">{t('Decoded transaction')}</summary>
<TxContent data={txData} />
</details>
{raw ? (
<details title={t('Raw transaction')} className="mt-3">
<summary className="cursor-pointer">{t('Raw transaction')}</summary>
<code className="break-all font-mono text-xs">{raw}</code>
</details>
) : null}
</div>
);
};
@@ -82,12 +68,16 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
switch (txData.type) {
case 'Submit Order':
return TxDetailsOrder;
case 'Submit Oracle Data':
return TxDetailsDataSubmission;
case 'Cancel Order':
return TxDetailsOrderCancel;
case 'Amend Order':
return TxDetailsOrderAmend;
case 'Validator Heartbeat':
return TxDetailsHeartbeat;
case 'Vote on Proposal':
return TxProposalVote;
case 'Batch Market Instructions':
return TxDetailsBatch;
case 'Chain Event':
@@ -55,6 +55,7 @@ export const TxDetailsLiquidityAmendment = ({
<PriceInMarket
price={amendment.commitmentAmount}
marketId={marketId}
decimalSource="SETTLEMENT_ASSET"
/>
</TableCell>
</TableRow>
@@ -27,7 +27,7 @@ export const TxDetailsLiquidityCancellation = ({
pubKey,
blockData,
}: TxDetailsLiquidityCancellationProps) => {
if (!txData || !txData.command.liquidityProvisionAmendment) {
if (!txData || !txData.command.liquidityProvisionCancellation) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
@@ -54,6 +54,7 @@ export const TxDetailsLiquiditySubmission = ({
<PriceInMarket
price={submission.commitmentAmount}
marketId={marketId}
decimalSource="SETTLEMENT_ASSET"
/>
</TableCell>
</TableRow>
@@ -6,6 +6,7 @@ import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
import Hash from '../../links/hash';
interface TxDetailsOrderProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -47,7 +48,13 @@ export const TxDetailsOrder = ({
<TableRow modifier="bordered">
<TableCell>{t('Order')}</TableCell>
<TableCell>
<code>{deterministicId}</code>
<Hash text={deterministicId} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Market ID')}</TableCell>
<TableCell>
<MarketLink id={marketId} showMarketName={false} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -0,0 +1,57 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import ProposalLink from '../../links/proposal-link/proposal-link';
interface TxProposalVoteProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* A vote on a proposal.
*
* One inconsistency here that feels right but should be standardised is that there are two rows
* for the proposal ID, one that creates a link with the text of the proposal title that takes
* a user out to the governance site, and the other that just shows the ID. Both are useful, but
* doesn't feel quite right. This could be fixed with a separate component to display a preview
* of the proposal and link off to the governance site, removing the title from the header. Or
* something else. For now, this is more useful than the default view
*/
export const TxProposalVote = ({
txData,
pubKey,
blockData,
}: TxProposalVoteProps) => {
if (!txData || !txData.command.voteSubmission) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const vote = txData.command.voteSubmission.value ? '👍' : '👎';
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Proposal ID')}</TableCell>
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Proposal details')}</TableCell>
<TableCell>
<ProposalLink id={txData.command.voteSubmission.proposalId} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Proposal')}</TableCell>
<TableCell>{txData.command.voteSubmission.proposalId}</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Vote')}</TableCell>
<TableCell>{vote}</TableCell>
</TableRow>
</TableWithTbody>
);
};
@@ -0,0 +1,75 @@
import { useState } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import get from 'lodash/get';
import { Select } from '@vegaprotocol/ui-toolkit';
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../routes/blocks/tendermint-blocks-response';
export function getClassName(showTxData: ShowTxDataType) {
const baseClasses =
'font-mono bg-neutral-300 text-[11px] leading-3 text-gray-900 w-full p-2 max-w-[615px]';
if (showTxData === 'JSON') {
return `${baseClasses} whitespace-pre overflow-x-scroll`;
} else {
return baseClasses;
}
}
export function getContents(
showTxData: ShowTxDataType,
txData: BlockExplorerTransactionResult | null,
blockData: TendermintBlocksResponse | null | undefined
) {
if (showTxData === 'JSON') {
if (txData) {
return JSON.stringify(txData.command, undefined, 1);
}
} else {
if (txData && blockData) {
return get(blockData, `result.block.data.txs[${txData.index}]`);
}
}
return '-';
}
type ShowTxDataType = 'JSON' | 'base64';
interface TxDataViewProps {
txData: BlockExplorerTransactionResult | undefined;
blockData: TendermintBlocksResponse | undefined;
}
export const TxDataView = ({ txData, blockData }: TxDataViewProps) => {
const [showTxData, setShowTxData] = useState<ShowTxDataType>('JSON');
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
return (
<details title={t('Show raw transaction')}>
<summary className="cursor-pointer">{t('Show raw transaction')}</summary>
<div className="py-4">
<textarea
readOnly={true}
className={getClassName(showTxData)}
rows={12}
cols={120}
value={getContents(showTxData, txData, blockData)}
/>
<div className="w-40">
<Select
placeholder="View as..."
onChange={(v) => setShowTxData(v.target.value as ShowTxDataType)}
value={'JSON'}
>
<option value={'JSON'}>JSON</option>
<option value={'base64'}>Base64</option>
</Select>
</div>
</div>
</details>
);
};
@@ -115,7 +115,7 @@ export const TxsInfiniteList = ({
className="List"
height={995}
itemCount={itemCount}
itemSize={isStacked ? 134 : 72}
itemSize={isStacked ? 134 : 50}
onItemsRendered={onItemsRendered}
ref={ref}
width={'100%'}
+1
View File
@@ -16,6 +16,7 @@ export const ENV = {
tendermintUrl: windowOrDefault('NX_TENDERMINT_URL'),
tendermintWebsocketUrl: windowOrDefault('NX_TENDERMINT_WEBSOCKET_URL'),
ethExplorerUrl: windowOrDefault('NX_ETHERSCAN_URL'),
governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'),
},
flags: {
assets: truthy.includes(windowOrDefault('NX_EXPLORER_ASSETS')),
@@ -51,6 +51,9 @@ fragment ExplorerOracleDataSource on OracleSpec {
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
@@ -79,12 +82,21 @@ fragment ExplorerOracleDataConnection on OracleSpec {
}
query ExplorerOracleSpecs {
oracleSpecsConnection {
oracleSpecsConnection(pagination: { first: 50 }) {
pageInfo {
hasNextPage
}
edges {
node {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
}
}
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
+55 -5
View File
@@ -5,12 +5,19 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, 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, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
export const ExplorerOracleDataSourceFragmentDoc = gql`
fragment ExplorerOracleDataSource on OracleSpec {
@@ -66,7 +73,10 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
`;
export const ExplorerOracleDataConnectionFragmentDoc = gql`
fragment ExplorerOracleDataConnection on OracleSpec {
dataConnection {
dataConnection(pagination: {first: 1}) {
pageInfo {
hasNextPage
}
edges {
node {
externalData {
@@ -96,7 +106,10 @@ export const ExplorerOracleDataConnectionFragmentDoc = gql`
`;
export const ExplorerOracleSpecsDocument = gql`
query ExplorerOracleSpecs {
oracleSpecsConnection {
oracleSpecsConnection(pagination: {first: 50}) {
pageInfo {
hasNextPage
}
edges {
node {
...ExplorerOracleDataSource
@@ -133,4 +146,41 @@ export function useExplorerOracleSpecsLazyQuery(baseOptions?: Apollo.LazyQueryHo
}
export type ExplorerOracleSpecsQueryHookResult = ReturnType<typeof useExplorerOracleSpecsQuery>;
export type ExplorerOracleSpecsLazyQueryHookResult = ReturnType<typeof useExplorerOracleSpecsLazyQuery>;
export type ExplorerOracleSpecsQueryResult = Apollo.QueryResult<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>;
export type ExplorerOracleSpecsQueryResult = Apollo.QueryResult<ExplorerOracleSpecsQuery, ExplorerOracleSpecsQueryVariables>;
export const ExplorerOracleSpecByIdDocument = gql`
query ExplorerOracleSpecById($id: ID!) {
oracleSpec(oracleSpecId: $id) {
...ExplorerOracleDataSource
...ExplorerOracleDataConnection
}
}
${ExplorerOracleDataSourceFragmentDoc}
${ExplorerOracleDataConnectionFragmentDoc}`;
/**
* __useExplorerOracleSpecByIdQuery__
*
* To run a query within a React component, call `useExplorerOracleSpecByIdQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerOracleSpecByIdQuery` 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 } = useExplorerOracleSpecByIdQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerOracleSpecByIdQuery(baseOptions: Apollo.QueryHookOptions<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>(ExplorerOracleSpecByIdDocument, options);
}
export function useExplorerOracleSpecByIdLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>(ExplorerOracleSpecByIdDocument, options);
}
export type ExplorerOracleSpecByIdQueryHookResult = ReturnType<typeof useExplorerOracleSpecByIdQuery>;
export type ExplorerOracleSpecByIdLazyQueryHookResult = ReturnType<typeof useExplorerOracleSpecByIdLazyQuery>;
export type ExplorerOracleSpecByIdQueryResult = Apollo.QueryResult<ExplorerOracleSpecByIdQuery, ExplorerOracleSpecByIdQueryVariables>;
@@ -37,7 +37,7 @@ describe('Oracle Data view', () => {
dataConnection: {
edges: [],
},
} as ExplorerOracleDataConnectionFragment)
} as unknown as ExplorerOracleDataConnectionFragment)
);
expect(res.container).toBeEmptyDOMElement();
});
@@ -67,7 +67,7 @@ describe('Oracle Signers component', () => {
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '123',
key: '1234567891234567789123456789123456778912345678912345677891234567',
},
},
],
@@ -4,6 +4,7 @@ import {
EthExplorerLinkTypes,
} from '../../../components/links/eth-explorer-link/eth-explorer-link';
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import { remove0x } from '@vegaprotocol/react-helpers';
import type { SourceType } from './oracle';
@@ -14,7 +15,15 @@ export type Signer = {
};
export function getAddressTypeLabel(signer: Signer) {
return signer.__typename === 'ETHAddress' ? 'ETH' : 'Vega';
const res = signer.__typename === 'ETHAddress' ? 'ETH' : 'Vega';
// This is a hack: some older oracles were submitted before proper checks stopped
// ETH addresses being returned as Vega addresses
if (res === 'Vega' && signer?.key?.length !== 64) {
return 'ETH';
} else {
return res;
}
}
export function getAddress(signer: Signer) {
@@ -29,6 +38,16 @@ export function getAddressLink(signer: Signer) {
if (signer.__typename === 'ETHAddress') {
return <EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />;
} else if (signer.__typename === 'PubKey' && address.length !== 64) {
// This is a hack: some older oracles were submitted before proper checks stopped
// ETH addresses being returned as Vega addresses
// Hacky 0x prefixing as a bonus
return (
<EthExplorerLink
id={`0x${remove0x(address)}`}
type={EthExplorerLinkTypes.address}
/>
);
} else if (signer.__typename === 'PubKey') {
return <PartyLink id={address} />;
}
@@ -13,6 +13,8 @@ import { OracleData } from './oracle-data';
import { OracleFilter } from './oracle-filter';
import { OracleDetailsType } from './oracle-details-type';
import { OracleMarkets } from './oracle-markets';
import { OracleSigners } from './oracle-signers';
import OracleLink from '../../../components/links/oracle-link/oracle-link';
export type SourceType =
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
@@ -21,36 +23,47 @@ interface OracleDetailsProps {
id: string;
dataSource: ExplorerOracleDataSourceFragment;
dataConnection: ExplorerOracleDataConnectionFragment;
// Defaults to false. Hides the count of 'broadcasts' this oracle has seen
showBroadcasts?: boolean;
}
/**
* Notes:
* - Matched data is really 'Data that matched this oracle' and given oracles are unique
* to each market, and each serves either as trading termination or settlement, really
* they will only ever see 1 match (most likely). So it should be more like 'Has seen
* data' vs 'Has not yet seen data'
*/
export const OracleDetails = ({
id,
dataSource,
dataConnection,
showBroadcasts = false,
}: OracleDetailsProps) => {
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
const reportsCount: number = dataConnection.dataConnection.edges?.length || 0;
return (
<div>
<TableWithTbody>
<TableWithTbody className="mb-2">
<TableRow modifier="bordered">
<TableHeader scope="row">{t('ID')}</TableHeader>
<TableCell modifier="bordered">{id}</TableCell>
<TableCell modifier="bordered">
<OracleLink id={id} />
</TableCell>
</TableRow>
<OracleDetailsType type={sourceType.__typename} />
{
// Disabled until https://github.com/vegaprotocol/vega/issues/7286 is released
/*<OracleSigners sourceType={sourceType} />*/
}
<OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} />
<TableRow modifier="bordered">
<TableHeader scope="row">{t('Broadcasts')}</TableHeader>
<TableCell modifier="bordered">{reportsCount}</TableCell>
<TableHeader scope="row">{t('Matched data')}</TableHeader>
<TableCell modifier="bordered">
{showBroadcasts ? reportsCount : reportsCount > 0 ? '✅' : '❌'}
</TableCell>
</TableRow>
</TableWithTbody>
<OracleFilter data={dataSource} />
<OracleData data={dataConnection} />
{showBroadcasts ? <OracleData data={dataConnection} /> : null}
</div>
);
};
@@ -0,0 +1,45 @@
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../../components/route-title';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { OracleDetails } from '../components/oracle';
import { useScrollToLocation } from '../../../hooks/scroll-to-location';
import filter from 'recursive-key-filter';
const Oracles = () => {
const { data, loading } = useExplorerOracleSpecsQuery();
useDocumentTitle(['Oracles']);
useScrollToLocation();
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{loading ? <Loader /> : null}
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id}
dataSource={o?.node}
dataConnection={o?.node}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
</section>
);
};
export default Oracles;
@@ -0,0 +1,49 @@
import { RouteTitle } from '../../../components/route-title';
import { RenderFetched } from '../../../components/render-fetched';
import { t, truncateByChars } from '@vegaprotocol/react-helpers';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useParams } from 'react-router-dom';
import { useExplorerOracleSpecByIdQuery } from '../__generated__/Oracles';
import { OracleDetails } from '../components/oracle';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import filter from 'recursive-key-filter';
import { TruncateInline } from '../../../components/truncate/truncate';
export const Oracle = () => {
const { id } = useParams<{ id: string }>();
useDocumentTitle(['Oracle', `Oracle #${truncateByChars(id || '1', 5, 5)}`]);
const { data, error, loading } = useExplorerOracleSpecByIdQuery({
variables: {
id: id || '1',
},
});
return (
<section>
<RouteTitle data-testid="block-header">
{t(`Oracle `)}
<TruncateInline startChars={5} endChars={5} text={id || '1'} />
</RouteTitle>
<RenderFetched error={error} loading={loading}>
{data?.oracleSpec ? (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id || ''}
dataSource={data?.oracleSpec}
dataConnection={data?.oracleSpec}
showBroadcasts={true}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(data, ['__typename'])} />
</details>
</div>
) : (
<span></span>
)}
</RenderFetched>
</section>
);
};
+4 -42
View File
@@ -1,45 +1,7 @@
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../components/route-title';
import { t } from '@vegaprotocol/react-helpers';
import { useExplorerOracleSpecsQuery } from './__generated__/Oracles';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { OracleDetails } from './components/oracle';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import filter from 'recursive-key-filter';
import { Outlet } from 'react-router-dom';
const Oracles = () => {
const { data, loading } = useExplorerOracleSpecsQuery();
useDocumentTitle(['Oracles']);
useScrollToLocation();
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{loading ? <Loader /> : null}
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10 cursor-pointer">
<OracleDetails
id={id}
dataSource={o?.node}
dataConnection={o?.node}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
</section>
);
const OraclePage = () => {
return <Outlet />;
};
export default Oracles;
export default OraclePage;
+19 -13
View File
@@ -3,7 +3,9 @@ import BlockPage from './blocks';
import Governance from './governance';
import Home from './home';
import Markets from './markets';
import Oracles from './oracles';
import OraclePage from './oracles';
import Oracles from './oracles/home';
import { Oracle } from './oracles/id';
import Party from './parties';
import { Parties } from './parties/home';
import { Party as PartySingle } from './parties/id';
@@ -85,17 +87,6 @@ const marketsRoutes = flags.markets
]
: [];
const oraclesRoutes = flags.oracles
? [
{
path: Routes.ORACLES,
name: 'Oracles',
text: t('Oracles'),
element: <Oracles />,
},
]
: [];
const networkParametersRoutes = flags.networkParameters
? [
{
@@ -161,12 +152,27 @@ const routerConfig = [
},
],
},
{
path: Routes.ORACLES,
name: 'Oracles',
text: t('Oracles'),
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
},
],
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...oraclesRoutes,
...networkParametersRoutes,
...validators,
];
@@ -10,7 +10,6 @@ import {
formatNumberPercentage,
t,
toBigNum,
getDateTimeFormat,
} from '@vegaprotocol/react-helpers';
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
import type * as Schema from '@vegaprotocol/types';
@@ -31,6 +30,7 @@ import { HealthBar } from '../../health-bar';
import { HealthDialog } from '../../health-dialog';
import { Status } from '../../status';
import { formatDistanceToNow } from 'date-fns';
import { getExpiryDate } from '@vegaprotocol/react-helpers';
export const MarketList = () => {
const { data, error, loading } = useMarketsLiquidity();
@@ -299,17 +299,20 @@ export const MarketList = () => {
/>
<AgGridColumn
headerName={t('Closing Time')}
field="proposal.terms.closingDatetime"
field="tradableInstrument.instrument.metadata.tags"
headerTooltip={t('Closing time of the market')}
valueFormatter={({
value,
}: VegaValueFormatterParams<
Market,
'proposal.terms.closingDatetime'
>) => {
return value
? getDateTimeFormat().format(new Date(value).getTime())
: '-';
data,
}: VegaValueFormatterParams<Market, ''>) => {
let expiry;
if (data?.tradableInstrument.instrument.metadata.tags) {
expiry = getExpiryDate(
data?.tradableInstrument.instrument.metadata.tags,
data?.marketTimestamps.close,
data?.state
);
}
return expiry ? expiry : '-';
}}
/>
</Grid>
File diff suppressed because it is too large Load Diff
+1
View File
@@ -11,6 +11,7 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -14,6 +14,7 @@ NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit suppl
NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -8,3 +8,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
+2 -1
View File
@@ -8,4 +8,5 @@ NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_DELEGATIONS_PAGINATION=50
+1
View File
@@ -8,3 +8,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://mirror.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_DELEGATIONS_PAGINATION=50
+1
View File
@@ -5,3 +5,4 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://sta
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json
NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
+1
View File
@@ -5,3 +5,4 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://sta
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
+1
View File
@@ -6,3 +6,4 @@ NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
+1
View File
@@ -9,3 +9,4 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
@@ -7,13 +7,13 @@ fragment WalletDelegationFields on Delegation {
epoch
}
query Delegations($partyId: ID!) {
query Delegations($partyId: ID!, $delegationsPagination: Pagination) {
epoch {
id
}
party(id: $partyId) {
id
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...WalletDelegationFields
@@ -7,6 +7,7 @@ export type WalletDelegationFieldsFragment = { __typename?: 'Delegation', amount
export type DelegationsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
@@ -23,13 +24,13 @@ export const WalletDelegationFieldsFragmentDoc = gql`
}
`;
export const DelegationsDocument = gql`
query Delegations($partyId: ID!) {
query Delegations($partyId: ID!, $delegationsPagination: Pagination) {
epoch {
id
}
party(id: $partyId) {
id
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...WalletDelegationFields
@@ -76,6 +77,7 @@ export const DelegationsDocument = gql`
* const { data, loading, error } = useDelegationsQuery({
* variables: {
* partyId: // value for 'partyId'
* delegationsPagination: // value for 'delegationsPagination'
* },
* });
*/
@@ -1,92 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type WalletDelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } };
export type DelegationsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type DelegationsQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string }, party?: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } } } } | null> | null } | null } | null };
export const WalletDelegationFieldsFragmentDoc = gql`
fragment WalletDelegationFields on Delegation {
amount
node {
id
name
}
epoch
}
`;
export const DelegationsDocument = gql`
query Delegations($partyId: ID!) {
epoch {
id
}
party(id: $partyId) {
id
delegationsConnection {
edges {
node {
...WalletDelegationFields
}
}
}
stakingSummary {
currentStakeAvailable
}
accountsConnection {
edges {
node {
asset {
name
id
decimals
symbol
source {
__typename
... on ERC20 {
contractAddress
}
}
}
type
balance
}
}
}
}
}
${WalletDelegationFieldsFragmentDoc}`;
/**
* __useDelegationsQuery__
*
* To run a query within a React component, call `useDelegationsQuery` and pass it any options that fit your needs.
* When your component renders, `useDelegationsQuery` 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 } = useDelegationsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useDelegationsQuery(baseOptions: Apollo.QueryHookOptions<DelegationsQuery, DelegationsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<DelegationsQuery, DelegationsQueryVariables>(DelegationsDocument, options);
}
export function useDelegationsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<DelegationsQuery, DelegationsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<DelegationsQuery, DelegationsQueryVariables>(DelegationsDocument, options);
}
export type DelegationsQueryHookResult = ReturnType<typeof useDelegationsQuery>;
export type DelegationsLazyQueryHookResult = ReturnType<typeof useDelegationsLazyQuery>;
export type DelegationsQueryResult = Apollo.QueryResult<DelegationsQuery, DelegationsQueryVariables>;
+13 -5
View File
@@ -4,6 +4,7 @@ import keyBy from 'lodash/keyBy';
import uniq from 'lodash/uniq';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ENV } from '../../config';
import noIcon from '../../images/token-no-icon.png';
import vegaBlack from '../../images/vega_black.png';
@@ -23,18 +24,18 @@ import type {
DelegationsQuery,
DelegationsQueryVariables,
WalletDelegationFieldsFragment,
} from './__generated___/Delegations';
import { DelegationsDocument } from './__generated___/Delegations';
} from './__generated__/Delegations';
import { DelegationsDocument } from './__generated__/Delegations';
export const usePollForDelegations = () => {
const { token: vegaToken } = useContracts();
const {
appState: { decimals },
} = useAppState();
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const client = useApolloClient();
const { delegationsPagination } = ENV;
const [delegations, setDelegations] = React.useState<
WalletDelegationFieldsFragment[]
>([]);
@@ -62,7 +63,14 @@ export const usePollForDelegations = () => {
client
.query<DelegationsQuery, DelegationsQueryVariables>({
query: DelegationsDocument,
variables: { partyId: pubKey },
variables: {
partyId: pubKey,
delegationsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
},
fetchPolicy: 'network-only',
})
.then((res) => {
@@ -207,7 +215,7 @@ export const usePollForDelegations = () => {
clearInterval(interval);
mounted = false;
};
}, [client, decimals, pubKey, t, vegaToken.address]);
}, [delegationsPagination, client, decimals, pubKey, t, vegaToken.address]);
return { delegations, currentStakeAvailable, delegatedNodes, accounts };
};
+1
View File
@@ -64,6 +64,7 @@ export const ENV = {
docsUrl: windowOrDefault('NX_VEGA_DOCS_URL'),
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
@@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js';
import create from 'zustand';
import { create } from 'zustand';
import type { UserTrancheBalance } from '../../contexts/app-state/app-state-context';
export interface AssociationBreakdown {
@@ -20,7 +20,7 @@ fragment DelegationFields on Delegation {
epoch
}
query Rewards($partyId: ID!) {
query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
rewardsConnection {
@@ -30,7 +30,7 @@ query Rewards($partyId: ID!) {
}
}
}
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...DelegationFields
+4 -2
View File
@@ -9,6 +9,7 @@ export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: stri
export type RewardsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
@@ -39,7 +40,7 @@ export const DelegationFieldsFragmentDoc = gql`
}
`;
export const RewardsDocument = gql`
query Rewards($partyId: ID!) {
query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
rewardsConnection {
@@ -49,7 +50,7 @@ export const RewardsDocument = gql`
}
}
}
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...DelegationFields
@@ -82,6 +83,7 @@ ${DelegationFieldsFragmentDoc}`;
* const { data, loading, error } = useRewardsQuery({
* variables: {
* partyId: // value for 'partyId'
* delegationsPagination: // value for 'delegationsPagination'
* },
* });
*/
@@ -1,98 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type RewardFieldsFragment = { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } };
export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number };
export type RewardsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
export const RewardFieldsFragmentDoc = gql`
fragment RewardFields on Reward {
rewardType
asset {
id
symbol
}
party {
id
}
epoch {
id
}
amount
percentageOfTotal
receivedAt
}
`;
export const DelegationFieldsFragmentDoc = gql`
fragment DelegationFields on Delegation {
amount
epoch
}
`;
export const RewardsDocument = gql`
query Rewards($partyId: ID!) {
party(id: $partyId) {
id
rewardsConnection {
edges {
node {
...RewardFields
}
}
}
delegationsConnection {
edges {
node {
...DelegationFields
}
}
}
}
epoch {
id
timestamps {
start
end
expiry
}
}
}
${RewardFieldsFragmentDoc}
${DelegationFieldsFragmentDoc}`;
/**
* __useRewardsQuery__
*
* To run a query within a React component, call `useRewardsQuery` and pass it any options that fit your needs.
* When your component renders, `useRewardsQuery` 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 } = useRewardsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useRewardsQuery(baseOptions: Apollo.QueryHookOptions<RewardsQuery, RewardsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<RewardsQuery, RewardsQueryVariables>(RewardsDocument, options);
}
export function useRewardsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<RewardsQuery, RewardsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<RewardsQuery, RewardsQueryVariables>(RewardsDocument, options);
}
export type RewardsQueryHookResult = ReturnType<typeof useRewardsQuery>;
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
@@ -9,7 +9,7 @@ import type {
RewardsQuery,
RewardFieldsFragment,
DelegationFieldsFragment,
} from './__generated___/Rewards';
} from './__generated__/Rewards';
import {
formatNumber,
removePaginationWrapper,
@@ -4,6 +4,7 @@ import { formatDistance } from 'date-fns';
import Duration from 'duration-js';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ENV } from '../../../config';
import { EpochCountdown } from '../../../components/epoch-countdown';
import { Heading } from '../../../components/heading';
@@ -15,7 +16,7 @@ import {
import { RewardInfo } from './reward-info';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useNetworkParams, NetworkParams } from '@vegaprotocol/react-helpers';
import { useRewardsQuery } from './__generated___/Rewards';
import { useRewardsQuery } from './__generated__/Rewards';
export const RewardsPage = () => {
const { t } = useTranslation();
@@ -24,8 +25,16 @@ export const RewardsPage = () => {
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { appDispatch } = useAppState();
const { delegationsPagination } = ENV;
const { data, loading, error } = useRewardsQuery({
variables: { partyId: pubKey || '' },
variables: {
partyId: pubKey || '',
delegationsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
},
skip: !pubKey,
});
const { params } = useNetworkParams([
@@ -6,10 +6,10 @@ fragment StakingDelegationsFields on Delegation {
epoch
}
query PartyDelegations($partyId: ID!) {
query PartyDelegations($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...StakingDelegationsFields
@@ -23,13 +23,13 @@ fragment StakingNodeFields on Node {
}
}
query Staking($partyId: ID!) {
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
stakingSummary {
currentStakeAvailable
}
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
amount
@@ -7,6 +7,7 @@ export type StakingDelegationsFieldsFragment = { __typename?: 'Delegation', amou
export type PartyDelegationsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
@@ -22,10 +23,10 @@ export const StakingDelegationsFieldsFragmentDoc = gql`
}
`;
export const PartyDelegationsDocument = gql`
query PartyDelegations($partyId: ID!) {
query PartyDelegations($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
...StakingDelegationsFields
@@ -52,6 +53,7 @@ export const PartyDelegationsDocument = gql`
* const { data, loading, error } = usePartyDelegationsQuery({
* variables: {
* partyId: // value for 'partyId'
* delegationsPagination: // value for 'delegationsPagination'
* },
* });
*/
+4 -2
View File
@@ -7,6 +7,7 @@ export type StakingNodeFieldsFragment = { __typename?: 'Node', id: string, name:
export type StakingQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
@@ -39,13 +40,13 @@ export const StakingNodeFieldsFragmentDoc = gql`
}
`;
export const StakingDocument = gql`
query Staking($partyId: ID!) {
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
stakingSummary {
currentStakeAvailable
}
delegationsConnection {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
amount
@@ -94,6 +95,7 @@ export const StakingDocument = gql`
* const { data, loading, error } = useStakingQuery({
* variables: {
* partyId: // value for 'partyId'
* delegationsPagination: // value for 'delegationsPagination'
* },
* });
*/
@@ -1,68 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type StakingDelegationsFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string } };
export type PartyDelegationsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyDelegationsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: 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 } };
export const StakingDelegationsFieldsFragmentDoc = gql`
fragment StakingDelegationsFields on Delegation {
amount
node {
id
}
epoch
}
`;
export const PartyDelegationsDocument = gql`
query PartyDelegations($partyId: ID!) {
party(id: $partyId) {
id
delegationsConnection {
edges {
node {
...StakingDelegationsFields
}
}
}
}
epoch {
id
}
}
${StakingDelegationsFieldsFragmentDoc}`;
/**
* __usePartyDelegationsQuery__
*
* To run a query within a React component, call `usePartyDelegationsQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyDelegationsQuery` 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 } = usePartyDelegationsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyDelegationsQuery(baseOptions: Apollo.QueryHookOptions<PartyDelegationsQuery, PartyDelegationsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyDelegationsQuery, PartyDelegationsQueryVariables>(PartyDelegationsDocument, options);
}
export function usePartyDelegationsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyDelegationsQuery, PartyDelegationsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyDelegationsQuery, PartyDelegationsQueryVariables>(PartyDelegationsDocument, options);
}
export type PartyDelegationsQueryHookResult = ReturnType<typeof usePartyDelegationsQuery>;
export type PartyDelegationsLazyQueryHookResult = ReturnType<typeof usePartyDelegationsLazyQuery>;
export type PartyDelegationsQueryResult = Apollo.QueryResult<PartyDelegationsQuery, PartyDelegationsQueryVariables>;
@@ -1,110 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
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 StakingQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
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 {
id
name
pubkey
infoUrl
location
ethereumAddress
stakedByOperator
stakedByDelegates
stakedTotal
pendingStake
epochData {
total
offline
online
}
rankingScore {
rankingScore
stakeScore
performanceScore
votingPower
status
}
}
`;
export const StakingDocument = gql`
query Staking($partyId: ID!) {
party(id: $partyId) {
id
stakingSummary {
currentStakeAvailable
}
delegationsConnection {
edges {
node {
amount
epoch
node {
id
}
}
}
}
}
epoch {
id
timestamps {
start
end
expiry
}
}
nodesConnection {
edges {
node {
...StakingNodeFields
}
}
}
nodeData {
stakedTotal
totalNodes
inactiveNodes
uptime
}
}
${StakingNodeFieldsFragmentDoc}`;
/**
* __useStakingQuery__
*
* To run a query within a React component, call `useStakingQuery` and pass it any options that fit your needs.
* When your component renders, `useStakingQuery` 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 } = useStakingQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useStakingQuery(baseOptions: Apollo.QueryHookOptions<StakingQuery, StakingQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StakingQuery, StakingQueryVariables>(StakingDocument, options);
}
export function useStakingLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StakingQuery, StakingQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StakingQuery, StakingQueryVariables>(StakingDocument, options);
}
export type StakingQueryHookResult = ReturnType<typeof useStakingQuery>;
export type StakingLazyQueryHookResult = ReturnType<typeof useStakingLazyQuery>;
export type StakingQueryResult = Apollo.QueryResult<StakingQuery, StakingQueryVariables>;
+1 -1
View File
@@ -20,7 +20,7 @@ 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 { StakingQuery } from './__generated__/Staking';
import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch';
interface StakingNodeProps {
@@ -1,14 +1,14 @@
import { ENV } from '../../../config';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useStakingQuery } from './__generated___/Staking';
import { SplashLoader } from '../../../components/splash-loader';
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
import type { StakingQuery } from './__generated___/Staking';
import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch';
import { useRefreshValidators } from '../../../hooks/use-refresh-validators';
import { SplashLoader } from '../../../components/splash-loader';
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';
// TODO should only request a single node. When migrating from deprecated APIs we should address this.
@@ -23,12 +23,20 @@ export const NodeContainer = ({
}: {
data?: StakingQuery;
previousEpochData?: PreviousEpochQuery;
}) => React.ReactElement;
}) => ReactElement;
}) => {
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const { delegationsPagination } = ENV;
const { data, loading, error, refetch } = useStakingQuery({
variables: { partyId: pubKey || '' },
variables: {
partyId: pubKey || '',
delegationsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
},
});
const { data: previousEpochData } = usePreviousEpochQuery({
variables: {

Some files were not shown because too many files have changed in this diff Show More