feat(trading): add oracle dialog trigger to oracle banner, display status reas… (#3727)

This commit is contained in:
Bartłomiej Głownia 2023-05-15 17:20:30 +02:00 committed by GitHub
parent 1a67c41bb4
commit c4cec47b0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
84 changed files with 252 additions and 359 deletions

View File

@ -7,6 +7,9 @@ const marketTitle = 'accordion-title';
const externalLink = 'external-link'; const externalLink = 'external-link';
const accordionContent = 'accordion-content'; const accordionContent = 'accordion-content';
const providerName = 'provider-name'; const providerName = 'provider-name';
const oracleBannerStatus = 'oracle-banner-status';
const oracleBannerDialogTrigger = 'oracle-banner-dialog-trigger';
const oracleFullProfile = 'oracle-full-profile';
describe('market info is displayed', { tags: '@smoke' }, () => { describe('market info is displayed', { tags: '@smoke' }, () => {
beforeEach(() => { beforeEach(() => {
@ -29,7 +32,11 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
it('show oracle banner', () => { it('show oracle banner', () => {
cy.getByTestId(marketTitle).contains('Oracle').click(); cy.getByTestId(marketTitle).contains('Oracle').click();
cy.getByTestId('oracle-status').should('contain.text', 'COMPROMISED'); cy.getByTestId(oracleBannerStatus).should('contain.text', 'COMPROMISED');
cy.getByTestId(oracleBannerDialogTrigger)
.should('contain.text', 'Show more')
.click();
cy.getByTestId(oracleFullProfile).should('exist');
}); });
it('current fees displayed', () => { it('current fees displayed', () => {

View File

@ -1,7 +1,7 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress'; import { aliasGQLQuery } from '@vegaprotocol/cypress';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
import type { CyHttpMessages } from 'cypress/types/net-stubbing'; import type { CyHttpMessages } from 'cypress/types/net-stubbing';
import type { Provider, Status } from '@vegaprotocol/oracles'; import type { Provider, Status } from '@vegaprotocol/market-info';
import { import {
accountsQuery, accountsQuery,
assetQuery, assetQuery,

View File

@ -0,0 +1,6 @@
function ReactMarkdown({ children }) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default ReactMarkdown;

View File

@ -13,7 +13,7 @@ import { memo, useState } from 'react';
import type { ReactNode, ComponentProps } from 'react'; import type { ReactNode, ComponentProps } from 'react';
import { DepthChartContainer } from '@vegaprotocol/market-depth'; import { DepthChartContainer } from '@vegaprotocol/market-depth';
import { CandlesChartContainer } from '@vegaprotocol/candles-chart'; import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
import { OracleBanner } from '../../components/banner'; import { OracleBanner } from '@vegaprotocol/market-info';
import { import {
Tab, Tab,
LocalStoragePersistTabs as Tabs, LocalStoragePersistTabs as Tabs,

View File

@ -6,8 +6,8 @@ import { MarketState } from '@vegaprotocol/types';
import { subDays } from 'date-fns'; import { subDays } from 'date-fns';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles'; import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/market-info';
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles'; import { OracleSpecDataConnectionDocument } from '@vegaprotocol/market-info';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet'; import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet'; import { VegaWalletContext } from '@vegaprotocol/wallet';
import type { import type {

View File

@ -2,8 +2,8 @@ import { render, screen } from '@testing-library/react';
import type { Property } from '@vegaprotocol/types'; import type { Property } from '@vegaprotocol/types';
import type { MockedResponse } from '@apollo/client/testing'; import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing'; import { MockedProvider } from '@apollo/client/testing';
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles'; import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/market-info';
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles'; import { OracleSpecDataConnectionDocument } from '@vegaprotocol/market-info';
import type { SettlementPriceCellProps } from './settlement-price-cell'; import type { SettlementPriceCellProps } from './settlement-price-cell';
import { SettlementPriceCell } from './settlement-price-cell'; import { SettlementPriceCell } from './settlement-price-cell';

View File

@ -1,6 +1,6 @@
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment'; import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useOracleSpecBindingData } from '@vegaprotocol/oracles'; import { useOracleSpecBindingData } from '@vegaprotocol/market-info';
import { Link } from '@vegaprotocol/ui-toolkit'; import { Link } from '@vegaprotocol/ui-toolkit';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils'; import { addDecimalsFormatNumber } from '@vegaprotocol/utils';

View File

@ -1,2 +1 @@
export * from './announcement-banner'; export * from './announcement-banner';
export * from './oracle-banner';

View File

@ -1,45 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { useMarketOracle } from '@vegaprotocol/market-info';
import ReactMarkdown from 'react-markdown';
import { Intent, NotificationBanner } from '@vegaprotocol/ui-toolkit';
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 oracle = useMarketOracle(marketId);
if (!oracle || oracle.status === 'GOOD') {
return null;
}
return (
<NotificationBanner intent={Intent.Danger}>
<div>
Oracle status for this market is{' '}
<span data-testId="oracle-status">{oracle.status}</span>.{' '}
{oracleStatuses[oracle.status]}
</div>
{oracle.status_reason ? (
<ReactMarkdown
className="react-markdown-container"
skipHtml={true}
disallowedElements={['img']}
linkTarget="_blank"
>
{oracle.status_reason}
</ReactMarkdown>
) : null}
</NotificationBanner>
);
};

View File

@ -12,13 +12,13 @@ export * from '../fills/src/lib/fills.mock';
export * from '../proposals/src/lib/proposals-data-provider/proposals.mock'; export * from '../proposals/src/lib/proposals-data-provider/proposals.mock';
export * from '../ledger/src/lib/ledger-entries.mock'; export * from '../ledger/src/lib/ledger-entries.mock';
export * from '../market-depth/src/lib/market-depth.mock'; export * from '../market-depth/src/lib/market-depth.mock';
export * from '../market-info/src/components/market-info/market-info.mock'; export * from '../market-info/src/lib/components/market-info/market-info.mock';
export * from '../market-list/src/lib/market-candles.mock'; export * from '../market-list/src/lib/market-candles.mock';
export * from '../market-list/src/lib/market-data.mock'; export * from '../market-list/src/lib/market-data.mock';
export * from '../market-list/src/lib/markets-candles.mock'; export * from '../market-list/src/lib/markets-candles.mock';
export * from '../market-list/src/lib/markets-data.mock'; export * from '../market-list/src/lib/markets-data.mock';
export * from '../market-list/src/lib/markets.mock'; export * from '../market-list/src/lib/markets.mock';
export * from '../oracles/src/lib/oracle-spec-data-connection.mock'; export * from '../market-info/src/lib/oracle-spec-data-connection.mock';
export * from '../orders/src/lib/components/order-data-provider/orders.mock'; export * from '../orders/src/lib/components/order-data-provider/orders.mock';
export * from '../positions/src/lib/positions.mock'; export * from '../positions/src/lib/positions.mock';
export * from '../network-parameters/src/network-params.mock'; export * from '../network-parameters/src/network-params.mock';

View File

@ -0,0 +1,6 @@
function ReactMarkdown({ children }) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default ReactMarkdown;

View File

@ -0,0 +1,6 @@
function ReactMarkdown({ children }) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default ReactMarkdown;

View File

@ -39,6 +39,37 @@
"passWithNoTests": true "passWithNoTests": true
} }
}, },
"storybook": {
"executor": "@nrwl/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"config": {
"configFolder": "libs/market-info/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@nrwl/storybook:build",
"outputs": ["{options.outputPath}"],
"options": {
"uiFramework": "@storybook/react",
"outputPath": "dist/storybook/market-info",
"config": {
"configFolder": "libs/market-info/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-spec": { "build-spec": {
"executor": "@nrwl/workspace:run-commands", "executor": "@nrwl/workspace:run-commands",
"outputs": [], "outputs": [],

View File

@ -1 +0,0 @@
export * from './use-market-oracle';

View File

@ -1,2 +1 @@
export * from './components'; export * from './lib';
export * from './hooks';

View File

@ -2,3 +2,6 @@ export * from './market-info';
export * from './last-24h-price-change'; export * from './last-24h-price-change';
export * from './last-24h-volume'; export * from './last-24h-volume';
export * from './fees-breakdown'; export * from './fees-breakdown';
export * from './oracle-basic-profile';
export * from './oracle-full-profile';
export * from './oracle-banner';

View File

@ -5,7 +5,7 @@ import {
} from '@vegaprotocol/types'; } from '@vegaprotocol/types';
import { DataSourceProof } from './market-info-panels'; import { DataSourceProof } from './market-info-panels';
jest.mock('@vegaprotocol/oracles', () => ({ jest.mock('../../hooks/use-oracle-markets', () => ({
useOracleMarkets: () => [], useOracleMarkets: () => [],
})); }));
@ -35,7 +35,7 @@ describe('DataSourceProof', () => {
providers: [], providers: [],
type: 'termination' as const, type: 'termination' as const,
}; };
render(<DataSourceProof id={''} {...props} />); render(<DataSourceProof dataSourceSpecId={''} {...props} />);
expect( expect(
screen.getByText('No oracle proof for termination') screen.getByText('No oracle proof for termination')
).toBeInTheDocument(); ).toBeInTheDocument();
@ -88,7 +88,7 @@ describe('DataSourceProof', () => {
], ],
type: 'settlementData' as const, type: 'settlementData' as const,
}; };
render(<DataSourceProof id={''} {...props} />); render(<DataSourceProof dataSourceSpecId={''} {...props} />);
expect( expect(
screen.getByText('No oracle proof for settlement data') screen.getByText('No oracle proof for settlement data')
).toBeInTheDocument(); ).toBeInTheDocument();
@ -128,7 +128,7 @@ describe('DataSourceProof', () => {
providers: [], providers: [],
type: 'termination' as const, type: 'termination' as const,
}; };
render(<DataSourceProof id={''} {...props} />); render(<DataSourceProof dataSourceSpecId={''} {...props} />);
expect(screen.getByText('Internal conditions')).toBeInTheDocument(); expect(screen.getByText('Internal conditions')).toBeInTheDocument();
expect( expect(
screen.getByText( screen.getByText(

View File

@ -26,13 +26,13 @@ import type { DataSourceDefinition, SignerKind } from '@vegaprotocol/types';
import { ConditionOperatorMapping } from '@vegaprotocol/types'; import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types'; import { MarketTradingModeMapping } from '@vegaprotocol/types';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import type { Provider } from '@vegaprotocol/oracles'; import type { Provider } from '../../oracle-schema';
import { OracleProfileTitle, OracleFullProfile } from '@vegaprotocol/oracles';
import { import {
OracleBasicProfile, OracleBasicProfile,
useOracleProofs, OracleProfileTitle,
useOracleMarkets, OracleFullProfile,
} from '@vegaprotocol/oracles'; } from '../../components';
import { useOracleProofs, useOracleMarkets } from '../../hooks';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
type PanelProps = Pick< type PanelProps = Pick<
@ -478,7 +478,7 @@ export const OracleInfoPanel = ({
} }
providers={data} providers={data}
type={type} type={type}
id={dataSourceSpecId} dataSourceSpecId={dataSourceSpecId}
/> />
<ExternalLink <ExternalLink
data-testid="oracle-spec-links" data-testid="oracle-spec-links"
@ -500,12 +500,12 @@ export const DataSourceProof = ({
data, data,
providers, providers,
type, type,
id, dataSourceSpecId,
}: { }: {
data: DataSourceDefinition; data: DataSourceDefinition;
providers: Provider[] | undefined; providers: Provider[] | undefined;
type: 'settlementData' | 'termination'; type: 'settlementData' | 'termination';
id: string; dataSourceSpecId: string;
}) => { }) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') { if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers = data.sourceType.sourceType.signers || []; const signers = data.sourceType.sourceType.signers || [];
@ -523,7 +523,7 @@ export const DataSourceProof = ({
providers={providers} providers={providers}
signer={signer} signer={signer}
type={type} type={type}
id={id} dataSourceSpecId={dataSourceSpecId}
/> />
); );
})} })}
@ -554,12 +554,12 @@ const OracleLink = ({
providers, providers,
signer, signer,
type, type,
id, dataSourceSpecId,
}: { }: {
providers: Provider[]; providers: Provider[];
signer: SignerKind; signer: SignerKind;
type: 'settlementData' | 'termination'; type: 'settlementData' | 'termination';
id: string; dataSourceSpecId: string;
}) => { }) => {
const signerProviders = providers.filter((p) => { const signerProviders = providers.filter((p) => {
if (signer.__typename === 'PubKey') { if (signer.__typename === 'PubKey') {
@ -590,7 +590,11 @@ const OracleLink = ({
return ( return (
<div> <div>
{signerProviders.map((provider) => ( {signerProviders.map((provider) => (
<OracleProfile key={id} provider={provider} id={id} /> <OracleProfile
key={dataSourceSpecId}
provider={provider}
dataSourceSpecId={dataSourceSpecId}
/>
))} ))}
</div> </div>
); );
@ -611,35 +615,46 @@ const NoOracleProof = ({
); );
}; };
const OracleProfile = ({ export const OracleDialog = ({
provider, provider,
id, dataSourceSpecId,
open,
onChange,
}: { }: {
dataSourceSpecId: string;
provider: Provider; provider: Provider;
id: string; open: boolean;
onChange?: (isOpen: boolean) => void;
}) => { }) => {
const [dialogOpen, setDialogOpen] = useState(false);
const oracleMarkets = useOracleMarkets(provider); const oracleMarkets = useOracleMarkets(provider);
return ( return (
<div key={provider.name}>
<OracleBasicProfile
provider={provider}
onClick={() => setDialogOpen(!dialogOpen)}
markets={oracleMarkets}
/>
<Dialog <Dialog
title={<OracleProfileTitle provider={provider} />} title={<OracleProfileTitle provider={provider} />}
open={dialogOpen}
onChange={() => setDialogOpen(!dialogOpen)}
aria-labelledby="oracle-proof-dialog" aria-labelledby="oracle-proof-dialog"
open={open}
onChange={onChange}
> >
<OracleFullProfile <OracleFullProfile
provider={provider} provider={provider}
id={id} dataSourceSpecId={dataSourceSpecId}
key={id}
markets={oracleMarkets} markets={oracleMarkets}
/> />
</Dialog> </Dialog>
);
};
const OracleProfile = (props: {
provider: Provider;
dataSourceSpecId: string;
}) => {
const [open, onChange] = useState(false);
return (
<div key={props.provider.name}>
<OracleBasicProfile
provider={props.provider}
onClick={() => onChange(!open)}
/>
<OracleDialog {...props} open={open} onChange={onChange} />
</div> </div>
); );
}; };

View File

@ -0,0 +1 @@
export * from './oracle-banner';

View File

@ -0,0 +1,53 @@
import { useState } from 'react';
import { t } from '@vegaprotocol/i18n';
import { useMarketOracle } from '../../hooks';
import {
Intent,
NotificationBanner,
ButtonLink,
} from '@vegaprotocol/ui-toolkit';
import { OracleDialog } from '../market-info';
export const oracleStatuses = {
UNKNOWN: t(
"This public key's proofs have not been verified yet, or no proofs have been provided yet."
),
GOOD: t("This public key's proofs have been verified."),
SUSPICIOUS: t(
'This public key is suspected to be acting in bad faith, pending investigation.'
),
MALICIOUS: t('This public key has been observed acting in bad faith.'),
RETIRED: t('This public key is no longer in use.'),
COMPROMISED: t(
'This public key is no longer in the control of its original owners.'
),
};
export const OracleBanner = ({ marketId }: { marketId: string }) => {
const [open, onChange] = useState(false);
const oracle = useMarketOracle(marketId);
if (!oracle || oracle.provider.oracle.status === 'GOOD') {
return null;
}
const { provider } = oracle;
return (
<>
<OracleDialog open={open} onChange={onChange} {...oracle} />
<NotificationBanner intent={Intent.Danger}>
<div>
Oracle status for this market is{' '}
<span data-testid="oracle-banner-status">
{provider.oracle.status}
</span>
. {oracleStatuses[provider.oracle.status]}{' '}
<ButtonLink
onClick={() => onChange(!open)}
data-testid="oracle-banner-dialog-trigger"
>
{t('Show more')}
</ButtonLink>
</div>
</NotificationBanner>
</>
);
};

View File

@ -31,7 +31,11 @@ describe('OracleFullProfile', () => {
it('should render successfully', () => { it('should render successfully', () => {
const component = ( const component = (
<OracleFullProfile provider={testProvider} id={''} markets={[]} /> <OracleFullProfile
provider={testProvider}
dataSourceSpecId={''}
markets={[]}
/>
); );
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
@ -40,7 +44,7 @@ describe('OracleFullProfile', () => {
render( render(
<OracleFullProfile <OracleFullProfile
provider={testProvider} provider={testProvider}
id={'oracle-id'} dataSourceSpecId={'oracle-id'}
markets={[]} markets={[]}
/> />
); );

View File

@ -7,7 +7,7 @@ export default {
} as Meta; } as Meta;
const Template: Story = (args) => ( const Template: Story = (args) => (
<OracleFullProfile provider={args['provider']} id="4578" /> <OracleFullProfile provider={args['provider']} dataSourceSpecId="4578" />
); );
export const OraclePrimary = Template.bind({}); export const OraclePrimary = Template.bind({});

View File

@ -2,19 +2,19 @@ import { t } from '@vegaprotocol/i18n';
import type { Provider } from '../../oracle-schema'; import type { Provider } from '../../oracle-schema';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types'; import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
import { import {
ButtonLink,
ExternalLink, ExternalLink,
Icon, Icon,
Intent, Intent,
VegaIcon, VegaIcon,
VegaIconNames, VegaIconNames,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import { oracleStatuses } from '../oracle-banner';
import type { IconName } from '@blueprintjs/icons'; import type { IconName } from '@blueprintjs/icons';
import classNames from 'classnames'; import classNames from 'classnames';
import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile'; import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile';
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleMarketsSpec'; import type { OracleMarketSpecFieldsFragment } from '../../__generated__/OracleMarketsSpec';
import { useState } from 'react'; import ReactMarkdown from 'react-markdown';
export const OracleProfileTitle = ({ provider }: { provider: Provider }) => { export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
const { icon, intent } = getVerifiedStatusIcon(provider); const { icon, intent } = getVerifiedStatusIcon(provider);
@ -49,18 +49,35 @@ export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
); );
}; };
const OracleStatus = ({ oracle }: { oracle: Provider['oracle'] }) => (
<div>
{t(`Oracle status`)}: {oracle.status}. {oracleStatuses[oracle.status]}
{oracle.status_reason ? (
<div>
<ReactMarkdown
className="react-markdown-container"
skipHtml={true}
disallowedElements={['img']}
linkTarget="_blank"
>
{oracle.status_reason}
</ReactMarkdown>
</div>
) : null}
</div>
);
export const OracleFullProfile = ({ export const OracleFullProfile = ({
provider, provider,
id, dataSourceSpecId,
markets: oracleMarkets, markets: oracleMarkets,
}: { }: {
provider: Provider; provider: Provider;
id: string; dataSourceSpecId: string;
markets?: OracleMarketSpecFieldsFragment[] | undefined; markets?: OracleMarketSpecFieldsFragment[] | undefined;
}) => { }) => {
const { message } = getVerifiedStatusIcon(provider); const { message } = getVerifiedStatusIcon(provider);
const { VEGA_EXPLORER_URL } = useEnvironment(); const { VEGA_EXPLORER_URL } = useEnvironment();
const [showMore, setShowMore] = useState(false);
const links = provider.proofs const links = provider.proofs
.filter((proof) => proof.format === 'url' && proof.available === true) .filter((proof) => proof.format === 'url' && proof.available === true)
@ -71,30 +88,24 @@ export const OracleFullProfile = ({
})); }));
return ( return (
<div className="flex flex-col text-sm"> <div className="flex flex-col text-sm" data-testid="oracle-full-profile">
<div className="dark:text-vega-light-300 text-vega-dark-300"> <div className="dark:text-vega-light-300 text-vega-dark-300">
<p className=" pb-2">{message}</p> {provider.oracle.status !== 'GOOD' ? (
{!showMore && ( <div className="mb-2">
<p className="pb-2"> <OracleStatus oracle={provider.oracle} />
{provider.description_markdown.slice(0, 100)} </div>
{'... '} ) : null}
<span className="ml-2"> <div className="mb-2">{message}</div>
<ButtonLink onClick={() => setShowMore(!showMore)}> <div className="mb-2">
Read more <ReactMarkdown
</ButtonLink> className="react-markdown-container"
</span> skipHtml={true}
</p> disallowedElements={['img']}
)} linkTarget="_blank"
{showMore && ( >
<p className="pb-2">
{provider.description_markdown} {provider.description_markdown}
<span className="ml-2"> </ReactMarkdown>
<ButtonLink onClick={() => setShowMore(!showMore)}> </div>
Show less
</ButtonLink>
</span>
</p>
)}
</div> </div>
<div className="grid grid-cols-2 gap-6"> <div className="grid grid-cols-2 gap-6">
<div className="col-span-1"> <div className="col-span-1">
@ -132,9 +143,9 @@ export const OracleFullProfile = ({
<p className="dark:text-vega-light-300 text-vega-dark-300 uppercase"> <p className="dark:text-vega-light-300 text-vega-dark-300 uppercase">
{t('Details')} {t('Details')}
</p> </p>
{id && ( {dataSourceSpecId && (
<ExternalLink <ExternalLink
href={`${VEGA_EXPLORER_URL}/oracles/${id}`} href={`${VEGA_EXPLORER_URL}/oracles/${dataSourceSpecId}`}
data-testid="block-explorer-link" data-testid="block-explorer-link"
> >
{t('Block explorer')} {t('Block explorer')}

View File

@ -1,3 +1,4 @@
export * from './use-market-oracle';
export * from './use-oracle-markets'; export * from './use-oracle-markets';
export * from './use-oracle-proofs'; export * from './use-oracle-proofs';
export * from './use-oracle-spec-binding-data'; export * from './use-oracle-spec-binding-data';

View File

@ -1,12 +1,13 @@
import { renderHook } from '@testing-library/react'; import { renderHook } from '@testing-library/react';
import { useMarketOracle } from './use-market-oracle'; import { useMarketOracle } from './use-market-oracle';
import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo'; import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo';
import type { Provider } from '@vegaprotocol/oracles'; import type { Provider } from '../oracle-schema';
const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL'; const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL';
const address = 'address'; const address = 'address';
const key = 'key'; const key = 'key';
const dataSourceSpecId = 'dataSourceSpecId';
const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL })); const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL }));
const mockDataProvider = jest.fn< const mockDataProvider = jest.fn<
@ -18,6 +19,7 @@ const mockDataProvider = jest.fn<
instrument: { instrument: {
product: { product: {
dataSourceSpecForSettlementData: { dataSourceSpecForSettlementData: {
id: dataSourceSpecId,
data: { data: {
sourceType: { sourceType: {
__typename: 'DataSourceDefinitionExternal', __typename: 'DataSourceDefinitionExternal',
@ -57,7 +59,7 @@ jest.mock('@vegaprotocol/data-provider', () => ({
useDataProvider: jest.fn((args) => mockDataProvider()), useDataProvider: jest.fn((args) => mockDataProvider()),
})); }));
jest.mock('@vegaprotocol/oracles', () => ({ jest.mock('./use-oracle-proofs', () => ({
useOracleProofs: jest.fn((args) => mockOracleProofs()), useOracleProofs: jest.fn((args) => mockOracleProofs()),
})); }));
@ -100,7 +102,8 @@ describe('useMarketOracle', () => {
data, data,
}); });
const { result } = renderHook(() => useMarketOracle(marketId)); const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current).toBe(data[1].oracle); expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
}); });
it('returns oracle matching by public_key', () => { it('returns oracle matching by public_key', () => {
@ -128,6 +131,7 @@ describe('useMarketOracle', () => {
data, data,
}); });
const { result } = renderHook(() => useMarketOracle(marketId)); const { result } = renderHook(() => useMarketOracle(marketId));
expect(result.current).toBe(data[1].oracle); expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId);
expect(result.current?.provider).toBe(data[1]);
}); });
}); });

View File

@ -1,5 +1,5 @@
import { useEnvironment } from '@vegaprotocol/environment'; import { useEnvironment } from '@vegaprotocol/environment';
import { useOracleProofs } from '@vegaprotocol/oracles'; import { useOracleProofs } from './use-oracle-proofs';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoProvider } from '../components/market-info/market-info-data-provider'; import { marketInfoProvider } from '../components/market-info/market-info-data-provider';
import { useMemo } from 'react'; import { useMemo } from 'react';
@ -15,10 +15,11 @@ export const useMarketOracle = (marketId: string) => {
if (!data || !marketInfo) { if (!data || !marketInfo) {
return undefined; return undefined;
} }
const dataSource = const dataSourceSpec =
marketInfo.tradableInstrument.instrument.product marketInfo.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data; .dataSourceSpecForSettlementData;
return data.find((provider) => const { data: dataSource, id: dataSourceSpecId } = dataSourceSpec;
const provider = data.find((provider) =>
provider.proofs.some((proof) => { provider.proofs.some((proof) => {
if ( if (
proof.type === 'eth_address' && proof.type === 'eth_address' &&
@ -42,6 +43,10 @@ export const useMarketOracle = (marketId: string) => {
} }
return false; return false;
}) })
)?.oracle; );
if (provider) {
return { provider, dataSourceSpecId };
}
return undefined;
}, [data, marketInfo]); }, [data, marketInfo]);
}; };

View File

@ -1,7 +1,4 @@
import '@testing-library/jest-dom'; import '@testing-library/jest-dom';
import 'jest-canvas-mock';
import ResizeObserver from 'resize-observer-polyfill'; import ResizeObserver from 'resize-observer-polyfill';
import { defaultFallbackInView } from 'react-intersection-observer';
defaultFallbackInView(true);
global.ResizeObserver = ResizeObserver; global.ResizeObserver = ResizeObserver;

View File

@ -19,6 +19,9 @@
}, },
{ {
"path": "./tsconfig.spec.json" "path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
} }
] ]
} }

View File

@ -6,7 +6,7 @@
}, },
"files": [ "files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts", "../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts" "../../node_modules/@nrwl/next/typings/image.d.ts"
], ],
"exclude": [ "exclude": [
"**/*.spec.ts", "**/*.spec.ts",
@ -17,6 +17,10 @@
"**/*.test.js", "**/*.test.js",
"**/*.spec.jsx", "**/*.spec.jsx",
"**/*.test.jsx", "**/*.test.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx",
"jest.config.ts" "jest.config.ts"
], ],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]

View File

@ -15,6 +15,8 @@
"**/*.test.jsx", "**/*.test.jsx",
"**/*.spec.jsx", "**/*.spec.jsx",
"**/*.d.ts", "**/*.d.ts",
"jest.config.ts" "jest.config.ts",
"../../index.d.ts",
"src/lib/use-oracle-spec-binding-data.spec.tsx"
] ]
} }

