feat(trading): oracle banner check termination and settlement data oracles (#3707)
This commit is contained in:
parent
1e9ff31fd7
commit
1a274a67c3
@ -199,7 +199,6 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
|||||||
.within(() => {
|
.within(() => {
|
||||||
cy.getByTestId('block-explorer-link').contains('Block explorer');
|
cy.getByTestId('block-explorer-link').contains('Block explorer');
|
||||||
cy.getByTestId('github-link').contains('Oracle repository');
|
cy.getByTestId('github-link').contains('Oracle repository');
|
||||||
cy.getByTestId('verified-accounts').contains('0 proofs of ownership');
|
|
||||||
});
|
});
|
||||||
cy.getByTestId('dialog-close').click();
|
cy.getByTestId('dialog-close').click();
|
||||||
|
|
||||||
|
@ -25,14 +25,30 @@ export const oracleStatuses = {
|
|||||||
|
|
||||||
export const OracleBanner = ({ marketId }: { marketId: string }) => {
|
export const OracleBanner = ({ marketId }: { marketId: string }) => {
|
||||||
const [open, onChange] = useState(false);
|
const [open, onChange] = useState(false);
|
||||||
const oracle = useMarketOracle(marketId);
|
const settlementOracle = useMarketOracle(marketId);
|
||||||
if (!oracle || oracle.provider.oracle.status === 'GOOD') {
|
const tradingTerminationOracle = useMarketOracle(
|
||||||
return null;
|
marketId,
|
||||||
|
'dataSourceSpecForTradingTermination'
|
||||||
|
);
|
||||||
|
let maliciousOracle = null;
|
||||||
|
if (settlementOracle?.provider.oracle.status !== 'GOOD') {
|
||||||
|
maliciousOracle = settlementOracle;
|
||||||
|
} else if (tradingTerminationOracle?.provider.oracle.status !== 'GOOD') {
|
||||||
|
maliciousOracle = tradingTerminationOracle;
|
||||||
}
|
}
|
||||||
const { provider } = oracle;
|
if (!settlementOracle && !tradingTerminationOracle) {
|
||||||
|
return (
|
||||||
|
<NotificationBanner intent={Intent.Primary}>
|
||||||
|
<div>{t('There is no oracle for this market yet.')} </div>
|
||||||
|
</NotificationBanner>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (!maliciousOracle) return null;
|
||||||
|
|
||||||
|
const { provider } = maliciousOracle;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<OracleDialog open={open} onChange={onChange} {...oracle} />
|
<OracleDialog open={open} onChange={onChange} {...maliciousOracle} />
|
||||||
<NotificationBanner intent={Intent.Danger}>
|
<NotificationBanner intent={Intent.Danger}>
|
||||||
<div>
|
<div>
|
||||||
Oracle status for this market is{' '}
|
Oracle status for this market is{' '}
|
||||||
|
@ -2,6 +2,7 @@ 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,
|
||||||
@ -15,6 +16,7 @@ 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 ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
|
export const OracleProfileTitle = ({ provider }: { provider: Provider }) => {
|
||||||
const { icon, intent } = getVerifiedStatusIcon(provider);
|
const { icon, intent } = getVerifiedStatusIcon(provider);
|
||||||
@ -78,6 +80,7 @@ export const OracleFullProfile = ({
|
|||||||
}) => {
|
}) => {
|
||||||
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)
|
||||||
@ -86,6 +89,9 @@ export const OracleFullProfile = ({
|
|||||||
url: 'url' in proof ? proof.url : '',
|
url: 'url' in proof ? proof.url : '',
|
||||||
icon: getLinkIcon(proof.type),
|
icon: getLinkIcon(proof.type),
|
||||||
}));
|
}));
|
||||||
|
const signedMessageProofs = provider.proofs.filter(
|
||||||
|
(proof) => proof.format === 'signed_message' && proof.available === true
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col text-sm" data-testid="oracle-full-profile">
|
<div className="flex flex-col text-sm" data-testid="oracle-full-profile">
|
||||||
@ -103,8 +109,15 @@ export const OracleFullProfile = ({
|
|||||||
disallowedElements={['img']}
|
disallowedElements={['img']}
|
||||||
linkTarget="_blank"
|
linkTarget="_blank"
|
||||||
>
|
>
|
||||||
{provider.description_markdown}
|
{showMore
|
||||||
|
? provider.description_markdown
|
||||||
|
: provider.description_markdown.slice(0, 100) + '...'}
|
||||||
</ReactMarkdown>
|
</ReactMarkdown>
|
||||||
|
<span>
|
||||||
|
<ButtonLink onClick={() => setShowMore(!showMore)}>
|
||||||
|
{!showMore ? t('Read more') : t('Show less')}
|
||||||
|
</ButtonLink>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-6">
|
<div className="grid grid-cols-2 gap-6">
|
||||||
@ -113,9 +126,12 @@ export const OracleFullProfile = ({
|
|||||||
className="dark:text-vega-light-300 text-vega-dark-300 uppercase"
|
className="dark:text-vega-light-300 text-vega-dark-300 uppercase"
|
||||||
data-testid="verified-accounts"
|
data-testid="verified-accounts"
|
||||||
>
|
>
|
||||||
{t('%s proofs of ownership', links.length.toString())}
|
{t('%s %s of ownership', [
|
||||||
|
provider.proofs.length.toString(),
|
||||||
|
provider.proofs.length === 1 ? 'proof' : 'proofs',
|
||||||
|
])}
|
||||||
</p>
|
</p>
|
||||||
{links.length > 0 ? (
|
{provider.proofs.length > 0 ? (
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{links.map((link) => (
|
{links.map((link) => (
|
||||||
<ExternalLink
|
<ExternalLink
|
||||||
@ -132,6 +148,31 @@ export const OracleFullProfile = ({
|
|||||||
</span>
|
</span>
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
))}
|
))}
|
||||||
|
{signedMessageProofs.length > 0 && (
|
||||||
|
<ExternalLink
|
||||||
|
key={'more-proofs'}
|
||||||
|
href={provider.github_link}
|
||||||
|
className="flex align-items-bottom underline text-sm pt-2"
|
||||||
|
>
|
||||||
|
{links.length > 0 ? (
|
||||||
|
<span className="underline">
|
||||||
|
{t('And %s more %s', [
|
||||||
|
signedMessageProofs.length.toString(),
|
||||||
|
signedMessageProofs.length === 1 ? 'proof' : 'proofs',
|
||||||
|
])}{' '}
|
||||||
|
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="underline">
|
||||||
|
{t('Verify %s %s of ownership', [
|
||||||
|
signedMessageProofs.length.toString(),
|
||||||
|
signedMessageProofs.length === 1 ? 'proof' : 'proofs',
|
||||||
|
])}{' '}
|
||||||
|
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</ExternalLink>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<p className="dark:text-vega-light-300 text-vega-dark-300">
|
<p className="dark:text-vega-light-300 text-vega-dark-300">
|
||||||
|
@ -4,7 +4,12 @@ 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';
|
||||||
|
|
||||||
export const useMarketOracle = (marketId: string) => {
|
export const useMarketOracle = (
|
||||||
|
marketId: string,
|
||||||
|
dataSourceSpecKey:
|
||||||
|
| 'dataSourceSpecForSettlementData'
|
||||||
|
| 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData'
|
||||||
|
) => {
|
||||||
const { ORACLE_PROOFS_URL } = useEnvironment();
|
const { ORACLE_PROOFS_URL } = useEnvironment();
|
||||||
const { data: marketInfo } = useDataProvider({
|
const { data: marketInfo } = useDataProvider({
|
||||||
dataProvider: marketInfoProvider,
|
dataProvider: marketInfoProvider,
|
||||||
@ -16,8 +21,7 @@ export const useMarketOracle = (marketId: string) => {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
const dataSourceSpec =
|
const dataSourceSpec =
|
||||||
marketInfo.tradableInstrument.instrument.product
|
marketInfo.tradableInstrument.instrument.product[dataSourceSpecKey];
|
||||||
.dataSourceSpecForSettlementData;
|
|
||||||
const { data: dataSource, id: dataSourceSpecId } = dataSourceSpec;
|
const { data: dataSource, id: dataSourceSpecId } = dataSourceSpec;
|
||||||
const provider = data.find((provider) =>
|
const provider = data.find((provider) =>
|
||||||
provider.proofs.some((proof) => {
|
provider.proofs.some((proof) => {
|
||||||
@ -48,5 +52,5 @@ export const useMarketOracle = (marketId: string) => {
|
|||||||
return { provider, dataSourceSpecId };
|
return { provider, dataSourceSpecId };
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}, [data, marketInfo]);
|
}, [data, dataSourceSpecKey, marketInfo]);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user