feat: highlight negative oracle statuses in market lists
This commit is contained in:
+1
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user