diff --git a/libs/markets/src/lib/components/market-info/market-info-panels.tsx b/libs/markets/src/lib/components/market-info/market-info-panels.tsx index 606931105..ad66695e9 100644 --- a/libs/markets/src/lib/components/market-info/market-info-panels.tsx +++ b/libs/markets/src/lib/components/market-info/market-info-panels.tsx @@ -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 { Dialog, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit'; +import { ExternalLink, Splash } from '@vegaprotocol/ui-toolkit'; import { addDecimalsFormatNumber, formatNumber, @@ -25,12 +25,9 @@ import { ConditionOperatorMapping } from '@vegaprotocol/types'; import { MarketTradingModeMapping } from '@vegaprotocol/types'; import { useEnvironment } from '@vegaprotocol/environment'; import type { Provider } from '../../oracle-schema'; -import { - OracleBasicProfile, - OracleProfileTitle, - OracleFullProfile, -} from '../../components'; -import { useOracleProofs, useOracleMarkets } from '../../hooks'; +import { OracleBasicProfile } from '../../components/oracle-basic-profile'; +import { useOracleProofs } from '../../hooks'; +import { OracleDialog } from '../oracle-dialog/oracle-dialog'; import { useDataProvider } from '@vegaprotocol/data-provider'; type PanelProps = Pick< @@ -613,34 +610,6 @@ const NoOracleProof = ({ ); }; -export const OracleDialog = ({ - provider, - dataSourceSpecId, - open, - onChange, -}: { - dataSourceSpecId: string; - provider: Provider; - open: boolean; - onChange?: (isOpen: boolean) => void; -}) => { - const oracleMarkets = useOracleMarkets(provider); - return ( - } - aria-labelledby="oracle-proof-dialog" - open={open} - onChange={onChange} - > - - - ); -}; - const OracleProfile = (props: { provider: Provider; dataSourceSpecId: string; diff --git a/libs/markets/src/lib/components/market-info/market-info.mock.ts b/libs/markets/src/lib/components/market-info/market-info.mock.ts index 102597d09..891d126ec 100644 --- a/libs/markets/src/lib/components/market-info/market-info.mock.ts +++ b/libs/markets/src/lib/components/market-info/market-info.mock.ts @@ -143,7 +143,7 @@ export const marketInfoQuery = ( __typename: 'Signer', signer: { __typename: 'PubKey', - key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f', + key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', }, }, ], @@ -164,7 +164,7 @@ export const marketInfoQuery = ( __typename: 'Signer', signer: { __typename: 'PubKey', - key: '69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f', + key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', }, }, ], diff --git a/libs/markets/src/lib/components/markets-container/oracle-status.tsx b/libs/markets/src/lib/components/markets-container/oracle-status.tsx index 9c65dd8a6..3165c6ce7 100644 --- a/libs/markets/src/lib/components/markets-container/oracle-status.tsx +++ b/libs/markets/src/lib/components/markets-container/oracle-status.tsx @@ -1,8 +1,10 @@ import { useMemo } from 'react'; import { useEnvironment } from '@vegaprotocol/environment'; -import { t } from '@vegaprotocol/i18n'; +import { Icon } from '@vegaprotocol/ui-toolkit'; +import type { IconName } from '@blueprintjs/icons'; import { getMatchingOracleProvider, useOracleProofs } from '../../hooks'; import type { Market } from '../../markets-provider'; +import { getVerifiedStatusIcon } from '../oracle-basic-profile'; export const OracleStatus = ({ dataSourceSpecForSettlementData, @@ -23,22 +25,15 @@ export const OracleStatus = ({ dataSourceSpecForTradingTermination.data, providers ); - if ( - (settlementDataProvider && - settlementDataProvider.oracle.status !== 'GOOD') || - (tradingTerminationDataProvider && - tradingTerminationDataProvider.oracle.status !== 'GOOD') - ) { - return ( - - ⛔ - - ); + let maliciousOracleProvider = null; + if (settlementDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = settlementDataProvider; + } else if (tradingTerminationDataProvider?.oracle.status !== 'GOOD') { + maliciousOracleProvider = tradingTerminationDataProvider; } + if (!maliciousOracleProvider) return null; + const { icon } = getVerifiedStatusIcon(maliciousOracleProvider); + return ; } return null; }, [ diff --git a/libs/markets/src/lib/components/oracle-banner/oracle-banner.tsx b/libs/markets/src/lib/components/oracle-banner/oracle-banner.tsx index 6a2781dcf..6ee4d47e4 100644 --- a/libs/markets/src/lib/components/oracle-banner/oracle-banner.tsx +++ b/libs/markets/src/lib/components/oracle-banner/oracle-banner.tsx @@ -6,27 +6,13 @@ import { 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.' - ), -}; +import { OracleDialog } from '../oracle-dialog'; +import { oracleStatuses } from './oracle-statuses'; export const OracleBanner = ({ marketId }: { marketId: string }) => { const [open, onChange] = useState(false); - const settlementOracle = useMarketOracle(marketId); - const tradingTerminationOracle = useMarketOracle( + const { data: settlementOracle } = useMarketOracle(marketId); + const { data: tradingTerminationOracle } = useMarketOracle( marketId, 'dataSourceSpecForTradingTermination' ); @@ -36,15 +22,7 @@ export const OracleBanner = ({ marketId }: { marketId: string }) => { } else if (tradingTerminationOracle?.provider.oracle.status !== 'GOOD') { maliciousOracle = tradingTerminationOracle; } - if (!maliciousOracle) return null; - if (!settlementOracle && !tradingTerminationOracle) { - return ( - -
{t('There is no oracle for this market.')}
-
- ); - } const { provider } = maliciousOracle; return ( diff --git a/libs/markets/src/lib/components/oracle-banner/oracle-statuses.ts b/libs/markets/src/lib/components/oracle-banner/oracle-statuses.ts new file mode 100644 index 000000000..0c5acd37a --- /dev/null +++ b/libs/markets/src/lib/components/oracle-banner/oracle-statuses.ts @@ -0,0 +1,16 @@ +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.' + ), +}; diff --git a/libs/markets/src/lib/components/oracle-dialog/index.tsx b/libs/markets/src/lib/components/oracle-dialog/index.tsx new file mode 100644 index 000000000..e1c0d5707 --- /dev/null +++ b/libs/markets/src/lib/components/oracle-dialog/index.tsx @@ -0,0 +1 @@ +export * from './oracle-dialog'; diff --git a/libs/markets/src/lib/components/oracle-dialog/oracle-dialog.tsx b/libs/markets/src/lib/components/oracle-dialog/oracle-dialog.tsx new file mode 100644 index 000000000..4e79d743e --- /dev/null +++ b/libs/markets/src/lib/components/oracle-dialog/oracle-dialog.tsx @@ -0,0 +1,35 @@ +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 ( + } + aria-labelledby="oracle-proof-dialog" + open={open} + onChange={onChange} + > + + + ); +}; diff --git a/libs/markets/src/lib/components/oracle-full-profile/index.ts b/libs/markets/src/lib/components/oracle-full-profile/index.ts index a16060bc4..31cdc0371 100644 --- a/libs/markets/src/lib/components/oracle-full-profile/index.ts +++ b/libs/markets/src/lib/components/oracle-full-profile/index.ts @@ -1,2 +1 @@ -export * from './oracle-full-profile.stories'; export * from './oracle-full-profile'; diff --git a/libs/markets/src/lib/components/oracle-full-profile/oracle-full-profile.tsx b/libs/markets/src/lib/components/oracle-full-profile/oracle-full-profile.tsx index dc2048149..233ef48f6 100644 --- a/libs/markets/src/lib/components/oracle-full-profile/oracle-full-profile.tsx +++ b/libs/markets/src/lib/components/oracle-full-profile/oracle-full-profile.tsx @@ -9,7 +9,7 @@ import { VegaIcon, VegaIconNames, } from '@vegaprotocol/ui-toolkit'; -import { oracleStatuses } from '../oracle-banner'; +import { oracleStatuses } from '../oracle-banner/oracle-statuses'; import type { IconName } from '@blueprintjs/icons'; import classNames from 'classnames'; import { getLinkIcon, getVerifiedStatusIcon } from '../oracle-basic-profile'; diff --git a/libs/markets/src/lib/hooks/use-market-oracle.spec.ts b/libs/markets/src/lib/hooks/use-market-oracle.spec.ts index 7d70700b9..8981064f2 100644 --- a/libs/markets/src/lib/hooks/use-market-oracle.spec.ts +++ b/libs/markets/src/lib/hooks/use-market-oracle.spec.ts @@ -1,6 +1,6 @@ import { renderHook } from '@testing-library/react'; import { useMarketOracle } from './use-market-oracle'; -import type { MarketInfoQuery } from '../components/market-info/__generated__/MarketInfo'; +import type { MarketFieldsFragment } from '../__generated__/markets'; import type { Provider } from '../oracle-schema'; const ORACLE_PROOFS_URL = 'ORACLE_PROOFS_URL'; @@ -10,43 +10,42 @@ const key = 'key'; const dataSourceSpecId = 'dataSourceSpecId'; const mockEnvironment = jest.fn(() => ({ ORACLE_PROOFS_URL })); -const mockDataProvider = jest.fn< - { data: MarketInfoQuery['market'] }, - unknown[] ->(() => ({ - data: { - tradableInstrument: { - instrument: { - product: { - dataSourceSpecForSettlementData: { - id: dataSourceSpecId, - data: { - sourceType: { - __typename: 'DataSourceDefinitionExternal', +const mockMarket = jest.fn<{ data: MarketFieldsFragment | null }, unknown[]>( + () => ({ + data: { + tradableInstrument: { + instrument: { + product: { + dataSourceSpecForSettlementData: { + id: dataSourceSpecId, + data: { sourceType: { - signers: [ - { - signer: { - __typename: 'ETHAddress', - address, + __typename: 'DataSourceDefinitionExternal', + sourceType: { + signers: [ + { + signer: { + __typename: 'ETHAddress', + address, + }, }, - }, - { - signer: { - __typename: 'PubKey', - key, + { + signer: { + __typename: 'PubKey', + key, + }, }, - }, - ], + ], + }, }, }, }, }, }, }, - }, - } as MarketInfoQuery['market'], -})); + } as MarketFieldsFragment, + }) +); const mockOracleProofs = jest.fn<{ data?: Provider[] }, unknown[]>(() => ({})); @@ -54,9 +53,8 @@ jest.mock('@vegaprotocol/environment', () => ({ useEnvironment: jest.fn((args) => mockEnvironment()), })); -jest.mock('@vegaprotocol/data-provider', () => ({ - ...jest.requireActual('@vegaprotocol/data-provider'), - useDataProvider: jest.fn((args) => mockDataProvider()), +jest.mock('../markets-provider', () => ({ + useMarket: jest.fn((args) => mockMarket()), })); jest.mock('./use-oracle-proofs', () => ({ @@ -66,15 +64,15 @@ jest.mock('./use-oracle-proofs', () => ({ const marketId = 'marketId'; describe('useMarketOracle', () => { it('returns undefined if no market info present', () => { - mockDataProvider.mockReturnValueOnce({ data: null }); + mockMarket.mockReturnValueOnce({ data: null }); const { result } = renderHook(() => useMarketOracle(marketId)); - expect(result.current).toBeUndefined(); + expect(result.current?.data).toBeUndefined(); }); it('returns undefined if no oracle proofs present', () => { mockOracleProofs.mockReturnValueOnce({ data: undefined }); const { result } = renderHook(() => useMarketOracle(marketId)); - expect(result.current).toBeUndefined(); + expect(result.current?.data).toBeUndefined(); }); it('returns oracle matched by eth_address', () => { @@ -102,8 +100,8 @@ describe('useMarketOracle', () => { data, }); const { result } = renderHook(() => useMarketOracle(marketId)); - expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId); - expect(result.current?.provider).toBe(data[1]); + expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId); + expect(result.current?.data?.provider).toBe(data[1]); }); it('returns oracle matching by public_key', () => { @@ -131,7 +129,7 @@ describe('useMarketOracle', () => { data, }); const { result } = renderHook(() => useMarketOracle(marketId)); - expect(result.current?.dataSourceSpecId).toBe(dataSourceSpecId); - expect(result.current?.provider).toBe(data[1]); + expect(result.current?.data?.dataSourceSpecId).toBe(dataSourceSpecId); + expect(result.current?.data?.provider).toBe(data[1]); }); }); diff --git a/libs/markets/src/lib/hooks/use-market-oracle.ts b/libs/markets/src/lib/hooks/use-market-oracle.ts index 5fe264605..1e9fb067e 100644 --- a/libs/markets/src/lib/hooks/use-market-oracle.ts +++ b/libs/markets/src/lib/hooks/use-market-oracle.ts @@ -1,7 +1,7 @@ import { useEnvironment } from '@vegaprotocol/environment'; import { useOracleProofs } from './use-oracle-proofs'; -import { useDataProvider } from '@vegaprotocol/data-provider'; -import { marketInfoProvider } from '../components/market-info/market-info-data-provider'; +import { useMarket } from '../markets-provider'; + import { useMemo } from 'react'; import type { Provider } from '../oracle-schema'; import type { DataSourceSpecFragment } from '../__generated__/OracleMarketsSpec'; @@ -41,23 +41,30 @@ export const useMarketOracle = ( dataSourceType: | 'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData' -) => { +): { + data?: { + provider: NonNullable>; + dataSourceSpecId: string; + }; + loading?: boolean; +} => { const { ORACLE_PROOFS_URL } = useEnvironment(); - const { data: marketInfo } = useDataProvider({ - dataProvider: marketInfoProvider, - variables: { marketId }, - }); - const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL); + const { data: market, loading: marketLoading } = useMarket(marketId); + const { data: providers, loading: providersLoading } = + useOracleProofs(ORACLE_PROOFS_URL); return useMemo(() => { - if (!providers || !marketInfo) { - return undefined; + if (marketLoading || providersLoading) { + return { loading: true }; + } + if (!providers || !market) { + return { data: undefined }; } const dataSourceSpec = - marketInfo.tradableInstrument.instrument.product[dataSourceType]; + market.tradableInstrument.instrument.product[dataSourceType]; const provider = getMatchingOracleProvider(dataSourceSpec.data, providers); if (provider) { - return { provider, dataSourceSpecId: dataSourceSpec.id }; + return { data: { provider, dataSourceSpecId: dataSourceSpec.id } }; } - return undefined; - }, [marketInfo, dataSourceType, providers]); + return { data: undefined }; + }, [market, dataSourceType, providers, marketLoading, providersLoading]); }; diff --git a/libs/markets/src/lib/markets.mock.ts b/libs/markets/src/lib/markets.mock.ts index c2b70f5a2..99affa793 100644 --- a/libs/markets/src/lib/markets.mock.ts +++ b/libs/markets/src/lib/markets.mock.ts @@ -64,26 +64,44 @@ export const createMarketFragment = ( }, dataSourceSpecForTradingTermination: { __typename: 'DataSourceSpec', - id: 'oracleId', + id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f', data: { __typename: 'DataSourceDefinition', sourceType: { __typename: 'DataSourceDefinitionExternal', sourceType: { __typename: 'DataSourceSpecConfiguration', + signers: [ + { + __typename: 'Signer', + signer: { + __typename: 'PubKey', + key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', + }, + }, + ], }, }, }, }, dataSourceSpecForSettlementData: { __typename: 'DataSourceSpec', - id: 'oracleId', + id: 'f028fe5ea7de3890962a05a7163fdde562629af649ed81b8c8902fafb6eef04f', data: { __typename: 'DataSourceDefinition', sourceType: { __typename: 'DataSourceDefinitionExternal', sourceType: { __typename: 'DataSourceSpecConfiguration', + signers: [ + { + __typename: 'Signer', + signer: { + __typename: 'PubKey', + key: '6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61', + }, + }, + ], }, }, },