View File

@ -1,12 +0,0 @@
{
"presets": [
[
"@nrwl/react/babel",
{
"runtime": "automatic",
"useBuiltIns": "usage"
}
]
],
"plugins": []
}

View File

@ -1,18 +0,0 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}

View File

@ -1,7 +0,0 @@
# oracles
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test oracles` to execute the unit tests via [Jest](https://jestjs.io).

View File

@ -1,11 +0,0 @@
/* eslint-disable */
export default {
displayName: 'oracles',
preset: '../../jest.preset.js',
transform: {
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/oracles',
setupFilesAfterEnv: ['./src/setup-tests.ts'],
};

View File

@ -1,4 +0,0 @@
{
"name": "@vegaprotocol/oracles",
"version": "0.0.1"
}

View File

@ -1,81 +0,0 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/oracles/src",
"projectType": "library",
"tags": [],
"targets": {
"build": {
"executor": "@nrwl/web:rollup",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/libs/oracles",
"tsConfig": "libs/oracles/tsconfig.lib.json",
"project": "libs/oracles/package.json",
"entryFile": "libs/oracles/src/index.ts",
"external": ["react/jsx-runtime"],
"rollupConfig": "@nrwl/react/plugins/bundle-rollup",
"compiler": "babel",
"assets": [
{
"glob": "libs/oracles/README.md",
"input": ".",
"output": "."
}
]
}
},
"lint": {
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["libs/oracles/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/libs/oracles"],
"options": {
"jestConfig": "libs/oracles/jest.config.ts",
"passWithNoTests": true
}
},
"storybook": {
"executor": "@nrwl/storybook:storybook",
"options": {
"uiFramework": "@storybook/react",
"port": 4400,
"config": {
"configFolder": "libs/oracles/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-storybook": {
"executor": "@nrwl/storybook:build",
"outputs": ["{options.outputPath}"],
"options": {
"uiFramework": "@storybook/react",
"outputPath": "dist/storybook/oracles",
"config": {
"configFolder": "libs/oracles/.storybook"
}
},
"configurations": {
"ci": {
"quiet": true
}
}
},
"build-spec": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./libs/oracles/tsconfig.spec.json"
}
}
}
}

View File

@ -1 +0,0 @@
export * from './lib';

View File

@ -1,2 +0,0 @@
export * from './oracle-basic-profile';
export * from './oracle-full-profile';

View File

@ -1,4 +0,0 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;

View File

@ -1,27 +0,0 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"include": [],
"references": [
{
"path": "./tsconfig.lib.json"
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
}
]
}

View File

@ -1,27 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/next/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
"**/*.test.ts",
"**/*.spec.tsx",
"**/*.test.tsx",
"**/*.spec.js",
"**/*.test.js",
"**/*.spec.jsx",
"**/*.test.jsx",
"**/*.stories.ts",
"**/*.stories.js",
"**/*.stories.jsx",
"**/*.stories.tsx",
"jest.config.ts"
],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

View File

@ -1,22 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"module": "commonjs",
"types": ["jest", "node", "@testing-library/jest-dom"]
},
"include": [
"**/*.test.ts",
"**/*.spec.ts",
"**/*.test.tsx",
"**/*.spec.tsx",
"**/*.test.js",
"**/*.spec.js",
"**/*.test.jsx",
"**/*.spec.jsx",
"**/*.d.ts",
"jest.config.ts",
"../../index.d.ts",
"src/lib/use-oracle-spec-binding-data.spec.tsx"
]
}

View File

@ -40,7 +40,6 @@
"libs/network-parameters/src/index.ts" "libs/network-parameters/src/index.ts"
], ],
"@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"], "@vegaprotocol/network-stats": ["libs/network-stats/src/index.ts"],
"@vegaprotocol/oracles": ["libs/oracles/src/index.ts"],
"@vegaprotocol/orders": ["libs/orders/src/index.ts"], "@vegaprotocol/orders": ["libs/orders/src/index.ts"],
"@vegaprotocol/positions": ["libs/positions/src/index.ts"], "@vegaprotocol/positions": ["libs/positions/src/index.ts"],
"@vegaprotocol/proposals": ["libs/proposals/src/index.ts"], "@vegaprotocol/proposals": ["libs/proposals/src/index.ts"],

View File

@ -28,7 +28,6 @@
"network-info": "libs/network-info", "network-info": "libs/network-info",
"network-parameters": "libs/network-parameters", "network-parameters": "libs/network-parameters",
"network-stats": "libs/network-stats", "network-stats": "libs/network-stats",
"oracles": "libs/oracles",
"orders": "libs/orders", "orders": "libs/orders",
"positions": "libs/positions", "positions": "libs/positions",
"proposals": "libs/proposals", "proposals": "libs/proposals",