Compare commits

..
48 changed files with 384 additions and 1320 deletions
+4 -6
View File
@@ -62,19 +62,17 @@ jobs:
- name: Define variables
run: |
envName=''
domain="vega.rocks"
if [[ "${{ github.event_name }}" = "push" ]]; then
domain="vega.rocks"
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
if [[ "${{ github.ref }}" =~ .*mainnet.* ]]; then
domain="vega.community"
fi
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
envName="stagnet1"
elif [[ "${{ matrix.app}}" = "trading" ]] && [[ ${{ startsWith(github.ref, 'refs/tags/v') && 'true' || 'false' }} = "true" ]]; then
envName="mainnet"
elif [[ "${{ github.ref }}" =~ .*main$ ]]; then
envName="mainnet"
fi
if [[ "${envName}" = "mainnet" ]]; then
domain="vega.xyz"
fi
bucketName="${{ matrix.app }}.${envName}.${domain}"
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
-1
View File
@@ -14,7 +14,6 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
# App flags
NX_EXPLORER_ASSETS=1
@@ -48,13 +48,11 @@ export const Footer = () => {
</Link>
</div>
{ENV.addresses.feedback ? (
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
) : null}
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
</div>
</footer>
);
@@ -64,5 +62,9 @@ const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
return <span className="cursor-default">{nodeUrl}</span>;
return (
<Link href={'https://' + nodeUrl} target="_blank">
{nodeUrl}
</Link>
);
};
-1
View File
@@ -17,7 +17,6 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -15,7 +15,6 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -17,7 +17,6 @@ NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
#Test configuration variables
CYPRESS_FAIRGROUND=false
-1
View File
@@ -11,4 +11,3 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
-1
View File
@@ -12,4 +12,3 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
-1
View File
@@ -8,4 +8,3 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
-1
View File
@@ -12,4 +12,3 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
-1
View File
@@ -9,4 +9,3 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
-1
View File
@@ -62,7 +62,6 @@ export const ENV = {
ethWalletMnemonic: windowOrDefault('NX_ETH_WALLET_MNEMONIC'),
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
rest: windowOrDefault('NX_VEGA_REST_URL'),
flags: {
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
@@ -43,17 +43,13 @@ jest.mock('../list-asset', () => ({
it('Renders with data-testid', async () => {
const proposal = generateProposal();
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal')).toBeInTheDocument();
});
it('renders each section', async () => {
const proposal = generateProposal();
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
@@ -80,8 +76,6 @@ it('renders whitelist section if proposal is new asset and source is erc20', asy
},
},
});
render(
<Proposal restData={{}} proposal={proposal as ProposalQuery['proposal']} />
);
render(<Proposal proposal={proposal as ProposalQuery['proposal']} />);
expect(screen.getByTestId('proposal-list-asset')).toBeInTheDocument();
});
@@ -24,11 +24,9 @@ export enum ProposalType {
}
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
}
export const Proposal = ({ proposal, restData }: ProposalProps) => {
export const Proposal = ({ proposal }: ProposalProps) => {
const { params, loading, error } = useNetworkParams([
NetworkParams.governance_proposal_market_minVoterBalance,
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
@@ -106,7 +104,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
<ProposalJson proposal={proposal} />
</div>
<div className="mb-10">
@@ -107,7 +107,7 @@ export const ProtocolUpgradeProposalsListItem = ({
<div className="grid grid-cols-1 mt-3">
<div className="justify-self-end">
<Link
to={`${Routes.PROTOCOL_UPGRADES}/${stripFullStops(
to={`${Routes.PROPOSALS}/protocol-upgrade/${stripFullStops(
proposal.vegaReleaseTag
)}`}
>
@@ -5,15 +5,9 @@ import { useParams } from 'react-router-dom';
import { Proposal } from '../components/proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useProposalQuery } from './__generated__/Proposal';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
const {
state: { loading: restLoading, error: restError, data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
console.log(restLoading, restError, restData);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -29,7 +23,7 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer loading={loading} error={error} data={data}>
{data?.proposal ? (
<Proposal proposal={data.proposal} restData={restData} />
<Proposal proposal={data.proposal} />
) : (
<ProposalNotFound />
)}
+4 -8
View File
@@ -224,10 +224,6 @@ const redirects = [
path: '/vesting',
element: <Navigate to={Routes.REDEEM} replace />,
},
{
path: Routes.PROTOCOL_UPGRADES,
element: <Navigate to={Routes.PROPOSALS} replace />,
},
];
const routerConfig = [
@@ -268,13 +264,13 @@ const routerConfig = [
},
{ path: 'proposals', element: <LazyProposalsList /> },
{ path: ':proposalId', element: <LazyProposal /> },
{
path: 'protocol-upgrade/:proposalReleaseTag',
element: <LazyProtocolUpgradeProposal />,
},
{ path: 'rejected', element: <LazyRejectedProposalsList /> },
],
},
{
path: `${Routes.PROTOCOL_UPGRADES}/:proposalReleaseTag`,
element: <LazyProtocolUpgradeProposal />,
},
{
path: Routes.VALIDATORS,
element: <LazyStaking name="Staking" />,
-1
View File
@@ -5,7 +5,6 @@ const Routes = {
REWARDS: '/rewards',
PROPOSALS: '/proposals',
PROPOSALS_REJECTED: '/proposals/rejected',
PROTOCOL_UPGRADES: '/protocol-upgrades',
NOT_PERMITTED: '/not-permitted',
NOT_FOUND: '/not-found',
CONTRACTS: '/contracts',
File diff suppressed because it is too large Load Diff
@@ -194,7 +194,6 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
});
it('shows node health', function () {
// 0006-NETW-010
const market = this.market;
cy.visit(`/#/markets/${market.id}`);
cy.getByTestId('node-health')
@@ -1,111 +0,0 @@
import { closeWelcomeDialog } from '../support/helpers';
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
describe('home', { tags: '@regression' }, () => {
before(() => {
cy.clearLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
});
describe('footer', () => {
it('shows current block height', () => {
closeWelcomeDialog();
// 0006-NETW-004
// 0006-NETW-005
// 0006-NETW-008
// 0006-NETW-009
// 0006-NETW-011
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(3000);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Warning delay ( >3 sec)');
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(1);
});
});
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational', { timeout: 10000 })
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
it('shows node switcher details', () => {
// 0006-NETW-012
// 0006-NETW-013
// 0006-NETW-014
// 0006-NETW-015
// 0006-NETW-016
cy.getByTestId(nodeHealth).click();
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
cy.getByTestId(dialogContent).should(
'contain.text',
'This app will only work on CUSTOM. Select a node to connect to.'
);
cy.getByTestId('node')
.first()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', 'Response time')
.next()
.should('contain.text', 'Block')
.next()
.should('contain.text', 'Subscription');
cy.getByTestId('custom-row').should('contain.text', 'Other');
cy.getByTestId('dialog-close').click();
});
it('switch to other node', () => {
// 0006-NETW-017
// 0006-NETW-018
// 0006-NETW-019
// 0006-NETW-020
cy.getByTestId(nodeHealth).click();
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
cy.getByTestId('connect').should('be.disabled');
cy.get("input[placeholder='https://']")
.focus()
.type(new URL(Cypress.env('VEGA_URL')).origin + '/graphql');
cy.getByTestId('connect').click();
cy.getByTestId(nodeHealth)
.children()
.first()
.should('contain.text', 'Operational');
});
});
describe('Network switcher', () => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
});
// 0006-NETW-002
// 0006-NETW-003
it('switch to fairground network', () => {
cy.getByTestId('network-switcher').click();
cy.getByTestId('network-item').contains('Fairground testnet').click();
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
});
});
});
+16 -3
View File
@@ -4,7 +4,6 @@ import { marketsDataQuery } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const selectMarketOverlay = 'select-market-list';
const dialogContent = 'dialog-content';
const generateProposal = (code: string): ProposalListFieldsFragment => ({
__typename: 'Proposal',
@@ -289,7 +288,7 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-1');
cy.getByTestId(dialogContent).should('not.exist');
cy.getByTestId('dialog-content').should('not.exist');
});
});
@@ -302,8 +301,22 @@ describe('home', { tags: '@regression' }, () => {
cy.visit('/');
cy.wait('@Markets');
cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.getByTestId(dialogContent).should('not.exist');
cy.getByTestId('dialog-content').should('not.exist');
});
});
});
describe('footer', () => {
it('shows current block height', () => {
cy.visit('/');
cy.getByTestId('node-health')
.children()
.first()
.should('contain.text', 'Operational')
.next()
.should('contain.text', new URL(Cypress.env('VEGA_URL')).origin)
.next()
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
});
});
});
+13 -1
View File
@@ -115,7 +115,19 @@ export function createClient({
)
: httpLink;
const errorLink = onError(({ graphQLErrors, networkError }) => {
const errorLink = onError(({ graphQLErrors, networkError, operation }) => {
// if any of these queries error don't capture any errors, its
// likely the user is connecting to a dodgy node, NodeGuard gets
// called on startup, NodeCheck and NodeCheckTimeUpdate are called
// by the NodeSwitcher component and the useNodeHealth hook
if (
['NodeGuard', 'NodeCheck', 'NodeCheckTimeUpdate'].includes(
operation.operationName
)
) {
return;
}
if (graphQLErrors) {
graphQLErrors.forEach((e) => {
if (e.extensions && e.extensions['type'] !== NOT_FOUND) {
@@ -107,7 +107,6 @@ export const NetworkSwitcher = ({
onOpenChange={handleOpen}
trigger={
<DropdownMenuTrigger
data-testid="network-switcher"
ref={menuRef}
className={classNames(
'flex justify-between items-center text-sm text-vega-dark-600 dark:text-vega-light-600 py-1 px-2 rounded border border-vega-dark-200 whitespace-nowrap dark:hover:bg-vega-dark-500 hover:bg-vega-light-500',
@@ -3,11 +3,13 @@ import { MockedProvider } from '@apollo/react-testing';
import { act, render, screen, waitFor } from '@testing-library/react';
import { RadioGroup } from '@vegaprotocol/ui-toolkit';
import type {
BlockTimeSubscription,
StatisticsQuery,
} from '../../utils/__generated__/Node';
import { BlockTimeDocument } from '../../utils/__generated__/Node';
import { StatisticsDocument } from '../../utils/__generated__/Node';
NodeCheckTimeUpdateSubscription,
NodeCheckQuery,
} from '../../utils/__generated__/NodeCheck';
import {
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../../utils/__generated__/NodeCheck';
import type { RowDataProps } from './row-data';
import { POLL_INTERVAL } from './row-data';
import { BLOCK_THRESHOLD, RowData } from './row-data';
@@ -19,9 +21,9 @@ jest.mock('@vegaprotocol/apollo-client', () => ({
useHeaderStore: jest.fn().mockReturnValue({}),
}));
const statsQueryMock: MockedResponse<StatisticsQuery> = {
const statsQueryMock: MockedResponse<NodeCheckQuery> = {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
result: {
data: {
@@ -34,9 +36,9 @@ const statsQueryMock: MockedResponse<StatisticsQuery> = {
},
};
const subMock: MockedResponse<BlockTimeSubscription> = {
const subMock: MockedResponse<NodeCheckTimeUpdateSubscription> = {
request: {
query: BlockTimeDocument,
query: NodeCheckTimeUpdateDocument,
},
result: {
data: {
@@ -71,8 +73,8 @@ const mockHeaders = (
const renderComponent = (
props: RowDataProps,
queryMock: MockedResponse<StatisticsQuery>,
subMock: MockedResponse<BlockTimeSubscription>
queryMock: MockedResponse<NodeCheckQuery>,
subMock: MockedResponse<NodeCheckTimeUpdateSubscription>
) => {
return (
<MockedProvider mocks={[queryMock, subMock, subMock, subMock]}>
@@ -127,16 +129,16 @@ describe('RowData', () => {
it('radio button still enabled if query fails', async () => {
mockHeaders(props.url, {});
const failedQueryMock: MockedResponse<StatisticsQuery> = {
const failedQueryMock: MockedResponse<NodeCheckQuery> = {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
error: new Error('failed'),
};
const failedSubMock: MockedResponse<BlockTimeSubscription> = {
const failedSubMock: MockedResponse<NodeCheckTimeUpdateSubscription> = {
request: {
query: BlockTimeDocument,
query: NodeCheckTimeUpdateDocument,
},
error: new Error('failed'),
};
@@ -244,10 +246,10 @@ describe('RowData', () => {
jest.useFakeTimers();
const createStatsQueryMock = (
blockHeight: string
): MockedResponse<StatisticsQuery> => {
): MockedResponse<NodeCheckQuery> => {
return {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
result: {
data: {
@@ -261,10 +263,10 @@ describe('RowData', () => {
};
};
const createFailedStatsQueryMock = (): MockedResponse<StatisticsQuery> => {
const createFailedStatsQueryMock = (): MockedResponse<NodeCheckQuery> => {
return {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
result: {
data: undefined,
@@ -6,9 +6,9 @@ import { Radio } from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
import { CUSTOM_NODE_KEY } from '../../types';
import {
useBlockTimeSubscription,
useStatisticsQuery,
} from '../../utils/__generated__/Node';
useNodeCheckQuery,
useNodeCheckTimeUpdateSubscription,
} from '../../utils/__generated__/NodeCheck';
import { LayoutCell } from './layout-cell';
export const POLL_INTERVAL = 1000;
@@ -30,13 +30,14 @@ export const RowData = ({
const [subFailed, setSubFailed] = useState(false);
const [time, setTime] = useState<number>();
// no use of data here as we need the data nodes reference to block height
const { data, error, loading, startPolling, stopPolling } =
useStatisticsQuery({
const { data, error, loading, startPolling, stopPolling } = useNodeCheckQuery(
{
pollInterval: POLL_INTERVAL,
// fix for pollInterval
// https://github.com/apollographql/apollo-client/issues/9819
ssr: false,
});
}
);
const headerStore = useHeaderStore();
const headers = headerStore[url];
@@ -44,7 +45,7 @@ export const RowData = ({
data: subData,
error: subError,
loading: subLoading,
} = useBlockTimeSubscription();
} = useNodeCheckTimeUpdateSubscription();
useEffect(() => {
const timeout = setTimeout(() => {
@@ -1,11 +1,11 @@
import {
StatisticsDocument,
BlockTimeDocument,
} from '../../utils/__generated__/Node';
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../../utils/__generated__/NodeCheck';
import type {
StatisticsQuery,
BlockTimeSubscription,
} from '../../utils/__generated__/Node';
NodeCheckQuery,
NodeCheckTimeUpdateSubscription,
} from '../../utils/__generated__/NodeCheck';
import { Networks } from '../../types';
import type { RequestHandlerResponse } from 'mock-apollo-client';
import { createMockClient } from 'mock-apollo-client';
@@ -21,7 +21,7 @@ type MockClientProps = {
busEvents?: MockRequestConfig;
};
export const getMockBusEventsResult = (): BlockTimeSubscription => ({
export const getMockBusEventsResult = (): NodeCheckTimeUpdateSubscription => ({
busEvents: [
{
__typename: 'BusEvent',
@@ -32,19 +32,21 @@ export const getMockBusEventsResult = (): BlockTimeSubscription => ({
export const getMockStatisticsResult = (
env: Networks = Networks.TESTNET
): StatisticsQuery => ({
): NodeCheckQuery => ({
statistics: {
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
export const getMockQueryResult = (env: Networks): StatisticsQuery => ({
export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({
statistics: {
__typename: 'Statistics',
chainId: `${env.toLowerCase()}-0123`,
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
});
@@ -72,11 +74,11 @@ export default function ({
const mockClient = createMockClient();
mockClient.setRequestHandler(
StatisticsDocument,
NodeCheckDocument,
getHandler(statistics, getMockStatisticsResult(network))
);
mockClient.setRequestHandler(
BlockTimeDocument,
NodeCheckTimeUpdateDocument,
getHandler(busEvents, getMockBusEventsResult())
);
+11 -9
View File
@@ -5,11 +5,13 @@ import { useEffect } from 'react';
import { create } from 'zustand';
import { createClient } from '@vegaprotocol/apollo-client';
import type {
BlockTimeSubscription,
StatisticsQuery,
} from '../utils/__generated__/Node';
import { BlockTimeDocument } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
NodeCheckTimeUpdateSubscription,
NodeCheckQuery,
} from '../utils/__generated__/NodeCheck';
import {
NodeCheckDocument,
NodeCheckTimeUpdateDocument,
} from '../utils/__generated__/NodeCheck';
import type { Environment } from '../types';
import { Networks } from '../types';
import { compileErrors } from '../utils/compile-errors';
@@ -220,8 +222,8 @@ const testNode = async (
*/
const testQuery = async (client: Client) => {
try {
const result = await client.query<StatisticsQuery>({
query: StatisticsDocument,
const result = await client.query<NodeCheckQuery>({
query: NodeCheckDocument,
});
if (!result || result.error) {
return false;
@@ -240,8 +242,8 @@ const testQuery = async (client: Client) => {
const testSubscription = (client: Client) => {
return new Promise((resolve) => {
const sub = client
.subscribe<BlockTimeSubscription>({
query: BlockTimeDocument,
.subscribe<NodeCheckTimeUpdateSubscription>({
query: NodeCheckTimeUpdateDocument,
errorPolicy: 'all',
})
.subscribe({
@@ -2,8 +2,8 @@ import { renderHook, waitFor } from '@testing-library/react';
import { useNodeHealth } from './use-node-health';
import type { MockedResponse } from '@apollo/react-testing';
import { MockedProvider } from '@apollo/react-testing';
import type { StatisticsQuery } from '../utils/__generated__/Node';
import { StatisticsDocument } from '../utils/__generated__/Node';
import type { NodeCheckQuery } from '../utils/__generated__/NodeCheck';
import { NodeCheckDocument } from '../utils/__generated__/NodeCheck';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { Intent } from '@vegaprotocol/ui-toolkit';
@@ -16,10 +16,10 @@ jest.mock('@vegaprotocol/apollo-client');
const createStatsMock = (
blockHeight: number
): MockedResponse<StatisticsQuery> => {
): MockedResponse<NodeCheckQuery> => {
return {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
result: {
data: {
@@ -34,7 +34,7 @@ const createStatsMock = (
};
function setup(
mock: MockedResponse<StatisticsQuery>,
mock: MockedResponse<NodeCheckQuery>,
headers:
| {
blockHeight: number;
@@ -93,9 +93,9 @@ describe('useNodeHealth', () => {
);
it('block diff is null if query fails indicating non operational', async () => {
const failedQuery: MockedResponse<StatisticsQuery> = {
const failedQuery: MockedResponse<NodeCheckQuery> = {
request: {
query: StatisticsDocument,
query: NodeCheckDocument,
},
result: {
// @ts-ignore failed query with no result
@@ -1,5 +1,5 @@
import { useEffect, useMemo } from 'react';
import { useStatisticsQuery } from '../utils/__generated__/Node';
import { useNodeCheckQuery } from '../utils/__generated__/NodeCheck';
import { useHeaderStore } from '@vegaprotocol/apollo-client';
import { useEnvironment } from './use-environment';
import { useNavigatorOnline } from '@vegaprotocol/react-helpers';
@@ -16,7 +16,7 @@ export const useNodeHealth = () => {
const url = useEnvironment((store) => store.VEGA_URL);
const headerStore = useHeaderStore();
const headers = url ? headerStore[url] : undefined;
const { data, error, startPolling, stopPolling } = useStatisticsQuery({
const { data, error, startPolling, stopPolling } = useNodeCheckQuery({
fetchPolicy: 'no-cache',
});
+1 -1
View File
@@ -8,4 +8,4 @@ export * from './hooks';
export * from './types';
// Utils
export * from './utils/__generated__/Node';
export * from './utils/__generated__/NodeCheck';
@@ -1,4 +1,4 @@
query Statistics {
query NodeCheck {
statistics {
chainId
blockHeight
@@ -6,7 +6,7 @@ query Statistics {
}
}
subscription BlockTime {
subscription NodeCheckTimeUpdate {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
-81
View File
@@ -1,81 +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 StatisticsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type StatisticsQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type BlockTimeSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type BlockTimeSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null };
export const StatisticsDocument = gql`
query Statistics {
statistics {
chainId
blockHeight
vegaTime
}
}
`;
/**
* __useStatisticsQuery__
*
* To run a query within a React component, call `useStatisticsQuery` and pass it any options that fit your needs.
* When your component renders, `useStatisticsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useStatisticsQuery({
* variables: {
* },
* });
*/
export function useStatisticsQuery(baseOptions?: Apollo.QueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export function useStatisticsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<StatisticsQuery, StatisticsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<StatisticsQuery, StatisticsQueryVariables>(StatisticsDocument, options);
}
export type StatisticsQueryHookResult = ReturnType<typeof useStatisticsQuery>;
export type StatisticsLazyQueryHookResult = ReturnType<typeof useStatisticsLazyQuery>;
export type StatisticsQueryResult = Apollo.QueryResult<StatisticsQuery, StatisticsQueryVariables>;
export const BlockTimeDocument = gql`
subscription BlockTime {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
}
`;
/**
* __useBlockTimeSubscription__
*
* To run a query within a React component, call `useBlockTimeSubscription` and pass it any options that fit your needs.
* When your component renders, `useBlockTimeSubscription` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useBlockTimeSubscription({
* variables: {
* },
* });
*/
export function useBlockTimeSubscription(baseOptions?: Apollo.SubscriptionHookOptions<BlockTimeSubscription, BlockTimeSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<BlockTimeSubscription, BlockTimeSubscriptionVariables>(BlockTimeDocument, options);
}
export type BlockTimeSubscriptionHookResult = ReturnType<typeof useBlockTimeSubscription>;
export type BlockTimeSubscriptionResult = Apollo.SubscriptionResult<BlockTimeSubscription>;
+81
View File
@@ -0,0 +1,81 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type NodeCheckQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type NodeCheckTimeUpdateSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeCheckTimeUpdateSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', id: string }> | null };
export const NodeCheckDocument = gql`
query NodeCheck {
statistics {
chainId
blockHeight
vegaTime
}
}
`;
/**
* __useNodeCheckQuery__
*
* To run a query within a React component, call `useNodeCheckQuery` and pass it any options that fit your needs.
* When your component renders, `useNodeCheckQuery` 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 } = useNodeCheckQuery({
* variables: {
* },
* });
*/
export function useNodeCheckQuery(baseOptions?: Apollo.QueryHookOptions<NodeCheckQuery, NodeCheckQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<NodeCheckQuery, NodeCheckQueryVariables>(NodeCheckDocument, options);
}
export function useNodeCheckLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<NodeCheckQuery, NodeCheckQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<NodeCheckQuery, NodeCheckQueryVariables>(NodeCheckDocument, options);
}
export type NodeCheckQueryHookResult = ReturnType<typeof useNodeCheckQuery>;
export type NodeCheckLazyQueryHookResult = ReturnType<typeof useNodeCheckLazyQuery>;
export type NodeCheckQueryResult = Apollo.QueryResult<NodeCheckQuery, NodeCheckQueryVariables>;
export const NodeCheckTimeUpdateDocument = gql`
subscription NodeCheckTimeUpdate {
busEvents(types: TimeUpdate, batchSize: 1) {
id
}
}
`;
/**
* __useNodeCheckTimeUpdateSubscription__
*
* To run a query within a React component, call `useNodeCheckTimeUpdateSubscription` and pass it any options that fit your needs.
* When your component renders, `useNodeCheckTimeUpdateSubscription` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useNodeCheckTimeUpdateSubscription({
* variables: {
* },
* });
*/
export function useNodeCheckTimeUpdateSubscription(baseOptions?: Apollo.SubscriptionHookOptions<NodeCheckTimeUpdateSubscription, NodeCheckTimeUpdateSubscriptionVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useSubscription<NodeCheckTimeUpdateSubscription, NodeCheckTimeUpdateSubscriptionVariables>(NodeCheckTimeUpdateDocument, options);
}
export type NodeCheckTimeUpdateSubscriptionHookResult = ReturnType<typeof useNodeCheckTimeUpdateSubscription>;
export type NodeCheckTimeUpdateSubscriptionResult = Apollo.SubscriptionResult<NodeCheckTimeUpdateSubscription>;
+4 -4
View File
@@ -1,11 +1,11 @@
import type { StatisticsQuery } from './__generated__/Node';
import type { NodeCheckQuery } from './__generated__/NodeCheck';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const statisticsQuery = (
override?: PartialDeep<StatisticsQuery>
): StatisticsQuery => {
const defaultResult: StatisticsQuery = {
override?: PartialDeep<NodeCheckQuery>
): NodeCheckQuery => {
const defaultResult: NodeCheckQuery = {
statistics: {
__typename: 'Statistics',
chainId: 'chain-id',
@@ -5,7 +5,7 @@ import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { marketDataProvider } from '../../market-data-provider';
import { totalFeesPercentage } from '../../market-utils';
import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import { Dialog, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
import {
addDecimalsFormatNumber,
formatNumber,
@@ -25,9 +25,12 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { useEnvironment } from '@vegaprotocol/environment';
import type { Provider } from '../../oracle-schema';
import { OracleBasicProfile } from '../../components/oracle-basic-profile';
import { useOracleProofs } from '../../hooks';
import { OracleDialog } from '../oracle-dialog/oracle-dialog';
import {
OracleBasicProfile,
OracleProfileTitle,
OracleFullProfile,
} from '../../components';
import { useOracleProofs, useOracleMarkets } from '../../hooks';
import { useDataProvider } from '@vegaprotocol/data-provider';
type PanelProps = Pick<
@@ -610,6 +613,34 @@ const NoOracleProof = ({
);
};
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
const OracleProfile = (props: {
provider: Provider;
dataSourceSpecId: string;
@@ -143,7 +143,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
@@ -164,7 +164,7 @@ export const marketInfoQuery = (
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f',
},
},
],
@@ -1,10 +1,8 @@
import { useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/environment';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { IconName } from '@blueprintjs/icons';
import { t } from '@vegaprotocol/i18n';
import { getMatchingOracleProvider, useOracleProofs } from '../../hooks';
import type { Market } from '../../markets-provider';
import { getVerifiedStatusIcon } from '../oracle-basic-profile';
export const OracleStatus = ({
dataSourceSpecForSettlementData,
@@ -25,15 +23,22 @@ export const OracleStatus = ({
dataSourceSpecForTradingTermination.data,
providers
);
let maliciousOracleProvider = null;
if (settlementDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = settlementDataProvider;
} else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') {
maliciousOracleProvider = tradingTerminationDataProvider;
if (
(settlementDataProvider &&
settlementDataProvider.oracle.status !== 'GOOD') ||
(tradingTerminationDataProvider &&
tradingTerminationDataProvider.oracle.status !== 'GOOD')
) {
return (
<span
className="ml-1"
role="img"
aria-label={t('oracle status not healthy')}
>
⛔
</span>
);
}
if (!maliciousOracleProvider) return null;
const { icon } = getVerifiedStatusIcon(maliciousOracleProvider);
return <Icon size={3} name={icon as IconName} className="ml-1" />;
}
return null;
}, [
@@ -6,13 +6,27 @@ import {
NotificationBanner,
ButtonLink,
} from '@vegaprotocol/ui-toolkit';
import { OracleDialog } from '../oracle-dialog';
import { oracleStatuses } from './oracle-statuses';
import { OracleDialog } from '../market-info';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
export const OracleBanner = ({ marketId }: { marketId: string }) => {
const [open, onChange] = useState(false);
const { data: settlementOracle } = useMarketOracle(marketId);
const { data: tradingTerminationOracle } = useMarketOracle(
const settlementOracle = useMarketOracle(marketId);
const tradingTerminationOracle = useMarketOracle(
marketId,
'dataSourceSpecForTradingTermination'
);
@@ -22,7 +36,15 @@ export const OracleBanner = ({ marketId }: { marketId: string }) => {
} else if (tradingTerminationOracle?.provider.oracle.status !== 'GOOD') {
maliciousOracle = tradingTerminationOracle;
}
if (!maliciousOracle) return null;
if (!settlementOracle && !tradingTerminationOracle) {
return (
<NotificationBanner intent={Intent.Primary}>
<div>{t('There is no oracle for this market.')} </div>
</NotificationBanner>
);
}
const { provider } = maliciousOracle;
return (
@@ -1,16 +0,0 @@
import { t } from '@vegaprotocol/i18n';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
@@ -1 +0,0 @@
export * from './oracle-dialog';
@@ -1,35 +0,0 @@
import { Dialog } from '@vegaprotocol/ui-toolkit';
import {
OracleProfileTitle,
OracleFullProfile,
} from '../../components/oracle-full-profile';
import { useOracleMarkets } from '../../hooks';
import type { Provider } from '../../oracle-schema';
export const OracleDialog = ({
provider,
dataSourceSpecId,
open,
onChange,
}: {
dataSourceSpecId: string;
provider: Provider;
open: boolean;
onChange?: (isOpen: boolean) => void;
}) => {
const oracleMarkets = useOracleMarkets(provider);
return (
<Dialog
title={<OracleProfileTitle provider={provider} />}
aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
>
<OracleFullProfile
provider={provider}
dataSourceSpecId={dataSourceSpecId}
markets={oracleMarkets}
/>
</Dialog>
);
};
@@ -1 +1,2 @@
export * from './oracle-full-profile.stories';
export * from './oracle-full-profile';
@@ -9,7 +9,7 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { oracleStatuses } from '../oracle-banner/oracle-statuses';
import { oracleStatuses } from '../oracle-banner';
import type { IconName } from '@blueprintjs/icons';
import classNames from 'classnames';
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
@@ -1,6 +1,6 @@
import { renderHook } from '@testing-library/react';
import { useMarketOracle } from './use-market-oracle';
import type { MarketFieldsFragment } from '../__generated__/markets';
import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo';
import type { Provider } from '../oracle-schema';
const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL';
@@ -10,42 +10,43 @@ const key = 'key';
const dataSourceSpecId = 'dataSourceSpecId';
const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL }));
const mockMarket = jest.fn<{ data: MarketFieldsFragment | null }, unknown[]>(
() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
const mockDataProvider = jest.fn<
{ data: MarketInfoQuery['market'] },
unknown[]
>(() => ({
data: {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: {
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
},
signers: [
{
signer: {
__typename: 'ETHAddress',
address,
},
{
signer: {
__typename: 'PubKey',
key,
},
},
{
signer: {
__typename: 'PubKey',
key,
},
],
},
},
],
},
},
},
},
},
},
} as MarketFieldsFragment,
})
);
},
} as MarketInfoQuery['market'],
}));
const mockOracleProofs = jest.fn<{ data?: Provider[] }, unknown[]>(() => ({}));
@@ -53,8 +54,9 @@ jest.mock('@vegaprotocol/environment', () => ({
useEnvironment: jest.fn((args) => mockEnvironment()),
}));
jest.mock('../markets-provider', () => ({
useMarket: jest.fn((args) => mockMarket()),
jest.mock('@vegaprotocol/data-provider', () => ({
...jest.requireActual('@vegaprotocol/data-provider'),
useDataProvider: jest.fn((args) => mockDataProvider()),
}));
jest.mock('./use-oracle-proofs', () => ({
@@ -64,15 +66,15 @@ jest.mock('./use-oracle-proofs', () => ({
const marketId = 'marketId';
describe('useMarketOracle', () => {
it('returns undefined if no market info present', () => {
mockMarket.mockReturnValueOnce({ data: null });
mockDataProvider.mockReturnValueOnce({ data: null });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data).toBeUndefined();
expect(result.current).toBeUndefined();
});
it('returns undefined if no oracle proofs present', () => {
mockOracleProofs.mockReturnValueOnce({ data: undefined });
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data).toBeUndefined();
expect(result.current).toBeUndefined();
});
it('returns oracle matched by eth_address', () => {
@@ -100,8 +102,8 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
});
it('returns oracle matching by public_key', () => {
@@ -129,7 +131,7 @@ describe('useMarketOracle', () => {
data,
});
const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.data?.provider).toBe(data[1]);
expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
});
});
+14 -21
View File
@@ -1,7 +1,7 @@
import { useEnvironment } from '@vegaprotocol/environment';
import { useOracleProofs } from './use-oracle-proofs';
import { useMarket } from '../markets-provider';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoProvider } from '../components/market-info/market-info-data-provider';
import { useMemo } from 'react';
import type { Provider } from '../oracle-schema';
import type { DataSourceSpecFragment } from '../__generated__/OracleMarketsSpec';
@@ -41,30 +41,23 @@ export const useMarketOracle = (
dataSourceType:
| 'dataSourceSpecForSettlementData'
| 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData'
): {
data?: {
provider: NonNullable<ReturnType<typeof getMatchingOracleProvider>>;
dataSourceSpecId: string;
};
loading?: boolean;
} => {
) => {
const { ORACLE_PROOFS_URL } = useEnvironment();
const { data: market, loading: marketLoading } = useMarket(marketId);
const { data: providers, loading: providersLoading } =
useOracleProofs(ORACLE_PROOFS_URL);
const { data: marketInfo } = useDataProvider({
dataProvider: marketInfoProvider,
variables: { marketId },
});
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
return useMemo(() => {
if (marketLoading || providersLoading) {
return { loading: true };
}
if (!providers || !market) {
return { data: undefined };
if (!providers || !marketInfo) {
return undefined;
}
const dataSourceSpec =
market.tradableInstrument.instrument.product[dataSourceType];
marketInfo.tradableInstrument.instrument.product[dataSourceType];
const provider = getMatchingOracleProvider(dataSourceSpec.data, providers);
if (provider) {
return { data: { provider, dataSourceSpecId: dataSourceSpec.id } };
return { provider, dataSourceSpecId: dataSourceSpec.id };
}
return { data: undefined };
}, [market, dataSourceType, providers, marketLoading, providersLoading]);
return undefined;
}, [marketInfo, dataSourceType, providers]);
};
+2 -20
View File
@@ -64,44 +64,26 @@ export const createMarketFragment = (
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceSpec',
id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f',
id: 'oracleId',
data: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionExternal',
sourceType: {
__typename: 'DataSourceSpecConfiguration',
signers: [
{
__typename: 'Signer',
signer: {
__typename: 'PubKey',
key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61',
},
},
],
},
},
},
@@ -1,5 +1,4 @@
import classNames from 'classnames';
import { useMemo } from 'react';
import Highlighter from 'react-syntax-highlighter';
export const SyntaxHighlighter = ({
@@ -9,13 +8,6 @@ export const SyntaxHighlighter = ({
data: unknown;
size?: 'smaller' | 'default';
}) => {
const parsedData = useMemo(() => {
try {
return JSON.stringify(data, null, ' ');
} catch (e) {
return 'Unable to parse data';
}
}, [data]);
return (
<div
className={classNames('syntax-highlighter-wrapper', {
@@ -23,7 +15,7 @@ export const SyntaxHighlighter = ({
})}
>
<Highlighter language="json" useInlineStyles={false}>
{parsedData}
{JSON.stringify(data, null, ' ')}
</Highlighter>
</div>
);