feat: highlight negative oracle statuses in market lists

This commit is contained in:
Bartłomiej Głownia
2023-05-18 08:21:10 +02:00
parent 69c6db8ec2
commit de3d6cbafb
5 changed files with 154 additions and 39 deletions
+1
View File
@@ -1 +1,2 @@
export * from './OracleMarketsSpec';
export * from './OracleSpecDataConnection';
@@ -3,10 +3,42 @@ import { useOracleProofs } from './use-oracle-proofs';
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';
export const getMatchingOracleProvider = (
dataSourceSpec: DataSourceSpecFragment,
providers: Provider[]
) =>
providers.find((provider) =>
provider.proofs.some((proof) => {
if (
proof.type === 'eth_address' &&
dataSourceSpec.sourceType.__typename === 'DataSourceDefinitionExternal'
) {
return dataSourceSpec.sourceType.sourceType.signers?.some(
(signer) =>
signer.signer.__typename === 'ETHAddress' &&
signer.signer.address === proof.eth_address
);
}
if (
proof.type === 'public_key' &&
dataSourceSpec.sourceType.__typename === 'DataSourceDefinitionExternal'
) {
return dataSourceSpec.sourceType.sourceType.signers?.some(
(signer) =>
signer.signer.__typename === 'PubKey' &&
signer.signer.key === proof.public_key
);
}
return false;
})
);
export const useMarketOracle = (
marketId: string,
dataSourceSpecKey:
dataSourceType:
| 'dataSourceSpecForSettlementData'
| 'dataSourceSpecForTradingTermination' = 'dataSourceSpecForSettlementData'
) => {
@@ -15,42 +47,17 @@ export const useMarketOracle = (
dataProvider: marketInfoProvider,
variables: { marketId },
});
const { data } = useOracleProofs(ORACLE_PROOFS_URL);
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
return useMemo(() => {
if (!data || !marketInfo) {
if (!providers || !marketInfo) {
return undefined;
}
const dataSourceSpec =
marketInfo.tradableInstrument.instrument.product[dataSourceSpecKey];
const { data: dataSource, id: dataSourceSpecId } = dataSourceSpec;
const provider = data.find((provider) =>
provider.proofs.some((proof) => {
if (
proof.type === 'eth_address' &&
dataSource.sourceType.__typename === 'DataSourceDefinitionExternal'
) {
return dataSource.sourceType.sourceType.signers?.some(
(signer) =>
signer.signer.__typename === 'ETHAddress' &&
signer.signer.address === proof.eth_address
);
}
if (
proof.type === 'public_key' &&
dataSource.sourceType.__typename === 'DataSourceDefinitionExternal'
) {
return dataSource.sourceType.sourceType.signers?.some(
(signer) =>
signer.signer.__typename === 'PubKey' &&
signer.signer.key === proof.public_key
);
}
return false;
})
);
marketInfo.tradableInstrument.instrument.product[dataSourceType];
const provider = getMatchingOracleProvider(dataSourceSpec.data, providers);
if (provider) {
return { provider, dataSourceSpecId };
return { provider, dataSourceSpecId: dataSourceSpec.id };
}
return undefined;
}, [data, dataSourceSpecKey, marketInfo]);
}, [marketInfo, dataSourceType, providers]);
};
+10 -3
View File
@@ -1,14 +1,15 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import { DataSourceSpecFragmentDoc } from '@vegaprotocol/market-info';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
export type MarketFieldsFragment = { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } };
export type MarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
export type MarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradingMode: Types.MarketTradingMode, fees: { __typename?: 'Fees', factors: { __typename?: 'FeeFactors', makerFee: string, infrastructureFee: string, liquidityFee: string } }, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', id: string, name: string, code: string, metadata: { __typename?: 'InstrumentMetadata', tags?: Array<string> | null }, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', id: string, symbol: string, name: string, decimals: number }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } } | { __typename?: 'DataSourceDefinitionInternal' } } }, dataSourceSpecBinding: { __typename?: 'DataSourceSpecToFutureBinding', settlementDataProperty: string, tradingTerminationProperty: string } } } }, marketTimestamps: { __typename?: 'MarketTimestamps', open: any, close: any } } }> } | null };
export const MarketFieldsFragmentDoc = gql`
fragment MarketFields on Market {
@@ -43,9 +44,15 @@ export const MarketFieldsFragmentDoc = gql`
quoteName
dataSourceSpecForTradingTermination {
id
data {
...DataSourceSpec
}
}
dataSourceSpecForSettlementData {
id
data {
...DataSourceSpec
}
}
dataSourceSpecBinding {
settlementDataProperty
@@ -60,7 +67,7 @@ export const MarketFieldsFragmentDoc = gql`
close
}
}
`;
${DataSourceSpecFragmentDoc}`;
export const MarketsDocument = gql`
query Markets {
marketsConnection {
@@ -1,4 +1,9 @@
import { forwardRef } from 'react';
import { forwardRef, useMemo } from 'react';
import { useEnvironment } from '@vegaprotocol/environment';
import {
getMatchingOracleProvider,
useOracleProofs,
} from '@vegaprotocol/market-info';
import { addDecimalsFormatNumber, toBigNum } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import type {
@@ -18,7 +23,7 @@ import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import type { AgGridReact } from 'ag-grid-react';
import * as Schema from '@vegaprotocol/types';
import type { MarketMaybeWithData } from '../../';
import type { MarketMaybeWithData, Market } from '../../';
import { MarketTableActions } from '../../';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
@@ -26,6 +31,74 @@ const { MarketTradingMode, AuctionTrigger } = Schema;
export const getRowId = ({ data }: { data: { id: string } }) => data.id;
const OracleStatus = ({
dataSourceSpecForSettlementData,
dataSourceSpecForTradingTermination,
}: Pick<
Market['tradableInstrument']['instrument']['product'],
'dataSourceSpecForSettlementData' | 'dataSourceSpecForTradingTermination'
>) => {
const { ORACLE_PROOFS_URL } = useEnvironment();
const { data: providers } = useOracleProofs(ORACLE_PROOFS_URL);
return useMemo(() => {
if (providers) {
const settlementDataProvider = getMatchingOracleProvider(
dataSourceSpecForSettlementData.data,
providers
);
const tradingTerminationDataProvider = getMatchingOracleProvider(
dataSourceSpecForTradingTermination.data,
providers
);
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>
);
}
}
return null;
}, [
providers,
dataSourceSpecForSettlementData,
dataSourceSpecForTradingTermination,
]);
};
interface MarketNameCellProps {
value?: string;
data?: MarketMaybeWithData;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
}
const MarketName = (props: MarketNameCellProps) => (
<>
<MarketNameCell {...props} />
{props.data ? (
<OracleStatus
dataSourceSpecForSettlementData={
props.data.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData
}
dataSourceSpecForTradingTermination={
props.data.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination
}
/>
) : null}
</>
);
export const MarketListTable = forwardRef<
AgGridReact,
TypedDataAgGrid<MarketMaybeWithData> & {
@@ -45,14 +118,14 @@ export const MarketListTable = forwardRef<
filterParams: { buttons: ['reset'] },
}}
suppressCellFocus
components={{ PriceFlashCell, MarketNameCell }}
components={{ PriceFlashCell, MarketName }}
storeKey="allMarkets"
{...props}
>
<AgGridColumn
headerName={t('Market')}
field="tradableInstrument.instrument.code"
cellRenderer="MarketNameCell"
cellRenderer="MarketName"
cellRendererParams={{ onMarketClick }}
/>
<AgGridColumn
+27
View File
@@ -1,3 +1,24 @@
fragment DataSourceSpec on DataSourceDefinition {
sourceType {
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
}
}
}
}
}
fragment MarketFields on Market {
id
decimalPlaces
@@ -30,9 +51,15 @@ fragment MarketFields on Market {
quoteName
dataSourceSpecForTradingTermination {
id
data {
...DataSourceSpec
}
}
dataSourceSpecForSettlementData {
id
data {
...DataSourceSpec
}
}
dataSourceSpecBinding {
settlementDataProperty