Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into feature/browse-perpetuals

This commit is contained in:
Madalina Raicu
2023-08-25 11:55:51 +01:00
4 changed files with 268 additions and 338 deletions
@@ -14,8 +14,6 @@ import {
SettlementAssetInfoPanel,
} from '@vegaprotocol/markets';
import {
Accordion,
AccordionItem,
Button,
CopyWithTooltip,
Dialog,
@@ -42,6 +40,9 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
})
);
const marketDataHeaderStyles =
'font-alpha calt text-base border-b border-vega-dark-200 mt-2 py-2';
export const ProposalMarketData = ({
marketData,
parentMarketData,
@@ -64,12 +65,6 @@ export const ProposalMarketData = ({
.dataSourceSpecForSettlementData.data as DataSourceDefinition)
: undefined;
const settlementScheduleData =
marketData.tradableInstrument.instrument.product.__typename === 'Perpetual'
? (marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementSchedule.data as DataSourceDefinition)
: undefined;
const parentSettlementData =
parentMarketData?.tradableInstrument.instrument.product.__typename ===
'Future' ||
@@ -79,6 +74,12 @@ export const ProposalMarketData = ({
?.dataSourceSpecForSettlementData?.data
: undefined;
const settlementScheduleData =
marketData.tradableInstrument.instrument.product.__typename === 'Perpetual'
? (marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementSchedule.data as DataSourceDefinition)
: undefined;
const parentSettlementScheduleData =
parentMarketData?.tradableInstrument.instrument.product.__typename ===
'Perpetual'
@@ -87,16 +88,17 @@ export const ProposalMarketData = ({
: undefined;
const terminationData =
'dataSourceSpecForTradingTermination' in
marketData.tradableInstrument.instrument.product &&
(marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition);
marketData.tradableInstrument.instrument.product.__typename === 'Future'
? (marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition)
: undefined;
const parentTerminationData =
parentMarketData &&
'dataSourceSpecForTradingTermination' in
parentMarketData.tradableInstrument.instrument.product &&
parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForTradingTermination?.data;
parentMarketData?.tradableInstrument.instrument.product.__typename ===
'Future'
? parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForTradingTermination?.data
: undefined;
// TODO add settlementScheduleData for Perp Proposal
@@ -112,6 +114,14 @@ export const ProposalMarketData = ({
parentTerminationData !== undefined &&
isEqual(terminationData, parentTerminationData);
const showParentPriceMonitoringBounds =
parentMarketData?.priceMonitoringSettings?.parameters?.triggers !==
undefined &&
!isEqual(
marketData.priceMonitoringSettings?.parameters?.triggers,
parentMarketData?.priceMonitoringSettings?.parameters?.triggers
);
const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers =
@@ -147,172 +157,160 @@ export const ProposalMarketData = ({
</Button>
</div>
<div className="mb-10">
<Accordion>
<AccordionItem
itemId="key-details"
title={t('Key details')}
content={
<KeyDetailsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
<AccordionItem
itemId="instrument"
title={t('Instrument')}
content={
<InstrumentInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
{settlementData &&
terminationData &&
isEqual(
getSigners(settlementData),
getSigners(terminationData)
) ? (
<AccordionItem
itemId="oracles"
title={t('Oracle')}
content={
<h2 className={marketDataHeaderStyles}>{t('Key details')}</h2>
<KeyDetailsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>{t('Instrument')}</h2>
<InstrumentInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
{settlementData &&
terminationData &&
isEqual(getSigners(settlementData), getSigners(terminationData)) ? (
<>
<h2 className={marketDataHeaderStyles}>{t('Oracle')}</h2>
<OracleInfoPanel
market={marketData}
type="settlementData"
parentMarket={
isParentSettlementDataEqual ||
isParentSettlementScheduleDataEqual
? undefined
: parentMarketData
}
/>
</>
) : (
<>
<h2 className={marketDataHeaderStyles}>
{t('Settlement oracle')}
</h2>
<OracleInfoPanel
market={marketData}
type="settlementData"
parentMarket={
isParentSettlementDataEqual ? undefined : parentMarketData
}
/>
{marketData.tradableInstrument.instrument.product.__typename ===
'Future' && (
<div>
<h2 className={marketDataHeaderStyles}>
{t('Termination oracle')}
</h2>
<OracleInfoPanel
market={marketData}
type="settlementData"
type="termination"
parentMarket={
isParentSettlementDataEqual
isParentTerminationDataEqual
? undefined
: parentMarketData
}
/>
}
/>
) : (
<>
<AccordionItem
itemId="settlement-oracle"
title={t('Settlement oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementData"
parentMarket={
isParentSettlementDataEqual
? undefined
: parentMarketData
}
/>
}
/>
</div>
)}
<AccordionItem
itemId="termination-oracle"
title={t('Termination oracle')}
content={
<OracleInfoPanel
market={marketData}
type="termination"
parentMarket={
isParentTerminationDataEqual
? undefined
: parentMarketData
}
/>
}
/>
<AccordionItem
itemId="settlement-schedule-oracle"
title={t('Settlement schedule oracle')}
content={
<OracleInfoPanel
market={marketData}
type="settlementSchedule"
parentMarket={
isParentSettlementScheduleDataEqual
? undefined
: parentMarketData
}
/>
}
/>
</>
)}
{/*Note: successor markets will not differ in their settlement*/}
{/*assets, so no need to pass in parent market data for comparison.*/}
<AccordionItem
itemId="settlement-asset"
title={t('Settlement asset')}
content={<SettlementAssetInfoPanel market={marketData} />}
/>
<AccordionItem
itemId="metadata"
title={t('Metadata')}
content={
<MetadataInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
<AccordionItem
itemId="risk-model"
title={t('Risk model')}
content={
<RiskModelInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
<AccordionItem
itemId="risk-parameters"
title={t('Risk parameters')}
content={
<RiskParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
<AccordionItem
itemId="risk-factors"
title={t('Risk factors')}
content={
<RiskFactorsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
{(
marketData.priceMonitoringSettings?.parameters?.triggers || []
).map((_, triggerIndex) => (
<AccordionItem
itemId={`trigger-${triggerIndex}`}
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
content={
<PriceMonitoringBoundsInfoPanel
{marketData.tradableInstrument.instrument.product.__typename ===
'Perpetual' && (
<div>
<h2 className={marketDataHeaderStyles}>
{t('Settlement schedule oracle')}
</h2>
<OracleInfoPanel
market={marketData}
parentMarket={parentMarketData}
type="settlementSchedule"
parentMarket={
isParentSettlementScheduleDataEqual
? undefined
: parentMarketData
}
/>
</div>
)}
</>
)}
{/*Note: successor markets will not differ in their settlement*/}
{/*assets, so no need to pass in parent market data for comparison.*/}
<h2 className={marketDataHeaderStyles}>{t('Settlement assets')}</h2>
<SettlementAssetInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>{t('Metadata')}</h2>
<MetadataInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>{t('Risk model')}</h2>
<RiskModelInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>{t('Risk parameters')}</h2>
<RiskParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
<h2 className={marketDataHeaderStyles}>{t('Risk factors')}</h2>
<RiskFactorsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
{showParentPriceMonitoringBounds &&
(
parentMarketData?.priceMonitoringSettings?.parameters
?.triggers || []
).map((_, triggerIndex) => (
<>
<h2 className={marketDataHeaderStyles}>
{t(`Parent price monitoring bounds ${triggerIndex + 1}`)}
</h2>
<div className="text-vega-dark-300 line-through">
<PriceMonitoringBoundsInfoPanel
market={parentMarketData}
triggerIndex={triggerIndex}
/>
}
/>
</div>
</>
))}
<AccordionItem
itemId="liquidity-monitoring-parameters"
title={t('Liquidity monitoring parameters')}
content={
<LiquidityMonitoringParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/>
</Accordion>
{(
marketData.priceMonitoringSettings?.parameters?.triggers || []
).map((_, triggerIndex) => (
<>
<h2 className={marketDataHeaderStyles}>
{t(`Price monitoring bounds ${triggerIndex + 1}`)}
</h2>
<PriceMonitoringBoundsInfoPanel
market={marketData}
triggerIndex={triggerIndex}
/>
</>
))}
<h2 className={marketDataHeaderStyles}>
{t('Liquidity monitoring parameters')}
</h2>
<LiquidityMonitoringParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
</div>
</>
)}
@@ -98,7 +98,7 @@ export const Row = ({
<div style={{ wordBreak: 'break-word' }}>
{valueDiffersFromParentMarket ? (
<div className="flex items-center gap-3">
<span className="line-through">
<span className="line-through dark:text-vega-dark-300">
{getFormattedValue(parentValue)}
</span>
<span>{formattedValue}</span>
@@ -1,13 +1,14 @@
import isEqual from 'lodash/isEqual';
import type { ReactNode } from 'react';
import { Fragment, useState } from 'react';
import { useMemo } from 'react';
import { Fragment, useMemo, useState } from 'react';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { marketDataProvider } from '../../market-data-provider';
import { totalFeesPercentage } from '../../market-utils';
import {
ExternalLink,
Intent,
Lozenge,
Splash,
Tooltip,
VegaIcon,
@@ -26,9 +27,15 @@ import type {
} from './market-info-data-provider';
import { Last24hVolume } from '../last-24h-volume';
import BigNumber from 'bignumber.js';
import type { DataSourceDefinition, SignerKind } from '@vegaprotocol/types';
import { ConditionOperatorMapping } from '@vegaprotocol/types';
import { MarketTradingModeMapping } from '@vegaprotocol/types';
import type {
DataSourceDefinition,
MarketTradingMode,
SignerKind,
} from '@vegaprotocol/types';
import {
ConditionOperatorMapping,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import {
DApp,
FLAGS,
@@ -47,9 +54,7 @@ import {
useSuccessorMarketQuery,
} from '../../__generated__';
import { useSuccessorMarketProposalDetailsQuery } from '@vegaprotocol/proposals';
import type { MarketTradingMode } from '@vegaprotocol/types';
import { getQuoteName, getAsset } from '../../market-utils';
import type { Signer } from '@vegaprotocol/types';
import classNames from 'classnames';
import compact from 'lodash/compact';
@@ -564,7 +569,6 @@ export const RiskFactorsInfoPanel = ({
export const PriceMonitoringBoundsInfoPanel = ({
market,
triggerIndex,
parentMarket,
}: MarketInfoProps & {
triggerIndex: number;
}) => {
@@ -573,31 +577,12 @@ export const PriceMonitoringBoundsInfoPanel = ({
variables: { marketId: market.id },
});
const { data: parentData } = useDataProvider({
dataProvider: marketDataProvider,
variables: { marketId: parentMarket?.id || '' },
skip:
!parentMarket ||
!parentMarket?.priceMonitoringSettings?.parameters?.triggers?.[
triggerIndex
],
});
const quoteUnit = getQuoteName(market);
const parentQuoteUnit = parentMarket && getQuoteName(parentMarket);
const isParentQuoteUnitEqual = quoteUnit === parentQuoteUnit;
const trigger =
market.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
const parentTrigger =
parentMarket?.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
const isParentTriggerEqual = isEqual(trigger, parentTrigger);
const bounds = data?.priceMonitoringBounds?.[triggerIndex];
const parentBounds = parentData?.priceMonitoringBounds?.[triggerIndex];
const shouldShowParentData =
isParentQuoteUnitEqual && isParentTriggerEqual && !!parentBounds;
if (!trigger) {
console.error(
@@ -625,14 +610,6 @@ export const PriceMonitoringBoundsInfoPanel = ({
highestPrice: bounds.maxValidPrice,
lowestPrice: bounds.minValidPrice,
}}
parentData={
shouldShowParentData
? {
highestPrice: parentBounds.maxValidPrice,
lowestPrice: parentBounds.minValidPrice,
}
: undefined
}
decimalPlaces={market.decimalPlaces}
assetSymbol={quoteUnit}
/>
@@ -719,43 +696,78 @@ export const OracleInfoPanel = ({
parentDataSourceSpec = parentDataSourceSpec.dataSourceSpec;
}
// We'll only provide successor parent data (if it differs) to the
// DataSourceProof component. Having an old external link struck through
// is unlikely to be useful.
const shouldShowParentData =
parentMarket !== undefined &&
parentDataSourceSpecId !== undefined &&
!isEqual(dataSourceSpec, parentDataSourceSpec);
const wrapperClasses = classNames('mb-4', {
'flex items-center gap-6': shouldShowParentData,
});
return (
<div className="flex flex-col gap-2">
{dataSourceSpec && dataSourceSpecId && (
<DataSourceProof
data-testid="oracle-proof-links"
providers={data}
type={type}
dataSourceSpecId={dataSourceSpecId}
parentDataSourceSpecId={
dataSourceSpecId === parentDataSourceSpecId
? undefined
: parentDataSourceSpecId
}
data={dataSourceSpec}
parentData={
dataSourceSpec === parentDataSourceSpec
? undefined
: parentDataSourceSpec
}
/>
<>
{shouldShowParentData && (
<Lozenge variant={Intent.Primary} className="text-sm">
{t('Updated')}
</Lozenge>
)}
{dataSourceSpecId && (
<ExternalLink
data-testid="oracle-spec-links"
href={`${VEGA_EXPLORER_URL}/oracles/${dataSourceSpecId}`}
>
{type === 'settlementData'
? t('View settlement data specification')
: type === 'settlementSchedule'
? t('View settlement schedule specification')
: t('View termination specification')}
</ExternalLink>
)}
</div>
<div className={wrapperClasses}>
{shouldShowParentData &&
parentDataSourceSpec &&
parentDataSourceSpecId &&
parentProduct && (
<div className="flex flex-col gap-2 text-vega-dark-300 line-through">
<DataSourceProof
data-testid="oracle-proof-links"
data={parentDataSourceSpec}
providers={data}
type={type}
dataSourceSpecId={parentDataSourceSpecId}
/>
{dataSourceSpecId && (
<ExternalLink
data-testid="oracle-spec-links"
href={`${VEGA_EXPLORER_URL}/oracles/${dataSourceSpecId}`}
>
{type === 'settlementData'
? t('View settlement data specification')
: type === 'settlementSchedule'
? t('View settlement schedule specification')
: t('View termination specification')}
</ExternalLink>
)}
</div>
)}
<div className="flex flex-col gap-2">
{dataSourceSpecId && dataSourceSpec && (
<DataSourceProof
data-testid="oracle-proof-links"
data={dataSourceSpec}
providers={data}
type={type}
dataSourceSpecId={dataSourceSpecId}
/>
)}
{dataSourceSpecId && (
<ExternalLink
data-testid="oracle-spec-links"
href={`${VEGA_EXPLORER_URL}/oracles/${dataSourceSpecId}`}
>
{type === 'settlementData'
? t('View settlement data specification')
: type === 'settlementSchedule'
? t('View settlement schedule specification')
: t('View termination specification')}
</ExternalLink>
)}
</div>
</div>
</>
);
};
@@ -764,34 +776,17 @@ export const DataSourceProof = ({
providers,
type,
dataSourceSpecId,
parentData,
parentDataSourceSpecId,
}: {
data: DataSourceDefinition;
providers: Provider[] | undefined;
type: 'settlementData' | 'termination' | 'settlementSchedule';
dataSourceSpecId: string;
parentData?: DataSourceDefinition;
parentDataSourceSpecId?: string;
}) => {
// If this is a successor market, we'll only pass parent data to child
// components for comparison if the data differs from the parent market.
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
const signers =
('signers' in data.sourceType.sourceType &&
data.sourceType.sourceType.signers) ||
[];
let parentSigners: Signer[];
if (
parentData &&
parentData.sourceType.__typename === 'DataSourceDefinitionExternal'
) {
parentSigners =
('signers' in parentData.sourceType.sourceType &&
parentData.sourceType.sourceType?.signers) ||
[];
}
if (!providers?.length) {
return <NoOracleProof type={type} />;
@@ -799,34 +794,15 @@ export const DataSourceProof = ({
return (
<div className="flex flex-col gap-2">
{signers.map(({ signer }, i) => {
const parentSigner = parentSigners?.find(
({ signer: ParentSigner }) =>
ParentSigner.__typename === signer.__typename
)?.signer;
const isParentSignerEqual = isEqual(signer, parentSigner);
return isParentSignerEqual ? (
<OracleLink
key={i}
providers={providers}
signer={signer}
type={type}
dataSourceSpecId={dataSourceSpecId}
/>
) : (
<OracleLink
key={i}
providers={providers}
signer={signer}
type={type}
dataSourceSpecId={dataSourceSpecId}
parentSigner={parentSigner}
parentDataSourceSpecId={parentDataSourceSpecId}
/>
);
})}
{signers.map(({ signer }, i) => (
<OracleLink
key={i}
providers={providers}
signer={signer}
type={type}
dataSourceSpecId={dataSourceSpecId}
/>
))}
</div>
);
}
@@ -939,22 +915,13 @@ const OracleLink = ({
signer,
type,
dataSourceSpecId,
parentSigner,
parentDataSourceSpecId,
}: {
providers: Provider[];
signer: SignerKind;
type: 'settlementData' | 'termination' | 'settlementSchedule';
dataSourceSpecId: string;
parentSigner?: SignerKind;
parentDataSourceSpecId?: string;
}) => {
// If this is a successor market, the parent market data will only have been passed
// in if it differs from the current data.
const signerProviders = getSignerProviders(signer, providers);
const parentSignerProviders = parentSigner
? getSignerProviders(parentSigner, providers)
: [];
if (!signerProviders.length) {
return <NoOracleProof type={type} />;
@@ -962,34 +929,13 @@ const OracleLink = ({
return (
<div className="mt-2">
{signerProviders.map((provider) => {
// Making the assumption here that if the provider name is the same,
// that it is the same provider that the parent market used.
const parentProvider = parentSignerProviders.find(
(p) => p.name === provider.name
);
const isParentProviderEqual =
parentProvider && isEqual(provider, parentProvider);
// We only want to pass the parent data to the child component if the
// data differs from the parent market.
return isParentProviderEqual ? (
<OracleProfile
key={dataSourceSpecId}
provider={provider}
dataSourceSpecId={dataSourceSpecId}
/>
) : (
<OracleProfile
key={dataSourceSpecId}
provider={provider}
dataSourceSpecId={dataSourceSpecId}
parentProvider={parentProvider}
parentDataSourceSpecId={parentDataSourceSpecId}
/>
);
})}
{signerProviders.map((provider) => (
<OracleProfile
key={dataSourceSpecId}
provider={provider}
dataSourceSpecId={dataSourceSpecId}
/>
))}
</div>
);
};
@@ -1012,18 +958,13 @@ const NoOracleProof = ({
const OracleProfile = (props: {
provider: Provider;
dataSourceSpecId: string;
parentProvider?: Provider;
parentDataSourceSpecId?: string;
}) => {
// If this is a successor market, the parent market data will only have been passed
// in if it differs from the current data.
const [open, onChange] = useState(false);
return (
<div key={props.provider.name}>
<OracleBasicProfile
provider={props.provider}
onClick={() => onChange(!open)}
parentProvider={props.parentProvider}
/>
<OracleDialog {...props} open={open} onChange={onChange} />
</div>
@@ -5,7 +5,6 @@ import {
ExternalLink,
Icon,
Intent,
Lozenge,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
@@ -60,12 +59,10 @@ export const OracleBasicProfile = ({
provider,
onClick,
markets: oracleMarkets,
parentProvider,
}: {
provider: Provider;
markets?: OracleMarketSpecFieldsFragment[] | undefined;
onClick?: (value?: boolean) => void;
parentProvider?: Provider;
}) => {
const { icon, message, intent } = getVerifiedStatusIcon(provider);
@@ -81,14 +78,8 @@ export const OracleBasicProfile = ({
icon: getLinkIcon(proof.type),
}));
// If this is a successor market and there's a different parent provider,
// we'll just show that there's been a change, rather than add old data
// in alongside the new provider.
return (
<>
{parentProvider && (
<Lozenge variant={Intent.Primary}>{t('Updated')}</Lozenge>
)}
<span className="flex gap-1">
{provider.url && (
<span className="flex align-items-bottom text-md gap-1">