Compare commits

...
61 changed files with 866 additions and 569 deletions
+1 -1
View File
@@ -24,7 +24,7 @@ NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1 -1
View File
@@ -20,7 +20,7 @@ NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
+1 -1
View File
@@ -15,7 +15,7 @@ NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
+1 -1
View File
@@ -13,7 +13,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
+1 -1
View File
@@ -18,7 +18,7 @@ NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/m
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
+1 -1
View File
@@ -14,7 +14,7 @@ NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
@@ -15,19 +15,16 @@ export const CollapsibleToggle = ({
dataTestId,
children,
}: CollapsibleToggleProps) => {
const classes = classnames(
'mb-4 transition-transform ease-in-out duration-300',
{
'rotate-180': toggleState,
}
);
const classes = classnames('transition-transform ease-in-out duration-300', {
'rotate-180': toggleState,
});
return (
<button
onClick={() => setToggleState(!toggleState)}
data-testid={dataTestId}
>
<div className="flex items-center gap-3">
<div className="flex items-baseline gap-3">
{children}
<div className={classes} data-testid="toggle-icon-wrapper">
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
@@ -1,7 +1,8 @@
import classNames from 'classnames';
import { type ReactNode } from 'react';
interface HeadingProps {
title?: string;
title?: ReactNode;
centerContent?: boolean;
marginTop?: boolean;
marginBottom?: boolean;
@@ -20,6 +20,7 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({
multisigStatus: MultisigStatus.noNodes,
showMultisigStatusError: true,
zeroScoreNodes: [],
});
});
@@ -35,6 +36,7 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({
multisigStatus: MultisigStatus.correct,
showMultisigStatusError: false,
zeroScoreNodes: [],
});
});
@@ -50,6 +52,22 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsRemoving,
showMultisigStatusError: true,
zeroScoreNodes: [
{
id: '1',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
{
id: '2',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
],
});
});
@@ -65,6 +83,15 @@ describe('getMultisigStatus', () => {
expect(result).toEqual({
multisigStatus: MultisigStatus.nodeNeedsAdding,
showMultisigStatusError: true,
zeroScoreNodes: [
{
id: '1',
rewardScore: {
multisigScore: '0',
},
stakedTotal: '1000',
},
],
});
});
});
@@ -1,5 +1,8 @@
import { removePaginationWrapper } from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from '../routes/staking/__generated__/PreviousEpoch';
import type {
PreviousEpochQuery,
ValidatorNodeFragment,
} from '../routes/staking/__generated__/PreviousEpoch';
export enum MultisigStatus {
'correct' = 'correct',
@@ -17,12 +20,15 @@ export const getMultisigStatusInfo = (
previousEpochData?.epoch.validatorsConnection?.edges
);
const hasZero = allNodesInPreviousEpoch.some(
(node) => Number(node?.rewardScore?.multisigScore) === 0
);
const hasOne = allNodesInPreviousEpoch.some(
(node) => Number(node?.rewardScore?.multisigScore) === 1
);
const zeroScore = (node: ValidatorNodeFragment) =>
Number(node.rewardScore?.multisigScore) === 0;
const oneScore = (node: ValidatorNodeFragment) =>
Number(node.rewardScore?.multisigScore) === 1;
const hasZero = allNodesInPreviousEpoch.some(zeroScore);
const hasOne = allNodesInPreviousEpoch.some(oneScore);
const zeroScoreNodes = allNodesInPreviousEpoch.filter(zeroScore);
if (hasZero && hasOne) {
// If any individual node has 0 it means that node is missing from the multisig and needs to be added
@@ -38,5 +44,6 @@ export const getMultisigStatusInfo = (
return {
showMultisigStatusError: status !== MultisigStatus.correct,
multisigStatus: status,
zeroScoreNodes,
};
};
@@ -36,8 +36,8 @@ import { type Proposal, type BatchProposal } from '../../types';
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
import { getIndicatorStyle } from '../proposal/colours';
import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator';
const ProposalTypeTags = ({
proposal,
@@ -54,11 +54,8 @@ const ProposalTypeTags = ({
if (proposal.__typename === 'BatchProposal') {
return (
<div data-testid="proposal-type" className="flex gap-1">
{proposal.subProposals?.map((subProposal, i) => {
if (!subProposal?.terms) return null;
return <ProposalTypeTag key={i} terms={subProposal.terms} />;
})}
<div data-testid="proposal-type">
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
</div>
);
}
@@ -253,7 +250,7 @@ const ProposalDetails = ({
}}
components={{
// @ts-ignore children passed by i18next
lozenge: <Lozenge />,
lozenge: <Lozenge className="text-xs" />,
}}
/>
);
@@ -315,8 +312,11 @@ const ProposalDetails = ({
{proposal.subProposals.map((p, i) => {
if (!p?.terms) return null;
return (
<li key={i} className="flex gap-3">
<span className={getIndicatorStyle(i + 1)}>{i + 1}</span>
<li
key={i}
className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 items-center"
>
<Indicator indicator={i + 1} />
<span>
<div>{renderDetails(p.terms)}</div>
<SubProposalStateText
@@ -18,7 +18,7 @@ export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
<SubHeading title={t('proposalJson')} />
</CollapsibleToggle>
{showDetails && <SyntaxHighlighter data={proposal} />}
{showDetails && <SyntaxHighlighter size="smaller" data={proposal} />}
</section>
);
};
@@ -5,6 +5,11 @@ import {
} from './proposal-market-changes';
import type { JsonValue } from '../../../../components/json-diff';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('applyImmutableKeysFromEarlierVersion', () => {
it('returns an empty object if any argument is not an object or null', () => {
const earlierVersion: JsonValue = null;
@@ -18,6 +18,7 @@ import {
type SingleProposalData,
type SubProposalData,
} from '../proposal/proposal-utils';
import { MarketName } from '../proposal/market-name';
const immutableKeys = [
'decimalPlaces',
@@ -139,11 +140,17 @@ export const ProposalMarketChanges = ({
setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'}
>
<SubHeading title={t('updatesToMarket')} />
<SubHeading
title={
<>
{t('UpdateToMarket')}: <MarketName marketId={marketId} truncate />
</>
}
/>
</CollapsibleToggle>
{showChanges && (
<div className="mb-6">
<div className="mb-6 bg-vega-cdark-900 p-2 rounded-lg">
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
</div>
)}
@@ -2,6 +2,11 @@ import { fireEvent, render, screen } from '@testing-library/react';
import { ProposalUpdateMarketState } from './proposal-update-market-state';
import { MarketUpdateType } from '@vegaprotocol/types';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('<ProposalUpdateMarketState />', () => {
const suspendProposal = {
__typename: 'UpdateMarketState' as const,
@@ -10,6 +10,7 @@ import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
import { MarketUpdateTypeMapping } from '@vegaprotocol/types';
import { MarketName } from '../proposal/market-name';
interface ProposalUpdateMarketStateProps {
change: UpdateMarketStatesFragment | null;
@@ -20,16 +21,18 @@ export const ProposalUpdateMarketState = ({
}: ProposalUpdateMarketStateProps) => {
const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false);
let market;
let isTerminate = false;
if (!change) {
if (!change || change.__typename !== 'UpdateMarketState') {
return null;
}
if (change.__typename === 'UpdateMarketState') {
market = change?.market;
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
const market = change?.market;
const isTerminate =
change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
let toggleTitle = t(change.updateType);
if (toggleTitle.length === 0) {
toggleTitle = t('MarketDetails');
}
return (
@@ -39,7 +42,13 @@ export const ProposalUpdateMarketState = ({
setToggleState={setShowDetails}
dataTestId="proposal-market-data-toggle"
>
<SubHeading title={t('MarketDetails')} />
<SubHeading
title={
<>
{toggleTitle}: <MarketName marketId={market?.id} />
</>
}
/>
</CollapsibleToggle>
{showDetails && (
@@ -16,18 +16,31 @@ const getColour = (indicator: number, max = COLOURS.length) => {
export const getStyle = (indicator: number, max = COLOURS.length) =>
classNames({
'bg-vega-yellow-400': 'yellow' === getColour(indicator, max),
'bg-vega-green-400': 'green' === getColour(indicator, max),
'bg-vega-blue-400': 'blue' === getColour(indicator, max),
'bg-vega-purple-400': 'purple' === getColour(indicator, max),
'bg-vega-pink-400': 'pink' === getColour(indicator, max),
'bg-vega-orange-400': 'orange' === getColour(indicator, max),
'bg-vega-red-400': 'red' === getColour(indicator, max),
'bg-vega-clight-600': 'none' === getColour(indicator, max),
'bg-vega-yellow-400 before:bg-vega-yellow-400':
'yellow' === getColour(indicator, max),
'bg-vega-green-400 before:bg-vega-green-400':
'green' === getColour(indicator, max),
'bg-vega-blue-400 before:bg-vega-blue-400':
'blue' === getColour(indicator, max),
'bg-vega-purple-400 before:bg-vega-purple-400':
'purple' === getColour(indicator, max),
'bg-vega-pink-400 before:bg-vega-pink-400':
'pink' === getColour(indicator, max),
'bg-vega-orange-400 before:bg-vega-orange-400':
'orange' === getColour(indicator, max),
'bg-vega-red-400 before:bg-vega-red-400':
'red' === getColour(indicator, max),
'bg-vega-clight-600 before:bg-vega-clight-600':
'none' === getColour(indicator, max),
});
export const getIndicatorStyle = (indicator: number) =>
classNames(
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8',
getStyle(indicator)
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8 w-7 text-center',
'text-border-1',
getStyle(indicator),
// Comment below if you want to remove the "chevron"
'relative mr-[11px] z-1',
'before:absolute before:z-0 before:top-1 before:right-[-11px] before:rounded-sm',
"before:w-[22.62px] before:h-[22.62px] before:rotate-45 before:content-['']"
);
@@ -0,0 +1,9 @@
import { getIndicatorStyle } from './colours';
export const Indicator = ({ indicator }: { indicator: number }) => (
<div className={getIndicatorStyle(indicator)}>
<span className="absolute top-0 left-0 p-1 w-full text-center z-1">
{indicator}
</span>
</div>
);
@@ -1,6 +1,13 @@
import { useMarketInfoQuery } from '@vegaprotocol/markets';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
export const MarketName = ({ marketId }: { marketId?: string }) => {
export const MarketName = ({
marketId,
truncate,
}: {
marketId?: string;
truncate?: boolean;
}) => {
const { data } = useMarketInfoQuery({
variables: {
marketId: marketId || '',
@@ -8,7 +15,7 @@ export const MarketName = ({ marketId }: { marketId?: string }) => {
skip: !marketId,
});
return (
<span>{data?.market?.tradableInstrument.instrument.code || marketId}</span>
);
const id = truncate ? truncateMiddle(marketId || '') : marketId;
return <span>{data?.market?.tradableInstrument.instrument.code || id}</span>;
};
@@ -1,3 +1,4 @@
import { Trans, useTranslation } from 'react-i18next';
import { type ProposalTermsFieldsFragment } from '../../__generated__/Proposals';
import { type Proposal, type BatchProposal } from '../../types';
import { ListAsset } from '../list-asset';
@@ -12,8 +13,10 @@ import {
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
import { getIndicatorStyle } from './colours';
import { type ProposalNode } from './proposal-utils';
import { Lozenge } from '@vegaprotocol/ui-toolkit';
import { Indicator } from './indicator';
import { SubHeading } from '../../../../components/heading';
export const ProposalChangeDetails = ({
proposal,
@@ -26,6 +29,7 @@ export const ProposalChangeDetails = ({
restData: ProposalNode | null;
indicator?: number;
}) => {
const { t } = useTranslation();
let details = null;
switch (terms.change.__typename) {
@@ -108,6 +112,25 @@ export const ProposalChangeDetails = ({
'rewards.activityStreak.benefitTiers'
) {
details = <ProposalUpdateBenefitTiers change={terms.change} />;
} else {
details = (
<div className="mb-4">
<SubHeading title={t(terms.change.__typename as string)} />
<span>
<Trans
i18nKey="Change <lozenge>{{key}}</lozenge> to <lozenge>{{value}}</lozenge>"
values={{
key: terms.change.networkParameter.key,
value: terms.change.networkParameter.value,
}}
components={{
// @ts-ignore children passed by i18next
lozenge: <Lozenge />,
}}
/>
</span>
</div>
);
}
break;
@@ -120,10 +143,12 @@ export const ProposalChangeDetails = ({
}
}
if (indicator != null) {
if (indicator != null && details != null) {
details = (
<div className="flex gap-3 mb-3">
<div className={getIndicatorStyle(indicator)}>{indicator}</div>
<div className="grid grid-cols-[40px_minmax(0,1fr)] grid-rows-1 gap-3 mb-3">
<div className="w-10">
<Indicator indicator={indicator} />
</div>
<div>{details}</div>
</div>
);
@@ -60,7 +60,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
<ProposalDescription description={proposal.rationale.description} />
</div>
<div className="mb-4">
<div className="mb-4 flex flex-col gap-0">
{proposal.__typename === 'Proposal' ? (
<ProposalChangeDetails
proposal={proposal}
@@ -206,7 +206,7 @@ export const ProposalsList = ({
/>
)}
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
<section className="-mx-4 p-4 mb-8 bg-vega-cdark-900 rounded-l-sm">
<SubHeading title={t('openProposals')} />
{sortedProposals.open.length > 0 ||
@@ -15,7 +15,8 @@ import {
type VoteFieldsFragment,
} from '../../__generated__/Proposals';
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
import { getIndicatorStyle } from '../proposal/colours';
import { MarketName } from '../proposal/market-name';
import { Indicator } from '../proposal/indicator';
export const CompactVotes = ({ number }: { number: BigNumber }) => (
<CompactNumber
@@ -40,8 +41,9 @@ const VoteProgress = ({
children,
}: VoteProgressProps) => {
const containerClasses = classNames(
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden',
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400'
'relative h-2 rounded-md overflow-hidden',
// 'border border-vega-dark-300',
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
);
const progressClasses = classNames(
@@ -50,17 +52,19 @@ const VoteProgress = ({
);
const textClasses = classNames(
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black'
'w-full flex items-center justify-start text-white text-sm pb-1'
);
return (
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
<div>
<div className={textClasses}>{children}</div>
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
</div>
</div>
);
};
@@ -79,14 +83,22 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
<div data-testid={testId}>
{reached ? (
<div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.TICK} size={20} />
<VegaIcon
name={VegaIconNames.TICK}
className="text-vega-green"
size={20}
/>
<span>
{threshold.toString()}% {text} {t('met')}
</span>
</div>
) : (
<div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.CROSS} size={20} />
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
size={20}
/>
<span>
{threshold.toString()}% {text} {t('not met')}
</span>
@@ -152,7 +164,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-pink"
className="text-vega-red"
size={20}
/>
{t(
@@ -215,7 +227,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-pink"
className="text-vega-red"
size={20}
/>
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
@@ -237,6 +249,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
if (!p?.terms) return null;
return (
<VoteBreakdownBatchSubProposal
indicator={i + 1}
key={i}
proposal={proposal}
votes={proposal.votes}
@@ -273,21 +286,37 @@ const VoteBreakdownBatchSubProposal = ({
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
const indicatorElement = indicator && (
<span className={getIndicatorStyle(indicator)}>{indicator}</span>
);
let marketId = undefined;
if (terms?.change?.__typename === 'UpdateMarket') {
marketId = terms.change.marketId;
}
if (terms?.change?.__typename === 'UpdateMarketState') {
marketId = terms.change.market.id;
}
const marketName = marketId ? (
<>
: <MarketName marketId={marketId} />
</>
) : null;
const indicatorElement = indicator && <Indicator indicator={indicator} />;
return (
<div>
<div className="flex items-baseline gap-3">
<div className="mb-6">
<div className="flex items-center gap-3 mb-3">
{indicatorElement}
<h4>{t(terms.change.__typename)}</h4>
<h4>
{t(terms.change.__typename)} {marketName}
</h4>
</div>
<div className="rounded-sm bg-vega-dark-100 p-3">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
);
};
@@ -302,11 +331,13 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
return (
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
<div className="mb-6">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
);
};
@@ -370,13 +401,13 @@ const VoteBreakDownUI = ({
'flex justify-between flex-wrap gap-6'
);
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
const headingClasses = classNames('mb-2 text-vega-dark-400');
const headingClasses = classNames('mb-2 text-sm text-white font-bold');
const progressDetailsClasses = classNames(
'flex justify-between flex-wrap mt-2 text-sm'
);
return (
<div className="mb-6">
<div>
{isProposalOpen && (
<div
data-testid="vote-status"
@@ -393,7 +424,7 @@ const VoteBreakDownUI = ({
<VegaIcon
name={VegaIconNames.CROSS}
size={20}
className="text-vega-pink"
className="text-vega-red"
/>
)}
</span>
@@ -409,103 +440,13 @@ const VoteBreakDownUI = ({
<p className="m-0">
<Trans
i18nKey={'Currently expected to <0>fail</0>'}
components={[<span className="text-vega-pink" />]}
components={[<span className="text-vega-red" />]}
/>
</p>
)}
</div>
)}
{isUpdateMarket && (
<div className="mb-4">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
<div className={sectionWrapperClasses}>
<section
@@ -605,6 +546,97 @@ const VoteBreakDownUI = ({
</div>
</section>
</div>
{/** Liquidity provider vote */}
{isUpdateMarket && (
<div className="mt-3">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
</div>
);
};
@@ -1,27 +1,31 @@
fragment ValidatorNode on Node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
query PreviousEpoch($epochId: ID) {
epoch(id: $epochId) {
id
validatorsConnection {
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
...ValidatorNode
}
}
}
@@ -3,6 +3,8 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ValidatorNodeFragment = { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } };
export type PreviousEpochQueryVariables = Types.Exact<{
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
@@ -10,7 +12,28 @@ export type PreviousEpochQueryVariables = Types.Exact<{
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
export const ValidatorNodeFragmentDoc = gql`
fragment ValidatorNode on Node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
`;
export const PreviousEpochDocument = gql`
query PreviousEpoch($epochId: ID) {
epoch(id: $epochId) {
@@ -18,30 +41,13 @@ export const PreviousEpochDocument = gql`
validatorsConnection {
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
...ValidatorNode
}
}
}
}
}
`;
${ValidatorNodeFragmentDoc}`;
/**
* __usePreviousEpochQuery__
@@ -37,6 +37,7 @@ import {
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
const statuses = {
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
@@ -104,6 +105,10 @@ export const ValidatorTable = ({
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
return (
<>
<p className="mb-12">
@@ -281,6 +286,34 @@ export const ValidatorTable = ({
</span>
</Tooltip>
</KeyValueTableRow>
<KeyValueTableRow>
<span className="uppercase">{t('multisigPenalty')}</span>
<span
data-testid="multisig-penalty"
className="flex gap-2 items-baseline"
>
{multisigStatus?.zeroScoreNodes.find(
(n) => n.id === node.id
) ? (
<Tooltip
description={t('multisigPenaltyThisNodeIndicator')}
>
<span className="inline-block w-2 h-2 rounded-full bg-vega-red-500"></span>
</Tooltip>
) : null}
<Tooltip description={t('multisigPenaltyDescription')}>
<span>
{formatNumberPercentage(
BigNumber(
multisigStatus?.showMultisigStatusError ? 100 : 0
),
2
)}
</span>
</Tooltip>
</span>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<span>
<strong>{t('TOTAL PENALTIES')}</strong>
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -13,7 +13,7 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
+1 -1
View File
@@ -14,7 +14,7 @@ NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -15,7 +15,7 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+1 -1
View File
@@ -16,7 +16,7 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/en-GB/firefox/addon/vega-wallet-beta/
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
# Cosmic elevator flags
@@ -7,7 +7,7 @@ import {
useMaliciousOracle,
} from '@vegaprotocol/markets';
import { useState } from 'react';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type ProposalFragment } from '@vegaprotocol/proposals';
import { MarketSuspendedBanner } from './market-suspended-banner';
import { MarketUpdateBanner } from './market-update-banner';
import { MarketUpdateStateBanner } from './market-update-state-banner';
@@ -22,17 +22,17 @@ import {
type UpdateMarketBanner = {
kind: 'UpdateMarket';
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
};
type UpdateMarketStateBanner = {
kind: 'UpdateMarketState';
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
};
type NewMarketBanner = {
kind: 'NewMarket'; // aka a proposal of NewMarket which succeeds the current market
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
};
type SettledBanner = {
@@ -1,5 +1,5 @@
import { Fragment } from 'react';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type ProposalFragment } from '@vegaprotocol/proposals';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
@@ -7,7 +7,7 @@ import { useT } from '../../lib/use-t';
export const MarketSuccessorProposalBanner = ({
proposals,
}: {
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
}) => {
const t = useT();
const tokenLink = useLinks(DApp.Governance);
@@ -28,7 +28,7 @@ export const MarketSuccessorProposalBanner = ({
}
)}{' '}
{proposals.map((item, i) => {
if (item.terms.change.__typename !== 'NewMarket') {
if (item.terms?.change.__typename !== 'NewMarket') {
return null;
}
@@ -1,7 +1,7 @@
import { render, screen } from '@testing-library/react';
import { ProposalState } from '@vegaprotocol/types';
import { MarketUpdateBanner } from './market-update-banner';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type ProposalFragment } from '@vegaprotocol/proposals';
describe('MarketUpdateBanner', () => {
const change = {
@@ -34,9 +34,7 @@ describe('MarketUpdateBanner', () => {
it('renders content for a single open proposal', () => {
render(
<MarketUpdateBanner
proposals={[openProposal as MarketViewProposalFieldsFragment]}
/>
<MarketUpdateBanner proposals={[openProposal as ProposalFragment]} />
);
expect(
@@ -50,9 +48,7 @@ describe('MarketUpdateBanner', () => {
it('renders content for a single passed proposal', () => {
render(
<MarketUpdateBanner
proposals={[passedProposal as MarketViewProposalFieldsFragment]}
/>
<MarketUpdateBanner proposals={[passedProposal as ProposalFragment]} />
);
expect(
@@ -65,10 +61,7 @@ describe('MarketUpdateBanner', () => {
});
it('renders content for multiple passed proposals', () => {
const proposals = [
openProposal,
openProposal,
] as MarketViewProposalFieldsFragment[];
const proposals = [openProposal, openProposal] as ProposalFragment[];
render(<MarketUpdateBanner proposals={proposals} />);
@@ -6,7 +6,7 @@ import {
TOKEN_PROPOSALS,
useLinks,
} from '@vegaprotocol/environment';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { type ProposalFragment } from '@vegaprotocol/proposals';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ProposalState } from '@vegaprotocol/types';
import { useT } from '../../lib/use-t';
@@ -14,17 +14,17 @@ import { useT } from '../../lib/use-t';
export const MarketUpdateBanner = ({
proposals,
}: {
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
}) => {
const governanceLink = useLinks(DApp.Governance);
const t = useT();
const openProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
(p) => p.terms.enactmentDatetime
(p) => p.terms?.enactmentDatetime
);
const passedProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
(p) => p.terms.enactmentDatetime
(p) => p.terms?.enactmentDatetime
);
let content = null;
@@ -49,7 +49,7 @@ export const MarketUpdateBanner = ({
content = (
<p>
{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>,
</p>
@@ -1,8 +1,8 @@
import { type ReactNode } from 'react';
import sortBy from 'lodash/sortBy';
import { format, formatDuration, intervalToDuration } from 'date-fns';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { ProposalState } from '@vegaprotocol/types';
import { type ProposalFragment } from '@vegaprotocol/proposals';
import { MarketUpdateType, ProposalState } from '@vegaprotocol/types';
import {
DApp,
TOKEN_PROPOSAL,
@@ -19,18 +19,29 @@ export const MarketUpdateStateBanner = ({
proposals,
}: {
market: Market;
proposals: MarketViewProposalFieldsFragment[];
proposals: ProposalFragment[];
}) => {
const t = useT();
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(
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
(p) => p.terms.enactmentDatetime
(p) => p.terms?.enactmentDatetime
);
const passedProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_PASSED),
(p) => p.terms.enactmentDatetime
(p) => p.terms?.enactmentDatetime
);
if (!passedProposals.length && !openProposals.length) {
@@ -45,12 +56,41 @@ export const MarketUpdateStateBanner = ({
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
: 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 =
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
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]);
content = (
<>
@@ -116,20 +156,23 @@ export const MarketUpdateStateBanner = ({
return <div data-testid={`update-state-banner-${market.id}`}>{content}</div>;
};
const getMessageVariables = (proposal: MarketViewProposalFieldsFragment) => {
const enactmentDatetime = new Date(proposal.terms.enactmentDatetime);
const date = format(enactmentDatetime, 'dd MMMM');
const duration = formatDuration(
intervalToDuration({
start: new Date(),
end: enactmentDatetime,
}),
{
format: ['days', 'hours'],
}
);
const getMessageVariables = (proposal: ProposalFragment) => {
const enactmentDatetime =
proposal.terms && new Date(proposal.terms.enactmentDatetime);
const date = enactmentDatetime && format(enactmentDatetime, 'dd MMMM');
const duration =
enactmentDatetime &&
formatDuration(
intervalToDuration({
start: new Date(),
end: enactmentDatetime,
}),
{
format: ['days', 'hours'],
}
);
const price =
proposal.terms.change.__typename === 'UpdateMarketState'
proposal.terms?.change.__typename === 'UpdateMarketState'
? proposal.terms.change.price
: '';
return {
@@ -1,10 +1,10 @@
import {
useMarketProposals,
type MarketViewProposalFieldsFragment,
type ProposalFragment,
} from '@vegaprotocol/proposals';
import { ProposalState, ProposalType } from '@vegaprotocol/types';
const isPending = (p: MarketViewProposalFieldsFragment) => {
const isPending = (p: ProposalFragment) => {
return [ProposalState.STATE_OPEN, ProposalState.STATE_PASSED].includes(
p.state
);
@@ -21,9 +21,10 @@ export const useUpdateMarketStateProposals = (
const proposals = data
? data.filter(isPending).filter((p) => {
const change = p.terms.change;
const change = p.terms?.change;
if (
change &&
change.__typename === 'UpdateMarketState' &&
change.market.id === marketId
) {
@@ -48,8 +49,9 @@ export const useUpdateMarketProposals = (
const proposals = data
? data.filter(isPending).filter((p) => {
const change = p.terms.change;
const change = p.terms?.change;
if (
change &&
change.__typename === 'UpdateMarket' &&
change.marketId === marketId
) {
@@ -73,8 +75,9 @@ export const useSuccessorMarketProposals = (
const proposals = data
? data.filter(isPending).filter((p) => {
const change = p.terms.change;
const change = p.terms?.change;
if (
change &&
change.__typename === 'NewMarket' &&
change.successorConfiguration?.parentMarketId === marketId
) {
@@ -12,6 +12,7 @@ import { useT } from '../../lib/use-t';
import classNames from 'classnames';
import { MarketHeaderStats } from '../../client-pages/market/market-header-stats';
import { MarketMarkPrice } from '../market-mark-price';
import { MarketBanner } from '../market-banner';
/**
* This is only rendered for the mobile navigation
*/
@@ -108,8 +109,11 @@ export const MobileMarketHeader = () => {
}
>
{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>
<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>
)}
</FullScreenPopover>
+24
View File
@@ -182,7 +182,27 @@ def vega(request):
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="session", autouse=True)
def shared_vega(request):
with init_vega(request) as vega_instance:
try:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
finally:
cleanup_container(vega_instance)
@pytest.fixture
def page_shared_vega(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page_instance:
yield page_instance
@pytest.fixture
def auth_shared_vega(shared_vega: VegaServiceNull, page_shared_vega: Page):
return auth_setup(shared_vega, page_shared_vega)
@pytest.fixture
def risk_accepted_shared_vega(page_shared_vega: Page):
risk_accepted_setup(page_shared_vega)
def cleanup_container(vega_instance):
try:
@@ -283,6 +303,10 @@ def opening_auction_market(vega):
return setup_opening_auction_market(vega)
@pytest.fixture(scope="function")
def shared_continuous_market(shared_vega:VegaServiceNull):
return setup_continuous_market(shared_vega)
@pytest.fixture(scope="function")
def continuous_market(vega):
return setup_continuous_market(vega)
@@ -2,35 +2,62 @@ import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from datetime import datetime, timedelta
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup, auth_setup
from fixtures.market import setup_continuous_market
from actions.utils import wait_for_toast_confirmation
from wallet_config import WalletConfig, MM_WALLET2
from actions.utils import (
change_keys,
create_and_faucet_wallet,
)
order_size = "order-size"
order_price = "order-price"
place_order = "place-order"
order_side_sell = "order-side-SIDE_SELL"
order_side_buy = "order-side-SIDE_BUY"
market_order = "order-type-Market"
limit_order = "order-type-Limit"
tif = "order-tif"
expire = "expire"
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function
yield vega_instance
@pytest.fixture(scope="module")
def page(shared_vega, browser, request, continuous_market):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(shared_vega, page)
basic_key = WalletConfig("basic_key", "basic_key")
create_and_faucet_wallet(vega=shared_vega, wallet=basic_key)
page.goto(f"/#/markets/{continuous_market}")
change_keys(page, shared_vega, "basic_key")
yield page
@pytest.fixture(scope="module")
def continuous_market(vega):
return setup_continuous_market(vega)
def continuous_market(shared_vega:VegaServiceNull):
keypairs = shared_vega.wallet.get_keypairs("MarketSim")
proposal_key = keypairs.get('market_maker')
termination_key=keypairs.get('FJMKnwfZdd48C8NqvYrG')
mm_2_key=keypairs.get('market_maker_2')
kwargs = {}
if proposal_key is not None:
kwargs['proposal_key'] = proposal_key
if termination_key is not None:
kwargs['termination_key'] = termination_key
if mm_2_key is not None:
kwargs['mm_2_key'] = mm_2_key
return setup_continuous_market(shared_vega, **kwargs)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order_GTT( shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(tif).select_option("Good 'til Time (GTT)")
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
@@ -47,30 +74,31 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Pag
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT:")
expect(page.get_by_role("row").nth(4)).to_contain_text("10+10LimitFilled120.00GTT:")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(2)
vega.wait_for_total_catchup()
shared_vega.wait_fn(2)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(6)).to_contain_text("10+10LimitFilled120.00GTC")
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("100")
page.get_by_test_id(order_side_sell).click()
@@ -84,15 +112,15 @@ def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10LimitFilled100.00GFN")
page.reload()
expect(page.get_by_role("row").nth(6)).to_contain_text("10-10LimitFilled100.00GFN")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_side_sell).click()
@@ -105,32 +133,32 @@ def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page)
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(8)).to_contain_text("10-10MarketFilled-IOC")
page.reload()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10MarketFilled-IOC")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(tif).select_option("Fill or Kill (FOK)")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-010
# 0003-WTXN-012
# 0003-WTXN-003
expect(page.get_by_role("row").nth(9)).to_contain_text("10+10MarketFilled-FOK")
expect(page.get_by_role("row").nth(8)).to_contain_text("10+10MarketFilled-FOK")
@pytest.mark.usefixtures("risk_accepted")
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_sidebar_should_be_open_after_reload(page: Page):
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
page.get_by_test_id("Order").click()
expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible()
@@ -1,4 +1,4 @@
import pytest
""" import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -87,3 +87,4 @@ def test_margin_and_fees_estimations(continuous_market, vega: VegaServiceNull, p
# expect(page.get_by_test_id("toast-content")).to_contain_text(
# "Your transaction has been confirmed"
# )
"""
@@ -44,25 +44,25 @@ def create_position(vega: VegaServiceNull, market_id):
vega.wait_fn(1)
vega.wait_for_total_catchup
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_stop_order_form_error_validation(continuous_market, page: Page):
@pytest.mark.usefixtures("auth_shared_vega", "risk_accepted_shared_vega")
def test_stop_order_form_error_validation(shared_continuous_market, page_shared_vega: Page):
# 7002-SORD-032
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(stop_order_btn).click()
page.get_by_test_id(stop_limit_order_btn).is_visible()
page.get_by_test_id(stop_limit_order_btn).click()
page.get_by_test_id(order_side_sell).click()
page.get_by_test_id(submit_stop_order).click()
expect(page.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
page_shared_vega.goto(f"/#/markets/{shared_continuous_market}")
page_shared_vega.get_by_test_id(stop_order_btn).click()
page_shared_vega.get_by_test_id(stop_limit_order_btn).is_visible()
page_shared_vega.get_by_test_id(stop_limit_order_btn).click()
page_shared_vega.get_by_test_id(order_side_sell).click()
page_shared_vega.get_by_test_id(submit_stop_order).click()
expect(page_shared_vega.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
"You need provide a price"
)
expect(page.get_by_test_id("stop-order-error-message-size")).to_have_text(
expect(page_shared_vega.get_by_test_id("stop-order-error-message-size")).to_have_text(
"Size cannot be lower than 1"
)
page.get_by_test_id(order_size).fill("1")
page.get_by_test_id(order_price).fill("0.0000001")
expect(page.get_by_test_id("stop-order-error-message-price")).to_have_text(
page_shared_vega.get_by_test_id(order_size).fill("1")
page_shared_vega.get_by_test_id(order_price).fill("0.0000001")
expect(page_shared_vega.get_by_test_id("stop-order-error-message-price")).to_have_text(
"Price cannot be lower than 0.00001"
)
@@ -257,7 +257,7 @@ def test_submit_stop_limit_order_cancel(
).to_have_text("Cancelled")
class TestStopOcoValidation:
""" class TestStopOcoValidation:
@pytest.fixture(scope="class")
def vega(request):
with init_vega(request) as vega_instance:
@@ -296,3 +296,4 @@ class TestStopOcoValidation:
expect(page.get_by_test_id("stop-order-warning-limit")).to_have_text(
"There is a limit of 4 active stop orders per market. Orders submitted above the limit will be immediately rejected."
)
"""
@@ -235,7 +235,7 @@ def test_submit_stop_oco_market_order_pending(
"PendingOCO"
)
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_submit_stop_oco_limit_order_pending(
continuous_market, vega: VegaServiceNull, page: Page
):
@@ -15,8 +15,7 @@ deal_ticket_deposit_dialog_button = "deal-ticket-deposit-dialog-button"
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(
vega_instance)) # Register the cleanup function
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@@ -25,7 +24,6 @@ def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.skip("tbd - issue only on the sim, should work in vega 0.74.0")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_should_display_info_and_button_for_deposit(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
@@ -33,17 +31,20 @@ def test_should_display_info_and_button_for_deposit(continuous_market, page: Pag
page.get_by_test_id(order_price).fill("20")
# 7002-SORD-060
expect(page.get_by_test_id(deal_ticket_warning_margin)).to_have_text(
"You may not have enough margin available to open this position.")
"You may not have enough margin available to open this position."
)
page.get_by_test_id(deal_ticket_warning_margin).hover()
expect(page.get_by_test_id("tooltip-content").nth(0)).to_have_text(
"1,661,888.12901 tDAI is currently required.You have only 999,991.49731.Deposit tDAI")
"1,661,888.12901 tDAI is currently required.You have only 999,991.49731.Deposit tDAI"
)
page.get_by_test_id(deal_ticket_deposit_dialog_button).nth(0).click()
expect(page.get_by_test_id("sidebar-content")
).to_contain_text("DepositFrom")
expect(page.get_by_test_id("sidebar-content")).to_contain_text("DepositFrom")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: VegaServiceNull, page: Page):
def test_should_show_an_error_if_your_balance_is_zero(
continuous_market, vega: VegaServiceNull, page: Page
):
page.goto(f"/#/markets/{continuous_market}")
vega.create_key("key_empty")
change_keys(page, vega, "key_empty")
@@ -52,6 +53,7 @@ def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: V
# 7002-SORD-060
expect(page.get_by_test_id(place_order)).to_be_enabled()
# 7002-SORD-003
expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")
).to_have_text("You need tDAI in your wallet to trade in this market.Make a deposit")
expect(page.get_by_test_id("deal-ticket-error-message-zero-balance")).to_have_text(
"You need tDAI in your wallet to trade in this market.Make a deposit"
)
expect(page.get_by_test_id(deal_ticket_deposit_dialog_button)).to_be_visible()
@@ -107,7 +107,6 @@ def test_market_info_liquidation(page: Page):
validate_info_section(page, fields)
@pytest.mark.skip("core issue #5681")
def test_market_info_key_details(page: Page, vega: VegaServiceNull):
# 6002-MDET-201
page.get_by_test_id(market_title_test_id).get_by_text(
@@ -117,8 +116,6 @@ def test_market_info_key_details(page: Page, vega: VegaServiceNull):
fields = [
["Market ID", short_market_id],
["Name", "BTC:DAI_2023"],
["Parent Market ID", "-"],
["Insurance Pool Fraction", "-"],
["Status", "Active"],
["Trading Mode", "Continuous"],
["Market Decimal Places", "5"],
@@ -9,6 +9,7 @@ from actions.utils import next_epoch
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
from vega_sim.api import governance
@pytest.mark.usefixtures("risk_accepted")
def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
# 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(
"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
market_id = proposed_market
page.goto(f"/#/markets/{market_id}")
# 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
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()
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"
@@ -123,16 +127,16 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
expect(market_state).to_have_text("Active")
vega.update_market_state(
market_id = market_id,
proposal_key = MM_WALLET.name,
market_state = MarketStateUpdateType.Suspend,
forward_time_to_enactment = False
market_id=market_id,
proposal_key=MM_WALLET.name,
market_state=MarketStateUpdateType.Suspend,
forward_time_to_enactment=False
)
expect(
page
.get_by_test_id("market-banner")
.get_by_test_id(f"update-state-banner-{market_id}")
.get_by_test_id("market-banner")
.get_by_test_id(f"update-state-banner-{market_id}")
).to_be_visible()
next_epoch(vega=vega)
@@ -143,12 +147,14 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
# banner should not show after resume
vega.update_market_state(
market_id = market_id,
proposal_key = MM_WALLET.name,
market_state = MarketStateUpdateType.Resume,
forward_time_to_enactment = False
market_id=market_id,
proposal_key=MM_WALLET.name,
market_state=MarketStateUpdateType.Resume,
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)
expect(page.get_by_test_id("market-banner")).not_to_be_visible()
@@ -1,39 +1,30 @@
import pytest
from playwright.sync_api import Page, expect, Locator
from conftest import init_page, init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
page.goto("/#/disclaimer")
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_network_switcher(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_network_switcher(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navbar_pages(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_links(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navigation_mobile(page: Page):
page.goto("/#/disclaimer")
page.set_viewport_size({"width": 800, "height": 1040})
navbar = page.locator('nav[aria-label="Main"]')
@@ -108,4 +99,4 @@ def assert_network_switcher(container: Locator):
expect(mainnet_link).to_be_visible()
# 0006-NETW-003
expect(mainnet_link).to_have_attribute("href", "https://console.vega.xyz")
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
@@ -49,7 +49,7 @@ def markets(vega: VegaServiceNull):
price=130,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -63,7 +63,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -77,7 +77,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -92,7 +92,7 @@ def markets(vega: VegaServiceNull):
wait=False,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -105,7 +105,7 @@ def markets(vega: VegaServiceNull):
volume=100,
price=104,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -120,7 +120,7 @@ def markets(vega: VegaServiceNull):
expires_at=vega.get_blockchain_time() + 5 * 1e9,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -134,7 +134,7 @@ def markets(vega: VegaServiceNull):
volume=20,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -148,7 +148,7 @@ def markets(vega: VegaServiceNull):
volume=40,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -162,7 +162,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -177,7 +177,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -190,7 +190,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=150,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -203,7 +203,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=160,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -216,7 +216,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=60,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -1,10 +1,12 @@
import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from typing import List
from actions.vega import submit_order, submit_liquidity, submit_multiple_orders
from conftest import init_vega, cleanup_container
from fixtures.market import setup_simple_market
from wallet_config import MM_WALLET, MM_WALLET2
from actions.utils import next_epoch
@pytest.fixture(scope="module")
@@ -15,7 +17,7 @@ def vega(request):
@pytest.fixture(scope="module")
def setup_market(vega):
def setup_market(vega: VegaServiceNull):
market_id = setup_simple_market(vega)
submit_liquidity(vega, MM_WALLET.name, market_id)
submit_multiple_orders(
@@ -33,10 +35,9 @@ def setup_market(vega):
[[10, 69.995], [5, 70], [5, 85], [3, 90], [3, 95]],
)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
return [
vega,
market_id,
@@ -46,19 +47,19 @@ def setup_market(vega):
# these values don't align with the multiple orders above as
# creating a trade triggers the liquidity provision
orderbook_content = [
[130.00500, 10, 94],
[130.00000, 3, 84],
[120.00000, 7, 81],
[110.00000, 5, 74],
[105.00000, 2, 69],
[101.00000, 67, 67],
[130.00500, 10, 126],
[130.00000, 3, 116],
[120.00000, 7, 113],
[110.00000, 5, 106],
[105.00000, 2, 101],
[101.00000, 99, 99],
# mid
[99.00000, 102, 102],
[95.00000, 3, 105],
[90.00000, 3, 108],
[85.00000, 5, 113],
[70.00000, 5, 118],
[69.99500, 10, 128],
[99.00000, 99, 99],
[95.00000, 3, 102],
[90.00000, 3, 105],
[85.00000, 5, 110],
[70.00000, 5, 115],
[69.99500, 10, 125],
]
@@ -81,7 +82,6 @@ def verify_prices_descending(page: Page):
assert prices == sorted(prices, reverse=True)
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("risk_accepted")
def test_orderbook_grid_content(setup_market, page: Page):
vega = setup_market[0]
@@ -109,6 +109,7 @@ def test_orderbook_grid_content(setup_market, page: Page):
vega.wait_fn(1)
vega.wait_for_total_catchup()
next_epoch(vega)
# 6003-ORDB-001
# 6003-ORDB-002
@@ -120,7 +121,6 @@ def test_orderbook_grid_content(setup_market, page: Page):
page.goto(f"/#/markets/{market_id}")
page.locator("[data-testid=Orderbook]").click()
# 6003-ORDB-013
assert (
float(page.locator("[data-testid*=last-traded]").text_content())
@@ -144,50 +144,46 @@ def test_orderbook_grid_content(setup_market, page: Page):
def test_orderbook_resolution_change(setup_market, page: Page):
market_id = setup_market[1]
# 6003-ORDB-008
orderbook_content_0_00 = [
[130.01, 10, 94],
[130.00, 3, 84],
[120.00, 7, 81],
[110.00, 5, 74],
[105.00, 2, 69],
[101.00, 67, 67],
orderbook_content_0_01 = [
[130.01, 10, 126],
[130.00, 3, 116],
[120.00, 7, 113],
[110.00, 5, 106],
[105.00, 2, 101],
[101.00, 99, 99],
# mid
[99.00, 102, 102],
[95.00, 3, 105],
[90.00, 3, 108],
[85.00, 5, 113],
[70.00, 15, 128],
[99.00, 99, 99],
[95.00, 3, 102],
[90.00, 3, 105],
[85.00, 5, 110],
[70.00, 15, 125],
]
orderbook_content_10 = [
[130, 13, 94],
[120, 7, 81],
[110, 7, 74],
[100, 67, 67],
orderbook_content_1 = [
[130, 13, 126],
[120, 7, 113],
[110, 5, 106],
[105, 2, 101],
[101, 99, 99],
# mid
[100, 105, 105],
[90, 8, 113],
[70, 15, 128],
[99, 99, 99],
[95, 3, 102],
[90, 3, 105],
[85, 5, 110],
[70, 15, 125],
]
orderbook_content_100 = [
[100, 94, 94],
# mid
[100, 128, 128],
]
resolutions = [
["0.00", orderbook_content_0_00],
["10", orderbook_content_10],
["100", orderbook_content_100],
["0.01", orderbook_content_0_01],
["1", orderbook_content_1],
]
page.goto(f"/#/markets/{market_id}")
# temporary skip
# for resolution in resolutions:
# page.get_by_test_id("resolution").click()
# page.get_by_role("menu").get_by_text(resolution[0], exact=True).click()
# verify_orderbook_grid(page, resolution[1])
for resolution in resolutions:
page.get_by_test_id("resolution").click()
page.get_by_role("menuitem").get_by_text(resolution[0], exact=True).click()
verify_orderbook_grid(page, resolution[1])
@pytest.mark.usefixtures("risk_accepted")
@@ -209,7 +205,6 @@ def test_orderbook_price_size_copy(setup_market, page: Page):
expect(page.get_by_test_id("order-size")).to_have_value(volume.text_content())
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("risk_accepted")
def test_orderbook_price_movement(setup_market, page: Page):
vega = setup_market[0]
@@ -119,7 +119,7 @@ def setup_market_and_referral_scheme(vega: VegaServiceNull, continuous_market: s
forward_time(vega)
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
@@ -163,7 +163,7 @@ def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaSer
"1%", "1", "1%", "0", "1", "1"))
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
@@ -1,16 +1,15 @@
import pytest
from playwright.sync_api import expect, Page
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_share_usage_data(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -41,7 +40,6 @@ ICON_TO_TOAST = {
}
@pytest.mark.usefixtures("risk_accepted")
def test_toast_positions(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -52,10 +50,10 @@ def test_toast_positions(page: Page):
expect(page.locator(f"[{toast_selector}]")).to_be_visible()
@pytest.mark.usefixtures("risk_accepted")
def test_dark_mode(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
expect(page.locator("html")).not_to_have_attribute("class", "dark")
page.locator("#switch-settings-theme-switch").click()
expect(page.locator("html")).to_have_attribute("class", "dark")
expect(page.locator("html")).to_have_attribute("class", "dark")
+2
View File
@@ -391,6 +391,8 @@
"multisigContractIncorrect": "was incorrectly configured as at the end of the last epoch so rewards were penalised. Validator and delegator rewards will continue to be penalised until this is resolved.",
"multisigContractLink": "Ethereum Multisig Contract",
"multisigPenalty": "Multisig penalty",
"multisigPenaltyThisNodeIndicator": "The multisig score for this node is equal to zero.",
"multisigPenaltyDescription": "The multisig score is used in the calculation of rewards. For each validator that gets a multisig score of zero, no staking rewards are paid to that consensus validators and their nominators until the epoch following the one in which the configuration issue is resolved.",
"myPendingStake": "My pending stake",
"myStake": "My stake",
"n/a": "N/A",
@@ -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(
$proposalType: ProposalType
$inState: ProposalState
) {
proposalsConnection(proposalType: $proposalType, inState: $inState) {
edges {
node {
...MarketViewProposalFields
proposalNode {
... on BatchProposal {
id
subProposals {
...SubProposal
}
}
... on Proposal {
...MarketViewProposalFields
}
}
}
}
@@ -500,6 +531,8 @@ query MarketViewProposals(
subscription MarketViewLiveProposals {
proposals {
...MarketViewProposalFields
... on Proposal {
...MarketViewProposalFields
}
}
}
File diff suppressed because one or more lines are too long
@@ -1,6 +1,2 @@
export {
marketViewProposalsDataProvider,
proposalsDataProvider,
useMarketProposals,
} from './proposals-data-provider';
export * from './proposals-data-provider';
export * from './__generated__/Proposals';
@@ -14,8 +14,13 @@ import {
type MarketViewProposalsQuery,
type MarketViewProposalsQueryVariables,
type MarketViewLiveProposalsSubscriptionVariables,
type SubProposalFragment,
} from './__generated__/Proposals';
import { removePaginationWrapper } from '@vegaprotocol/utils';
export type ProposalFragment =
| MarketViewProposalFieldsFragment
| SubProposalFragment;
export type ProposalFragments = Array<ProposalFragment>;
const getData = (responseData: ProposalsListQuery | null) =>
responseData?.proposalsConnection?.edges
@@ -63,23 +68,23 @@ const ProposalTypeMap: Record<
};
const matchFilter = (
data: MarketViewProposalFieldsFragment,
data: ProposalFragment,
variables: MarketViewProposalsQueryVariables
) => {
return (
(!variables.inState || data.state === variables.inState) &&
(!variables.proposalType ||
data.terms.change.__typename === ProposalTypeMap[variables.proposalType])
data.terms?.change.__typename === ProposalTypeMap[variables.proposalType])
);
};
export const update: Update<
MarketViewProposalFieldsFragment[] | null,
MarketViewProposalFieldsFragment,
ProposalFragment[] | null,
ProposalFragment,
MarketViewProposalsQueryVariables
> = (
data: MarketViewProposalFieldsFragment[] | null,
delta: MarketViewProposalFieldsFragment,
data: ProposalFragment[] | null,
delta: ProposalFragment,
reload,
variables
) => {
@@ -104,13 +109,30 @@ export const update: Update<
const getMarketProposalsData = (
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 = {};
export const marketViewProposalsDataProvider = makeDataProvider<
MarketViewProposalsQuery,
MarketViewProposalFieldsFragment[],
ProposalFragments,
MarketViewLiveProposalsSubscription,
MarketViewProposalFieldsFragment,
MarketViewProposalsQueryVariables,
@@ -23,13 +23,14 @@ const vegaCustomClasses = plugin(function ({ addUtilities }) {
background: colors.white,
color: colors.neutral[700],
border: `1px solid ${colors.neutral[300]}`,
borderRadius: '0.5rem',
},
'.syntax-highlighter-wrapper-sm .hljs': {
fontSize: '0.875rem',
lineHeight: '1.25rem',
fontSize: '0.75rem',
lineHeight: '1rem',
},
'.dark .syntax-highlighter-wrapper .hljs': {
background: colors.neutral[800],
background: theme.colors.vega.cdark[900],
color: theme.colors.vega.green.DEFAULT,
border: 0,
},
+22 -8
View File
@@ -1232,6 +1232,14 @@ export type EpochTimestamps = {
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 */
export type Erc20ListAssetBundle = {
__typename?: 'Erc20ListAssetBundle';
@@ -1955,7 +1963,7 @@ export type LiquidityOrderReference = {
/** Information about a liquidity provider */
export type 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>;
/** Market ID the liquidity provision is for */
marketId: Scalars['ID'];
@@ -1983,7 +1991,7 @@ export type LiquidityProviderEdge = {
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 = {
__typename?: 'LiquidityProviderFeeShare';
/** The average entry valuation of the liquidity provider for the market */
@@ -2483,7 +2491,7 @@ export type MarketData = {
indicativeVolume: Scalars['String'];
/** The last traded price (an unsigned integer) */
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>>;
/** SLA performance statistics */
liquidityProviderSla?: Maybe<Array<LiquidityProviderSLA>>;
@@ -3032,7 +3040,7 @@ export type Normaliser = {
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 = {
__typename?: 'ObservableLiquidityProviderFeeShare';
/** The average entry valuation of the liquidity provider for the market */
@@ -3099,7 +3107,7 @@ export type ObservableMarketData = {
indicativeVolume: Scalars['String'];
/** The last traded price (an unsigned integer) */
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>>;
/** SLA performance statistics */
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 */
export type PartytransfersConnectionArgs = {
direction?: InputMaybe<TransferDirection>;
fromAccountType?: InputMaybe<AccountType>;
fromEpoch?: InputMaybe<Scalars['Int']>;
gameId?: InputMaybe<Scalars['ID']>;
isReward?: InputMaybe<Scalars['Boolean']>;
pagination?: InputMaybe<Pagination>;
scope?: InputMaybe<TransferScope>;
status?: InputMaybe<TransferStatus>;
toAccountType?: InputMaybe<AccountType>;
toEpoch?: InputMaybe<Scalars['Int']>;
};
@@ -4471,7 +4481,7 @@ export enum ProposalRejectionReason {
PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID = 'PROPOSAL_ERROR_GOVERNANCE_TRANSFER_PROPOSAL_INVALID',
/** Proposal terms timestamps are not compatible (Validation < Closing < Enactment) */
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',
/** The proposer for this proposal has insufficient tokens */
PROPOSAL_ERROR_INSUFFICIENT_TOKENS = 'PROPOSAL_ERROR_INSUFFICIENT_TOKENS',
@@ -4653,7 +4663,7 @@ export type ProposalVoteEdge = {
export type 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'];
/** Total number of votes cast for this side */
totalNumber: Scalars['String'];
@@ -5466,6 +5476,7 @@ export type QuerytransferArgs = {
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerytransfersConnectionArgs = {
direction?: InputMaybe<TransferDirection>;
fromAccountType?: InputMaybe<AccountType>;
fromEpoch?: InputMaybe<Scalars['Int']>;
gameId?: InputMaybe<Scalars['ID']>;
isReward?: InputMaybe<Scalars['Boolean']>;
@@ -5473,6 +5484,7 @@ export type QuerytransfersConnectionArgs = {
partyId?: InputMaybe<Scalars['ID']>;
scope?: InputMaybe<TransferScope>;
status?: InputMaybe<TransferStatus>;
toAccountType?: InputMaybe<AccountType>;
toEpoch?: InputMaybe<Scalars['Int']>;
};
@@ -7238,7 +7250,9 @@ export type Vote = {
__typename?: 'Vote';
/** RFC3339Nano time and date when the vote reached Vega network */
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'];
/** Total number of governance tokens for the party that cast the vote */
governanceTokenBalance: Scalars['String'];
-1
View File
@@ -199,7 +199,6 @@
"flush-promises": "^1.0.2",
"glob": "^8.0.3",
"husky": "^7.0.4",
"inquirer": "^8.0.0",
"jest": "29.4.3",
"jest-canvas-mock": "^2.3.1",
"jest-environment-jsdom": "^29.4.1",
-82
View File
@@ -1,82 +0,0 @@
const { execSync } = require('child_process');
const inquirer = require('inquirer');
const getTags = (take) => {
const tags = execSync(
"git for-each-ref --sort=taggerdate --format '%(refname)' refs/tags"
)
.toString()
.trim()
.split('\n')
.map((t) => t.replace('refs/tags/', ''))
.reverse()
.slice(0, take);
return tags;
};
const getReleaseBranches = () => {
const branches = execSync('git branch --remote | grep origin/release/')
.toString()
.trim()
.split('\n')
.map((b) => b.trim().replace('origin/release/', ''));
return branches;
};
const release = (tag, branch) => {
const steps = [
`git checkout ${branch}`,
'git pull',
`git reset --hard ${tag}`,
'git push --force',
];
try {
for (const cmd of steps) {
const result = execSync(cmd).toString();
}
} catch (err) {
console.error('Could not make a release');
}
};
inquirer
.prompt([
{
type: 'list',
name: 'tag',
message: 'What version would you like to release?',
choices: getTags(),
},
{
type: 'checkbox',
name: 'envs',
message: 'To what environment you wish to release?',
choices: ['mainnet', ...getReleaseBranches()],
validate(answer) {
return answer.length > 0;
},
},
])
.then((answers) => {
inquirer
.prompt({
type: 'confirm',
name: 'sure',
message: `Are you sure? This will release ${
answers.tag
} to ${answers.envs.join(', ')}`,
})
.then(() => {
for (const env of answers.envs) {
const branch = env === 'mainnet' ? 'main' : `release/${env}`;
release(answers.tag, branch);
}
execSync('git checkout develop');
})
.catch((err) => {
console.log('Something went wrong', err.toString());
});
})
.catch((err) => {
console.log('Something went wrong', err.toString());
});