fix(trading): banner for resume trading (#5849)
Co-authored-by: Art <artur@vegaprotocol.io> Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
This commit is contained in:
parent
52e5a37da3
commit
4f8d6bd876
@ -7,7 +7,7 @@ import {
|
|||||||
useMaliciousOracle,
|
useMaliciousOracle,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { MarketSuspendedBanner } from './market-suspended-banner';
|
import { MarketSuspendedBanner } from './market-suspended-banner';
|
||||||
import { MarketUpdateBanner } from './market-update-banner';
|
import { MarketUpdateBanner } from './market-update-banner';
|
||||||
import { MarketUpdateStateBanner } from './market-update-state-banner';
|
import { MarketUpdateStateBanner } from './market-update-state-banner';
|
||||||
@ -22,17 +22,17 @@ import {
|
|||||||
|
|
||||||
type UpdateMarketBanner = {
|
type UpdateMarketBanner = {
|
||||||
kind: 'UpdateMarket';
|
kind: 'UpdateMarket';
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type UpdateMarketStateBanner = {
|
type UpdateMarketStateBanner = {
|
||||||
kind: 'UpdateMarketState';
|
kind: 'UpdateMarketState';
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type NewMarketBanner = {
|
type NewMarketBanner = {
|
||||||
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
|
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SettledBanner = {
|
type SettledBanner = {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { Fragment } from 'react';
|
import { Fragment } from 'react';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
@ -7,7 +7,7 @@ import { useT } from '../../lib/use-t';
|
|||||||
export const MarketSuccessorProposalBanner = ({
|
export const MarketSuccessorProposalBanner = ({
|
||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const tokenLink = useLinks(DApp.Governance);
|
const tokenLink = useLinks(DApp.Governance);
|
||||||
@ -28,7 +28,7 @@ export const MarketSuccessorProposalBanner = ({
|
|||||||
}
|
}
|
||||||
)}{' '}
|
)}{' '}
|
||||||
{proposals.map((item, i) => {
|
{proposals.map((item, i) => {
|
||||||
if (item.terms.change.__typename !== 'NewMarket') {
|
if (item.terms?.change.__typename !== 'NewMarket') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { MarketUpdateBanner } from './market-update-banner';
|
import { MarketUpdateBanner } from './market-update-banner';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
describe('MarketUpdateBanner', () => {
|
describe('MarketUpdateBanner', () => {
|
||||||
const change = {
|
const change = {
|
||||||
@ -34,9 +34,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
|
|
||||||
it('renders content for a single open proposal', () => {
|
it('renders content for a single open proposal', () => {
|
||||||
render(
|
render(
|
||||||
<MarketUpdateBanner
|
<MarketUpdateBanner proposals={[openProposal as ProposalFragment]} />
|
||||||
proposals={[openProposal as MarketViewProposalFieldsFragment]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -50,9 +48,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
|
|
||||||
it('renders content for a single passed proposal', () => {
|
it('renders content for a single passed proposal', () => {
|
||||||
render(
|
render(
|
||||||
<MarketUpdateBanner
|
<MarketUpdateBanner proposals={[passedProposal as ProposalFragment]} />
|
||||||
proposals={[passedProposal as MarketViewProposalFieldsFragment]}
|
|
||||||
/>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -65,10 +61,7 @@ describe('MarketUpdateBanner', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders content for multiple passed proposals', () => {
|
it('renders content for multiple passed proposals', () => {
|
||||||
const proposals = [
|
const proposals = [openProposal, openProposal] as ProposalFragment[];
|
||||||
openProposal,
|
|
||||||
openProposal,
|
|
||||||
] as MarketViewProposalFieldsFragment[];
|
|
||||||
|
|
||||||
render(<MarketUpdateBanner proposals={proposals} />);
|
render(<MarketUpdateBanner proposals={proposals} />);
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
TOKEN_PROPOSALS,
|
TOKEN_PROPOSALS,
|
||||||
useLinks,
|
useLinks,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { useT } from '../../lib/use-t';
|
import { useT } from '../../lib/use-t';
|
||||||
@ -14,17 +14,17 @@ import { useT } from '../../lib/use-t';
|
|||||||
export const MarketUpdateBanner = ({
|
export const MarketUpdateBanner = ({
|
||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const governanceLink = useLinks(DApp.Governance);
|
const governanceLink = useLinks(DApp.Governance);
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const openProposals = sortBy(
|
const openProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
const passedProposals = sortBy(
|
const passedProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
|
|
||||||
let content = null;
|
let content = null;
|
||||||
@ -49,7 +49,7 @@ export const MarketUpdateBanner = ({
|
|||||||
content = (
|
content = (
|
||||||
<p>
|
<p>
|
||||||
{t('Proposal set to change market on {{date}}.', {
|
{t('Proposal set to change market on {{date}}.', {
|
||||||
date: format(new Date(proposal.terms.enactmentDatetime), 'dd MMMM'),
|
date: format(new Date(proposal.terms?.enactmentDatetime), 'dd MMMM'),
|
||||||
})}
|
})}
|
||||||
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>,
|
<ExternalLink href={proposalLink}>{t('View proposal')}</ExternalLink>,
|
||||||
</p>
|
</p>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import sortBy from 'lodash/sortBy';
|
import sortBy from 'lodash/sortBy';
|
||||||
import { format, formatDuration, intervalToDuration } from 'date-fns';
|
import { format, formatDuration, intervalToDuration } from 'date-fns';
|
||||||
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProposalFragment } from '@vegaprotocol/proposals';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { MarketUpdateType, ProposalState } from '@vegaprotocol/types';
|
||||||
import {
|
import {
|
||||||
DApp,
|
DApp,
|
||||||
TOKEN_PROPOSAL,
|
TOKEN_PROPOSAL,
|
||||||
@ -19,18 +19,29 @@ export const MarketUpdateStateBanner = ({
|
|||||||
proposals,
|
proposals,
|
||||||
}: {
|
}: {
|
||||||
market: Market;
|
market: Market;
|
||||||
proposals: MarketViewProposalFieldsFragment[];
|
proposals: ProposalFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const t = useT();
|
const t = useT();
|
||||||
const governanceLink = useLinks(DApp.Governance);
|
const governanceLink = useLinks(DApp.Governance);
|
||||||
|
|
||||||
|
const openTradingProposals = sortBy(
|
||||||
|
proposals.filter(
|
||||||
|
(p) =>
|
||||||
|
p.terms &&
|
||||||
|
p.terms.change.__typename === 'UpdateMarketState' &&
|
||||||
|
p.terms.change.updateType ===
|
||||||
|
MarketUpdateType.MARKET_STATE_UPDATE_TYPE_RESUME
|
||||||
|
),
|
||||||
|
(p) => p.terms?.enactmentDatetime
|
||||||
|
);
|
||||||
|
|
||||||
const openProposals = sortBy(
|
const openProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
const passedProposals = sortBy(
|
const passedProposals = sortBy(
|
||||||
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
|
||||||
(p) => p.terms.enactmentDatetime
|
(p) => p.terms?.enactmentDatetime
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!passedProposals.length && !openProposals.length) {
|
if (!passedProposals.length && !openProposals.length) {
|
||||||
@ -45,12 +56,41 @@ export const MarketUpdateStateBanner = ({
|
|||||||
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
|
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const openTradingProposalsLink =
|
||||||
|
openTradingProposals[0]?.__typename === 'Proposal' &&
|
||||||
|
openTradingProposals[0]?.id
|
||||||
|
? governanceLink(
|
||||||
|
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].id)
|
||||||
|
)
|
||||||
|
: openTradingProposals[0]?.__typename === 'ProposalDetail' &&
|
||||||
|
openTradingProposals[0]?.batchId
|
||||||
|
? governanceLink(
|
||||||
|
TOKEN_PROPOSAL.replace(':id', openTradingProposals[0].batchId)
|
||||||
|
)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const proposalsLink =
|
const proposalsLink =
|
||||||
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
|
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
|
||||||
|
|
||||||
let content: ReactNode;
|
let content: ReactNode;
|
||||||
|
|
||||||
if (passedProposals.length) {
|
if (openTradingProposals.length >= 1) {
|
||||||
|
content = (
|
||||||
|
<>
|
||||||
|
<p className="mb-1">
|
||||||
|
{t(
|
||||||
|
'Trading on market {{name}} was suspended by governance. There are open proposals to resume trading on this market.',
|
||||||
|
{ name }
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<ExternalLink href={openTradingProposalsLink}>
|
||||||
|
{t('View proposals')}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
} else if (passedProposals.length) {
|
||||||
const { date, duration, price } = getMessageVariables(passedProposals[0]);
|
const { date, duration, price } = getMessageVariables(passedProposals[0]);
|
||||||
content = (
|
content = (
|
||||||
<>
|
<>
|
||||||
@ -116,20 +156,23 @@ export const MarketUpdateStateBanner = ({
|
|||||||
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
|
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMessageVariables = (proposal: MarketViewProposalFieldsFragment) => {
|
const getMessageVariables = (proposal: ProposalFragment) => {
|
||||||
const enactmentDatetime = new Date(proposal.terms.enactmentDatetime);
|
const enactmentDatetime =
|
||||||
const date = format(enactmentDatetime, 'dd MMMM');
|
proposal.terms && new Date(proposal.terms.enactmentDatetime);
|
||||||
const duration = formatDuration(
|
const date = enactmentDatetime && format(enactmentDatetime, 'dd MMMM');
|
||||||
intervalToDuration({
|
const duration =
|
||||||
start: new Date(),
|
enactmentDatetime &&
|
||||||
end: enactmentDatetime,
|
formatDuration(
|
||||||
}),
|
intervalToDuration({
|
||||||
{
|
start: new Date(),
|
||||||
format: ['days', 'hours'],
|
end: enactmentDatetime,
|
||||||
}
|
}),
|
||||||
);
|
{
|
||||||
|
format: ['days', 'hours'],
|
||||||
|
}
|
||||||
|
);
|
||||||
const price =
|
const price =
|
||||||
proposal.terms.change.__typename === 'UpdateMarketState'
|
proposal.terms?.change.__typename === 'UpdateMarketState'
|
||||||
? proposal.terms.change.price
|
? proposal.terms.change.price
|
||||||
: '';
|
: '';
|
||||||
return {
|
return {
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
useMarketProposals,
|
useMarketProposals,
|
||||||
type MarketViewProposalFieldsFragment,
|
type ProposalFragment,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ProposalState, ProposalType } from '@vegaprotocol/types';
|
import { ProposalState, ProposalType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
const isPending = (p: MarketViewProposalFieldsFragment) => {
|
const isPending = (p: ProposalFragment) => {
|
||||||
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
|
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
|
||||||
p.state
|
p.state
|
||||||
);
|
);
|
||||||
@ -21,9 +21,10 @@ export const useUpdateMarketStateProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'UpdateMarketState' &&
|
change.__typename === 'UpdateMarketState' &&
|
||||||
change.market.id === marketId
|
change.market.id === marketId
|
||||||
) {
|
) {
|
||||||
@ -48,8 +49,9 @@ export const useUpdateMarketProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'UpdateMarket' &&
|
change.__typename === 'UpdateMarket' &&
|
||||||
change.marketId === marketId
|
change.marketId === marketId
|
||||||
) {
|
) {
|
||||||
@ -73,8 +75,9 @@ export const useSuccessorMarketProposals = (
|
|||||||
|
|
||||||
const proposals = data
|
const proposals = data
|
||||||
? data.filter(isPending).filter((p) => {
|
? data.filter(isPending).filter((p) => {
|
||||||
const change = p.terms.change;
|
const change = p.terms?.change;
|
||||||
if (
|
if (
|
||||||
|
change &&
|
||||||
change.__typename === 'NewMarket' &&
|
change.__typename === 'NewMarket' &&
|
||||||
change.successorConfiguration?.parentMarketId === marketId
|
change.successorConfiguration?.parentMarketId === marketId
|
||||||
) {
|
) {
|
||||||
|
@ -12,6 +12,7 @@ import { useT } from '../../lib/use-t';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
|
||||||
import { MarketMarkPrice } from '../market-mark-price';
|
import { MarketMarkPrice } from '../market-mark-price';
|
||||||
|
import { MarketBanner } from '../market-banner';
|
||||||
/**
|
/**
|
||||||
* This is only rendered for the mobile navigation
|
* This is only rendered for the mobile navigation
|
||||||
*/
|
*/
|
||||||
@ -108,8 +109,11 @@ export const MobileMarketHeader = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{data && (
|
{data && (
|
||||||
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
|
<div>
|
||||||
<MarketHeaderStats market={data} />
|
<MarketBanner market={data} />
|
||||||
|
<div className="px-3 py-6 text-sm grid grid-cols-2 items-center gap-x-4 gap-y-6">
|
||||||
|
<MarketHeaderStats market={data} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</FullScreenPopover>
|
</FullScreenPopover>
|
||||||
|
@ -9,6 +9,7 @@ from actions.utils import next_epoch
|
|||||||
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
|
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
|
||||||
from vega_sim.api import governance
|
from vega_sim.api import governance
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures("risk_accepted")
|
@pytest.mark.usefixtures("risk_accepted")
|
||||||
def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
||||||
# 7002-SORD-001
|
# 7002-SORD-001
|
||||||
@ -16,15 +17,18 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
|||||||
trading_mode = page.get_by_test_id("market-trading-mode").get_by_test_id(
|
trading_mode = page.get_by_test_id("market-trading-mode").get_by_test_id(
|
||||||
"item-value"
|
"item-value"
|
||||||
)
|
)
|
||||||
market_state = page.get_by_test_id("market-state").get_by_test_id("item-value")
|
market_state = page.get_by_test_id(
|
||||||
|
"market-state").get_by_test_id("item-value")
|
||||||
|
|
||||||
# setup market in proposed step, without liquidity provided
|
# setup market in proposed step, without liquidity provided
|
||||||
market_id = proposed_market
|
market_id = proposed_market
|
||||||
page.goto(f"/#/markets/{market_id}")
|
page.goto(f"/#/markets/{market_id}")
|
||||||
# 6002-MDET-001
|
# 6002-MDET-001
|
||||||
expect(page.get_by_test_id("header-title")).to_have_text("BTC:DAI_2023Futr")
|
expect(page.get_by_test_id("header-title")
|
||||||
|
).to_have_text("BTC:DAI_2023Futr")
|
||||||
# 6002-MDET-002
|
# 6002-MDET-002
|
||||||
expect(page.get_by_test_id("market-expiry")).to_have_text("ExpiryNot time-based")
|
expect(page.get_by_test_id("market-expiry")
|
||||||
|
).to_have_text("ExpiryNot time-based")
|
||||||
page.get_by_test_id("market-expiry").hover()
|
page.get_by_test_id("market-expiry").hover()
|
||||||
expect(page.get_by_test_id("expiry-tooltip").first).to_have_text(
|
expect(page.get_by_test_id("expiry-tooltip").first).to_have_text(
|
||||||
"This market expires when triggered by its oracle, not on a set date.View oracle specification"
|
"This market expires when triggered by its oracle, not on a set date.View oracle specification"
|
||||||
@ -123,16 +127,16 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
|||||||
expect(market_state).to_have_text("Active")
|
expect(market_state).to_have_text("Active")
|
||||||
|
|
||||||
vega.update_market_state(
|
vega.update_market_state(
|
||||||
market_id = market_id,
|
market_id=market_id,
|
||||||
proposal_key = MM_WALLET.name,
|
proposal_key=MM_WALLET.name,
|
||||||
market_state = MarketStateUpdateType.Suspend,
|
market_state=MarketStateUpdateType.Suspend,
|
||||||
forward_time_to_enactment = False
|
forward_time_to_enactment=False
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
page
|
page
|
||||||
.get_by_test_id("market-banner")
|
.get_by_test_id("market-banner")
|
||||||
.get_by_test_id(f"update-state-banner-{market_id}")
|
.get_by_test_id(f"update-state-banner-{market_id}")
|
||||||
).to_be_visible()
|
).to_be_visible()
|
||||||
|
|
||||||
next_epoch(vega=vega)
|
next_epoch(vega=vega)
|
||||||
@ -143,12 +147,14 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
|
|||||||
|
|
||||||
# banner should not show after resume
|
# banner should not show after resume
|
||||||
vega.update_market_state(
|
vega.update_market_state(
|
||||||
market_id = market_id,
|
market_id=market_id,
|
||||||
proposal_key = MM_WALLET.name,
|
proposal_key=MM_WALLET.name,
|
||||||
market_state = MarketStateUpdateType.Resume,
|
market_state=MarketStateUpdateType.Resume,
|
||||||
forward_time_to_enactment = False
|
forward_time_to_enactment=False
|
||||||
)
|
)
|
||||||
|
|
||||||
|
expect(page.get_by_test_id("market-banner")
|
||||||
|
).to_have_text(" Trading on market BTC:DAI_2023 was suspended by governance. There are open proposals to resume trading on this market.View proposals1/2")
|
||||||
next_epoch(vega=vega)
|
next_epoch(vega=vega)
|
||||||
|
|
||||||
expect(page.get_by_test_id("market-banner")).not_to_be_visible()
|
expect(page.get_by_test_id("market-banner")).not_to_be_visible()
|
||||||
|
@ -485,14 +485,45 @@ fragment MarketViewProposalFields on Proposal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment SubProposal on ProposalDetail {
|
||||||
|
id
|
||||||
|
batchId
|
||||||
|
reference
|
||||||
|
state
|
||||||
|
terms {
|
||||||
|
closingDatetime
|
||||||
|
enactmentDatetime
|
||||||
|
change {
|
||||||
|
__typename
|
||||||
|
... on UpdateMarketState {
|
||||||
|
...UpdateMarketStateFields
|
||||||
|
}
|
||||||
|
... on NewMarket {
|
||||||
|
...NewMarketSuccessorFields
|
||||||
|
}
|
||||||
|
... on UpdateMarket {
|
||||||
|
...UpdateMarketFields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query MarketViewProposals(
|
query MarketViewProposals(
|
||||||
$proposalType: ProposalType
|
$proposalType: ProposalType
|
||||||
$inState: ProposalState
|
$inState: ProposalState
|
||||||
) {
|
) {
|
||||||
proposalsConnection(proposalType: $proposalType, inState: $inState) {
|
proposalsConnection(proposalType: $proposalType, inState: $inState) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
proposalNode {
|
||||||
...MarketViewProposalFields
|
... on BatchProposal {
|
||||||
|
id
|
||||||
|
subProposals {
|
||||||
|
...SubProposal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on Proposal {
|
||||||
|
...MarketViewProposalFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -500,6 +531,8 @@ query MarketViewProposals(
|
|||||||
|
|
||||||
subscription MarketViewLiveProposals {
|
subscription MarketViewLiveProposals {
|
||||||
proposals {
|
proposals {
|
||||||
...MarketViewProposalFields
|
... on Proposal {
|
||||||
|
...MarketViewProposalFields
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,2 @@
|
|||||||
export {
|
export * from './proposals-data-provider';
|
||||||
marketViewProposalsDataProvider,
|
|
||||||
proposalsDataProvider,
|
|
||||||
useMarketProposals,
|
|
||||||
} from './proposals-data-provider';
|
|
||||||
export * from './__generated__/Proposals';
|
export * from './__generated__/Proposals';
|
||||||
|
@ -14,8 +14,13 @@ import {
|
|||||||
type MarketViewProposalsQuery,
|
type MarketViewProposalsQuery,
|
||||||
type MarketViewProposalsQueryVariables,
|
type MarketViewProposalsQueryVariables,
|
||||||
type MarketViewLiveProposalsSubscriptionVariables,
|
type MarketViewLiveProposalsSubscriptionVariables,
|
||||||
|
type SubProposalFragment,
|
||||||
} from './__generated__/Proposals';
|
} from './__generated__/Proposals';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
|
||||||
|
export type ProposalFragment =
|
||||||
|
| MarketViewProposalFieldsFragment
|
||||||
|
| SubProposalFragment;
|
||||||
|
export type ProposalFragments = Array<ProposalFragment>;
|
||||||
|
|
||||||
const getData = (responseData: ProposalsListQuery | null) =>
|
const getData = (responseData: ProposalsListQuery | null) =>
|
||||||
responseData?.proposalsConnection?.edges
|
responseData?.proposalsConnection?.edges
|
||||||
@ -63,23 +68,23 @@ const ProposalTypeMap: Record<
|
|||||||
};
|
};
|
||||||
|
|
||||||
const matchFilter = (
|
const matchFilter = (
|
||||||
data: MarketViewProposalFieldsFragment,
|
data: ProposalFragment,
|
||||||
variables: MarketViewProposalsQueryVariables
|
variables: MarketViewProposalsQueryVariables
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
(!variables.inState || data.state === variables.inState) &&
|
(!variables.inState || data.state === variables.inState) &&
|
||||||
(!variables.proposalType ||
|
(!variables.proposalType ||
|
||||||
data.terms.change.__typename === ProposalTypeMap[variables.proposalType])
|
data.terms?.change.__typename === ProposalTypeMap[variables.proposalType])
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const update: Update<
|
export const update: Update<
|
||||||
MarketViewProposalFieldsFragment[] | null,
|
ProposalFragment[] | null,
|
||||||
MarketViewProposalFieldsFragment,
|
ProposalFragment,
|
||||||
MarketViewProposalsQueryVariables
|
MarketViewProposalsQueryVariables
|
||||||
> = (
|
> = (
|
||||||
data: MarketViewProposalFieldsFragment[] | null,
|
data: ProposalFragment[] | null,
|
||||||
delta: MarketViewProposalFieldsFragment,
|
delta: ProposalFragment,
|
||||||
reload,
|
reload,
|
||||||
variables
|
variables
|
||||||
) => {
|
) => {
|
||||||
@ -104,13 +109,30 @@ export const update: Update<
|
|||||||
|
|
||||||
const getMarketProposalsData = (
|
const getMarketProposalsData = (
|
||||||
responseData: MarketViewProposalsQuery | null
|
responseData: MarketViewProposalsQuery | null
|
||||||
) => removePaginationWrapper(responseData?.proposalsConnection?.edges) || [];
|
): ProposalFragments => {
|
||||||
|
const proposals: ProposalFragment[] = [];
|
||||||
|
responseData?.proposalsConnection?.edges?.forEach((edge) => {
|
||||||
|
if (edge?.proposalNode) {
|
||||||
|
if (edge.proposalNode.__typename === 'Proposal') {
|
||||||
|
proposals.push(edge.proposalNode);
|
||||||
|
} else if (
|
||||||
|
edge.proposalNode.__typename === 'BatchProposal' &&
|
||||||
|
edge.proposalNode.subProposals
|
||||||
|
) {
|
||||||
|
proposals.push(
|
||||||
|
...(edge.proposalNode.subProposals as ProposalFragments)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return proposals;
|
||||||
|
};
|
||||||
|
|
||||||
const subscriptionVariables: MarketViewLiveProposalsSubscriptionVariables = {};
|
const subscriptionVariables: MarketViewLiveProposalsSubscriptionVariables = {};
|
||||||
|
|
||||||
export const marketViewProposalsDataProvider = makeDataProvider<
|
export const marketViewProposalsDataProvider = makeDataProvider<
|
||||||
MarketViewProposalsQuery,
|
MarketViewProposalsQuery,
|
||||||
MarketViewProposalFieldsFragment[],
|
ProposalFragments,
|
||||||
MarketViewLiveProposalsSubscription,
|
MarketViewLiveProposalsSubscription,
|
||||||
MarketViewProposalFieldsFragment,
|
MarketViewProposalFieldsFragment,
|
||||||
MarketViewProposalsQueryVariables,
|
MarketViewProposalsQueryVariables,
|
||||||
|
30
libs/types/src/__generated__/types.ts
generated
30
libs/types/src/__generated__/types.ts
generated
@ -1232,6 +1232,14 @@ export type EpochTimestamps = {
|
|||||||
start?: Maybe<Scalars['Timestamp']>;
|
start?: Maybe<Scalars['Timestamp']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EquityLikeShareWeightPerMarket = {
|
||||||
|
__typename?: 'EquityLikeShareWeightPerMarket';
|
||||||
|
/** The equity-like share weight for this market */
|
||||||
|
equityLikeShareWeight: Scalars['String'];
|
||||||
|
/** The market ID */
|
||||||
|
marketId: Scalars['String'];
|
||||||
|
};
|
||||||
|
|
||||||
/** Response for the signature bundle to allowlist an ERC20 token in the collateral bridge */
|
/** Response for the signature bundle to allowlist an ERC20 token in the collateral bridge */
|
||||||
export type Erc20ListAssetBundle = {
|
export type Erc20ListAssetBundle = {
|
||||||
__typename?: 'Erc20ListAssetBundle';
|
__typename?: 'Erc20ListAssetBundle';
|
||||||
@ -1955,7 +1963,7 @@ export type LiquidityOrderReference = {
|
|||||||
/** Information about a liquidity provider */
|
/** Information about a liquidity provider */
|
||||||
export type LiquidityProvider = {
|
export type LiquidityProvider = {
|
||||||
__typename?: 'LiquidityProvider';
|
__typename?: 'LiquidityProvider';
|
||||||
/** Information used for calculating an LP's fee share, such as the equity like share, average entry valuation and liquidity score, for the given liquidity provider and market */
|
/** Information used for calculating an LP's fee share, such as the equity-like share, average entry valuation and liquidity score, for the given liquidity provider and market */
|
||||||
feeShare?: Maybe<LiquidityProviderFeeShare>;
|
feeShare?: Maybe<LiquidityProviderFeeShare>;
|
||||||
/** Market ID the liquidity provision is for */
|
/** Market ID the liquidity provision is for */
|
||||||
marketId: Scalars['ID'];
|
marketId: Scalars['ID'];
|
||||||
@ -1983,7 +1991,7 @@ export type LiquidityProviderEdge = {
|
|||||||
node: LiquidityProvider;
|
node: LiquidityProvider;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The equity like share of liquidity fee for each liquidity provider */
|
/** The equity-like share of liquidity fee for each liquidity provider */
|
||||||
export type LiquidityProviderFeeShare = {
|
export type LiquidityProviderFeeShare = {
|
||||||
__typename?: 'LiquidityProviderFeeShare';
|
__typename?: 'LiquidityProviderFeeShare';
|
||||||
/** The average entry valuation of the liquidity provider for the market */
|
/** The average entry valuation of the liquidity provider for the market */
|
||||||
@ -2483,7 +2491,7 @@ export type MarketData = {
|
|||||||
indicativeVolume: Scalars['String'];
|
indicativeVolume: Scalars['String'];
|
||||||
/** The last traded price (an unsigned integer) */
|
/** The last traded price (an unsigned integer) */
|
||||||
lastTradedPrice: Scalars['String'];
|
lastTradedPrice: Scalars['String'];
|
||||||
/** The equity like share of liquidity fee for each liquidity provider */
|
/** The equity-like share of liquidity fee for each liquidity provider */
|
||||||
liquidityProviderFeeShare?: Maybe<Array<LiquidityProviderFeeShare>>;
|
liquidityProviderFeeShare?: Maybe<Array<LiquidityProviderFeeShare>>;
|
||||||
/** SLA performance statistics */
|
/** SLA performance statistics */
|
||||||
liquidityProviderSla?: Maybe<Array<LiquidityProviderSLA>>;
|
liquidityProviderSla?: Maybe<Array<LiquidityProviderSLA>>;
|
||||||
@ -3032,7 +3040,7 @@ export type Normaliser = {
|
|||||||
name: Scalars['String'];
|
name: Scalars['String'];
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The equity like share of liquidity fee for each liquidity provider */
|
/** The equity-like share of liquidity fee for each liquidity provider */
|
||||||
export type ObservableLiquidityProviderFeeShare = {
|
export type ObservableLiquidityProviderFeeShare = {
|
||||||
__typename?: 'ObservableLiquidityProviderFeeShare';
|
__typename?: 'ObservableLiquidityProviderFeeShare';
|
||||||
/** The average entry valuation of the liquidity provider for the market */
|
/** The average entry valuation of the liquidity provider for the market */
|
||||||
@ -3099,7 +3107,7 @@ export type ObservableMarketData = {
|
|||||||
indicativeVolume: Scalars['String'];
|
indicativeVolume: Scalars['String'];
|
||||||
/** The last traded price (an unsigned integer) */
|
/** The last traded price (an unsigned integer) */
|
||||||
lastTradedPrice: Scalars['String'];
|
lastTradedPrice: Scalars['String'];
|
||||||
/** The equity like share of liquidity fee for each liquidity provider */
|
/** The equity-like share of liquidity fee for each liquidity provider */
|
||||||
liquidityProviderFeeShare?: Maybe<Array<ObservableLiquidityProviderFeeShare>>;
|
liquidityProviderFeeShare?: Maybe<Array<ObservableLiquidityProviderFeeShare>>;
|
||||||
/** SLA performance statistics */
|
/** SLA performance statistics */
|
||||||
liquidityProviderSla?: Maybe<Array<ObservableLiquidityProviderSLA>>;
|
liquidityProviderSla?: Maybe<Array<ObservableLiquidityProviderSLA>>;
|
||||||
@ -3830,12 +3838,14 @@ export type PartytradesConnectionArgs = {
|
|||||||
/** Represents a party on Vega, could be an ethereum wallet address in the future */
|
/** Represents a party on Vega, could be an ethereum wallet address in the future */
|
||||||
export type PartytransfersConnectionArgs = {
|
export type PartytransfersConnectionArgs = {
|
||||||
direction?: InputMaybe<TransferDirection>;
|
direction?: InputMaybe<TransferDirection>;
|
||||||
|
fromAccountType?: InputMaybe<AccountType>;
|
||||||
fromEpoch?: InputMaybe<Scalars['Int']>;
|
fromEpoch?: InputMaybe<Scalars['Int']>;
|
||||||
gameId?: InputMaybe<Scalars['ID']>;
|
gameId?: InputMaybe<Scalars['ID']>;
|
||||||
isReward?: InputMaybe<Scalars['Boolean']>;
|
isReward?: InputMaybe<Scalars['Boolean']>;
|
||||||
pagination?: InputMaybe<Pagination>;
|
pagination?: InputMaybe<Pagination>;
|
||||||
scope?: InputMaybe<TransferScope>;
|
scope?: InputMaybe<TransferScope>;
|
||||||
status?: InputMaybe<TransferStatus>;
|
status?: InputMaybe<TransferStatus>;
|
||||||
|
toAccountType?: InputMaybe<AccountType>;
|
||||||
toEpoch?: InputMaybe<Scalars['Int']>;
|
toEpoch?: InputMaybe<Scalars['Int']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4471,7 +4481,7 @@ export enum ProposalRejectionReason {
|
|||||||
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID = 'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID',
|
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID = 'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID',
|
||||||
/** Proposal terms timestamps are not compatible (Validation < Closing < Enactment) */
|
/** Proposal terms timestamps are not compatible (Validation < Closing < Enactment) */
|
||||||
PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS = 'PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS',
|
PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS = 'PROPOSAL_ERROR_INCOMPATIBLE_TIMESTAMPS',
|
||||||
/** The proposal is rejected because the party does not have enough equity like share in the market */
|
/** The proposal is rejected because the party does not have enough equity-like share in the market */
|
||||||
PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE = 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE',
|
PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE = 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE',
|
||||||
/** The proposer for this proposal has insufficient tokens */
|
/** The proposer for this proposal has insufficient tokens */
|
||||||
PROPOSAL_ERROR_INSUFFICIENT_TOKENS = 'PROPOSAL_ERROR_INSUFFICIENT_TOKENS',
|
PROPOSAL_ERROR_INSUFFICIENT_TOKENS = 'PROPOSAL_ERROR_INSUFFICIENT_TOKENS',
|
||||||
@ -4653,7 +4663,7 @@ export type ProposalVoteEdge = {
|
|||||||
|
|
||||||
export type ProposalVoteSide = {
|
export type ProposalVoteSide = {
|
||||||
__typename?: 'ProposalVoteSide';
|
__typename?: 'ProposalVoteSide';
|
||||||
/** Total equity like share weight for this side (only for UpdateMarket Proposals) */
|
/** Total equity-like share weight for this side (only for UpdateMarket Proposals) */
|
||||||
totalEquityLikeShareWeight: Scalars['String'];
|
totalEquityLikeShareWeight: Scalars['String'];
|
||||||
/** Total number of votes cast for this side */
|
/** Total number of votes cast for this side */
|
||||||
totalNumber: Scalars['String'];
|
totalNumber: Scalars['String'];
|
||||||
@ -5466,6 +5476,7 @@ export type QuerytransferArgs = {
|
|||||||
/** Queries allow a caller to read data and filter data via GraphQL. */
|
/** Queries allow a caller to read data and filter data via GraphQL. */
|
||||||
export type QuerytransfersConnectionArgs = {
|
export type QuerytransfersConnectionArgs = {
|
||||||
direction?: InputMaybe<TransferDirection>;
|
direction?: InputMaybe<TransferDirection>;
|
||||||
|
fromAccountType?: InputMaybe<AccountType>;
|
||||||
fromEpoch?: InputMaybe<Scalars['Int']>;
|
fromEpoch?: InputMaybe<Scalars['Int']>;
|
||||||
gameId?: InputMaybe<Scalars['ID']>;
|
gameId?: InputMaybe<Scalars['ID']>;
|
||||||
isReward?: InputMaybe<Scalars['Boolean']>;
|
isReward?: InputMaybe<Scalars['Boolean']>;
|
||||||
@ -5473,6 +5484,7 @@ export type QuerytransfersConnectionArgs = {
|
|||||||
partyId?: InputMaybe<Scalars['ID']>;
|
partyId?: InputMaybe<Scalars['ID']>;
|
||||||
scope?: InputMaybe<TransferScope>;
|
scope?: InputMaybe<TransferScope>;
|
||||||
status?: InputMaybe<TransferStatus>;
|
status?: InputMaybe<TransferStatus>;
|
||||||
|
toAccountType?: InputMaybe<AccountType>;
|
||||||
toEpoch?: InputMaybe<Scalars['Int']>;
|
toEpoch?: InputMaybe<Scalars['Int']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -7238,7 +7250,9 @@ export type Vote = {
|
|||||||
__typename?: 'Vote';
|
__typename?: 'Vote';
|
||||||
/** RFC3339Nano time and date when the vote reached Vega network */
|
/** RFC3339Nano time and date when the vote reached Vega network */
|
||||||
datetime: Scalars['Timestamp'];
|
datetime: Scalars['Timestamp'];
|
||||||
/** The weight of this vote based on the total equity like share */
|
/** The equity-like share weight per market (only for batch proposals) */
|
||||||
|
equityLikeSharePerMarket?: Maybe<Array<Maybe<EquityLikeShareWeightPerMarket>>>;
|
||||||
|
/** The weight of this vote based on the total equity-like share */
|
||||||
equityLikeShareWeight: Scalars['String'];
|
equityLikeShareWeight: Scalars['String'];
|
||||||
/** Total number of governance tokens for the party that cast the vote */
|
/** Total number of governance tokens for the party that cast the vote */
|
||||||
governanceTokenBalance: Scalars['String'];
|
governanceTokenBalance: Scalars['String'];
|
||||||
|
Loading…
Reference in New Issue
Block a user