diff --git a/apps/governance/.env b/apps/governance/.env index 2a0465470..91458552d 100644 --- a/apps/governance/.env +++ b/apps/governance/.env @@ -25,3 +25,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket CYPRESS_FAIRGROUND=false LC_ALL="en_US.UTF-8" +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=true \ No newline at end of file diff --git a/apps/governance/.env.capsule b/apps/governance/.env.capsule index d4099e074..b8814b856 100644 --- a/apps/governance/.env.capsule +++ b/apps/governance/.env.capsule @@ -25,3 +25,5 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket #Test configuration variables CYPRESS_FAIRGROUND=false +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=false \ No newline at end of file diff --git a/apps/governance/.env.devnet b/apps/governance/.env.devnet index 3c90793b6..51aa96100 100644 --- a/apps/governance/.env.devnet +++ b/apps/governance/.env.devnet @@ -16,3 +16,6 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/ NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/ NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=true \ No newline at end of file diff --git a/apps/governance/.env.mainnet b/apps/governance/.env.mainnet index 8781da016..9ac987b42 100644 --- a/apps/governance/.env.mainnet +++ b/apps/governance/.env.mainnet @@ -17,3 +17,6 @@ NX_VEGA_REST_URL=https://api.vega.community/api/v2/ NX_TENDERMINT_URL=https://be.vega.community NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=false \ No newline at end of file diff --git a/apps/governance/.env.mainnet-mirror b/apps/governance/.env.mainnet-mirror index fab05ed68..22fd23a50 100644 --- a/apps/governance/.env.mainnet-mirror +++ b/apps/governance/.env.mainnet-mirror @@ -16,3 +16,6 @@ NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/ NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=false \ No newline at end of file diff --git a/apps/governance/.env.stagnet1 b/apps/governance/.env.stagnet1 index 2ac16547a..53f1e733f 100644 --- a/apps/governance/.env.stagnet1 +++ b/apps/governance/.env.stagnet1 @@ -11,4 +11,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks -NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket \ No newline at end of file +NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=true \ No newline at end of file diff --git a/apps/governance/.env.testnet b/apps/governance/.env.testnet index 454b47479..0a817b1d2 100644 --- a/apps/governance/.env.testnet +++ b/apps/governance/.env.testnet @@ -16,4 +16,7 @@ NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz -NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket \ No newline at end of file +NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=true \ No newline at end of file diff --git a/apps/governance/.env.validators-testnet b/apps/governance/.env.validators-testnet index 169330f85..0064e8152 100644 --- a/apps/governance/.env.validators-testnet +++ b/apps/governance/.env.validators-testnet @@ -13,4 +13,7 @@ NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996 NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks -NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega. \ No newline at end of file +NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega. + +# Cosmic elevator flags +NX_SUCCESSOR_MARKETS=false \ No newline at end of file diff --git a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx index 855bccad8..f4a39187f 100644 --- a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx +++ b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx @@ -22,6 +22,16 @@ import { } from '../../test-helpers/mocks'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; import type { MockedResponse } from '@apollo/client/testing'; +import { FLAGS } from '@vegaprotocol/environment'; +import { BrowserRouter } from 'react-router-dom'; + +jest.mock('@vegaprotocol/proposals', () => ({ + ...jest.requireActual('@vegaprotocol/proposals'), + useSuccessorMarketProposalDetails: () => ({ + code: 'PARENT_CODE', + parentMarketId: 'PARENT_ID', + }), +})); const renderComponent = ( proposal: ProposalQuery['proposal'], @@ -30,20 +40,27 @@ const renderComponent = ( ) => render( - - - - - + + + + + + + ); describe('Proposal header', () => { + afterAll(() => { + jest.clearAllMocks(); + }); it('Renders New market proposal', () => { + const mockedFlags = jest.mocked(FLAGS); + mockedFlags.SUCCESSOR_MARKETS = true; renderComponent( generateProposal({ rationale: { @@ -76,6 +93,9 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-details')).toHaveTextContent( 'tGBP settled future.' ); + expect(screen.getByTestId('proposal-successor-info')).toHaveTextContent( + 'PARENT_CODE' + ); }); it('Renders Update market proposal', () => { diff --git a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx index d4cd74214..c3cfdd2b6 100644 --- a/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx +++ b/apps/governance/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx @@ -11,6 +11,10 @@ import { ProposalInfoLabel } from '../proposal-info-label'; import { useUserVote } from '../vote-details/use-user-vote'; import { ProposalVotingStatus } from '../proposal-voting-status'; import type { NetworkParamsResult } from '@vegaprotocol/network-parameters'; +import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals'; +import { FLAGS } from '@vegaprotocol/environment'; +import Routes from '../../../routes'; +import { Link } from 'react-router-dom'; export const ProposalHeader = ({ proposal, @@ -39,6 +43,9 @@ export const ProposalHeader = ({ fallbackTitle = t('NewMarketProposal'); details = ( <> + {FLAGS.SUCCESSOR_MARKETS && ( + + )} {t('Code')}: {change.instrument.code}. {' '} @@ -181,3 +188,20 @@ export const ProposalHeader = ({ ); }; + +const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => { + const { t } = useTranslation(); + const successor = useSuccessorMarketProposalDetails(proposalId); + + return successor.parentMarketId || successor.code ? ( + + {t('Successor market to')}:{' '} + + {successor.code || successor.parentMarketId} + + + ) : null; +}; diff --git a/apps/trading/client-pages/markets/closed.spec.tsx b/apps/trading/client-pages/markets/closed.spec.tsx index 4d6aa2bed..26c799b55 100644 --- a/apps/trading/client-pages/markets/closed.spec.tsx +++ b/apps/trading/client-pages/markets/closed.spec.tsx @@ -49,11 +49,6 @@ jest.mock('@vegaprotocol/markets', () => ({ : { data: undefined }, })); -jest.mock('@vegaprotocol/environment', () => ({ - ...jest.requireActual('@vegaprotocol/environment'), - FLAGS: { SUCCESSOR_MARKETS: true } as Partial, -})); - jest.mock('@vegaprotocol/environment', () => { const actual = jest.requireActual('@vegaprotocol/environment'); return { @@ -61,7 +56,7 @@ jest.mock('@vegaprotocol/environment', () => { FLAGS: { ...actual.FLAGS, SUCCESSOR_MARKETS: true, - }, + } as FeatureFlags, }; }); 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 185d16814..abfb16f7a 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 @@ -30,6 +30,7 @@ import { useOracleProofs } from '../../hooks'; import { OracleDialog } from '../oracle-dialog/oracle-dialog'; import { useDataProvider } from '@vegaprotocol/data-provider'; import { useParentMarketIdQuery } from '../../__generated__'; +import { useSuccessorMarketProposalDetailsQuery } from '@vegaprotocol/proposals'; type MarketInfoProps = { market: MarketInfo; @@ -144,6 +145,14 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => { }, skip: !FLAGS.SUCCESSOR_MARKETS, }); + + const { data: successor } = useSuccessorMarketProposalDetailsQuery({ + variables: { + proposalId: market.proposal?.id || '', + }, + skip: !FLAGS.SUCCESSOR_MARKETS || !market.proposal?.id, + }); + const assetDecimals = market.tradableInstrument.instrument.product.settlementAsset.decimals; @@ -155,6 +164,11 @@ export const KeyDetailsInfoPanel = ({ market }: MarketInfoProps) => { name: market.tradableInstrument.instrument.name, marketID: market.id, parentMarketID: parentData?.market?.parentMarketID || '-', + insurancePoolFraction: + (successor?.proposal?.terms.change.__typename === 'NewMarket' && + successor.proposal.terms.change.successorConfiguration + ?.insurancePoolFraction) || + '-', tradingMode: market.tradingMode && MarketTradingModeMapping[market.tradingMode], diff --git a/libs/markets/src/lib/components/market-info/tooltip-mapping.tsx b/libs/markets/src/lib/components/market-info/tooltip-mapping.tsx index 6c44f10f8..61c57ed4a 100644 --- a/libs/markets/src/lib/components/market-info/tooltip-mapping.tsx +++ b/libs/markets/src/lib/components/market-info/tooltip-mapping.tsx @@ -102,5 +102,8 @@ export const tooltipMapping: Record = { `The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.` ), suppliedStake: t('The current amount of liquidity supplied for this market.'), - parentMarketID: t('The ID of the market this market succeeds'), + parentMarketID: t('The ID of the market this market succeeds.'), + insurancePoolFraction: t( + 'The fraction of the insurance pool balance that is carried over from the parent market to the successor.' + ), }; diff --git a/libs/proposals/src/lib/proposals-hooks/Proposal.graphql b/libs/proposals/src/lib/proposals-hooks/Proposal.graphql index 9a0857ac0..f736fbfc9 100644 --- a/libs/proposals/src/lib/proposals-hooks/Proposal.graphql +++ b/libs/proposals/src/lib/proposals-hooks/Proposal.graphql @@ -40,3 +40,30 @@ query ProposalOfMarket($marketId: ID!) { } } } + +query SuccessorMarketProposalDetails($proposalId: ID!) { + proposal(id: $proposalId) { + id + terms { + change { + ... on NewMarket { + successorConfiguration { + parentMarketId + insurancePoolFraction + } + } + } + } + } +} + +query InstrumentDetails($marketId: ID!) { + market(id: $marketId) { + tradableInstrument { + instrument { + code + name + } + } + } +} diff --git a/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts b/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts index 18413086c..333217755 100644 --- a/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts +++ b/libs/proposals/src/lib/proposals-hooks/__generated__/Proposal.ts @@ -27,6 +27,20 @@ export type ProposalOfMarketQueryVariables = Types.Exact<{ export type ProposalOfMarketQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null } } | null }; +export type SuccessorMarketProposalDetailsQueryVariables = Types.Exact<{ + proposalId: Types.Scalars['ID']; +}>; + + +export type SuccessorMarketProposalDetailsQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', successorConfiguration?: { __typename?: 'SuccessorConfiguration', parentMarketId: string, insurancePoolFraction: string } | null } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter' } } } | null }; + +export type InstrumentDetailsQueryVariables = Types.Exact<{ + marketId: Types.Scalars['ID']; +}>; + + +export type InstrumentDetailsQuery = { __typename?: 'Query', market?: { __typename?: 'Market', tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', code: string, name: string } } } | null }; + export const ProposalEventFieldsFragmentDoc = gql` fragment ProposalEventFields on Proposal { id @@ -147,4 +161,89 @@ export function useProposalOfMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookO } export type ProposalOfMarketQueryHookResult = ReturnType; export type ProposalOfMarketLazyQueryHookResult = ReturnType; -export type ProposalOfMarketQueryResult = Apollo.QueryResult; \ No newline at end of file +export type ProposalOfMarketQueryResult = Apollo.QueryResult; +export const SuccessorMarketProposalDetailsDocument = gql` + query SuccessorMarketProposalDetails($proposalId: ID!) { + proposal(id: $proposalId) { + id + terms { + change { + ... on NewMarket { + successorConfiguration { + parentMarketId + insurancePoolFraction + } + } + } + } + } +} + `; + +/** + * __useSuccessorMarketProposalDetailsQuery__ + * + * To run a query within a React component, call `useSuccessorMarketProposalDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useSuccessorMarketProposalDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useSuccessorMarketProposalDetailsQuery({ + * variables: { + * proposalId: // value for 'proposalId' + * }, + * }); + */ +export function useSuccessorMarketProposalDetailsQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(SuccessorMarketProposalDetailsDocument, options); + } +export function useSuccessorMarketProposalDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(SuccessorMarketProposalDetailsDocument, options); + } +export type SuccessorMarketProposalDetailsQueryHookResult = ReturnType; +export type SuccessorMarketProposalDetailsLazyQueryHookResult = ReturnType; +export type SuccessorMarketProposalDetailsQueryResult = Apollo.QueryResult; +export const InstrumentDetailsDocument = gql` + query InstrumentDetails($marketId: ID!) { + market(id: $marketId) { + tradableInstrument { + instrument { + code + name + } + } + } +} + `; + +/** + * __useInstrumentDetailsQuery__ + * + * To run a query within a React component, call `useInstrumentDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useInstrumentDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useInstrumentDetailsQuery({ + * variables: { + * marketId: // value for 'marketId' + * }, + * }); + */ +export function useInstrumentDetailsQuery(baseOptions: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(InstrumentDetailsDocument, options); + } +export function useInstrumentDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(InstrumentDetailsDocument, options); + } +export type InstrumentDetailsQueryHookResult = ReturnType; +export type InstrumentDetailsLazyQueryHookResult = ReturnType; +export type InstrumentDetailsQueryResult = Apollo.QueryResult; \ No newline at end of file diff --git a/libs/proposals/src/lib/proposals-hooks/index.ts b/libs/proposals/src/lib/proposals-hooks/index.ts index 96bb36091..744b0bc5f 100644 --- a/libs/proposals/src/lib/proposals-hooks/index.ts +++ b/libs/proposals/src/lib/proposals-hooks/index.ts @@ -3,3 +3,4 @@ export * from './use-proposal-event'; export * from './use-proposal-submit'; export * from './use-update-proposal'; export * from './use-update-network-paramaters-toasts'; +export * from './use-successor-market-proposal-details'; diff --git a/libs/proposals/src/lib/proposals-hooks/use-successor-market-proposal-details.ts b/libs/proposals/src/lib/proposals-hooks/use-successor-market-proposal-details.ts new file mode 100644 index 000000000..cea43aaf0 --- /dev/null +++ b/libs/proposals/src/lib/proposals-hooks/use-successor-market-proposal-details.ts @@ -0,0 +1,39 @@ +import omit from 'lodash/omit'; +import { + useInstrumentDetailsQuery, + useSuccessorMarketProposalDetailsQuery, +} from './__generated__/Proposal'; + +export const useSuccessorMarketProposalDetails = ( + proposalId?: string | null +) => { + const { data: proposal } = useSuccessorMarketProposalDetailsQuery({ + variables: { + proposalId: proposalId || '', + }, + skip: !proposalId || proposalId.length === 0, + }); + + const successorDetails = + (proposal?.proposal && + proposal.proposal?.terms.change.__typename === 'NewMarket' && + proposal.proposal.terms.change.successorConfiguration) || + undefined; + + const { data: market } = useInstrumentDetailsQuery({ + variables: { + marketId: successorDetails?.parentMarketId || '', + }, + skip: + !successorDetails?.parentMarketId || + successorDetails.parentMarketId.length === 0, + }); + + const details = { + ...successorDetails, + code: market?.market?.tradableInstrument.instrument.code, + name: market?.market?.tradableInstrument.instrument.name, + }; + + return omit(details, '__typename'); +};