Compare commits

..
Author SHA1 Message Date
asiaznik 3588be1a32 chore: reduced dialog spacing on mobile 2023-12-06 11:08:20 +01:00
asiaznik 067fe3b8ba fix(environment): node switcher on mobile 2023-12-06 10:48:37 +01:00
216 changed files with 2179 additions and 6151 deletions
-1
View File
@@ -77,7 +77,6 @@
"fixStyle": "inline-type-imports"
}
],
"@typescript-eslint/no-useless-constructor": 0,
"curly": ["error", "multi-line"]
}
},
-3
View File
@@ -1,8 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Auto-format all files
yarn nx format:write
# Lint all staged files
yarn lint-staged
+3 -3
View File
@@ -1,8 +1,8 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Lint all staged files - this brings more value as pre-commit
# yarn nx format:check
# Lint all staged files
yarn nx format:check
# Test all projects with changes
# yarn nx affected -t test --exclude trading
yarn nx affected -t test --exclude trading
+2 -6
View File
@@ -12,8 +12,7 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
import { RouterProvider } from 'react-router-dom';
import { useRouterConfig } from './routes/router-config';
import { createBrowserRouter } from 'react-router-dom';
import { router } from './routes/router-config';
import { t } from '@vegaprotocol/i18n';
import { Suspense } from 'react';
@@ -39,10 +38,7 @@ function App() {
}
>
<Suspense fallback={splashLoading}>
<RouterProvider
router={createBrowserRouter(useRouterConfig())}
fallbackElement={splashLoading}
/>
<RouterProvider router={router} fallbackElement={splashLoading} />
</Suspense>
</NodeGuard>
<NodeSwitcherDialog
@@ -14,7 +14,7 @@ import { Routes } from '../../routes/route-names';
import { NetworkSwitcher } from '@vegaprotocol/environment';
import type { Navigable } from '../../routes/router-config';
import { isNavigable } from '../../routes/router-config';
import { useRouterConfig } from '../../routes/router-config';
import { routerConfig } from '../../routes/router-config';
import { useMemo } from 'react';
import compact from 'lodash/compact';
import { Search } from '../search';
@@ -26,7 +26,6 @@ const routeToNavigationItem = (r: Navigable) => (
);
export const Header = () => {
const routerConfig = useRouterConfig();
const isHome = Boolean(useMatch(Routes.HOME));
const pages = routerConfig[0].children || [];
const mainItems = compact(
@@ -6,7 +6,6 @@ import { Time } from '../time';
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market';
import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg';
import { OrderTypeMapping } from '@vegaprotocol/types';
export interface DeterministicOrderDetailsProps {
id: string;
@@ -70,7 +69,7 @@ const DeterministicOrderDetails = ({
<span className="mx-5 text-base">@</span>
<PriceInMarket price={o.price} marketId={o.market.id} />
</h2>
<p className="text-gray-400 dark:text-gray-600">
<p className="text-gray-200">
In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />.
</p>
{o.peggedOrder ? (
@@ -84,12 +83,13 @@ const DeterministicOrderDetails = ({
/>
</p>
) : null}
{o.reference ? (
<p className="text-gray-500 mt-4">
<span>{t('Reference')}</span>: {o.reference}
</p>
) : null}
<div className="grid md:grid-cols-5 gap-x-6 mt-4">
<div className="grid md:grid-cols-4 gap-x-6 mt-4">
<div className="mb-12 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-4">
{t('Status')}
@@ -114,16 +114,6 @@ const DeterministicOrderDetails = ({
{o.version}
</h5>
</div>
{o.type ? (
<div className="">
<h2 className="text-2xl font-bold text-dark mb-4">
{t('Type')}
</h2>
<h5 className="text-lg font-medium text-gray-500 mb-0">
{OrderTypeMapping[o.type]}
</h5>
</div>
) : null}
</div>
</div>
</div>
@@ -80,12 +80,6 @@ export function getLabelForOrderType(
if (command.orderSubmission.icebergOpts) {
return 'Iceberg';
}
if (command.orderSubmission.type === 'TYPE_MARKET') {
return 'Market order';
}
if (command.orderSubmission.type === 'TYPE_LIMIT') {
return 'Limit order';
}
}
return 'Order';
}
+287 -290
View File
@@ -18,6 +18,7 @@ import { t } from '@vegaprotocol/i18n';
import { Routes } from './route-names';
import { NetworkParameters } from './network-parameters';
import type { Params, RouteObject } from 'react-router-dom';
import { createBrowserRouter } from 'react-router-dom';
import { Link } from 'react-router-dom';
import { MarketPage, MarketsPage } from './markets';
import type { ReactNode } from 'react';
@@ -28,7 +29,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import RestrictedPage from './restricted';
export type Navigable = {
@@ -59,315 +60,311 @@ type Route = RouteItem & {
children?: RouteItem[];
};
export const useRouterConfig = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const partiesRoutes: Route[] = featureFlags.EXPLORER_PARTIES
? [
{
path: Routes.PARTIES,
element: <Party />,
handle: {
name: t('Parties'),
text: t('Parties'),
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
const partiesRoutes: Route[] = FLAGS.EXPLORER_PARTIES
? [
{
path: Routes.PARTIES,
element: <Party />,
handle: {
name: t('Parties'),
text: t('Parties'),
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
},
children: [
{
index: true,
element: <Parties />,
},
children: [
{
index: true,
element: <Parties />,
},
{
path: ':party',
element: <Party />,
{
path: ':party',
element: <Party />,
children: [
{
index: true,
element: <PartySingle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
children: [
{
index: true,
element: <PartySingle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
{
path: 'assets',
element: <Party />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
children: [
{
index: true,
element: <PartyAccountsByAsset />,
handle: {
breadcrumb: () => {
return t('Assets');
},
},
{
path: 'assets',
element: <Party />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
children: [
{
index: true,
element: <PartyAccountsByAsset />,
handle: {
breadcrumb: () => {
return t('Assets');
},
},
],
},
],
},
],
},
]
: [];
const assetsRoutes: Route[] = featureFlags.EXPLORER_ASSETS
? [
{
path: Routes.ASSETS,
handle: {
name: t('Assets'),
text: t('Assets'),
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
},
children: [
{
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<AssetLink assetId={params.assetId as string} />
),
},
],
},
},
],
},
]
: [];
const genesisRoutes: Route[] = featureFlags.EXPLORER_GENESIS
? [
{
path: Routes.GENESIS,
handle: {
name: t('Genesis'),
text: t('Genesis Parameters'),
breadcrumb: () => (
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
),
],
},
element: <Genesis />,
},
]
: [];
],
},
]
: [];
const governanceRoutes: Route[] = featureFlags.EXPLORER_GOVERNANCE
? [
{
path: Routes.GOVERNANCE,
handle: {
name: t('Governance proposals'),
text: t('Governance Proposals'),
breadcrumb: () => (
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
),
},
element: <Proposals />,
const assetsRoutes: Route[] = FLAGS.EXPLORER_ASSETS
? [
{
path: Routes.ASSETS,
handle: {
name: t('Assets'),
text: t('Assets'),
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
},
]
: [];
const marketsRoutes: Route[] = featureFlags.EXPLORER_MARKETS
? [
{
path: Routes.MARKETS,
handle: {
name: t('Markets'),
text: t('Markets'),
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
},
children: [
{
index: true,
element: <MarketsPage />,
},
{
path: ':marketId',
element: <MarketPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<MarketLink id={params.marketId as string} />
),
},
},
],
},
]
: [];
const networkParametersRoutes: Route[] =
featureFlags.EXPLORER_NETWORK_PARAMETERS
? [
children: [
{
path: Routes.NETWORK_PARAMETERS,
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
handle: {
name: t('NetworkParameters'),
text: t('Network Parameters'),
breadcrumb: () => (
<Link to={Routes.NETWORK_PARAMETERS}>
{t('Network Parameters')}
breadcrumb: (params: Params<string>) => (
<AssetLink assetId={params.assetId as string} />
),
},
},
],
},
]
: [];
const genesisRoutes: Route[] = FLAGS.EXPLORER_GENESIS
? [
{
path: Routes.GENESIS,
handle: {
name: t('Genesis'),
text: t('Genesis Parameters'),
breadcrumb: () => (
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
),
},
element: <Genesis />,
},
]
: [];
const governanceRoutes: Route[] = FLAGS.EXPLORER_GOVERNANCE
? [
{
path: Routes.GOVERNANCE,
handle: {
name: t('Governance proposals'),
text: t('Governance Proposals'),
breadcrumb: () => (
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
),
},
element: <Proposals />,
},
]
: [];
const marketsRoutes: Route[] = FLAGS.EXPLORER_MARKETS
? [
{
path: Routes.MARKETS,
handle: {
name: t('Markets'),
text: t('Markets'),
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
},
children: [
{
index: true,
element: <MarketsPage />,
},
{
path: ':marketId',
element: <MarketPage />,
handle: {
breadcrumb: (params: Params<string>) => (
<MarketLink id={params.marketId as string} />
),
},
},
],
},
]
: [];
const networkParametersRoutes: Route[] = FLAGS.EXPLORER_NETWORK_PARAMETERS
? [
{
path: Routes.NETWORK_PARAMETERS,
handle: {
name: t('NetworkParameters'),
text: t('Network Parameters'),
breadcrumb: () => (
<Link to={Routes.NETWORK_PARAMETERS}>
{t('Network Parameters')}
</Link>
),
},
element: <NetworkParameters />,
},
]
: [];
const validators: Route[] = FLAGS.EXPLORER_VALIDATORS
? [
{
path: Routes.VALIDATORS,
handle: {
name: t('Validators'),
text: t('Validators'),
breadcrumb: () => (
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
),
},
element: <ValidatorsPage />,
},
]
: [];
const linkTo = (...segments: (string | undefined)[]) =>
compact(segments).join('/');
export const routerConfig: Route[] = [
{
path: Routes.HOME,
element: <Layout />,
handle: {
name: t('Home'),
text: t('Home'),
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
},
errorElement: <ErrorBoundary />,
children: [
{
index: true,
element: <Home />,
},
{
path: Routes.TX,
handle: {
name: t('Txs'),
text: t('Transactions'),
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
},
children: [
{
path: ':txHash',
element: <Tx />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.TX, params.txHash)}>
{truncateMiddle(remove0x(params.txHash as string))}
</Link>
),
},
element: <NetworkParameters />,
},
]
: [];
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
? [
{
path: Routes.VALIDATORS,
handle: {
name: t('Validators'),
text: t('Validators'),
breadcrumb: () => (
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
),
{
index: true,
element: <TxsList />,
},
element: <ValidatorsPage />,
},
]
: [];
const linkTo = (...segments: (string | undefined)[]) =>
compact(segments).join('/');
const routerConfig: Route[] = [
{
path: Routes.HOME,
element: <Layout />,
handle: {
name: t('Home'),
text: t('Home'),
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
],
},
errorElement: <ErrorBoundary />,
children: [
{
index: true,
element: <Home />,
{
path: Routes.BLOCKS,
handle: {
name: t('Blocks'),
text: t('Blocks'),
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
},
{
path: Routes.TX,
handle: {
name: t('Txs'),
text: t('Transactions'),
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
element: <BlockPage />,
children: [
{
index: true,
element: <Blocks />,
},
children: [
{
path: ':txHash',
element: <Tx />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.TX, params.txHash)}>
{truncateMiddle(remove0x(params.txHash as string))}
</Link>
),
},
{
path: ':block',
element: <Block />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.BLOCKS, params.block)}>
{params.block}
</Link>
),
},
{
index: true,
element: <TxsList />,
},
],
},
{
path: Routes.BLOCKS,
handle: {
name: t('Blocks'),
text: t('Blocks'),
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
},
element: <BlockPage />,
children: [
{
index: true,
element: <Blocks />,
},
{
path: ':block',
element: <Block />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.BLOCKS, params.block)}>
{params.block}
</Link>
),
},
},
],
},
{
path: Routes.ORACLES,
handle: {
name: t('Oracles'),
text: t('Oracles'),
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
},
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.ORACLES, params.id)}>
{truncateMiddle(params.id as string)}
</Link>
),
},
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...networkParametersRoutes,
...validators,
],
},
{
path: Routes.RESTRICTED,
element: <RestrictedPage />,
handle: {
name: t('Restricted'),
text: t('Restricted'),
],
},
{
path: Routes.ORACLES,
handle: {
name: t('Oracles'),
text: t('Oracles'),
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
},
element: <OraclePage />,
children: [
{
index: true,
element: <Oracles />,
},
{
path: ':id',
element: <Oracle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.ORACLES, params.id)}>
{truncateMiddle(params.id as string)}
</Link>
),
},
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
...governanceRoutes,
...marketsRoutes,
...networkParametersRoutes,
...validators,
],
},
{
path: Routes.RESTRICTED,
element: <RestrictedPage />,
handle: {
name: t('Restricted'),
text: t('Restricted'),
},
];
return routerConfig;
};
},
];
export const router = createBrowserRouter(routerConfig);
+9 -17
View File
@@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
import { toBigNum } from '@vegaprotocol/utils';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
import { useWeb3React } from '@web3-react/core';
import React, { Suspense } from 'react';
import { useTranslation } from 'react-i18next';
@@ -15,23 +15,21 @@ import {
} from './contexts/app-state/app-state-context';
import { useContracts } from './contexts/contracts/contracts-context';
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
import { useConnectors } from './lib/vega-connectors';
import { Connectors } from './lib/vega-connectors';
import { useSearchParams } from 'react-router-dom';
const useVegaWalletEagerConnect = () => {
const connectors = useConnectors();
const vegaConnecting = useEagerConnect(connectors);
const vegaConnecting = useEagerConnect(Connectors);
const { pubKey, connect } = useVegaWallet();
const [searchParams] = useSearchParams();
const [query] = React.useState(searchParams.get('address'));
if (query && !pubKey) {
connect(connectors.view);
connect(Connectors['view']);
}
return vegaConnecting;
};
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { account } = useWeb3React();
const { VEGA_URL } = useEnvironment();
@@ -81,16 +79,10 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
}
};
if (!featureFlags.GOVERNANCE_NETWORK_DOWN) {
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
run();
}
}, [
token,
appDispatch,
staking,
vesting,
featureFlags.GOVERNANCE_NETWORK_DOWN,
]);
}, [token, appDispatch, staking, vesting]);
React.useEffect(() => {
if (account && pubKey) {
@@ -155,16 +147,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
};
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
if (featureFlags.GOVERNANCE_NETWORK_LIMITS) {
if (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
getNetworkLimits();
}
return () => {
stopPoll();
};
}, [appDispatch, VEGA_URL, t, featureFlags.GOVERNANCE_NETWORK_LIMITS]);
}, [appDispatch, VEGA_URL, t]);
if (featureFlags.GOVERNANCE_NETWORK_DOWN) {
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
return (
<Splash>
<SplashError />
@@ -7,16 +7,16 @@ import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { useConnectors } from '../../lib/vega-connectors';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState();
const connectors = useConnectors();
return (
<>
<VegaConnectDialog
connectors={connectors}
connectors={Connectors}
riskMessage={<RiskMessage />}
/>
@@ -30,7 +30,7 @@ export const VegaWalletDialogs = () => {
}
/>
<ViewAsDialog connector={connectors.view} />
<ViewAsDialog connector={Connectors.view} />
</>
);
};
@@ -49,8 +49,12 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
? activeProvider
: defaultProvider;
if (account && provider && typeof provider.getSigner === 'function') {
signer = provider.getSigner(account);
if (
account &&
activeProvider &&
typeof activeProvider.getSigner === 'function'
) {
signer = provider.getSigner();
}
const tokenVestingAddress =
+9 -14
View File
@@ -1,5 +1,4 @@
import { useFeatureFlags } from '@vegaprotocol/environment';
import { useMemo } from 'react';
import { FLAGS } from '@vegaprotocol/environment';
import {
JsonRpcConnector,
ViewConnector,
@@ -14,17 +13,13 @@ export const jsonRpc = new JsonRpcConnector();
export const injected = new InjectedConnector();
export const view = new ViewConnector(urlParams.get('address'));
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
export const snap = FLAGS.METAMASK_SNAPS
? new SnapConnector(DEFAULT_SNAP_ID)
: undefined;
export const useConnectors = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
return useMemo(
() => ({
injected,
jsonRpc,
view,
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
}),
[featureFlags.METAMASK_SNAPS]
);
export const Connectors = {
injected,
jsonRpc,
view,
snap,
};
+4 -5
View File
@@ -12,7 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
import Routes from '../routes';
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
import { ExternalLinks, FLAGS } from '@vegaprotocol/environment';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
@@ -175,7 +175,6 @@ export const ValidatorDetailsLink = ({
};
const GovernanceHome = ({ name }: RouteChildProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
useDocumentTitle(name);
const { t } = useTranslation();
const {
@@ -187,9 +186,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
},
});
@@ -19,7 +19,7 @@ import {
mockWalletContext,
createUserVoteQueryMock,
} from '../../test-helpers/mocks';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import { BrowserRouter } from 'react-router-dom';
import { VoteState } from '../vote-details/use-user-vote';
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
@@ -62,7 +62,8 @@ describe('Proposal header', () => {
jest.clearAllMocks();
});
it('Renders New market proposal', () => {
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
const mockedFlags = jest.mocked(FLAGS);
mockedFlags.SUCCESSOR_MARKETS = true;
renderComponent(
generateProposal({
rationale: {
@@ -12,7 +12,7 @@ import {
useNewTransferProposalDetails,
useSuccessorMarketProposalDetails,
} from '@vegaprotocol/proposals';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import Routes from '../../../routes';
import { Link } from 'react-router-dom';
import type { VoteState } from '../vote-details/use-user-vote';
@@ -28,7 +28,6 @@ export const ProposalHeader = ({
isListItem?: boolean;
voteState?: VoteState | null;
}) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const change = proposal?.terms.change;
@@ -55,14 +54,13 @@ export const ProposalHeader = ({
switch (change?.__typename) {
case 'NewMarket': {
proposalType =
featureFlags.PRODUCT_PERPETUALS &&
change?.instrument?.product?.__typename
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
? `NewMarket${change?.instrument?.product?.__typename}`
: 'NewMarket';
fallbackTitle = t('NewMarketProposal');
details = (
<>
{featureFlags.SUCCESSOR_MARKETS && (
{FLAGS.SUCCESSOR_MARKETS && (
<SuccessorCode proposalId={proposal?.id} />
)}
<span>
@@ -84,13 +82,13 @@ export const ProposalHeader = ({
}
case 'UpdateMarketState': {
proposalType =
featureFlags.UPDATE_MARKET_STATE && change?.updateType
FLAGS.UPDATE_MARKET_STATE && change?.updateType
? t(change.updateType)
: 'UpdateMarketState';
fallbackTitle = t('UpdateMarketStateProposal');
details = (
<span>
{featureFlags.UPDATE_MARKET_STATE &&
{FLAGS.UPDATE_MARKET_STATE &&
change?.market?.id &&
change.updateType ? (
<>
@@ -179,14 +177,14 @@ export const ProposalHeader = ({
case 'NewTransfer':
proposalType = 'NewTransfer';
fallbackTitle = t('NewTransferProposal');
details = featureFlags.GOVERNANCE_TRANSFERS ? (
details = FLAGS.GOVERNANCE_TRANSFERS ? (
<NewTransferSummary proposalId={proposal?.id} />
) : null;
break;
case 'CancelTransfer':
proposalType = 'CancelTransfer';
fallbackTitle = t('CancelTransferProposal');
details = featureFlags.GOVERNANCE_TRANSFERS ? (
details = FLAGS.GOVERNANCE_TRANSFERS ? (
<CancelTransferSummary proposalId={proposal?.id} />
) : null;
break;
@@ -54,8 +54,8 @@ export const ProposalReferralProgramDetails = ({
return null;
}
const benefitTiers = proposal?.terms?.change?.benefitTiers.slice();
const stakingTiers = proposal?.terms?.change?.stakingTiers.slice();
const benefitTiers = proposal?.terms?.change?.benefitTiers;
const stakingTiers = proposal?.terms?.change?.stakingTiers;
const windowLength = proposal?.terms?.change?.windowLength;
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
@@ -26,7 +26,7 @@ import {
ProposalCancelTransferDetails,
ProposalTransferDetails,
} from '../proposal-transfer';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
export interface ProposalProps {
@@ -53,7 +53,6 @@ export const Proposal = ({
originalMarketProposalRestData,
mostRecentlyEnactedAssociatedMarketProposal,
}: ProposalProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
@@ -133,7 +132,7 @@ export const Proposal = ({
}
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
const governanceTransferDetails = featureFlags.GOVERNANCE_TRANSFERS && (
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
<>
{proposal.terms.change.__typename === 'NewTransfer' && (
/** Governance New Transfer Details */
@@ -15,11 +15,10 @@ import {
useNetworkParams,
} from '@vegaprotocol/network-parameters';
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
export const ProposalContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const [
mostRecentlyEnactedAssociatedMarketProposal,
setMostRecentlyEnactedAssociatedMarketProposal,
@@ -60,9 +59,9 @@ export const ProposalContainer = () => {
errorPolicy: 'ignore',
variables: {
proposalId: params.proposalId || '',
includeNewMarketProductField: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralProgram: !!featureFlags.REFERRALS,
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
},
skip: !params.proposalId,
});
@@ -121,7 +120,7 @@ export const ProposalContainer = () => {
variables: {
marketId: marketData?.id || '',
},
skip: !featureFlags.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
});
const {
@@ -134,7 +133,7 @@ export const ProposalContainer = () => {
variables: {
marketId: parentMarketId?.market?.parentMarketID || '',
skip:
!featureFlags.SUCCESSOR_MARKETS ||
!FLAGS.SUCCESSOR_MARKETS ||
!isSuccessor ||
!parentMarketId?.market?.parentMarketID,
},
@@ -17,7 +17,7 @@ import {
} from './__generated__/Proposals';
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
return flow([
@@ -43,16 +43,15 @@ export function getNotRejectedProtocolUpgradeProposals<
}
export const ProposalsContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { data, loading, error } = useProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
},
});
@@ -10,7 +10,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
import flow from 'lodash/flow';
import orderBy from 'lodash/orderBy';
import { ProposalState } from '@vegaprotocol/types';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
@@ -33,16 +33,15 @@ export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
}
export const RejectedProposalsContainer = () => {
const featureFlags = useFeatureFlags((state) => state.flags);
const { t } = useTranslation();
const { data, loading, error } = useProposalsQuery({
pollInterval: 5000,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
variables: {
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
},
});
-4
View File
@@ -104,10 +104,6 @@
list-style: circle;
}
.react-markdown-container a {
text-decoration: underline;
}
.jsondiffpatch-delta,
.jsondiffpatch-delta pre {
font-family: 'Roboto Mono', monospace !important;
+3 -2
View File
@@ -22,9 +22,10 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
# NX_DISABLE_CLOSE_POSITION=false
NX_REFERRALS=false
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_DISABLE_CLOSE_POSITION=true
+1 -4
View File
@@ -24,7 +24,4 @@ NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
NX_REFERRALS=true
+1 -3
View File
@@ -23,11 +23,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
+6 -11
View File
@@ -1,9 +1,8 @@
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { ErrorBoundary } from '../../components/error-boundary';
import { FeesContainer } from '../../components/fees-container';
import { useT } from '../../lib/use-t';
import { usePageTitleStore } from '../../stores';
import { titlefy } from '@vegaprotocol/utils';
import { useEffect } from 'react';
export const Fees = () => {
const t = useT();
@@ -11,17 +10,13 @@ export const Fees = () => {
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
}));
useEffect(() => {
updateTitle(titlefy([title]));
}, [updateTitle, title]);
return (
<ErrorBoundary feature="fees">
<div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<FeesContainer />
</div>
</ErrorBoundary>
<div className="container p-4 mx-auto">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<FeesContainer />
</div>
);
};
@@ -6,7 +6,6 @@ import { useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import { LiquidityContainer } from '../../components/liquidity-container';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const enum LiquidityTabs {
Active = 'active',
@@ -59,28 +58,19 @@ export const LiquidityViewContainer = ({
name={t('My liquidity provision')}
hidden={!pubKey}
>
<ErrorBoundary feature="liquidity-party">
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</ErrorBoundary>
<LiquidityContainer
marketId={marketId}
filter={{ partyId: pubKey || undefined }}
/>
</Tab>
<Tab id={LiquidityTabs.Active} name={t('Active')}>
<ErrorBoundary feature="liquidity-active">
<LiquidityContainer
marketId={marketId}
filter={{ active: true }}
/>
</ErrorBoundary>
<LiquidityContainer marketId={marketId} filter={{ active: true }} />
</Tab>
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
<ErrorBoundary feature="liquidity-inactive">
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</ErrorBoundary>
<LiquidityContainer
marketId={marketId}
filter={{ active: false }}
/>
</Tab>
</Tabs>
</div>
+15 -1
View File
@@ -9,6 +9,8 @@ import { TradeGrid } from './trade-grid';
import { TradePanels } from './trade-panels';
import { useNavigate, useParams } from 'react-router-dom';
import { Links } from '../../lib/links';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
@@ -59,6 +61,9 @@ export const MarketPage = () => {
const t = useT();
const { marketId } = useParams();
const navigate = useNavigate();
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
const update = useGlobalStore((store) => store.update);
@@ -67,11 +72,20 @@ export const MarketPage = () => {
const { data, loading } = useMarket(marketId);
useEffect(() => {
if (data?.id && data.id !== lastMarketId) {
if (data?.id && data.id !== lastMarketId && !closed) {
update({ marketId: data.id });
}
}, [update, lastMarketId, data?.id]);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews(
{ type: closed ? ViewType.Info : ViewType.Order },
currentRouteId
);
}
}, [setViews, view, currentRouteId, largeScreen]);
const pinnedAsset = data && getAsset(data);
const tradeView = useMemo(() => {
+20 -53
View File
@@ -18,9 +18,8 @@ import {
MarketSuccessorProposalBanner,
MarketTerminationBanner,
} from '../../components/market-banner';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
interface TradeGridProps {
market: Market | null;
@@ -35,7 +34,6 @@ const MainGrid = memo(
marketId: string;
pinnedAsset?: PinnedAsset;
}) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const t = useT();
const { data: market } = useMarket(marketId);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
@@ -62,40 +60,30 @@ const MainGrid = memo(
id="chart"
overflowHidden
name={t('Chart')}
menu={<TradingViews.chart.menu />}
menu={<TradingViews.candles.menu />}
>
<ErrorBoundary feature="chart">
<TradingViews.chart.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.candles.component marketId={marketId} />
</Tab>
<Tab id="depth" name={t('Depth')}>
<ErrorBoundary feature="depth">
<TradingViews.depth.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.depth.component marketId={marketId} />
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<ErrorBoundary feature="liquidity">
<TradingViews.liquidity.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.liquidity.component marketId={marketId} />
</Tab>
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding-history" name={t('Funding history')}>
<ErrorBoundary feature="funding-history">
<TradingViews.funding.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.funding.component marketId={marketId} />
</Tab>
) : null}
{market &&
market.tradableInstrument.instrument.product.__typename ===
'Perpetual' ? (
<Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="funding-payments">
<TradingViews.fundingPayments.component
marketId={marketId}
/>
</ErrorBoundary>
<TradingViews.fundingPayments.component
marketId={marketId}
/>
</Tab>
) : null}
</Tabs>
@@ -108,14 +96,10 @@ const MainGrid = memo(
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-right">
<Tab id="orderbook" name={t('Orderbook')}>
<ErrorBoundary feature="orderbook">
<TradingViews.orderbook.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.orderbook.component marketId={marketId} />
</Tab>
<Tab id="trades" name={t('Trades')}>
<ErrorBoundary feature="trades">
<TradingViews.trades.component marketId={marketId} />
</ErrorBoundary>
<TradingViews.trades.component marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
@@ -134,43 +118,31 @@ const MainGrid = memo(
name={t('Positions')}
menu={<TradingViews.positions.menu />}
>
<ErrorBoundary feature="positions">
<TradingViews.positions.component />
</ErrorBoundary>
<TradingViews.positions.component />
</Tab>
<Tab
id="open-orders"
name={t('Open')}
menu={<TradingViews.activeOrders.menu />}
>
<ErrorBoundary feature="activeOrders">
<TradingViews.activeOrders.component />
</ErrorBoundary>
<TradingViews.activeOrders.component />
</Tab>
<Tab id="closed-orders" name={t('Closed')}>
<ErrorBoundary feature="closedOrders">
<TradingViews.closedOrders.component />
</ErrorBoundary>
<TradingViews.closedOrders.component />
</Tab>
<Tab id="rejected-orders" name={t('Rejected')}>
<ErrorBoundary feature="rejectedOrders">
<TradingViews.rejectedOrders.component />
</ErrorBoundary>
<TradingViews.rejectedOrders.component />
</Tab>
<Tab
id="orders"
name={t('All')}
menu={<TradingViews.orders.menu />}
>
<ErrorBoundary feature="orders">
<TradingViews.orders.component />
</ErrorBoundary>
<TradingViews.orders.component />
</Tab>
{featureFlags.STOP_ORDERS ? (
{FLAGS.STOP_ORDERS ? (
<Tab id="stop-orders" name={t('Stop orders')}>
<ErrorBoundary feature="stop-orders">
<TradingViews.stopOrders.component />
</ErrorBoundary>
<TradingViews.stopOrders.component />
</Tab>
) : null}
<Tab id="fills" name={t('Fills')}>
@@ -181,11 +153,7 @@ const MainGrid = memo(
name={t('Collateral')}
menu={<TradingViews.collateral.menu />}
>
<ErrorBoundary feature="collateral">
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
/>
</ErrorBoundary>
<TradingViews.collateral.component pinnedAsset={pinnedAsset} />
</Tab>
</Tabs>
</TradeGridChild>
@@ -197,7 +165,6 @@ const MainGrid = memo(
MainGrid.displayName = 'MainGrid';
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const wrapperClasses = classNames(
'h-full grid',
'grid-rows-[min-content_1fr]'
@@ -206,7 +173,7 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
return (
<div className={wrapperClasses}>
<div>
{featureFlags.SUCCESSOR_MARKETS && (
{FLAGS.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
@@ -1,20 +1,19 @@
import { type PinnedAsset } from '@vegaprotocol/accounts';
import { type Market } from '@vegaprotocol/markets';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import type { Market } from '@vegaprotocol/markets';
import { OracleBanner } from '@vegaprotocol/markets';
import type { TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useState } from 'react';
import AutoSizer from 'react-virtualized-auto-sizer';
import classNames from 'classnames';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
import {
MarketSuccessorBanner,
MarketSuccessorProposalBanner,
MarketTerminationBanner,
} from '../../components/market-banner';
import { ErrorBoundary } from '../../components/error-boundary';
import { type TradingView } from './trade-views';
import { TradingViews } from './trade-views';
import { useFeatureFlags } from '@vegaprotocol/environment';
import { FLAGS } from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { Splash } from '@vegaprotocol/ui-toolkit';
interface TradePanelsProps {
market: Market | null;
@@ -22,8 +21,7 @@ interface TradePanelsProps {
}
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const [view, setView] = useState<TradingView>('chart');
const [view, setView] = useState<TradingView>('candles');
const renderView = () => {
const Component = TradingViews[view].component;
@@ -36,11 +34,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
// Watch out here, we don't know what component is being rendered
// so watch out for clashes in props
return (
<ErrorBoundary feature={view}>
<Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
</ErrorBoundary>
);
return <Component marketId={market?.id} pinnedAsset={pinnedAsset} />;
};
const renderMenu = () => {
@@ -50,7 +44,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
const Menu = viewCfg.menu;
return (
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
<Menu />
</div>
);
@@ -62,7 +56,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
return (
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
<div>
{featureFlags.SUCCESSOR_MARKETS && (
{FLAGS.SUCCESSOR_MARKETS && (
<>
<MarketSuccessorBanner market={market} />
<MarketSuccessorProposalBanner marketId={market?.id} />
@@ -149,7 +143,7 @@ const useViewLabel = (view: TradingView) => {
const t = useT();
const labels = {
chart: t('Chart'),
candles: t('Candles'),
depth: t('Depth'),
liquidity: t('Liquidity'),
funding: t('Funding'),
@@ -1,4 +1,8 @@
import { DepthChartContainer } from '@vegaprotocol/market-depth';
import {
CandlesChartContainer,
CandlesMenu,
} from '@vegaprotocol/candles-chart';
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
import { TradesContainer } from '../../components/trades-container';
import { OrderbookContainer } from '../../components/orderbook-container';
@@ -12,14 +16,13 @@ import { OrdersContainer } from '../../components/orders-container';
import { StopOrdersContainer } from '../../components/stop-orders-container';
import { AccountsMenu } from '../../components/accounts-menu';
import { PositionsMenu } from '../../components/positions-menu';
import { ChartContainer, ChartMenu } from '../../components/chart-container';
export type TradingView = keyof typeof TradingViews;
export const TradingViews = {
chart: {
component: ChartContainer,
menu: ChartMenu,
candles: {
component: CandlesChartContainer,
menu: CandlesMenu,
},
depth: {
component: DepthChartContainer,
@@ -15,7 +15,6 @@ import {
useLinks,
} from '@vegaprotocol/environment';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
export const MarketsPage = () => {
const t = useT();
@@ -35,9 +34,7 @@ export const MarketsPage = () => {
<div className="h-full my-1 border rounded-sm border-default">
<Tabs storageKey="console-markets">
<Tab id="open-markets" name={t('Open markets')}>
<ErrorBoundary feature="markets-open">
<OpenMarkets />
</ErrorBoundary>
<OpenMarkets />
</Tab>
<Tab
id="proposed-markets"
@@ -52,14 +49,10 @@ export const MarketsPage = () => {
</TradingAnchorButton>
}
>
<ErrorBoundary feature="markets-proposed">
<Proposed />
</ErrorBoundary>
<Proposed />
</Tab>
<Tab id="closed-markets" name={t('Closed markets')}>
<ErrorBoundary feature="markets-closed">
<Closed />
</ErrorBoundary>
<Closed />
</Tab>
</Tabs>
</div>
@@ -4,26 +4,9 @@ import {
SidebarButton,
SidebarDivider,
ViewType,
useSidebar,
} from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useEffect } from 'react';
const ViewInitializer = () => {
const currentRouteId = useGetCurrentRouteId();
const { setViews, getView } = useSidebar();
const view = getView(currentRouteId);
const { screenSize } = useScreenDimensions();
const largeScreen = ['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize);
useEffect(() => {
if (largeScreen && view === undefined) {
setViews({ type: ViewType.Order }, currentRouteId);
}
}, [setViews, view, currentRouteId, largeScreen]);
return null;
};
export const MarketsSidebar = () => {
const t = useT();
@@ -54,7 +37,6 @@ export const MarketsSidebar = () => {
path=":marketId"
element={
<>
<ViewInitializer />
<SidebarDivider />
<SidebarButton
view={ViewType.Order}
@@ -25,7 +25,6 @@ import { DepositsMenu } from '../../components/deposits-menu';
import { WithdrawalsMenu } from '../../components/withdrawals-menu';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const WithdrawalsIndicator = () => {
const { ready } = useIncompleteWithdrawals();
@@ -39,21 +38,11 @@ const WithdrawalsIndicator = () => {
);
};
const SidebarViewInitializer = () => {
export const Portfolio = () => {
const t = useT();
const currentRouteId = useGetCurrentRouteId();
const { getView, setViews } = useSidebar();
const view = getView(currentRouteId);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
return null;
};
export const Portfolio = () => {
const t = useT();
const { updateTitle } = usePageTitleStore((store) => ({
updateTitle: store.updateTitle,
@@ -63,11 +52,17 @@ export const Portfolio = () => {
updateTitle(titlefy([t('Portfolio')]));
}, [updateTitle, t]);
// Make transfer sidebar open by default
useEffect(() => {
if (view === undefined) {
setViews({ type: ViewType.Transfer }, currentRouteId);
}
}, [view, setViews, currentRouteId]);
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
const wrapperClasses = 'p-0.5 h-full max-h-full flex flex-col';
return (
<div className={wrapperClasses}>
<SidebarViewInitializer />
<ResizableGrid vertical onChange={handleOnLayoutChange}>
<ResizableGridPanel minSize={75}>
<PortfolioGridChild>
@@ -77,29 +72,19 @@ export const Portfolio = () => {
name={t('Positions')}
menu={<PositionsMenu />}
>
<ErrorBoundary feature="portfolio-positions">
<PositionsContainer allKeys />
</ErrorBoundary>
<PositionsContainer allKeys />
</Tab>
<Tab id="orders" name={t('Orders')}>
<ErrorBoundary feature="portfolio-orders">
<OrdersContainer />
</ErrorBoundary>
<OrdersContainer />
</Tab>
<Tab id="fills" name={t('Fills')}>
<ErrorBoundary feature="portfolio-fills">
<FillsContainer />
</ErrorBoundary>
<FillsContainer />
</Tab>
<Tab id="funding-payments" name={t('Funding payments')}>
<ErrorBoundary feature="portfolio-funding-payments">
<FundingPaymentsContainer />
</ErrorBoundary>
<FundingPaymentsContainer />
</Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}>
<ErrorBoundary feature="portfolio-ledger">
<LedgerContainer />
</ErrorBoundary>
<LedgerContainer />
</Tab>
</Tabs>
</PortfolioGridChild>
@@ -116,14 +101,10 @@ export const Portfolio = () => {
name={t('Collateral')}
menu={<AccountsMenu />}
>
<ErrorBoundary feature="portfolio-accounts">
<AccountsContainer />
</ErrorBoundary>
<AccountsContainer />
</Tab>
<Tab id="deposits" name={t('Deposits')} menu={<DepositsMenu />}>
<ErrorBoundary feature="portfolio-deposit">
<DepositsContainer />
</ErrorBoundary>
<DepositsContainer />
</Tab>
<Tab
id="withdrawals"
@@ -131,9 +112,7 @@ export const Portfolio = () => {
indicator={<WithdrawalsIndicator />}
menu={<WithdrawalsMenu />}
>
<ErrorBoundary feature="portfolio-deposit">
<WithdrawalsContainer />
</ErrorBoundary>
<WithdrawalsContainer />
</Tab>
</Tabs>
</PortfolioGridChild>
@@ -13,40 +13,15 @@ import type { ButtonHTMLAttributes, MouseEventHandler } from 'react';
import { useCallback, useEffect, useRef, useState } from 'react';
import { RainbowButton } from './buttons';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useIsInReferralSet, useReferral } from './hooks/use-referral';
import { useReferral } from './hooks/use-referral';
import { Routes } from '../../lib/links';
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
import { Statistics, useStats } from './referral-statistics';
import { useReferralProgram } from './hooks/use-referral-program';
import { ns, useT } from '../../lib/use-t';
import { useFundsAvailable } from './hooks/use-funds-available';
import { ViewType, useSidebar } from '../../components/sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { QUSDTooltip } from './qusd-tooltip';
import { Trans } from 'react-i18next';
import { useT } from '../../lib/use-t';
const RELOAD_DELAY = 3000;
const SPAM_PROTECTION_ERR = 'SPAM_PROTECTION_ERR';
const SpamProtectionErr = ({
requiredFunds,
}: {
requiredFunds?: string | number | bigint;
}) => {
if (!requiredFunds) return null;
// eslint-disable-next-line react/jsx-no-undef
return (
<Trans
defaults="To protect the network from spam, you must have at least {{requiredFunds}} <0>qUSD</0> of any asset on the network to proceed."
values={{
requiredFunds,
}}
components={[<QUSDTooltip key="qusd" />]}
ns={ns}
/>
);
};
const validateCode = (value: string, t: ReturnType<typeof useT>) => {
const number = +`0x${value}`;
if (!value || value.length !== 64) {
@@ -57,23 +32,20 @@ const validateCode = (value: string, t: ReturnType<typeof useT>) => {
return true;
};
export const ApplyCodeFormContainer = ({
onSuccess,
}: {
onSuccess?: () => void;
}) => {
export const ApplyCodeFormContainer = () => {
const { pubKey } = useVegaWallet();
const isInReferralSet = useIsInReferralSet(pubKey);
const { data: referee } = useReferral({ pubKey, role: 'referee' });
const { data: referrer } = useReferral({ pubKey, role: 'referrer' });
// Navigate to the index page when already in the referral set.
if (isInReferralSet) {
// go to main page if the current pubkey is already a referrer or referee
if (referee || referrer) {
return <Navigate to={Routes.REFERRALS} />;
}
return <ApplyCodeForm onSuccess={onSuccess} />;
return <ApplyCodeForm />;
};
export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
export const ApplyCodeForm = () => {
const t = useT();
const program = useReferralProgram();
const navigate = useNavigate();
@@ -82,15 +54,10 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
);
const [status, setStatus] = useState<
'requested' | 'no-funds' | 'successful' | null
'requested' | 'failed' | 'successful' | null
>(null);
const txHash = useRef<string | null>(null);
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
const { isEligible, requiredFunds } = useFundsAvailable();
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((s) => s.setViews);
const {
register,
handleSubmit,
@@ -106,17 +73,6 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
code: validateCode(codeField, t) ? codeField : undefined,
});
/**
* Validates if a connected party can apply a code (min funds span protection)
*/
const validateFundsAvailable = useCallback(() => {
if (requiredFunds && !isEligible) {
const err = SPAM_PROTECTION_ERR;
return err;
}
return true;
}, [isEligible, requiredFunds]);
/**
* Validates the set a user tries to apply to.
*/
@@ -140,15 +96,6 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
if (code) setValue('code', code);
}, [params, setValue]);
useEffect(() => {
const err = validateFundsAvailable();
if (err !== true) {
setStatus('no-funds');
} else {
setStatus(null);
}
}, [isEligible, validateFundsAvailable]);
const onSubmit = ({ code }: FieldValues) => {
if (isReadOnly || !pubKey || !code || code.length === 0) {
return;
@@ -220,11 +167,10 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
useEffect(() => {
if (status === 'successful') {
setTimeout(() => {
if (onSuccess) onSuccess();
navigate(Routes.REFERRALS);
}, RELOAD_DELAY);
}
}, [navigate, onSuccess, status]);
}, [navigate, status]);
// show "code applied" message when successfully applied
if (status === 'successful') {
@@ -261,18 +207,6 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
};
}
if (status === 'no-funds') {
return {
disabled: false,
children: t('Deposit funds'),
type: 'button' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
onClick: ((event) => {
event.preventDefault();
setViews({ type: ViewType.Deposit }, currentRouteId);
}) as MouseEventHandler,
};
}
if (status === 'requested') {
return {
disabled: true,
@@ -302,9 +236,7 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
{t('Apply a referral code')}
</h3>
<p className="mb-4 text-center text-base">
{t(
'Apply a referral code to access the discount benefits of the current program.'
)}
{t('Enter a referral code to get trading discounts.')}
</p>
<form
className={classNames('flex w-full flex-col gap-4', {
@@ -319,10 +251,8 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
{...register('code', {
required: t('You have to provide a code to apply it.'),
validate: (value) => {
const codeErr = validateCode(value, t);
if (codeErr !== true) return codeErr;
const fundsErr = validateFundsAvailable();
if (fundsErr !== true) return fundsErr;
const err = validateCode(value, t);
if (err !== true) return err;
return validateSet();
},
})}
@@ -332,26 +262,10 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
</label>
<RainbowButton variant="border" {...getButtonProps()} />
</form>
{status === 'no-funds' ? (
<InputError intent="warning" className="overflow-auto break-words">
<span>
<SpamProtectionErr requiredFunds={requiredFunds?.toString()} />
</span>
{errors.code && (
<InputError className="overflow-auto break-words">
{errors.code.message?.toString()}
</InputError>
) : (
errors.code && (
<InputError intent="warning" className="overflow-auto break-words">
{errors.code.message === SPAM_PROTECTION_ERR ? (
<span>
<SpamProtectionErr
requiredFunds={requiredFunds?.toString()}
/>
</span>
) : (
errors.code.message?.toString()
)}
</InputError>
)
)}
</div>
{validateCode(codeField, t) === true && previewLoading && !previewData ? (
@@ -6,8 +6,6 @@ export const SKY_BACKGROUND =
'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat bg-local';
// TODO: Update the links to use the correct referral related pages
export const REFERRAL_DOCS_LINK =
'https://docs.vega.xyz/mainnet/concepts/trading-on-vega/discounts-rewards#referral-program';
export const ABOUT_REFERRAL_DOCS_LINK =
'https://docs.vega.xyz/mainnet/concepts/trading-on-vega/discounts-rewards#referral-program';
export const REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
export const ABOUT_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
export const DISCLAIMER_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
@@ -19,22 +19,14 @@ import {
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
import { useStakeAvailable } from './hooks/use-stake-available';
import { ABOUT_REFERRAL_DOCS_LINK } from './constants';
import { useIsInReferralSet, useReferral } from './hooks/use-referral';
import {
ABOUT_REFERRAL_DOCS_LINK,
DISCLAIMER_REFERRAL_DOCS_LINK,
} from './constants';
import { useReferral } from './hooks/use-referral';
import { useT } from '../../lib/use-t';
import { Navigate } from 'react-router-dom';
import { Routes } from '../../lib/links';
import { useReferralProgram } from './hooks/use-referral-program';
export const CreateCodeContainer = () => {
const { pubKey } = useVegaWallet();
const isInReferralSet = useIsInReferralSet(pubKey);
// Navigate to the index page when already in the referral set.
if (isInReferralSet) {
return <Navigate to={Routes.REFERRALS} />;
}
return <CreateCodeForm />;
};
@@ -56,7 +48,7 @@ export const CreateCodeForm = () => {
</h3>
<p className="mb-4 text-center text-base">
{t(
'Generate a referral code to share with your friends and access the commission benefits of the current program.'
'Generate a referral code to share with your friends and start earning commission.'
)}
</p>
@@ -106,7 +98,10 @@ const CreateCodeDialog = ({
const { stakeAvailable: currentStakeAvailable, requiredStake } =
useStakeAvailable();
const { details: programDetails } = useReferralProgram();
const { data: referralSets } = useReferral({
pubKey,
role: 'referrer',
});
const onSubmit = () => {
if (isReadOnly || !pubKey) {
@@ -206,7 +201,7 @@ const CreateCodeDialog = ({
);
}
if (!programDetails) {
if (!referralSets) {
return (
<div className="flex flex-col gap-4">
{(status === 'idle' || status === 'loading' || status === 'error') && (
@@ -242,9 +237,7 @@ const CreateCodeDialog = ({
intent={Intent.Primary}
onClick={() => onSubmit()}
{...getButtonProps()}
>
{t('Yes')}
</TradingButton>
></TradingButton>
{status === 'idle' && (
<TradingButton
fill={true}
@@ -262,6 +255,9 @@ const CreateCodeDialog = ({
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
{t('About the referral program')}
</ExternalLink>
<ExternalLink href={DISCLAIMER_REFERRAL_DOCS_LINK}>
{t('Disclaimer')}
</ExternalLink>
</div>
</div>
);
@@ -272,7 +268,7 @@ const CreateCodeDialog = ({
{(status === 'idle' || status === 'loading' || status === 'error') && (
<p>
{t(
'Generate a referral code to share with your friends and access the commission benefits of the current program.'
'Generate a referral code to share with your friends and start earning commission.'
)}
</p>
)}
@@ -303,6 +299,9 @@ const CreateCodeDialog = ({
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
{t('About the referral program')}
</ExternalLink>
<ExternalLink href={DISCLAIMER_REFERRAL_DOCS_LINK}>
{t('Disclaimer')}
</ExternalLink>
</div>
</div>
);
@@ -53,7 +53,7 @@ export const NotFound = () => {
const navigate = useNavigate();
return (
<LayoutWithSky className="pt-32">
<div className="pt-32">
<div
aria-hidden
className="absolute top-64 right-[220px] md:right-[340px] max-sm:hidden"
@@ -75,6 +75,6 @@ export const NotFound = () => {
{t('Go back and try again')}
</RainbowButton>
</p>
</LayoutWithSky>
</div>
);
};
@@ -1,20 +0,0 @@
query FundsAvailable($partyId: ID!) {
party(id: $partyId) {
accountsConnection {
edges {
node {
balance
asset {
decimals
symbol
id
}
}
}
}
}
networkParameter(key: "spam.protection.applyReferral.min.funds") {
key
value
}
}
@@ -1,63 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type FundsAvailableQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type FundsAvailableQuery = { __typename?: 'Query', party?: { __typename?: 'Party', accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', balance: string, asset: { __typename?: 'Asset', decimals: number, symbol: string, id: string } } } | null> | null } | null } | null, networkParameter?: { __typename?: 'NetworkParameter', key: string, value: string } | null };
export const FundsAvailableDocument = gql`
query FundsAvailable($partyId: ID!) {
party(id: $partyId) {
accountsConnection {
edges {
node {
balance
asset {
decimals
symbol
id
}
}
}
}
}
networkParameter(key: "spam.protection.applyReferral.min.funds") {
key
value
}
}
`;
/**
* __useFundsAvailableQuery__
*
* To run a query within a React component, call `useFundsAvailableQuery` and pass it any options that fit your needs.
* When your component renders, `useFundsAvailableQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useFundsAvailableQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function useFundsAvailableQuery(baseOptions: Apollo.QueryHookOptions<FundsAvailableQuery, FundsAvailableQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<FundsAvailableQuery, FundsAvailableQueryVariables>(FundsAvailableDocument, options);
}
export function useFundsAvailableLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<FundsAvailableQuery, FundsAvailableQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<FundsAvailableQuery, FundsAvailableQueryVariables>(FundsAvailableDocument, options);
}
export type FundsAvailableQueryHookResult = ReturnType<typeof useFundsAvailableQuery>;
export type FundsAvailableLazyQueryHookResult = ReturnType<typeof useFundsAvailableLazyQuery>;
export type FundsAvailableQueryResult = Apollo.QueryResult<FundsAvailableQuery, FundsAvailableQueryVariables>;
@@ -1,48 +0,0 @@
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useFundsAvailableQuery } from './__generated__/FundsAvailable';
import compact from 'lodash/compact';
import BigNumber from 'bignumber.js';
/**
* Gets the funds for given public key and required min for
* the referral program.
*
* (Uses currently connected public key if left empty)
*/
export const useFundsAvailable = (pubKey?: string) => {
const { pubKey: currentPubKey } = useVegaWallet();
const partyId = pubKey || currentPubKey;
const { data, stopPolling } = useFundsAvailableQuery({
variables: { partyId: partyId || '' },
skip: !partyId,
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
pollInterval: 5000,
});
const fundsAvailable = data
? compact(data.party?.accountsConnection?.edges?.map((e) => e?.node))
: undefined;
const requiredFunds = data
? BigNumber(data.networkParameter?.value || '0')
: undefined;
const sumOfFunds =
fundsAvailable
?.filter((fa) => fa.balance)
.reduce((sum, fa) => sum.plus(BigNumber(fa.balance)), BigNumber(0)) ||
BigNumber(0);
if (requiredFunds && sumOfFunds.isGreaterThanOrEqualTo(requiredFunds)) {
stopPolling();
}
return {
fundsAvailable,
requiredFunds,
isEligible:
fundsAvailable != null &&
requiredFunds != null &&
sumOfFunds.isGreaterThanOrEqualTo(requiredFunds),
};
};
@@ -1,8 +1,14 @@
import { getNumberFormat } from '@vegaprotocol/utils';
import { addDays } from 'date-fns';
import sortBy from 'lodash/sortBy';
import omit from 'lodash/omit';
import { useReferralProgramQuery } from './__generated__/CurrentReferralProgram';
import BigNumber from 'bignumber.js';
const STAKING_TIERS_MAPPING: Record<number, string> = {
1: 'Tradestarter',
2: 'Mid level degen',
3: 'Reward hoarder',
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const MOCK = {
@@ -10,76 +16,46 @@ const MOCK = {
currentReferralProgram: {
id: 'abc',
version: 1,
endOfProgramTimestamp: addDays(new Date(), 10).toISOString(),
windowLength: 10,
benefitTiers: [
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '100000',
referralDiscountFactor: '0.1',
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '30000',
referralDiscountFactor: '0.01',
referralRewardFactor: '0.01',
},
{
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '20000',
referralDiscountFactor: '0.05',
referralRewardFactor: '0.05',
},
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '1000000',
referralDiscountFactor: '0.1',
referralRewardFactor: '0.075',
},
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '5000000',
referralDiscountFactor: '0.1',
referralRewardFactor: '0.1',
},
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '25000000',
referralDiscountFactor: '0.1',
referralRewardFactor: '0.125',
},
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '75000000',
referralDiscountFactor: '0.1',
referralRewardFactor: '0.15',
},
{
minimumEpochs: 1,
minimumRunningNotionalTakerVolume: '150000000',
referralDiscountFactor: '0.07',
referralRewardFactor: '0.175',
minimumEpochs: 5,
minimumRunningNotionalTakerVolume: '10000',
referralDiscountFactor: '0.001',
referralRewardFactor: '0.001',
},
],
stakingTiers: [
{
minimumStakedTokens: '100000000000000000000',
referralRewardMultiplier: '1.025',
minimumStakedTokens: '10000',
referralRewardMultiplier: '1',
},
{
minimumStakedTokens: '1000000000000000000000',
referralRewardMultiplier: '1.05',
minimumStakedTokens: '20000',
referralRewardMultiplier: '2',
},
{
minimumStakedTokens: '5000000000000000000000',
referralRewardMultiplier: '1.1',
},
{
minimumStakedTokens: '50000000000000000000000',
referralRewardMultiplier: '1.2',
},
{
minimumStakedTokens: '250000000000000000000000',
referralRewardMultiplier: '1.25',
},
{
minimumStakedTokens: '500000000000000000000000',
referralRewardMultiplier: '1.3',
minimumStakedTokens: '30000',
referralRewardMultiplier: '3',
},
],
endOfProgramTimestamp: '2024-12-31T01:00:00Z',
windowLength: 30,
},
loading: false,
error: undefined,
},
loading: false,
error: undefined,
};
export const useReferralProgram = () => {
@@ -103,9 +79,9 @@ export const useReferralProgram = () => {
return {
tier: i + 1, // sorted in asc order, hence first is the lowest tier
rewardFactor: Number(t.referralRewardFactor),
commission: BigNumber(t.referralRewardFactor).times(100).toFixed(2) + '%',
commission: Number(t.referralRewardFactor) * 100 + '%',
discountFactor: Number(t.referralDiscountFactor),
discount: BigNumber(t.referralDiscountFactor).times(100).toFixed(2) + '%',
discount: Number(t.referralDiscountFactor) * 100 + '%',
minimumVolume: Number(t.minimumRunningNotionalTakerVolume),
volume: getNumberFormat(0).format(
Number(t.minimumRunningNotionalTakerVolume)
@@ -114,11 +90,13 @@ export const useReferralProgram = () => {
};
});
const stakingTiers = sortBy(data.currentReferralProgram.stakingTiers, (t) =>
parseFloat(t.referralRewardMultiplier)
const stakingTiers = sortBy(
data.currentReferralProgram.stakingTiers,
(t) => t.referralRewardMultiplier
).map((t, i) => {
return {
tier: i + 1,
label: STAKING_TIERS_MAPPING[i + 1],
...t,
};
});
@@ -75,7 +75,10 @@ export const useReferralToasts = () => {
data-testid="toast-apply-code"
size="xs"
onClick={() => {
const matched = matchPath(Routes.REFERRALS, pathname);
const matched = matchPath(
Routes.REFERRALS_APPLY_CODE,
pathname
);
if (!matched) navigate(Routes.REFERRALS_APPLY_CODE);
updateToast(NON_ELIGIBLE_REFERRAL_SET_TOAST_ID + epoch, {
hidden: true,
@@ -2,11 +2,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useCallback } from 'react';
import { useRefereesQuery } from './__generated__/Referees';
import compact from 'lodash/compact';
import pick from 'lodash/pick';
import type {
ReferralSetsQuery,
ReferralSetsQueryVariables,
} from './__generated__/ReferralSets';
import type { ReferralSetsQueryVariables } from './__generated__/ReferralSets';
import { useReferralSetsQuery } from './__generated__/ReferralSets';
import { useStakeAvailable } from './use-stake-available';
@@ -122,96 +118,3 @@ export const useReferral = (args: UseReferralArgs) => {
refetch,
};
};
type Referee = NonNullable<
NonNullable<ReturnType<typeof useReferral>['data']>['referee']
>;
type RefereeProperties = (keyof Referee)[];
const findReferee = (referee: Referee, referees: Referee[]) =>
referees.find((r) => r.refereeId === referee?.refereeId) || referee;
const updateReferee = (
referee: Referee,
referees: Referee[],
properties: RefereeProperties
) => ({
...referee,
...pick(findReferee(referee, referees), properties),
});
export const useUpdateReferees = (
referral: ReturnType<typeof useReferral>,
aggregationEpochs: number,
properties: RefereeProperties,
skip?: boolean
): ReturnType<typeof useReferral> => {
const { data, loading, error, refetch } = useRefereesQuery({
variables: {
code: referral?.data?.code as string,
aggregationEpochs,
},
skip: skip || !referral?.data?.code,
fetchPolicy: 'cache-and-network',
context: { isEnlargedTimeout: true },
});
const refetchAll = useCallback(() => {
refetch();
referral.refetch();
}, [refetch, referral]);
if (!referral.data || skip) {
return referral;
}
const referees = compact(
removePaginationWrapper(data?.referralSetReferees.edges)
);
return {
data: data && {
...referral.data,
referees: referral.data.referees.map((referee) =>
updateReferee(referee, referees, properties)
),
referee:
referral.data.referee &&
updateReferee(referral.data.referee, referees, properties),
},
loading: loading || referral.loading,
error: error || referral.error,
refetch: refetchAll,
};
};
const retrieveReferralSetData = (data: ReferralSetsQuery | undefined) =>
data?.referralSets.edges && data.referralSets.edges.length > 0
? data.referralSets.edges[0]?.node
: undefined;
export const useIsInReferralSet = (pubKey: string | null) => {
const [asRefereeVariables, asRefereeSkip] = prepareVariables({
pubKey,
role: 'referee',
});
const [asReferrerVariables, asReferrerSkip] = prepareVariables({
pubKey,
role: 'referrer',
});
const { data: asRefereeData } = useReferralSetsQuery({
variables: asRefereeVariables,
skip: asRefereeSkip,
fetchPolicy: 'cache-and-network',
});
const { data: asReferrerData } = useReferralSetsQuery({
variables: asReferrerVariables,
skip: asReferrerSkip,
fetchPolicy: 'cache-and-network',
});
return Boolean(
retrieveReferralSetData(asRefereeData) ||
retrieveReferralSetData(asReferrerData)
);
};
@@ -13,6 +13,7 @@ export const useStakeAvailable = (pubKey?: string) => {
const { data } = useStakeAvailableQuery({
variables: { partyId: partyId || '' },
skip: !partyId,
// TODO: remove when network params available
errorPolicy: 'ignore',
});
@@ -15,16 +15,11 @@ export const LandingBanner = () => {
</div>
<div className="pt-20 sm:w-[50%]">
<h1 className="text-6xl font-alpha calt mb-10">
{t('Vega community referrals')}
{t('Earn commission & stake rewards')}
</h1>
<p className="text-lg mb-1">
{t(
'Referral programs can be proposed and created via community governance.'
)}
</p>
<p className="text-lg mb-10">
{t(
'Once live, users can generate referral codes to share with their friends and earn commission on their trades, while referred traders can access fee discounts based on the running volume of the group.'
'Invite friends and earn rewards from the trading fees they pay. Stake those rewards to earn multipliers on future rewards.'
)}
</p>
</div>
@@ -1,28 +0,0 @@
import { DocsLinks } from '@vegaprotocol/environment';
import { ExternalLink, Tooltip } from '@vegaprotocol/ui-toolkit';
import { useT } from '../../lib/use-t';
export const QUSDTooltip = () => {
const t = useT();
return (
<Tooltip
description={
<>
<p className="mb-1">
{t(
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
)}
</p>
{DocsLinks && (
<ExternalLink href={DocsLinks.QUANTUM}>
{t('Find out more')}
</ExternalLink>
)}
</>
}
underline={true}
>
<span>{t('qUSD')}</span>
</Tooltip>
);
};
@@ -151,27 +151,6 @@ const MOCK_REFEREES: RefereesQuery = {
},
};
const MOCK_REFEREES_30: RefereesQuery = {
referralSetReferees: {
__typename: 'ReferralSetRefereeConnection',
edges: [
{
node: {
atEpoch: 1,
joinedAt: '2023-11-21T14:17:09.257235Z',
refereeId:
'0987654321098765432109876543210987654321098765432109876543219876',
referralSetId:
'3772e570fbab89e50e563036b01dd949c554e5b5fe7908449672dfce9a8adffa',
totalRefereeGeneratedRewards: '12340',
totalRefereeNotionalTakerVolume: '56780',
__typename: 'ReferralSetReferee',
},
},
],
},
};
const programMock: MockedResponse<ReferralProgramQuery> = {
request: {
query: ReferralProgramDocument,
@@ -283,19 +262,6 @@ const refereesMock: MockedResponse<RefereesQuery, RefereesQueryVariables> = {
},
};
const refereesMock30: MockedResponse<RefereesQuery, RefereesQueryVariables> = {
request: {
query: RefereesDocument,
variables: {
code: MOCK_REFERRER_SET.referralSets.edges[0]?.node.id as string,
aggregationEpochs: 30,
},
},
result: {
data: MOCK_REFEREES_30,
},
};
jest.mock('@vegaprotocol/wallet', () => {
return {
...jest.requireActual('@vegaprotocol/wallet'),
@@ -309,35 +275,30 @@ jest.mock('@vegaprotocol/wallet', () => {
});
describe('ReferralStatistics', () => {
it('displays apply code when no data has been found for given pubkey', () => {
it('displays create code when no data has been found for given pubkey', () => {
const { queryByTestId } = render(
<MemoryRouter>
<MockedProvider mocks={[]} showWarnings={false}>
<ReferralStatistics />
</MockedProvider>
</MemoryRouter>
<MockedProvider mocks={[]} showWarnings={false}>
<ReferralStatistics />
</MockedProvider>
);
expect(queryByTestId('referral-apply-code-form')).toBeInTheDocument();
expect(queryByTestId('referral-create-code-form')).toBeInTheDocument();
});
it('displays referrer stats when given pubkey is a referrer', async () => {
const { queryByTestId } = render(
<MemoryRouter>
<MockedProvider
mocks={[
programMock,
referralSetAsReferrerMock,
noReferralSetAsRefereeMock,
stakeAvailableMock,
refereesMock,
refereesMock30,
]}
showWarnings={false}
>
<ReferralStatistics />
</MockedProvider>
</MemoryRouter>
<MockedProvider
mocks={[
programMock,
referralSetAsReferrerMock,
noReferralSetAsRefereeMock,
stakeAvailableMock,
refereesMock,
]}
showWarnings={false}
>
<ReferralStatistics />
</MockedProvider>
);
await waitFor(() => {
@@ -348,10 +309,6 @@ describe('ReferralStatistics', () => {
expect(queryByTestId('referral-statistics')?.dataset.as).toEqual(
'referrer'
);
// gets commision from 30 epochs query
expect(queryByTestId('total-commission-value')).toHaveTextContent(
'12,340'
);
});
});
@@ -4,15 +4,13 @@ import {
VegaIcon,
VegaIconNames,
truncateMiddle,
TextChildrenTooltip as Tooltip,
ExternalLink,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
DEFAULT_AGGREGATION_DAYS,
useReferral,
useUpdateReferees,
} from './hooks/use-referral';
import { DEFAULT_AGGREGATION_DAYS, useReferral } from './hooks/use-referral';
import { CreateCodeContainer } from './create-code-form';
import classNames from 'classnames';
import { Table } from './table';
import {
@@ -28,45 +26,34 @@ import compact from 'lodash/compact';
import { useReferralProgram } from './hooks/use-referral-program';
import { useStakeAvailable } from './hooks/use-stake-available';
import sortBy from 'lodash/sortBy';
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
import BigNumber from 'bignumber.js';
import { DocsLinks } from '@vegaprotocol/environment';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form';
import { QUSDTooltip } from './qusd-tooltip';
import { ApplyCodeForm } from './apply-code-form';
export const ReferralStatistics = () => {
const { pubKey } = useVegaWallet();
const program = useReferralProgram();
const { data: referee, refetch: refereeRefetch } = useReferral({
const { data: referee } = useReferral({
pubKey,
role: 'referee',
aggregationEpochs: program.details?.windowLength,
});
const { data: referrer, refetch: referrerRefetch } = useUpdateReferees(
useReferral({
pubKey,
role: 'referrer',
aggregationEpochs: program.details?.windowLength,
}),
DEFAULT_AGGREGATION_DAYS,
['totalRefereeGeneratedRewards'],
DEFAULT_AGGREGATION_DAYS === program.details?.windowLength
);
const refetch = useCallback(() => {
refereeRefetch();
referrerRefetch();
}, [refereeRefetch, referrerRefetch]);
const { data: referrer } = useReferral({
pubKey,
role: 'referrer',
aggregationEpochs: program.details?.windowLength,
});
if (referee?.code) {
return (
<>
<Statistics data={referee} program={program} as="referee" />
<Statistics data={referee} program={program} as="referee" />;
{!referee.isEligible && <ApplyCodeForm />}
</>
);
@@ -75,26 +62,26 @@ export const ReferralStatistics = () => {
if (referrer?.code) {
return (
<>
<Statistics data={referrer} program={program} as="referrer" />
<Statistics data={referrer} program={program} as="referrer" />;
<RefereesTable data={referrer} program={program} />
</>
);
}
return <ApplyCodeFormContainer onSuccess={refetch} />;
return <CreateCodeContainer />;
};
export const useStats = ({
data,
program,
as,
}: {
data?: NonNullable<ReturnType<typeof useReferral>['data']>;
program: ReturnType<typeof useReferralProgram>;
as?: 'referrer' | 'referee';
}) => {
const { benefitTiers } = program;
const { data: epochData } = useCurrentEpochInfoQuery({
fetchPolicy: 'network-only',
});
const { data: epochData } = useCurrentEpochInfoQuery();
const { data: statsData } = useReferralSetStatsQuery({
variables: {
code: data?.code || '',
@@ -128,7 +115,7 @@ export const useStats = ({
: 1;
const finalCommissionValue = isNaN(multiplier)
? baseCommissionValue
: new BigNumber(multiplier).times(baseCommissionValue).toNumber();
: multiplier * baseCommissionValue;
const discountFactorValue = refereeStats?.discountFactor
? Number(refereeStats.discountFactor)
@@ -187,10 +174,9 @@ export const Statistics = ({
discountFactorValue,
currentBenefitTierValue,
epochsValue,
nextBenefitTierValue,
nextBenefitTierVolumeValue,
nextBenefitTierEpochsValue,
} = useStats({ data, program });
} = useStats({ data, program, as });
const isApplyCodePreview = useMemo(
() => data.referee === null,
@@ -221,8 +207,6 @@ export const Statistics = ({
).toString(),
}
)}
testId="base-commission-rate"
overrideWithNoProgram={!details}
>
{baseCommissionValue * 100}%
</StatTile>
@@ -231,7 +215,6 @@ export const Statistics = ({
const stakingMultiplierTile = (
<StatTile
title={t('Staking multiplier')}
testId="staking-multiplier"
description={
<span
className={classNames({
@@ -246,36 +229,27 @@ export const Statistics = ({
})}
</span>
}
overrideWithNoProgram={!details}
>
{multiplier || t('None')}
</StatTile>
);
const baseCommissionFormatted = BigNumber(baseCommissionValue)
.times(100)
.toString();
const finalCommissionFormatted = new BigNumber(finalCommissionValue)
.times(100)
.toString();
const finalCommissionTile = (
<StatTile
title={t('Final commission rate')}
description={
!isNaN(multiplier)
? `(${baseCommissionFormatted}% ⨉ ${multiplier} = ${finalCommissionFormatted}%)`
? `(${baseCommissionValue * 100}% ⨉ ${multiplier} = ${
finalCommissionValue * 100
}%)`
: undefined
}
testId="final-commission-rate"
overrideWithNoProgram={!details}
>
{finalCommissionFormatted}%
{finalCommissionValue * 100}%
</StatTile>
);
const numberOfTradersValue = data.referees.length;
const numberOfTradersTile = (
<StatTile title={t('Number of traders')} testId="number-of-traders">
{numberOfTradersValue}
</StatTile>
<StatTile title={t('Number of traders')}>{numberOfTradersValue}</StatTile>
);
const codeTile = (
@@ -290,8 +264,6 @@ export const Statistics = ({
title={t('myVolume', 'My volume (last {{count}} epochs)', {
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
})}
testId="my-volume"
overrideWithNoProgram={!details}
>
{compactNumFormat.format(referrerVolumeValue)}
</StatTile>
@@ -302,26 +274,9 @@ export const Statistics = ({
.reduce((all, r) => all.plus(r), new BigNumber(0));
const totalCommissionTile = (
<StatTile
testId="total-commission"
title={
<Trans
i18nKey="totalCommission"
defaults="Total commission (<0>last {{count}} epochs</0>)"
values={{
count: DEFAULT_AGGREGATION_DAYS,
}}
components={[
<Tooltip
key="1"
description={t(
'Depending on data node retention you may not be able see the full 30 days'
)}
>
last 30 epochs
</Tooltip>,
]}
/>
}
title={t('totalCommission', 'Total commission (last {{count}}} epochs)', {
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
})}
description={<QUSDTooltip />}
>
{getNumberFormat(0).format(Number(totalCommissionValue))}
@@ -346,30 +301,15 @@ export const Statistics = ({
);
const currentBenefitTierTile = (
<StatTile
title={t('Current tier')}
testId="current-tier"
description={
nextBenefitTierValue?.tier
? t('(Next tier: {{nextTier}})', {
nextTier: nextBenefitTierValue?.tier,
})
: undefined
}
overrideWithNoProgram={!details}
>
<StatTile title={t('Current tier')}>
{isApplyCodePreview
? currentBenefitTierValue?.tier || benefitTiers[0]?.tier || 'None'
: currentBenefitTierValue?.tier || 'None'}
</StatTile>
);
const discountFactorTile = (
<StatTile
title={t('Discount')}
testId="discount"
overrideWithNoProgram={!details}
>
{isApplyCodePreview && benefitTiers.length >= 1
<StatTile title={t('Discount')}>
{isApplyCodePreview
? benefitTiers[0].discountFactor * 100
: discountFactorValue * 100}
%
@@ -384,34 +324,22 @@ export const Statistics = ({
count: details?.windowLength,
}
)}
testId="combined-volume"
overrideWithNoProgram={!details}
>
{compactNumFormat.format(runningVolumeValue)}
</StatTile>
);
const epochsTile = (
<StatTile title={t('Epochs in set')} testId="epochs-in-set">
{epochsValue}
</StatTile>
<StatTile title={t('Epochs in set')}>{epochsValue}</StatTile>
);
const nextTierVolumeTile = (
<StatTile
title={t('Volume to next tier')}
testId="vol-to-next-tier"
overrideWithNoProgram={!details}
>
<StatTile title={t('Volume to next tier')}>
{nextBenefitTierVolumeValue <= 0
? '0'
: compactNumFormat.format(nextBenefitTierVolumeValue)}
</StatTile>
);
const nextTierEpochsTile = (
<StatTile
title={t('Epochs to next tier')}
testId="epochs-to-next-tier"
overrideWithNoProgram={!details}
>
<StatTile title={t('Epochs to next tier')}>
{nextBenefitTierEpochsValue <= 0 ? '0' : nextBenefitTierEpochsValue}
</StatTile>
);
@@ -528,20 +456,10 @@ export const RefereesTable = ({
displayName: (
<Trans
i18nKey="referralStatisticsCommission"
defaults="Commission earned in <0>qUSD</0> (<1>last {{count}} epochs</1>)"
components={[
<QUSDTooltip key="0" />,
<Tooltip
key="1"
description={t(
'Depending on data node retention you may not be able see the full 30 days'
)}
>
last 30 epochs
</Tooltip>,
]}
defaults="Commission earned in <0>qUSD</0> (last {{count}} epochs)"
values={{
count: DEFAULT_AGGREGATION_DAYS,
count:
details?.windowLength || DEFAULT_AGGREGATION_DAYS,
}}
ns={ns}
/>
@@ -574,3 +492,28 @@ export const RefereesTable = ({
</>
);
};
export const QUSDTooltip = () => {
const t = useT();
return (
<Tooltip
description={
<>
<p className="mb-1">
{t(
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
)}
</p>
{DocsLinks && (
<ExternalLink href={DocsLinks.QUANTUM}>
{t('Find out more')}
</ExternalLink>
)}
</>
}
underline={true}
>
<span>{t('qUSD')}</span>
</Tooltip>
);
};
@@ -4,10 +4,11 @@ import {
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { HowItWorksTable } from './how-it-works-table';
import { LandingBanner } from './landing-banner';
import { TiersContainer } from './tiers';
import { TabLink } from './buttons';
import { Outlet, useMatch } from 'react-router-dom';
import { Outlet } from 'react-router-dom';
import { Routes } from '../../lib/links';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useReferral } from './hooks/use-referral';
@@ -17,17 +18,15 @@ import { usePageTitleStore } from '../../stores';
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../../components/error-boundary';
const Nav = () => {
const t = useT();
const match = useMatch(Routes.REFERRALS_APPLY_CODE);
return (
<div className="flex justify-center border-b border-vega-cdark-500">
<TabLink end to={match ? Routes.REFERRALS_APPLY_CODE : Routes.REFERRALS}>
{t('Apply code')}
<TabLink end to={Routes.REFERRALS}>
{t('I want a code')}
</TabLink>
<TabLink to={Routes.REFERRALS_CREATE_CODE}>{t('Create code')}</TabLink>
<TabLink to={Routes.REFERRALS_APPLY_CODE}>{t('I have a code')}</TabLink>
</div>
);
};
@@ -66,7 +65,7 @@ export const Referrals = () => {
}, [updateTitle, t]);
return (
<ErrorBoundary feature="referrals">
<>
<LandingBanner />
{showNav && <Nav />}
@@ -96,16 +95,18 @@ export const Referrals = () => {
<h2 className="text-2xl">{t('How it works')}</h2>
</div>
<div className="md:w-[60%] mx-auto">
<HowItWorksTable />
<div className="mt-5">
<TradingAnchorButton
className="mx-auto w-max"
href={REFERRAL_DOCS_LINK}
target="_blank"
>
{t('Read the docs')} <VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
{t('Read the terms')}{' '}
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</TradingAnchorButton>
</div>
</div>
</ErrorBoundary>
</>
);
};
+2 -4
View File
@@ -14,10 +14,8 @@ export const Tag = ({
className={classNames(
'w-max border rounded-[1rem] py-[0.125rem] px-2 text-xs',
{
'border-vega-yellow-550 text-vega-yellow-550 dark:border-vega-yellow-500 dark:text-vega-yellow-500':
color === 'yellow',
'border-vega-green-550 text-vega-green-550 dark:border-vega-green-500 dark:text-vega-green-500':
color === 'green',
'border-vega-yellow-500 text-vega-yellow-500': color === 'yellow',
'border-vega-green-500 text-vega-green-500': color === 'green',
'border-vega-blue-500 text-vega-blue-500': color === 'blue',
'border-vega-purple-500 text-vega-purple-500': color === 'purple',
'border-vega-pink-500 text-vega-pink-500': color === 'pink',
+88 -196
View File
@@ -1,43 +1,20 @@
import {
addDecimalsFormatNumber,
getDateTimeFormat,
} from '@vegaprotocol/utils';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { useReferralProgram } from './hooks/use-referral-program';
import { Table } from './table';
import classNames from 'classnames';
import { BORDER_COLOR, GRADIENT } from './constants';
import { Tag } from './tag';
import type { ComponentProps, ReactNode } from 'react';
import { ExternalLink, truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import {
DApp,
DocsLinks,
TOKEN_PROPOSAL,
TOKEN_PROPOSALS,
useLinks,
} from '@vegaprotocol/environment';
import { useT, ns } from '../../lib/use-t';
import { Trans } from 'react-i18next';
// rainbow-ish order
const TIER_COLORS: Array<ComponentProps<typeof Tag>['color']> = [
'pink',
'orange',
'yellow',
'green',
'blue',
'purple',
];
const getTierColor = (tier: number) => {
const tiers = Object.keys(TIER_COLORS).length;
let index = Math.abs(tier - 1);
if (tier >= tiers) {
index = index % tiers;
}
return TIER_COLORS[index];
};
const Loading = ({ variant }: { variant: 'large' | 'inline' }) => (
<div
className={classNames(
@@ -51,63 +28,51 @@ const Loading = ({ variant }: { variant: 'large' | 'inline' }) => (
const StakingTier = ({
tier,
label,
referralRewardMultiplier,
minimumStakedTokens,
}: {
tier: number;
label: string;
referralRewardMultiplier: string;
minimumStakedTokens: string;
}) => {
const t = useT();
const minimum = addDecimalsFormatNumber(minimumStakedTokens, 18);
// TODO: Decide what to do with the multiplier images
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const multiplierImage = (
<div
aria-hidden
className={classNames(
'w-full max-w-[80px] h-full min-h-[80px]',
'bg-cover bg-right-bottom',
{
"bg-[url('/1x.png')]": tier === 1,
"bg-[url('/2x.png')]": tier === 2,
"bg-[url('/3x.png')]": tier === 3,
}
)}
>
<span className="sr-only">{`${referralRewardMultiplier}x multiplier`}</span>
</div>
);
const color: Record<number, ComponentProps<typeof Tag>['color']> = {
1: 'green',
2: 'blue',
3: 'pink',
};
return (
<div
className={classNames(
'overflow-hidden',
'border rounded-md w-full',
'flex flex-row',
'bg-white dark:bg-vega-cdark-900',
GRADIENT,
BORDER_COLOR
)}
>
<div
className={classNames(
'p-3 flex flex-row min-h-[80px] h-full items-center'
<div aria-hidden className="max-w-[120px]">
{tier < 4 && (
// eslint-disable-next-line @next/next/no-img-element
<img
src={`/${tier}x.png`}
alt={`${referralRewardMultiplier}x multiplier`}
width={240}
height={240}
className="w-full h-full"
/>
)}
>
<div>
<Tag color={getTierColor(tier)}>
{t('Multiplier')} {referralRewardMultiplier}x
</Tag>
<p className="mt-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
<Trans
defaults="Stake a minimum of <0>{{minimum}}</0> $VEGA tokens"
values={{ minimum }}
components={[<b key={minimum}></b>]}
/>
</p>
</div>
</div>
<div className={classNames('p-3')}>
<Tag color={color[tier]}>Multiplier {referralRewardMultiplier}x</Tag>
<h3 className="mt-1 mb-1 text-base">{label}</h3>
<p className="text-sm text-vega-clight-100 dark:text-vega-cdark-100">
{t('Stake a minimum of {{minimumStakedTokens}} $VEGA tokens', {
minimumStakedTokens,
})}
</p>
</div>
</div>
);
@@ -126,29 +91,21 @@ export const TiersContainer = () => {
if ((!loading && !details) || error) {
return (
<div className="bg-vega-clight-800 dark:bg-vega-cdark-800 text-black dark:text-white rounded-lg p-6 mt-1 mb-20 text-sm text-center">
<div className="text-base px-5 py-10 text-center">
<Trans
defaults="There are currently no active referral programs. Check the <0>Governance App</0> to see if there are any proposals in progress and vote."
components={[
<ExternalLink
href={governanceLink(TOKEN_PROPOSALS)}
key="link"
className="underline"
>
<ExternalLink href={governanceLink(TOKEN_PROPOSALS)} key="link">
{t('Governance App')}
</ExternalLink>,
]}
ns={ns}
/>{' '}
/>
<Trans
defaults="Use the <0>docs</0> tutorial to propose a new program."
defaults="You can propose a new program via the <0>Docs</0>."
components={[
<ExternalLink
href={DocsLinks?.REFERRALS}
key="link"
className="underline"
>
{t('docs')}
<ExternalLink href={DocsLinks?.REFERRALS} key="link">
{t('Docs')}
</ExternalLink>,
]}
ns={ns}
@@ -159,93 +116,47 @@ export const TiersContainer = () => {
return (
<>
<h2 className="text-3xl mt-10">{t('Current program details')}</h2>
{details?.id && (
<p>
<Trans
defaults="As a result of governance proposal <0>{{proposal}}</0> the program below is currently active on the Vega network."
values={{ proposal: truncateMiddle(details.id) }}
components={[
<ExternalLink
key="referral-program-proposal-link"
href={governanceLink(TOKEN_PROPOSAL.replace(':id', details.id))}
className="underline"
>
proposal
</ExternalLink>,
]}
/>
</p>
)}
{/* Meta */}
<div className="mt-10 flex flex-row items-baseline justify-between text-xs text-vega-clight-100 dark:text-vega-cdark-100 font-alpha calt">
{details?.id && (
<span>
{t('Proposal ID:')}{' '}
<ExternalLink
href={governanceLink(TOKEN_PROPOSAL.replace(':id', details.id))}
>
<span>{truncateMiddle(details.id)}</span>
</ExternalLink>
</span>
)}
{/* Benefit tiers */}
<div className="flex flex-col items-baseline justify-between mt-10 mb-5">
<h2 className="text-2xl">{t('Referral tiers')}</h2>
{ends && (
<span>
<span className="text-sm text-vega-clight-200 dark:text-vega-cdark-200">
{t('Program ends:')} {ends}
</span>
)}
</div>
<div className="mb-20">
{loading || !benefitTiers || benefitTiers.length === 0 ? (
<Loading variant="large" />
) : (
<TiersTable
windowLength={details?.windowLength}
data={benefitTiers.map((bt) => ({
...bt,
tierElement: (
<div className="rounded-full bg-vega-clight-900 dark:bg-vega-cdark-900 p-1 w-8 h-8 text-center">
{bt.tier}
</div>
),
}))}
/>
)}
</div>
{/* Container */}
<div className="bg-vega-clight-800 dark:bg-vega-cdark-800 text-black dark:text-white rounded-lg p-6 mt-1 mb-20">
{/* Benefit tiers */}
<div className="flex flex-col mb-5">
<h3 className="text-2xl calt">{t('Benefit tiers')}</h3>
<p className="text-sm text-vega-clight-200 dark:text-vega-cdark-200">
{t(
'Members of a referral group can access the increasing commission and discount benefits defined in the program based on their combined running volume.'
)}
</p>
</div>
<div className="mb-10">
{loading || !benefitTiers || benefitTiers.length === 0 ? (
{/* Staking tiers */}
<div className="flex flex-row items-baseline justify-between mb-5">
<h2 className="text-2xl">{t('Staking multipliers')}</h2>
</div>
<div className="mb-20 flex flex-col justify-items-stretch lg:flex-row gap-5">
{loading || !stakingTiers || stakingTiers.length === 0 ? (
<>
<Loading variant="large" />
) : (
<TiersTable
windowLength={details?.windowLength}
data={benefitTiers.map((bt) => ({
...bt,
tierElement: (
<div className="rounded-full bg-vega-clight-900 dark:bg-vega-cdark-900 p-1 w-8 h-8 text-center">
{bt.tier}
</div>
),
}))}
/>
)}
</div>
{/* Staking tiers */}
<div className="flex flex-col mb-5">
<h3 className="text-2xl calt">{t('Staking multipliers')}</h3>
<p className="text-sm text-vega-clight-200 dark:text-vega-cdark-200">
{t(
'Referrers can access the commission multipliers defined in the program by staking VEGA tokens in the amounts shown.'
)}
</p>
</div>
<div className="gap-5 grid lg:grid-cols-3">
{loading || !stakingTiers || stakingTiers.length === 0 ? (
<>
<Loading variant="large" />
<Loading variant="large" />
<Loading variant="large" />
</>
) : (
<StakingTiers data={stakingTiers} />
)}
</div>
<Loading variant="large" />
<Loading variant="large" />
</>
) : (
<StakingTiers data={stakingTiers} />
)}
</div>
</>
);
@@ -257,14 +168,17 @@ const StakingTiers = ({
data: ReturnType<typeof useReferralProgram>['stakingTiers'];
}) => (
<>
{data.map(({ tier, referralRewardMultiplier, minimumStakedTokens }, i) => (
<StakingTier
key={i}
tier={tier}
referralRewardMultiplier={referralRewardMultiplier}
minimumStakedTokens={minimumStakedTokens}
/>
))}
{data.map(
({ tier, label, referralRewardMultiplier, minimumStakedTokens }, i) => (
<StakingTier
key={i}
tier={tier}
label={label}
referralRewardMultiplier={referralRewardMultiplier}
minimumStakedTokens={minimumStakedTokens}
/>
)
)}
</>
);
@@ -289,17 +203,9 @@ const TiersTable = ({
{
name: 'commission',
displayName: t('Referrer commission'),
tooltip: t(
"The proportion of the referee's taker fees to be rewarded to the referrer"
),
},
{
name: 'discount',
displayName: t('Referee trading discount'),
tooltip: t(
"The proportion of the referee's taker fees to be discounted"
),
tooltip: t('A percentage of commission earned by the referrer'),
},
{ name: 'discount', displayName: t('Referrer trading discount') },
{
name: 'volume',
displayName: t(
@@ -309,34 +215,20 @@ const TiersTable = ({
count: windowLength,
}
),
tooltip: t('The minimum running notional for the given benefit tier'),
},
{
name: 'epochs',
displayName: t('Min. epochs'),
tooltip: t(
'The minimum number of epochs the party needs to be in the referral set to be eligible for the benefit'
),
},
{ name: 'epochs', displayName: t('Min. epochs') },
]}
className="bg-white dark:bg-vega-cdark-900"
data={data.map((d) => ({
...d,
className: classNames({
'from-vega-yellow-400 dark:from-vega-yellow-600 to-20% bg-highlight':
'yellow' === getTierColor(d.tier),
'from-vega-green-400 dark:from-vega-green-600 to-20% bg-highlight':
'green' === getTierColor(d.tier),
'from-vega-blue-400 dark:from-vega-blue-600 to-20% bg-highlight':
'blue' === getTierColor(d.tier),
'from-vega-purple-400 dark:from-vega-purple-600 to-20% bg-highlight':
'purple' === getTierColor(d.tier),
'from-vega-pink-400 dark:from-vega-pink-600 to-20% bg-highlight':
'pink' === getTierColor(d.tier),
d.tier >= 3,
'from-vega-purple-400 dark:from-vega-purple-600 to-20% bg-highlight':
d.tier === 2,
'from-vega-blue-400 dark:from-vega-blue-600 to-20% bg-highlight':
d.tier === 1,
'from-vega-orange-400 dark:from-vega-orange-600 to-20% bg-highlight':
'orange' === getTierColor(d.tier),
'from-vega-clight-200 dark:from-vega-cdark-200 to-20% bg-highlight':
'none' === getTierColor(d.tier),
d.tier == 0,
}),
}))}
/>
+8 -38
View File
@@ -31,52 +31,22 @@ export const Tile = ({
};
type StatTileProps = {
title: ReactNode;
testId?: string;
title: string;
description?: ReactNode;
children?: ReactNode;
overrideWithNoProgram?: boolean;
};
export const StatTile = ({
title,
description,
children,
testId,
overrideWithNoProgram = false,
}: StatTileProps) => {
if (overrideWithNoProgram) {
return <NoProgramTile title={title} />;
}
return (
<Tile>
<h3
data-testid={testId}
className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt"
>
{title}
</h3>
<div data-testid={`${testId}-value`} className="text-5xl text-left">
{children}
</div>
{description && (
<div className="text-sm text-left text-vega-clight-100 dark:text-vega-cdark-100">
{description}
</div>
)}
</Tile>
);
};
export const NoProgramTile = ({ title }: Pick<StatTileProps, 'title'>) => {
const t = useT();
export const StatTile = ({ title, description, children }: StatTileProps) => {
return (
<Tile>
<h3 className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt">
{title}
</h3>
<div className="text-xs text-vega-clight-300 dark:text-vega-cdark-300 leading-[3rem]">
{t('No active program')}
</div>
<div className="text-5xl text-left">{children}</div>
{description && (
<div className="text-sm text-left text-vega-clight-100 dark:text-vega-cdark-100">
{description}
</div>
)}
</Tile>
);
};
@@ -1,9 +1,8 @@
import { useEffect } from 'react';
import { titlefy } from '@vegaprotocol/utils';
import { useT } from '../../lib/use-t';
import { RewardsContainer } from '../../components/rewards-container';
import { usePageTitleStore } from '../../stores';
import { ErrorBoundary } from '../../components/error-boundary';
import { titlefy } from '@vegaprotocol/utils';
import { useEffect } from 'react';
export const Rewards = () => {
const t = useT();
@@ -15,11 +14,9 @@ export const Rewards = () => {
updateTitle(titlefy([title]));
}, [updateTitle, title]);
return (
<ErrorBoundary feature="rewards">
<div className="container mx-auto p-4">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<RewardsContainer />
</div>
</ErrorBoundary>
<div className="container mx-auto p-4">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<RewardsContainer />
</div>
);
};
@@ -13,7 +13,6 @@ import { ViewType, useSidebar } from '../sidebar';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { usePersistentDepositStore } from '@vegaprotocol/deposits';
export const AccountsContainer = ({
pinnedAsset,
@@ -26,7 +25,6 @@ export const AccountsContainer = ({
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const currentRouteId = useGetCurrentRouteId();
const setViews = useSidebar((store) => store.setViews);
const setDepositAsset = usePersistentDepositStore((store) => store.saveValue);
const gridStore = useAccountStore((store) => store.gridStore);
const updateGridStore = useAccountStore((store) => store.updateGridStore);
@@ -57,10 +55,7 @@ export const AccountsContainer = ({
setViews({ type: ViewType.Withdraw, assetId }, currentRouteId);
}}
onClickDeposit={(assetId) => {
setViews({ type: ViewType.Deposit }, currentRouteId);
if (assetId) {
setDepositAsset({ assetId });
}
setViews({ type: ViewType.Deposit, assetId }, currentRouteId);
}}
onClickTransfer={(assetId) => {
setViews({ type: ViewType.Transfer, assetId }, currentRouteId);
-3
View File
@@ -8,18 +8,15 @@ export const Card = ({
className,
loading = false,
highlight = false,
testId,
}: {
children: ReactNode;
title: string;
className?: string;
loading?: boolean;
highlight?: boolean;
testId?: string;
}) => {
return (
<div
data-testid={testId}
className={classNames(
'bg-vega-clight-800 dark:bg-vega-cdark-800 col-span-full p-0.5 lg:col-auto',
'rounded-lg',
@@ -1,66 +0,0 @@
import { render, screen } from '@testing-library/react';
import { ChartContainer } from './chart-container';
import { useChartSettingsStore } from './use-chart-settings';
import { useEnvironment } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/candles-chart', () => ({
...jest.requireActual('@vegaprotocol/candles-chart'),
CandlesChartContainer: ({ marketId }: { marketId: string }) => (
<div data-testid="pennant">{marketId}</div>
),
}));
jest.mock('@vegaprotocol/trading-view', () => ({
...jest.requireActual('@vegaprotocol/trading-view'),
TradingViewContainer: ({ marketId }: { marketId: string }) => (
<div data-testid="tradingview">{marketId}</div>
),
}));
describe('ChartContainer', () => {
it('renders pennant if no library path is set', () => {
useChartSettingsStore.setState({
chartlib: 'tradingview',
});
useEnvironment.setState({
CHARTING_LIBRARY_PATH: undefined,
CHARTING_LIBRARY_HASH: undefined,
});
const marketId = 'market-id';
render(<ChartContainer marketId={marketId} />);
expect(screen.getByTestId('pennant')).toHaveTextContent(marketId);
});
it('renders trading view if library path is set', () => {
useChartSettingsStore.setState({
chartlib: 'tradingview',
});
useEnvironment.setState({
CHARTING_LIBRARY_PATH: 'dummy-path',
CHARTING_LIBRARY_HASH: 'hash',
});
const marketId = 'market-id';
render(<ChartContainer marketId={marketId} />);
expect(screen.getByTestId('tradingview')).toHaveTextContent(marketId);
});
it('renders pennant chart if stored in settings', () => {
useChartSettingsStore.setState({
chartlib: 'pennant',
});
const marketId = 'market-id';
render(<ChartContainer marketId={marketId} />);
expect(screen.getByTestId('pennant')).toHaveTextContent(marketId);
});
});
@@ -1,120 +0,0 @@
import invert from 'lodash/invert';
import { type Interval } from '@vegaprotocol/types';
import {
TradingViewContainer,
ALLOWED_TRADINGVIEW_HOSTNAMES,
TRADINGVIEW_INTERVAL_MAP,
} from '@vegaprotocol/trading-view';
import {
CandlesChartContainer,
PENNANT_INTERVAL_MAP,
} from '@vegaprotocol/candles-chart';
import { useEnvironment } from '@vegaprotocol/environment';
import { useChartSettings, STUDY_SIZE } from './use-chart-settings';
/**
* Renders either the pennant chart or the tradingview chart
*/
export const ChartContainer = ({ marketId }: { marketId: string }) => {
const { CHARTING_LIBRARY_PATH, CHARTING_LIBRARY_HASH } = useEnvironment();
const {
chartlib,
interval,
chartType,
overlays,
studies,
studySizes,
tradingViewStudies,
setInterval,
setStudies,
setStudySizes,
setOverlays,
setTradingViewStudies,
} = useChartSettings();
const pennantChart = (
<CandlesChartContainer
marketId={marketId}
interval={toPennantInterval(interval)}
chartType={chartType}
overlays={overlays}
studies={studies}
studySizes={studySizes}
setStudySizes={setStudySizes}
setStudies={setStudies}
setOverlays={setOverlays}
defaultStudySize={STUDY_SIZE}
/>
);
if (!ALLOWED_TRADINGVIEW_HOSTNAMES.includes(window.location.hostname)) {
return pennantChart;
}
if (!CHARTING_LIBRARY_PATH || !CHARTING_LIBRARY_HASH) {
return pennantChart;
}
switch (chartlib) {
case 'tradingview': {
return (
<TradingViewContainer
libraryPath={CHARTING_LIBRARY_PATH}
libraryHash={CHARTING_LIBRARY_HASH}
marketId={marketId}
interval={toTradingViewResolution(interval)}
studies={tradingViewStudies}
onIntervalChange={(newInterval) => {
setInterval(fromTradingViewResolution(newInterval));
}}
onAutoSaveNeeded={(data: { studies: string[] }) => {
setTradingViewStudies(data.studies);
}}
/>
);
}
case 'pennant': {
return pennantChart;
}
default: {
throw new Error('invalid chart lib');
}
}
};
const toTradingViewResolution = (interval: Interval) => {
const resolution = TRADINGVIEW_INTERVAL_MAP[interval];
if (!resolution) {
throw new Error(
`failed to convert interval: ${interval} to valid resolution`
);
}
return resolution;
};
const fromTradingViewResolution = (resolution: string) => {
const interval = invert(TRADINGVIEW_INTERVAL_MAP)[resolution];
if (!interval) {
throw new Error(
`failed to convert resolution: ${resolution} to valid interval`
);
}
return interval as Interval;
};
const toPennantInterval = (interval: Interval) => {
const pennantInterval = PENNANT_INTERVAL_MAP[interval];
if (!pennantInterval) {
throw new Error(
`failed to convert interval: ${interval} to valid pennant interval`
);
}
return pennantInterval;
};
@@ -1,140 +0,0 @@
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { ChartMenu } from './chart-menu';
import {
useChartSettingsStore,
DEFAULT_CHART_SETTINGS,
} from './use-chart-settings';
import { Overlay, Study, overlayLabels, studyLabels } from 'pennant';
import { useEnvironment } from '@vegaprotocol/environment';
describe('ChartMenu', () => {
it('doesnt show trading view option if library path undefined', () => {
useEnvironment.setState({ CHARTING_LIBRARY_PATH: undefined });
render(<ChartMenu />);
expect(
screen.queryByRole('button', { name: 'TradingView' })
).not.toBeInTheDocument();
expect(
screen.queryByRole('button', { name: 'Vega chart' })
).not.toBeInTheDocument();
});
it('can switch between charts if library path', async () => {
useEnvironment.setState({ CHARTING_LIBRARY_PATH: 'dummy' });
render(<ChartMenu />);
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
await userEvent.click(screen.getByRole('button', { name: 'Vega chart' }));
expect(useChartSettingsStore.getState().chartlib).toEqual('pennant');
});
describe('tradingview', () => {
beforeEach(() => {
useEnvironment.setState({ CHARTING_LIBRARY_PATH: 'dummy-path' });
// clear store each time to avoid conditional testing of defaults
useChartSettingsStore.setState({
chartlib: 'tradingview',
});
});
it('only shows chartlib switch and attribution', () => {
render(<ChartMenu />);
const buttons = screen.getAllByRole('button');
expect(buttons).toHaveLength(1);
expect(buttons[0]).toHaveTextContent('Vega chart');
expect(screen.getByText('Chart by')).toBeInTheDocument();
});
});
describe('pennant', () => {
const openDropdown = async () => {
await userEvent.click(
screen.getByRole('button', {
name: 'Indicators',
})
);
};
beforeEach(() => {
// clear store each time to avoid conditional testing of defaults
useChartSettingsStore.setState({
chartlib: 'pennant',
overlays: [],
studies: [],
});
});
it.each(Object.values(Overlay))('can set %s overlay', async (overlay) => {
render(<ChartMenu />);
await openDropdown();
const menu = within(await screen.findByRole('menu'));
await userEvent.click(menu.getByText(overlayLabels[overlay as Overlay]));
// re-open the dropdown
await openDropdown();
expect(
screen.getByText(overlayLabels[overlay as Overlay])
).toHaveAttribute('data-state', 'checked');
});
it.each(Object.values(Study))('can set %s study', async (study) => {
render(<ChartMenu />);
await openDropdown();
const menu = within(await screen.findByRole('menu'));
await userEvent.click(menu.getByText(studyLabels[study as Study]));
// re-open the dropdown
await openDropdown();
expect(screen.getByText(studyLabels[study as Study])).toHaveAttribute(
'data-state',
'checked'
);
});
it('should render with the correct default studies and overlays', async () => {
useChartSettingsStore.setState({
...DEFAULT_CHART_SETTINGS,
chartlib: 'pennant',
});
render(<ChartMenu />);
await userEvent.click(
screen.getByRole('button', {
name: 'Indicators',
})
);
const menu = within(await screen.findByRole('menu'));
expect(menu.getByText(studyLabels.volume)).toHaveAttribute(
'data-state',
'checked'
);
expect(menu.getByText(studyLabels.macd)).toHaveAttribute(
'data-state',
'checked'
);
expect(menu.getByText(overlayLabels.movingAverage)).toHaveAttribute(
'data-state',
'checked'
);
});
});
});
@@ -1,2 +0,0 @@
export { ChartContainer } from './chart-container';
export { ChartMenu } from './chart-menu';
@@ -1,79 +0,0 @@
import { render, screen } from '@testing-library/react';
import { ErrorBoundary } from './error-boundary';
import { localLoggerFactory } from '@vegaprotocol/logger';
jest.mock('@vegaprotocol/logger', () => ({
localLoggerFactory: jest.fn(),
}));
describe('ErrorBoundary', () => {
const mockLogError = jest.fn();
const originalConsoleError = console.error;
const mockLoggerFactory = localLoggerFactory as jest.Mock;
beforeAll(() => {
console.error = () => {};
});
afterAll(() => {
console.error = originalConsoleError;
});
beforeEach(() => {
mockLoggerFactory.mockImplementation(() => ({
error: mockLogError,
}));
});
afterEach(() => {
mockLogError.mockClear();
});
it('renders children', () => {
render(
<ErrorBoundary feature="feature">
<div data-testid="child" />
</ErrorBoundary>
);
expect(screen.getByTestId('child')).toBeInTheDocument();
});
it('renders fallback ui and logs an error', () => {
const error = new Error('bork!');
const BorkedComponent = () => {
throw error;
};
render(
<ErrorBoundary feature="test-feature">
<BorkedComponent />
</ErrorBoundary>
);
expect(screen.getByText('Something went wrong')).toBeInTheDocument();
expect(mockLogError).toHaveBeenCalledTimes(1);
expect(mockLogError).toHaveBeenCalledWith(
error.message,
expect.stringContaining('componentStack')
);
});
it('renders fallback render prop if error', () => {
const error = new Error('bork!');
const BorkedComponent = () => {
throw error;
};
render(
<ErrorBoundary
feature="test-feature"
fallback={<div data-testid="custom-ui" />}
>
<BorkedComponent />
</ErrorBoundary>
);
expect(screen.getByTestId('custom-ui')).toBeInTheDocument();
});
});
@@ -1,53 +0,0 @@
import { localLoggerFactory, type LocalLogger } from '@vegaprotocol/logger';
import { Component, type ErrorInfo, type ReactNode } from 'react';
import { useT } from '../../lib/use-t';
interface ErrorBoundaryProps {
children: ReactNode;
feature: string;
fallback?: ReactNode;
}
interface ErrorBoundaryState {
hasError: boolean;
}
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
logger: LocalLogger | null = null;
constructor(props: ErrorBoundaryProps) {
super(props);
this.logger = localLoggerFactory({ application: props.feature });
this.state = {
hasError: false,
};
}
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, info: ErrorInfo) {
if (this.logger) {
this.logger.error(error.message, JSON.stringify(info));
}
}
render() {
if (this.state.hasError) {
return this.props.fallback || <DefaultFallback />;
}
return this.props.children;
}
}
const DefaultFallback = () => {
const t = useT();
return <p className="text-xs">{t('Something went wrong')}</p>;
};
@@ -1 +0,0 @@
export { ErrorBoundary } from './error-boundary';
@@ -84,10 +84,7 @@ export const FeesContainer = () => {
);
return (
<div
className="grid auto-rows-min grid-cols-4 gap-3"
data-testid="fees-container"
>
<div className="grid auto-rows-min grid-cols-4 gap-3">
{isConnected && (
<>
<Card
@@ -127,10 +124,7 @@ export const FeesContainer = () => {
windowLength={volumeDiscountWindowLength}
/>
) : (
<p
className="text-muted pt-3 text-sm"
data-testid="no-volume-discount"
>
<p className="text-muted pt-3 text-sm">
{t('No volume discount program active')}
</p>
)}
@@ -139,22 +133,17 @@ export const FeesContainer = () => {
title={t('Referral benefits')}
className="sm:col-span-2"
loading={loading}
data-testid="referral-benefits-card"
>
{isReferrer ? (
<ReferrerInfo code={code} data-testid="referrer-info" />
<ReferrerInfo code={code} />
) : isReferralProgramRunning ? (
<ReferralBenefits
setRunningNotionalTakerVolume={referralVolumeInWindow}
epochsInSet={epochsInSet}
epochs={referralDiscountWindowLength}
data-testid="referral-benefits"
/>
) : (
<p
className="text-muted pt-3 text-sm"
data-testid="no-referral-program"
>
<p className="text-muted pt-3 text-sm">
{t('No referral program active')}
</p>
)}
@@ -165,7 +154,6 @@ export const FeesContainer = () => {
title={t('Volume discount')}
className="lg:col-span-full xl:col-span-2"
loading={loading}
data-testid="volume-discount-card"
>
<VolumeTiers
tiers={volumeTiers}
@@ -178,7 +166,6 @@ export const FeesContainer = () => {
title={t('Referral discount')}
className="lg:col-span-full xl:col-span-2"
loading={loading}
data-testid="referral-discount-card"
>
<ReferralTiers
tiers={referralTiers}
@@ -191,7 +178,6 @@ export const FeesContainer = () => {
title={t('Fees by market')}
className="lg:col-span-full"
loading={marketsLoading}
data-testid="fees-by-market-card"
>
<MarketFees
markets={markets}
@@ -259,7 +245,7 @@ export const TradingFees = ({
}
return (
<div className="pt-4" data-testid="trading-fees">
<div className="pt-4">
<div className="leading-none">
<p className="block text-3xl leading-none" data-testid="adjusted-fees">
{minAdjustedTotal !== undefined && maxAdjustedTotal !== undefined
@@ -269,7 +255,7 @@ export const TradingFees = ({
: `${formatPercentage(adjustedTotal)}%`}
</p>
<CardTable>
<tr className="text-default" data-testid="total-fee-before-discount">
<tr className="text-default">
<CardTableTH>{t('Total fee before discount')}</CardTableTH>
<CardTableTD>
{minTotal !== undefined && maxTotal !== undefined
@@ -279,7 +265,7 @@ export const TradingFees = ({
: `${formatPercentage(total.toNumber())}%`}
</CardTableTD>
</tr>
<tr data-testid="infrastructure-fees">
<tr>
<CardTableTH>{t('Infrastructure')}</CardTableTH>
<CardTableTD>
{formatPercentage(
@@ -288,14 +274,14 @@ export const TradingFees = ({
%
</CardTableTD>
</tr>
<tr data-testid="maker-fees">
<tr>
<CardTableTH>{t('Maker')}</CardTableTH>
<CardTableTD>
{formatPercentage(Number(params.market_fee_factors_makerFee))}%
</CardTableTD>
</tr>
{minLiq && maxLiq && (
<tr data-testid="liquidity-fees">
<tr>
<CardTableTH>{t('Liquidity')}</CardTableTH>
<CardTableTD>
{formatPercentage(Number(minLiq.fees.factors.liquidityFee))}%
@@ -331,7 +317,7 @@ export const CurrentVolume = ({
const currentVolume = new BigNumber(windowLengthVolume);
return (
<div className="flex flex-col gap-3 pt-4" data-testid="current-volume">
<div className="flex flex-col gap-3 pt-4">
<CardStat
value={
currentVolume.isZero()
@@ -341,13 +327,11 @@ export const CurrentVolume = ({
text={t('pastEpochs', 'Past {{count}} epochs', {
count: windowLength,
})}
testId="past-epochs-volume"
/>
{requiredForNextTier.isGreaterThan(0) && (
<CardStat
value={formatNumber(requiredForNextTier)}
text={t('Required for next tier')}
testId="required-for-next-tier"
/>
)}
</div>
@@ -365,7 +349,7 @@ const ReferralBenefits = ({
}) => {
const t = useT();
return (
<div className="flex flex-col gap-3 pt-4" data-testid="referral-benefits">
<div className="flex flex-col gap-3 pt-4">
<CardStat
// all sets volume (not just current party)
value={formatNumber(setRunningNotionalTakerVolume)}
@@ -376,13 +360,8 @@ const ReferralBenefits = ({
count: epochs,
}
)}
testId="running-notional-taker-volume"
/>
<CardStat
value={epochsInSet}
text={t('epochs in referral set')}
testId="epochs-in-referral-set"
/>
<CardStat value={epochsInSet} text={t('epochs in referral set')} />
</div>
);
};
@@ -410,7 +389,7 @@ const TotalDiscount = ({
);
return (
<div className="pt-4" data-testid="total-discount-card-stats">
<div className="pt-4">
<CardStat
description={
<>
@@ -420,10 +399,9 @@ const TotalDiscount = ({
}
value={formatPercentage(totalDiscount) + '%'}
highlight={true}
testId="total-discount"
/>
<CardTable>
<tr data-testid="volume-discount-row">
<tr>
<CardTableTH>{t('Volume discount')}</CardTableTH>
<CardTableTD>
{formatPercentage(volumeDiscount)}%
@@ -437,7 +415,7 @@ const TotalDiscount = ({
)}
</CardTableTD>
</tr>
<tr data-testid="referral-discount-row">
<tr>
<CardTableTH>{t('Referral discount')}</CardTableTH>
<CardTableTD>
{formatPercentage(referralDiscount)}%
@@ -483,37 +461,29 @@ const VolumeTiers = ({
<div>
<Table>
<THead>
<Tr>
<Th data-testid="tier-header">{t('Tier')}</Th>
<Th data-testid="discount-header">{t('Discount')}</Th>
<Th data-testid="min-volume-header">{t('Min. trading volume')}</Th>
<Th data-testid="my-volume-header">
<tr>
<Th>{t('Tier')}</Th>
<Th>{t('Discount')}</Th>
<Th>{t('Min. trading volume')}</Th>
<Th>
{t('myVolume', 'My volume (last {{count}} epochs)', {
count: windowLength,
})}
</Th>
<Th data-testid="actions-header" />
</Tr>
<Th />
</tr>
</THead>
<tbody>
{Array.from(tiers).map((tier, i) => {
const isUserTier = tierIndex === i;
return (
<Tr key={i} data-testid={`tier-row-${i}`}>
<Td data-testid={`tier-value-${i}`}>{i + 1}</Td>
<Td data-testid={`discount-value-${i}`}>
{formatPercentage(Number(tier.volumeDiscountFactor))}%
</Td>
<Td data-testid={`min-volume-value-${i}`}>
{formatNumber(tier.minimumRunningNotionalTakerVolume)}
</Td>
<Td data-testid={`my-volume-value-${i}`}>
{isUserTier ? formatNumber(lastEpochVolume) : ''}
</Td>
<Td data-testid={`your-tier-${i}`}>
{isUserTier ? <YourTier /> : null}
</Td>
<Tr key={i}>
<Td>{i + 1}</Td>
<Td>{formatPercentage(Number(tier.volumeDiscountFactor))}%</Td>
<Td>{formatNumber(tier.minimumRunningNotionalTakerVolume)}</Td>
<Td>{isUserTier ? formatNumber(lastEpochVolume) : ''}</Td>
<Td>{isUserTier ? <YourTier /> : null}</Td>
</Tr>
);
})}
@@ -550,53 +520,39 @@ const ReferralTiers = ({
<div>
<Table>
<THead>
<Tr>
<Th data-testid="tier-header">{t('Tier')}</Th>
<Th data-testid="discount-header">{t('Discount')}</Th>
<Th data-testid="min-volume-header">{t('Min. trading volume')}</Th>
<Th data-testid="required-epochs-header">{t('Required epochs')}</Th>
<Th data-testid="extra-header" />
</Tr>
<tr>
<Th>{t('Tier')}</Th>
<Th>{t('Discount')}</Th>
<Th>{t('Min. trading volume')}</Th>
<Th>{t('Required epochs')}</Th>
<Th />
</tr>
</THead>
<tbody>
{Array.from(tiers).map((tier, i) => {
{Array.from(tiers).map((t, i) => {
const isUserTier = tierIndex === i;
const requiredVolume = Number(
tier.minimumRunningNotionalTakerVolume
);
const requiredVolume = Number(t.minimumRunningNotionalTakerVolume);
let unlocksIn = null;
if (
referralVolumeInWindow >= requiredVolume &&
epochsInSet < tier.minimumEpochs
epochsInSet < t.minimumEpochs
) {
unlocksIn = (
<span className="text-muted">
Unlocks in {tier.minimumEpochs - epochsInSet} epochs
Unlocks in {t.minimumEpochs - epochsInSet} epochs
</span>
);
}
return (
<Tr key={i} data-testid={`tier-row-${i}`}>
<Td data-testid={`tier-value-${i}`}>{i + 1}</Td>
<Td data-testid={`discount-value-${i}`}>
{formatPercentage(Number(tier.referralDiscountFactor))}%
</Td>
<Td data-testid={`min-volume-value-${i}`}>
{formatNumber(tier.minimumRunningNotionalTakerVolume)}
</Td>
<Td data-testid={`required-epochs-value-${i}`}>
{tier.minimumEpochs}
</Td>
<Td data-testid={`user-tier-or-unlocks-${i}`}>
{isUserTier ? (
<YourTier testId={`your-tier-${i}`} />
) : (
unlocksIn
)}
</Td>
<Tr key={i}>
<Td>{i + 1}</Td>
<Td>{formatPercentage(Number(t.referralDiscountFactor))}%</Td>
<Td>{formatNumber(t.minimumRunningNotionalTakerVolume)}</Td>
<Td>{t.minimumEpochs}</Td>
<Td>{isUserTier ? <YourTier /> : unlocksIn}</Td>
</Tr>
);
})}
@@ -606,18 +562,11 @@ const ReferralTiers = ({
);
};
interface YourTierProps {
testId?: string;
}
const YourTier = ({ testId }: YourTierProps) => {
const YourTier = () => {
const t = useT();
return (
<span
className="bg-rainbow whitespace-nowrap rounded-xl px-4 py-1.5 text-white"
data-testid={testId}
>
<span className="bg-rainbow whitespace-nowrap rounded-xl px-4 py-1.5 text-white">
{t('Your tier')}
</span>
);
@@ -3,31 +3,21 @@ import type { ReactNode } from 'react';
const cellClass = 'px-4 py-2 text-xs font-normal text-left last:text-right';
export const Th = ({ children, ...props }: { children?: ReactNode }) => {
export const Th = ({ children }: { children?: ReactNode }) => {
return (
<th
className={classNames(cellClass, 'text-secondary leading-none py-3')}
{...props}
>
<th className={classNames(cellClass, 'text-secondary leading-none py-3')}>
{children}
</th>
);
};
export const Td = ({ children, ...props }: { children?: ReactNode }) => {
return (
<th className={cellClass} {...props}>
{children}
</th>
);
export const Td = ({ children }: { children?: ReactNode }) => {
return <th className={cellClass}>{children}</th>;
};
export const Tr = ({ children, ...props }: { children?: ReactNode }) => {
export const Tr = ({ children }: { children?: ReactNode }) => {
return (
<tr
className="hover:bg-vega-clight-600 dark:hover:bg-vega-cdark-700"
{...props}
>
<tr className="hover:bg-vega-clight-600 dark:hover:bg-vega-cdark-700">
{children}
</tr>
);
@@ -40,9 +40,6 @@ const DateRange = {
RANGE_ALL: 'All',
};
const priceFormat = (fundingRate: number) =>
`${(fundingRate * 100).toFixed(4)}%`;
export const FundingContainer = ({ marketId }: { marketId: string }) => {
const t = useT();
const { theme } = useThemeSwitcher();
@@ -85,7 +82,7 @@ export const FundingContainer = ({ marketId }: { marketId: string }) => {
<LineChart
data={values}
theme={theme}
priceFormat={priceFormat}
priceFormat={(fundingRate) => `${(fundingRate * 100).toFixed(4)}%`}
yAxisTickFormat="%"
/>
);
@@ -5,7 +5,7 @@ import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { Navbar } from './navbar';
import { useGlobalStore } from '../../stores';
import { ENV, useFeatureFlags } from '@vegaprotocol/environment';
import { ENV, FLAGS } from '@vegaprotocol/environment';
jest.mock('@vegaprotocol/proposals', () => ({
ProtocolUpgradeCountdown: () => null,
@@ -48,7 +48,8 @@ describe('Navbar', () => {
beforeAll(() => {
useGlobalStore.setState({ marketId });
useFeatureFlags.setState({ flags: { REFERRALS: true } });
const mockedFLAGS = jest.mocked(FLAGS);
mockedFLAGS.REFERRALS = true;
const mockedENV = jest.mocked(ENV);
mockedENV.VEGA_TOKEN_URL = 'governance';
});
+2 -3
View File
@@ -6,7 +6,7 @@ import {
Networks,
DApp,
useLinks,
useFeatureFlags,
FLAGS,
useEnvNameMapping,
} from '@vegaprotocol/environment';
import { useGlobalStore } from '../../stores';
@@ -157,7 +157,6 @@ export const Navbar = ({
* of the navigation
*/
const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
const featureFlags = useFeatureFlags((state) => state.flags);
const t = useT();
const envNameMapping = useEnvNameMapping();
const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment();
@@ -202,7 +201,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
{t('Portfolio')}
</NavbarLink>
</NavbarItem>
{featureFlags.REFERRALS && (
{FLAGS.REFERRALS && (
<NavbarItem>
<NavbarLink end={false} to={Links.REFERRALS()} onClick={onClick}>
{t('Referrals')}
+1 -13
View File
@@ -10,18 +10,6 @@ import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
import { useState, type ReactNode } from 'react';
import classNames from 'classnames';
import { useT } from '../../lib/use-t';
import { useFeatureFlags, type FeatureFlags } from '@vegaprotocol/environment';
export const FeatureFlagSwitch = ({ flag }: { flag: keyof FeatureFlags }) => {
const flags = useFeatureFlags((state) => state.flags);
const setFeatureFlag = useFeatureFlags((state) => state.setFeatureFlag);
return (
<Switch
onCheckedChange={(checked) => setFeatureFlag(flag, !!checked)}
checked={flags[flag]}
/>
);
};
export const Settings = () => {
const t = useT();
@@ -132,7 +120,7 @@ const SettingsGroup = ({
})}
>
<div className={classNames({ 'w-3/4': inline, 'mb-2': !inline })}>
<div className="text-sm">{label}</div>
<label className="text-sm">{label}</label>
{helpText && <p className="text-xs text-muted">{helpText}</p>}
</div>
{children}
+11 -24
View File
@@ -16,7 +16,6 @@ import { GetStarted } from '../welcome-dialog';
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { ErrorBoundary } from '../error-boundary';
export enum ViewType {
Order = 'Order',
@@ -164,14 +163,12 @@ export const SidebarContent = () => {
if (params.marketId) {
return (
<ContentWrapper>
<ErrorBoundary feature="deal-ticket">
<DealTicketContainer
marketId={params.marketId}
onDeposit={(assetId) =>
setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
}
/>
</ErrorBoundary>
<DealTicketContainer
marketId={params.marketId}
onDeposit={(assetId) =>
setViews({ type: ViewType.Deposit, assetId }, currentRouteId)
}
/>
<GetStarted />
</ContentWrapper>
);
@@ -184,9 +181,7 @@ export const SidebarContent = () => {
if (params.marketId) {
return (
<ContentWrapper>
<ErrorBoundary feature="market-info">
<MarketInfoAccordionContainer marketId={params.marketId} />
</ErrorBoundary>
<MarketInfoAccordionContainer marketId={params.marketId} />
</ContentWrapper>
);
} else {
@@ -197,9 +192,7 @@ export const SidebarContent = () => {
if (view.type === ViewType.Deposit) {
return (
<ContentWrapper title={t('Deposit')}>
<ErrorBoundary feature="deposit">
<DepositContainer assetId={view.assetId} />
</ErrorBoundary>
<DepositContainer assetId={view.assetId} />
</ContentWrapper>
);
}
@@ -207,9 +200,7 @@ export const SidebarContent = () => {
if (view.type === ViewType.Withdraw) {
return (
<ContentWrapper title={t('Withdraw')}>
<ErrorBoundary feature="withdraw">
<WithdrawContainer assetId={view.assetId} />
</ErrorBoundary>
<WithdrawContainer assetId={view.assetId} />
</ContentWrapper>
);
}
@@ -217,9 +208,7 @@ export const SidebarContent = () => {
if (view.type === ViewType.Transfer) {
return (
<ContentWrapper title={t('Transfer')}>
<ErrorBoundary feature="transfer">
<TransferContainer assetId={view.assetId} />
</ErrorBoundary>
<TransferContainer assetId={view.assetId} />
</ContentWrapper>
);
}
@@ -227,9 +216,7 @@ export const SidebarContent = () => {
if (view.type === ViewType.Settings) {
return (
<ContentWrapper title={t('Settings')}>
<ErrorBoundary feature="settings">
<Settings />
</ErrorBoundary>
<Settings />
</ContentWrapper>
);
}
@@ -24,13 +24,7 @@ import classNames from 'classnames';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
export const VegaWalletConnectButton = ({
intent = Intent.None,
onClick,
}: {
intent?: Intent;
onClick?: () => void;
}) => {
export const VegaWalletConnectButton = () => {
const t = useT();
const [dropdownOpen, setDropdownOpen] = useState(false);
const openVegaWalletDialog = useVegaWalletDialogStore(
@@ -123,12 +117,9 @@ export const VegaWalletConnectButton = ({
return (
<Button
data-testid="connect-vega-wallet"
onClick={() => {
onClick?.();
openVegaWalletDialog();
}}
onClick={openVegaWalletDialog}
size="small"
intent={intent}
intent={Intent.None}
icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />}
>
<span className="whitespace-nowrap uppercase">
@@ -10,7 +10,6 @@ import { positionsDataProvider } from '@vegaprotocol/positions';
import { useGlobalStore } from '../../stores';
const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
export const useOnboardingStore = create<{
dialogOpen: boolean;
walletDialogOpen: boolean;
@@ -21,7 +20,7 @@ export const useOnboardingStore = create<{
}>()(
persist(
(set) => ({
dialogOpen: false,
dialogOpen: true,
walletDialogOpen: false,
dismissed: false,
dismiss: () => set({ dismissed: true }),
@@ -1,30 +1,18 @@
import { useEffect } from 'react';
import { matchPath, useLocation } from 'react-router-dom';
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
import { useEnvironment } from '@vegaprotocol/environment';
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { useConnectors } from '../../lib/vega-connectors';
import { useT } from '../../lib/use-t';
import { Routes } from '../../lib/links';
import { RiskMessage } from './risk-message';
import { WelcomeDialogContent } from './welcome-dialog-content';
import { useOnboardingStore } from './use-get-onboarding-step';
import { ensureSuffix } from '@vegaprotocol/utils';
/**
* A list of paths on which the welcome dialog should be omitted.
*/
const OMIT_ON_LIST = [ensureSuffix(Routes.REFERRALS, '/*')];
import { VegaConnectDialog } from '@vegaprotocol/wallet';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
import { useT } from '../../lib/use-t';
export const WelcomeDialog = () => {
const { pathname } = useLocation();
const t = useT();
const { VEGA_ENV } = useEnvironment();
const connectors = useConnectors();
const dismissed = useOnboardingStore((store) => store.dismissed);
const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
const dismiss = useOnboardingStore((store) => store.dismiss);
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
const walletDialogOpen = useOnboardingStore(
(store) => store.walletDialogOpen
);
@@ -32,19 +20,9 @@ export const WelcomeDialog = () => {
(store) => store.setWalletDialogOpen
);
useEffect(() => {
const shouldOmit = OMIT_ON_LIST.map((path) =>
matchPath(path, pathname)
).some((m) => !!m);
if (dismissed || shouldOmit) return;
setDialogOpen(true);
}, [dismissed, pathname, setDialogOpen]);
const content = walletDialogOpen ? (
<VegaConnectDialog
connectors={connectors}
connectors={Connectors}
riskMessage={<RiskMessage />}
onClose={() => setWalletDialogOpen(false)}
contentOnly
@@ -53,12 +31,7 @@ export const WelcomeDialog = () => {
<WelcomeDialogContent />
);
const onClose = walletDialogOpen
? () => setWalletDialogOpen(false)
: () => {
setDialogOpen(false);
dismiss();
};
const onClose = walletDialogOpen ? () => setWalletDialogOpen(false) : dismiss;
const title = walletDialogOpen ? null : (
<span className="font-alpha calt" data-testid="welcome-title">
+1 -2
View File
@@ -1,3 +1,2 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
VEGA_VERSION=v0.73.9
LOCAL_SERVER=false
VEGA_VERSION=v0.73.6
+1 -1
View File
@@ -1,2 +1,2 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
VEGA_VERSION=v0.73.9
VEGA_VERSION=v0.73.6
+1 -2
View File
@@ -1,3 +1,2 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:main
VEGA_VERSION=v0.73.8
LOCAL_SERVER=false
VEGA_VERSION=v0.73.6
+2 -22
View File
@@ -24,12 +24,6 @@ poetry shell
5. **Install python dependencies**
To make sure you are on the latest version of our market-sim branch.
```bash
poetry update vega-sim
```
```bash
poetry install
```
@@ -93,9 +87,10 @@ docker build -f docker/node-outside-docker.Dockerfile --build-arg APP=trading --
## Running Tests 🧪
Before running make sure the docker daemon is running.
Before running make sure the docker daemon is runnign so that the app can be served.
To run a specific test, use the `-k` option followed by the name of the test.
Run all tests:
```bash
@@ -114,21 +109,6 @@ Run from anywhere:
yarn trading:test -- "test_name" -s --headed
```
Run using your locally served console:
Within one terminal
```bash
yarn nx build trading
```
```bash
yarn nx serve trading
```
Once console is served you can update the .env file to have local_server to true.
## Running Tests in Parallel 🔢
To run tests in parallel, use the `--numprocesses auto` option. The `--dist loadfile` setting ensures that multiple runners are not assigned to a single test file.
+4 -30
View File
@@ -6,27 +6,23 @@ from typing import Optional
WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
ASSET_NAME = "tDAI"
def wait_for_toast_confirmation(page: Page, timeout: int = 30000):
page.wait_for_function("""
document.querySelector('[data-testid="toast-content"]') &&
document.querySelector('[data-testid="toast-content"]').innerText.includes('AWAITING CONFIRMATION')
""", timeout=timeout)
def create_and_faucet_wallet(
vega: VegaServiceNull,
wallet: WalletConfig,
symbol: Optional[str] = None,
amount: float = 1e4,
):
asset_id = vega.find_asset_id(
symbol=symbol if symbol is not None else ASSET_NAME)
asset_id = vega.find_asset_id(symbol=symbol if symbol is not None else ASSET_NAME)
vega.create_key(wallet.name)
vega.mint(wallet.name, asset_id, amount)
def next_epoch(vega: VegaServiceNull):
forwards = 0
epoch_seq = vega.statistics().epoch_seq
@@ -40,35 +36,13 @@ def next_epoch(vega: VegaServiceNull):
vega.wait_fn(1)
vega.wait_for_total_catchup()
def truncate_middle(market_id, start=6, end=4):
if len(market_id) < 11:
return market_id
return market_id[:start] + '\u2026' + market_id[-end:]
def change_keys(page: Page, vega: VegaServiceNull, key_name):
def change_keys(page: Page, vega:VegaServiceNull, key_name):
page.get_by_test_id("manage-vega-wallet").click()
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
page.click(
f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
page.click(f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
page.reload()
def forward_time(vega:VegaServiceNull, forward_epoch: bool = False):
vega.wait_fn(1)
vega.wait_for_total_catchup()
if forward_epoch:
next_epoch(vega)
# This is for when the element will initially load but contain an outdated value. It will wait for the element to contain the expected text, returning False after a timeout or exception
def selector_contains_text(page: Page, selector, expected_text, timeout=5000):
try:
page.wait_for_selector(
f'{selector} >> text={expected_text}', timeout=timeout)
return True
except:
return False
+4 -5
View File
@@ -1,7 +1,6 @@
from typing import List, Tuple, Optional
from vega_sim.service import VegaService, PeggedOrder
def submit_order(
vega: VegaService,
wallet_name: str,
@@ -36,7 +35,7 @@ def submit_multiple_orders(
submit_order(vega, wallet_name, market_id, side, volume, price)
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str, buy_vol=99, sell_vol=99, custom_price=None):
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
vega.submit_simple_liquidity(
key_name=wallet_name,
market_id=market_id,
@@ -52,7 +51,7 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str, buy_vo
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=buy_vol,
volume=99,
)
vega.submit_order(
market_id=market_id,
@@ -62,5 +61,5 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str, buy_vo
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=sell_vol,
)
volume=99,
)
+22 -36
View File
@@ -6,10 +6,10 @@ import requests
import time
import docker
import http.server
import sys
from dotenv import load_dotenv
from contextlib import contextmanager
from vega_sim.null_service import VegaServiceNull, Ports
from vega_sim.null_service import VegaServiceNull
from playwright.sync_api import Browser, Page
from config import console_image_name, vega_version
from datetime import datetime, timedelta
@@ -20,6 +20,7 @@ from fixtures.market import (
setup_perps_market,
)
import sys
# Workaround for current xdist issue with displaying live logs from multiple workers
# https://github.com/pytest-dev/pytest-xdist/issues/402
@@ -28,8 +29,6 @@ sys.stdout = sys.stderr
docker_client = docker.from_env()
logger = logging.getLogger()
load_dotenv()
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call):
@@ -51,24 +50,16 @@ def pytest_configure(config):
level=config.getini("log_file_level"),
)
class CustomHttpRequestHandler(http.server.SimpleHTTPRequestHandler):
def do_GET(self):
# Set the path to your website's directory here
if self.path == "/":
self.path = "dist/apps/trading/exported/index.html"
if self.path == '/':
self.path = 'dist/apps/trading/exported/index.html'
return http.server.SimpleHTTPRequestHandler.do_GET(self)
# Start VegaServiceNull
@contextmanager
def init_vega(request=None):
local_server = os.getenv("LOCAL_SERVER", "false").lower() == "true"
port_config = None
if local_server:
port_config = {
Ports.DATA_NODE_REST: 8001,
}
default_seconds = 1
seconds_per_block = default_seconds
if request and hasattr(request, "param"):
@@ -80,26 +71,21 @@ def init_vega(request=None):
)
logger.info(f"Using console image: {console_image_name}")
logger.info(f"Using vega version: {vega_version}")
vega_service_args = {
"run_with_console": False,
"launch_graphql": False,
"retain_log_files": True,
"use_full_vega_wallet": True,
"store_transactions": True,
"transactions_per_block": 1000,
"seconds_per_block": seconds_per_block,
"genesis_time": datetime.now() - timedelta(days=1),
}
if port_config is not None:
vega_service_args["port_config"] = port_config
with VegaServiceNull(**vega_service_args) as vega:
with VegaServiceNull(
run_with_console=False,
launch_graphql=False,
retain_log_files=True,
use_full_vega_wallet=True,
store_transactions=True,
transactions_per_block=1000,
seconds_per_block=seconds_per_block,
genesis_time= datetime.now() - timedelta(days=1),
) as vega:
try:
container = docker_client.containers.run(
console_image_name, detach=True, ports={"80/tcp": vega.console_port}
)
# docker setup
logger.info(
f"Container {container.id} started",
extra={"worker_id": os.environ.get("PYTEST_XDIST_WORKER")},
@@ -112,17 +98,15 @@ def init_vega(request=None):
finally:
logger.info(f"Stopping container {container.id}")
container.stop()
# Remove the container
logger.info(f"Removing container {container.id}")
container.remove()
@contextmanager
def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRequest):
local_server = os.getenv("LOCAL_SERVER", "false").lower() == "true"
server_port = "4200" if local_server else str(vega.console_port)
with browser.new_context(
viewport={"width": 1920, "height": 1080},
base_url=f"http://localhost:{server_port}",
base_url=f"http://localhost:{vega.console_port}",
) as context, context.new_page() as page:
context.tracing.start(screenshots=True, snapshots=True, sources=True)
try:
@@ -130,7 +114,9 @@ def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRe
attempts = 0
while attempts < 100:
try:
code = requests.get(f"http://localhost:{server_port}/").status_code
code = requests.get(
f"http://localhost:{vega.console_port}/"
).status_code
if code == 200:
break
except requests.exceptions.ConnectionError as e:
+11 -38
View File
@@ -8,17 +8,12 @@ logger = logging.getLogger()
mint_amount: float = 10e5
market_name = "BTC:DAI_2023"
default_sell_orders = [[1, 110], [1, 105]]
default_buy_orders = [[1, 90], [1, 95]]
def setup_simple_market(
vega: VegaService,
approve_proposal=True,
custom_market_name=market_name,
custom_asset_name="tDAI",
custom_asset_symbol="tDAI",
custom_quantum=1
):
for wallet in wallets:
vega.create_key(wallet.name)
@@ -42,7 +37,6 @@ def setup_simple_market(
symbol=custom_asset_symbol,
decimals=5,
max_faucet_amount=1e10,
quantum=custom_quantum,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -117,17 +111,16 @@ def setup_simple_successor_market(
return market_id
def setup_opening_auction_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
if not market_exists(vega, market_id):
def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwargs):
if market_id is None or market_id not in vega.all_markets():
market_id = setup_simple_market(vega, **kwargs)
if add_liquidity:
submit_liquidity(vega, MM_WALLET.name, market_id)
submit_liquidity(vega, MM_WALLET.name, market_id)
submit_multiple_orders(
vega, MM_WALLET.name, market_id, "SIDE_SELL", sell_orders
vega, MM_WALLET.name, market_id, "SIDE_SELL", [[1, 110], [1, 105]]
)
submit_multiple_orders(
vega, MM_WALLET2.name, market_id, "SIDE_BUY", buy_orders
vega, MM_WALLET2.name, market_id, "SIDE_BUY", [[1, 90], [1, 95]]
)
vega.forward("10s")
@@ -137,22 +130,11 @@ def setup_opening_auction_market(vega: VegaService, market_id: str = None, buy_o
return market_id
def market_exists(vega: VegaService, market_id: str):
if market_id is None:
return False
all_markets = vega.all_markets()
market_ids = [market.id for market in all_markets]
return market_id in market_ids
def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs):
if market_id is None or market_id not in vega.all_markets():
market_id = setup_opening_auction_market(vega, **kwargs)
# Add sell orders and buy orders to put on the book
def setup_continuous_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
if not market_exists(vega, market_id) or buy_orders != default_buy_orders or sell_orders != default_sell_orders:
market_id = setup_opening_auction_market(
vega, market_id, buy_orders, sell_orders, add_liquidity, **kwargs)
submit_order(vega, "Key 1", market_id, "SIDE_BUY",
sell_orders[0][0], sell_orders[0][1])
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110)
vega.forward("10s")
vega.wait_fn(1)
@@ -160,7 +142,6 @@ def setup_continuous_market(vega: VegaService, market_id: str = None, buy_orders
return market_id
def setup_perps_market(
vega: VegaService,
custom_asset_name="tDAI",
@@ -229,7 +210,7 @@ def setup_perps_market(
settlement_data_key=TERMINATE_WALLET.name,
funding_payment_frequency_in_seconds=10,
market_decimals=5,
)
)
vega.wait_for_total_catchup()
submit_liquidity(vega, MM_WALLET.name, market_id)
@@ -244,12 +225,4 @@ def setup_perps_market(
vega.wait_fn(1)
vega.wait_for_total_catchup()
return market_id
def market_exists(vega: VegaService, market_id: str):
if market_id is None:
return False
all_markets = vega.all_markets()
market_ids = [market.id for market in all_markets]
return market_id in market_ids
return market_id
+4 -4
View File
@@ -1159,9 +1159,9 @@ profile = ["pytest-profiling", "snakeviz"]
[package.source]
type = "git"
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
reference = "fix/genesis_panic"
resolved_reference = "7ab04931924380db8000544b7f3d65fcb39b5467"
url = "https://github.com/vegaprotocol/vega-market-sim.git"
reference = "HEAD"
resolved_reference = "fbcb974b2055bbc80169cdfd69987f087f9969fb"
[[package]]
name = "websocket-client"
@@ -1342,4 +1342,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.11"
content-hash = "68ed0de55290a3b929d47eb7f7b031fb7e172261c7bbeb4f554b7c27a4462754"
content-hash = "d1231fe591b774e34b8f94a54cd02e4d7dae924c57785263841c3b0b0feed505"
+8 -14
View File
@@ -56,12 +56,11 @@ label_value_tooltip_pairs = [
def tooltip(page: Page, index: int, test_id: str, tooltip: str):
page.locator(f"data-testid={index}_{test_id}").hover()
expect(page.locator('[role="tooltip"]').locator(
"div")).to_have_text(tooltip)
expect(page.locator('[role="tooltip"]').locator("div")).to_have_text(tooltip)
page.get_by_test_id("dialog-title").click()
@pytest.mark.usefixtures("continuous_market", "auth", "risk_accepted")
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
def test_asset_details(page: Page):
page.goto("/#/portfolio")
page.locator('[data-testid="tab-collateral"] >> text=tDAI').click()
@@ -74,22 +73,17 @@ def test_asset_details(page: Page):
value = pair.get("value", "")
label_tooltip = pair.get("labelTooltip", "")
value_tooltip = pair.get("valueToolTip", "")
if label == "ID":
expect(page.get_by_role(
"button", name="Copy id to clipboard")).to_be_visible()
asset_id_text = page.locator(
f"[data-testid='{index}_value']").inner_text()
expect(page.get_by_role("button", name="Copy id to clipboard")).to_be_visible()
asset_id_text = page.locator(f"[data-testid='{index}_value']").inner_text()
pattern = r"^[0-9a-f]{6}\u2026[0-9a-f]{4}"
assert re.match(
pattern, asset_id_text), f"Expected ID to match pattern but got {asset_id_text}"
assert re.match(pattern, asset_id_text), f"Expected ID to match pattern but got {asset_id_text}"
else:
expect(page.locator(
f"[data-testid='{index}_label']")).to_have_text(label)
expect(page.locator(
f"[data-testid='{index}_value']")).to_have_text(value)
expect(page.locator(f"[data-testid='{index}_label']")).to_have_text(label)
expect(page.locator(f"[data-testid='{index}_value']")).to_have_text(value)
if label_tooltip:
tooltip(page, index, "label", label_tooltip)
@@ -14,16 +14,19 @@ market_order = "order-type-Market"
tif = "order-tif"
expire = "expire"
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega:
yield vega
@pytest.fixture(scope="module")
def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_limit_buy_order_GTT(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(tif).select_option("Good 'til Time (GTT)")
@@ -51,7 +54,8 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaService, page: Page):
"BTC:DAI_2023Futr10+10LimitFilled120.00GTT:"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_limit_buy_order(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
@@ -68,7 +72,8 @@ def test_limit_buy_order(continuous_market, vega: VegaService, page: Page):
"BTC:DAI_2023Futr10+10LimitFilled120.00GTC"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_limit_sell_order(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(order_size).fill("10")
@@ -92,7 +97,8 @@ def test_limit_sell_order(continuous_market, vega: VegaService, page: Page):
"BTC:DAI_2023Futr10-10LimitFilled100.00GFN"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_market_sell_order(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(market_order).click()
@@ -116,7 +122,8 @@ def test_market_sell_order(continuous_market, vega: VegaService, page: Page):
"BTC:DAI_2023Futr10-10MarketFilled-IOC"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_market_buy_order(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(market_order).click()
@@ -13,7 +13,7 @@ def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.skip("We currently can't approve wallet connection through Sim")
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("page", "risk_accepted")
def test_connect_vega_wallet(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id("order-price").fill("101")
@@ -25,7 +25,7 @@ def test_connect_vega_wallet(continuous_market, page: Page):
expect(page.get_by_test_id("order-type-Limit")).to_be_checked()
expect(page.get_by_test_id("order-price")).to_have_value("101")
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("page", "risk_accepted")
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
@@ -12,7 +12,7 @@ market_trading_mode = "market-trading-mode"
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_margin_and_fees_estimations(continuous_market, vega: VegaService, page: Page):
# setup continuous trading market with one user buy trade
market_id = continuous_market
@@ -38,6 +38,7 @@ timeInForce_col = '[col-id="submission.timeInForce"]'
updatedAt_col = '[col-id="updatedAt"]'
close_toast = "toast-close"
def create_position(vega: VegaService, market_id):
submit_order(vega, "Key 1", market_id, "SIDE_SELL", 100, 110)
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 100, 110)
@@ -45,7 +46,7 @@ def create_position(vega: VegaService, market_id):
vega.wait_fn(1)
vega.wait_for_total_catchup
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
def test_stop_order_form_error_validation(continuous_market, page: Page):
# 7002-SORD-032
page.goto(f"/#/markets/{continuous_market}")
@@ -68,7 +69,7 @@ def test_stop_order_form_error_validation(continuous_market, page: Page):
)
@pytest.mark.skip("core issue")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_order_rejected(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(stop_orders_tab).click()
@@ -106,7 +107,7 @@ def test_submit_stop_order_rejected(continuous_market, vega: VegaService, page:
).not_to_be_empty()
@pytest.mark.skip("core issue")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_market_order_triggered(
continuous_market, vega: VegaService, page: Page
):
@@ -164,7 +165,7 @@ def test_submit_stop_market_order_triggered(
).not_to_be_empty()
@pytest.mark.skip("core issue")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("continuous_market", "auth", "risk_accepted")
def test_submit_stop_limit_order_pending(
continuous_market, vega: VegaService, page: Page
):
@@ -225,7 +226,7 @@ def test_submit_stop_limit_order_pending(
).not_to_be_empty()
@pytest.mark.skip("core issue")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("continuous_market", "auth", "risk_accepted")
def test_submit_stop_limit_order_cancel(
continuous_market, vega: VegaService, page: Page
):
@@ -269,7 +270,7 @@ class TestStopOcoValidation:
def continuous_market(self, vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_stop_market_order_form_validation(self, continuous_market, page: Page):
# 7002-SORD-052
# 7002-SORD-055
@@ -302,7 +303,7 @@ class TestStopOcoValidation:
expect(page.get_by_test_id(order_size)).to_be_empty
expect(page.get_by_test_id(order_price)).not_to_be_visible()
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_stop_limit_order_form_validation(self, continuous_market, page: Page):
# 7002-SORD-020
# 7002-SORD-021
@@ -346,7 +347,7 @@ class TestStopOcoValidation:
expect(page.get_by_test_id(order_price)).to_be_empty()
@pytest.mark.skip("core issue")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_maximum_number_of_active_stop_orders(
self, continuous_market, vega: VegaService, page: Page
):
@@ -4,6 +4,7 @@ from vega_sim.service import VegaService
from actions.vega import submit_order
from actions.utils import wait_for_toast_confirmation
stop_order_btn = "order-type-Stop"
stop_limit_order_btn = "order-type-StopLimit"
stop_market_order_btn = "order-type-StopMarket"
@@ -49,7 +50,7 @@ def create_position(vega: VegaService, market_id):
vega.wait_for_total_catchup
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_order_market_oco_rejected(
continuous_market, vega: VegaService, page: Page
):
@@ -126,7 +127,7 @@ def test_submit_stop_order_market_oco_rejected(
assert trigger_price_list.sort() == trigger_value_list.sort()
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_oco_market_order_triggered(
continuous_market, vega: VegaService, page: Page
):
@@ -203,7 +204,7 @@ def test_submit_stop_oco_market_order_triggered(
assert trigger_price_list.sort() == trigger_value_list.sort()
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_oco_market_order_pending(
continuous_market, vega: VegaService, page: Page
):
@@ -235,7 +236,7 @@ def test_submit_stop_oco_market_order_pending(
"PendingOCO"
)
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_oco_limit_order_pending(
continuous_market, vega: VegaService, page: Page
):
@@ -286,7 +287,7 @@ def test_submit_stop_oco_limit_order_pending(
assert trigger_price_list.sort() == trigger_value_list.sort()
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "vega", "continuous_market", "auth", "risk_accepted")
def test_submit_stop_oco_limit_order_cancel(
continuous_market, vega: VegaService, page: Page
):
@@ -324,3 +325,5 @@ def test_submit_stop_oco_limit_order_cancel(
expect(
page.locator(".ag-center-cols-container").locator('[col-id="status"]').last
).to_have_text("CancelledOCO")
@@ -5,6 +5,8 @@ from actions.utils import change_keys
from conftest import init_vega
from fixtures.market import setup_continuous_market
order_size = "order-size"
order_price = "order-price"
place_order = "place-order"
@@ -16,12 +18,13 @@ def vega(request):
with init_vega(request) as vega:
yield vega
@pytest.fixture(scope="module")
def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_should_display_info_and_button_for_deposit(continuous_market, page: Page):
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_should_display_info_and_button_for_deposit(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(order_size).fill("200000")
page.get_by_test_id(order_price).fill("20")
@@ -32,7 +35,7 @@ def test_should_display_info_and_button_for_deposit(continuous_market, page: Pag
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")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_should_show_an_error_if_your_balance_is_zero(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
vega.create_key("key_empty")
-682
View File
@@ -1,682 +0,0 @@
import pytest
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from actions.vega import submit_order
from wallet_config import MM_WALLET
from conftest import init_vega, init_page, auth_setup
from actions.utils import next_epoch, change_keys, forward_time
from fixtures.market import market_exists, setup_continuous_market
# region Constants for test IDs
ADJUSTED_FEES = "adjusted-fees"
TOTAL_FEE_BEFORE_DISCOUNT = "total-fee-before-discount"
INFRASTRUCTURE_FEES = "infrastructure-fees"
MAKER_FEES = "maker-fees"
LIQUIDITY_FEES = "liquidity-fees"
TOTAL_DISCOUNT = "total-discount"
VOLUME_DISCOUNT_ROW = "volume-discount-row"
REFERRAL_DISCOUNT_ROW = "referral-discount-row"
PAST_EPOCHS_VOLUME = "past-epochs-volume"
REQUIRED_FOR_NEXT_TIER = "required-for-next-tier"
TIER_VALUE_0 = "tier-value-0"
TIER_VALUE_1 = "tier-value-1"
DISCOUNT_VALUE_0 = "discount-value-0"
DISCOUNT_VALUE_1 = "discount-value-1"
MIN_VOLUME_VALUE_0 = "min-volume-value-0"
MIN_VOLUME_VALUE_1 = "min-volume-value-1"
MY_VOLUME_VALUE_0 = "my-volume-value-0"
MY_VOLUME_VALUE_1 = "my-volume-value-1"
YOUR_TIER_0 = "your-tier-0"
YOUR_TIER_1 = "your-tier-1"
ORDER_SIZE = "order-size"
ORDER_PRICE = "order-price"
DISCOUNT_PILL = "discount-pill"
FEES_TEXT = "fees-text"
TOOLTIP_CONTENT = "tooltip-content"
INFRASTRUCTURE_FEE_FACTOR = "infrastructure-fee-factor"
INFRASTRUCTURE_FEE_VALUE = "infrastructure-fee-value"
LIQUIDITY_FEE_FACTOR = "liquidity-fee-factor"
LIQUIDITY_FEE_VALUE = "liquidity-fee-value"
MAKER_FEE_FACTOR = "maker-fee-factor"
MAKER_FEE_VALUE = "maker-fee-value"
SUBTOTAL_FEE_FACTOR = "subtotal-fee-factor"
SUBTOTAL_FEE_VALUE = "subtotal-fee-value"
DISCOUNT_FEE_FACTOR = "discount-fee-factor"
DISCOUNT_FEE_VALUE = "discount-fee-value"
TOTAL_FEE_VALUE = "total-fee-value"
RUNNING_NOTIONAL_TAKER_VOLUME = "running-notional-taker-volume"
EPOCHS_IN_REFERRAL_SET = "epochs-in-referral-set"
REQUIRED_EPOCHS_VALUE_0 = "required-epochs-value-0"
REQUIRED_EPOCHS_VALUE_1 = "required-epochs-value-1"
FILLS = "Fills"
TAB_FILLS = "tab-fills"
FEE_BREAKDOWN_TOOLTIP = "fee-breakdown-tooltip"
ROW_LOCATOR = ".ag-center-cols-container .ag-row"
# Col-Ids:
COL_INSTRUMENT_CODE = '[col-id="market.tradableInstrument.instrument.code"]'
COL_CODE = '[col-id="code"]'
COL_SIZE = '[col-id="size"]'
COL_PRICE = '[col-id="price"]'
COL_PRICE_1 = '[col-id="price_1"]'
COL_AGGRESSOR = '[col-id="aggressor"]'
COL_FEE = '[col-id="fee"]'
COL_FEE_DISCOUNT = '[col-id="fee-discount"]'
COL_FEE_AFTER_DISCOUNT = '[col-id="feeAfterDiscount"]'
COL_INFRA_FEE = '[col-id="infraFee"]'
COL_MAKER_FEE = '[col-id="makerFee"]'
COL_LIQUIDITY_FEE = '[col-id="liquidityFee"]'
COL_TOTAL_FEE = '[col-id="totalFee"]'
# endregion
@pytest.fixture(scope="module")
def market_ids():
return {
"tier_1_volume": "default_id",
"tier_2_volume": "default_id",
"tier_1_referral": "default_id",
"tier_2_referral": "default_id",
"combo": "default_id",
}
@pytest.fixture(scope="module")
def vega_volume_discount_tier_1(request):
with init_vega(request) as vega_volume_discount_tier_1:
yield vega_volume_discount_tier_1
@pytest.fixture(scope="module")
def vega_volume_discount_tier_2(request):
with init_vega(request) as vega_volume_discount_tier_2:
yield vega_volume_discount_tier_2
@pytest.fixture(scope="module")
def vega_referral_discount_tier_1(request):
with init_vega(request) as vega_referral_discount_tier_1:
yield vega_referral_discount_tier_1
@pytest.fixture(scope="module")
def vega_referral_discount_tier_2(request):
with init_vega(request) as vega_referral_discount_tier_2:
yield vega_referral_discount_tier_2
@pytest.fixture(scope="module")
def vega_referral_and_volume_discount(request):
with init_vega(request) as vega_referral_and_volume_discount:
yield vega_referral_and_volume_discount
@pytest.fixture
def page(vega_instance, browser, request):
with init_page(vega_instance, browser, request) as page_instance:
yield page_instance
@pytest.fixture
def vega_instance(
tier,
discount_program,
vega_volume_discount_tier_1,
vega_volume_discount_tier_2,
vega_referral_discount_tier_1,
vega_referral_discount_tier_2,
vega_referral_and_volume_discount,
):
if discount_program == "volume":
return vega_volume_discount_tier_1 if tier == 1 else vega_volume_discount_tier_2
elif discount_program == "referral":
return (
vega_referral_discount_tier_1
if tier == 1
else vega_referral_discount_tier_2
)
elif discount_program == "combo":
return vega_referral_and_volume_discount
@pytest.fixture
def auth(vega_instance, page):
return auth_setup(vega_instance, page)
def setup_market_with_volume_discount_program(vega: VegaService, tier: int):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
next_epoch(vega=vega)
order_count = 2 if tier == 1 else 3
for _ in range(order_count):
submit_order(vega, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega, True if _ < order_count - 1 else False)
return market
def setup_market_with_referral_discount_program(vega: VegaService, tier: int):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega=vega)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega=vega)
order_count = 2
order_size = 1 if tier == 1 else 2
for _ in range(order_count):
submit_order(vega, "Key 1", market, "SIDE_BUY", order_size, 110)
forward_time(vega, True if _ < order_count - 1 else False)
return market
def setup_combined_market(vega: VegaService):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
next_epoch(vega=vega)
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega=vega)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega=vega)
order_count = 2
order_size = 2
for _ in range(order_count):
submit_order(vega, "Key 1", market, "SIDE_BUY", order_size, 110)
forward_time(vega, True if _ < order_count - 1 else False)
return market
def set_market_volume_discount(vega, tier, discount_program, market_ids):
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key, "default_id")
print(f"Checking if market exists: {market_id}")
if not market_exists(vega, market_id):
print(
f"Market doesn't exist for {discount_program} tier {tier}. Setting up new market."
)
if discount_program == "volume":
market_id = setup_market_with_volume_discount_program(vega, tier)
elif discount_program == "referral":
market_id = setup_market_with_referral_discount_program(vega, tier)
elif discount_program == "combo":
market_id = setup_combined_market(vega)
market_ids[market_id_key] = market_id
print(f"Using market ID: {market_id}")
return market_ids
@pytest.mark.parametrize(
"tier, discount_program, expected_text",
[
(1, "volume", "9.045%-9.045%"),
(2, "volume", "8.04%-8.04%"),
(1, "referral", "9.045%-9.045%"),
(2, "referral", "8.04%-8.04%"),
(2, "combo", "6.432%-6.432%"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_discount_program_my_trading_fees(
tier, expected_text, discount_program, vega_instance, page: Page, market_ids
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text(expected_text)
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
"Total fee before discount10.05%-10.05%"
)
expect(page.get_by_test_id(INFRASTRUCTURE_FEES)).to_have_text("Infrastructure0.05%")
expect(page.get_by_test_id(MAKER_FEES)).to_have_text("Maker10%")
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.parametrize(
"tier, discount_program, volume_discount, total_discount, referral_discount",
[
(1, "volume", "Volume discount10%", "10%", "Referral discount0%"),
(2, "volume", "Volume discount20%", "20%", "Referral discount0%"),
(1, "referral", "Volume discount0%", "10%", "Referral discount10%"),
(2, "referral", "Volume discount0%", "20%", "Referral discount20%"),
(2, "combo", "Volume discount20%", "36%", "Referral discount20%"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_discount_program_total_discount(
tier,
discount_program,
volume_discount,
referral_discount,
total_discount,
vega_instance,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text(total_discount)
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text(volume_discount)
expect(page.get_by_test_id(REFERRAL_DISCOUNT_ROW)).to_have_text(referral_discount)
page.get_by_test_id(TOTAL_DISCOUNT).hover()
expect(page.get_by_test_id(TOOLTIP_CONTENT).nth(0)).to_have_text(
"The total discount is calculated according to the following formula: 1 - (1 - dvolume) ⋇ (1 - dreferral)"
)
@pytest.mark.parametrize(
"tier, discount_program, past_epochs_volume, required_for_next_tier",
[(1, "volume", "103", "97"), (2, "volume", "206", "")],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_volume_discount_program_my_current_volume(
tier,
discount_program,
past_epochs_volume,
required_for_next_tier,
vega_instance,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
expect(page.get_by_test_id(PAST_EPOCHS_VOLUME)).to_have_text(past_epochs_volume)
if tier == 1:
expect(page.get_by_test_id(REQUIRED_FOR_NEXT_TIER)).to_have_text(
required_for_next_tier
)
else:
expect(page.get_by_test_id(REQUIRED_FOR_NEXT_TIER)).not_to_be_visible()
@pytest.mark.parametrize(
"tier, discount_program, notional_taker_volume, epochs_in_set",
[(1, "referral", "103", "1"), (2, "referral", "207", "1")],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_referral_discount_program_referral_benefits(
tier,
vega_instance,
discount_program,
notional_taker_volume,
epochs_in_set,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
expect(page.get_by_test_id(RUNNING_NOTIONAL_TAKER_VOLUME)).to_have_text(
notional_taker_volume
)
expect(page.get_by_test_id(EPOCHS_IN_REFERRAL_SET)).to_have_text(epochs_in_set)
@pytest.mark.parametrize(
"tier, discount_program, my_volume_test_id, my_volume_value, your_tier",
[
(1, "volume", "my-volume-value-0", "103", "your-tier-0"),
(2, "volume", "my-volume-value-1", "206", "your-tier-1"),
(1, "referral", "my-volume-value-0", "103", "your-tier-0"),
(2, "referral", "my-volume-value-1", "206", "your-tier-1"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_discount_program_discount(
tier,
discount_program,
my_volume_test_id,
my_volume_value,
your_tier,
vega_instance,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
expect(page.get_by_test_id(TIER_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(TIER_VALUE_1)).to_have_text("2")
expect(page.get_by_test_id(DISCOUNT_VALUE_0)).to_have_text("10%")
expect(page.get_by_test_id(DISCOUNT_VALUE_1)).to_have_text("20%")
expect(page.get_by_test_id(MIN_VOLUME_VALUE_0)).to_have_text("100")
expect(page.get_by_test_id(MIN_VOLUME_VALUE_1)).to_have_text("200")
if discount_program == "volume":
expect(page.get_by_test_id(my_volume_test_id)).to_have_text(my_volume_value)
else:
expect(page.get_by_test_id(REQUIRED_EPOCHS_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(REQUIRED_EPOCHS_VALUE_1)).to_have_text("2")
expect(page.get_by_test_id(your_tier)).to_be_visible()
expect(page.get_by_test_id(your_tier)).to_have_text("Your tier")
@pytest.mark.parametrize(
"tier, discount_program, fees_after_discount",
[
(1, "volume", "9.045%"),
(2, "volume", "8.04%"),
(1, "referral", "9.045%"),
(2, "referral", "8.04%"),
(2, "combo", "6.432%"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fees_page_discount_program_fees_by_market(
tier, discount_program, fees_after_discount, vega_instance, page: Page, market_ids
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
page.goto("/#/fees")
row = page.locator(ROW_LOCATOR)
expect(row.locator(COL_CODE)).to_have_text("BTC:DAI_2023Futr")
expect(row.locator(COL_FEE_AFTER_DISCOUNT)).to_have_text(fees_after_discount)
expect(row.locator(COL_INFRA_FEE)).to_have_text("0.05%")
expect(row.locator(COL_MAKER_FEE)).to_have_text("10%")
expect(row.locator(COL_LIQUIDITY_FEE)).to_have_text("0%")
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.parametrize(
"tier, discount_program, discount, discount_value, total_fee",
[
(1, "volume", "-10%", "-0.01005 tDAI", "0.09045 tDAI"),
(2, "volume", "-20%", "-0.0201 tDAI", "0.0804 tDAI"),
(1, "referral", "-10%", "-0.01005 tDAI", "0.09045 tDAI"),
(2, "referral", "-20%", "-0.0201 tDAI", "0.0804 tDAI"),
(2, "combo", "-36%", "-0.03618 tDAI", "0.06432 tDAI"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_deal_ticket_discount_program(
tier,
discount_program,
discount,
discount_value,
total_fee,
vega_instance,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key)
page.goto(f"/#/markets/{market_id}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text(discount)
page.get_by_test_id(FEES_TEXT).hover()
tooltip = page.get_by_test_id(TOOLTIP_CONTENT).first
expect(tooltip.get_by_test_id(INFRASTRUCTURE_FEE_FACTOR)).to_have_text("0.05%")
expect(tooltip.get_by_test_id(INFRASTRUCTURE_FEE_VALUE)).to_have_text("0.0005 tDAI")
expect(tooltip.get_by_test_id(LIQUIDITY_FEE_FACTOR)).to_have_text("0%")
expect(tooltip.get_by_test_id(LIQUIDITY_FEE_VALUE)).to_have_text("0.00 tDAI")
expect(tooltip.get_by_test_id(MAKER_FEE_FACTOR)).to_have_text("10%")
expect(tooltip.get_by_test_id(MAKER_FEE_VALUE)).to_have_text("0.10 tDAI")
expect(tooltip.get_by_test_id(SUBTOTAL_FEE_FACTOR)).to_have_text("10.05%")
expect(tooltip.get_by_test_id(SUBTOTAL_FEE_VALUE)).to_have_text("0.1005 tDAI")
expect(tooltip.get_by_test_id(DISCOUNT_FEE_FACTOR)).to_have_text(discount)
expect(tooltip.get_by_test_id(DISCOUNT_FEE_VALUE)).to_have_text(discount_value)
expect(tooltip.get_by_test_id(TOTAL_FEE_VALUE)).to_have_text(total_fee)
@pytest.mark.parametrize(
"tier, discount_program, fee, fee_discount, price_1, size",
[
(1, "volume", "9.36158 tDAI", "1.04017 tDAI", "103.50 tDAI", "+1"),
(2, "volume", "8.3214 tDAI", "2.08035 tDAI", "103.50 tDAI", "+1"),
(
1,
"referral",
"8.42543 tDAI ",
"1.04017 tDAI",
"103.50 tDAI",
"+1",
),
(
2,
"referral",
"13.31424 tDAI",
"4.1607 tDAI",
"207.00 tDAI",
"+2",
),
(2, "combo", "10.6514 tDAI ", "7.48926 tDAI", "207.00 tDAI", "+2"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fills_taker_discount_program(
tier,
discount_program,
fee,
fee_discount,
price_1,
size,
vega_instance,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key)
page.goto(f"/#/markets/{market_id}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
expect(row.locator(COL_SIZE)).to_have_text(size)
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
expect(row.locator(COL_AGGRESSOR)).to_have_text("Taker")
expect(row.locator(COL_FEE)).to_have_text(fee)
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text(fee_discount)
@pytest.mark.parametrize(
"tier, discount_program, fee, fee_discount, size, price_1",
[
(1, "volume", "-9.315 tDAI", "1.035 tDAI", "-1", "103.50 tDAI"),
(2, "volume", "-8.28 tDAI", "2.07 tDAI", "-1", "103.50 tDAI"),
(1, "referral", "-8.3835 tDAI", "1.035 tDAI", "-1", "103.50 tDAI"),
(2, "referral", "-13.248 tDAI", "4.14 tDAI", "-2", "207.00 tDAI"),
(2, "combo", "-10.5984 tDAI ", "7.452 tDAI", "-2", "207.00 tDAI"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fills_maker_discount_program(
tier,
discount_program,
vega_instance,
fee,
fee_discount,
size,
price_1,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key)
page.goto(f"/#/markets/{market_id}")
change_keys(page, vega_instance, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
expect(row.locator(COL_SIZE)).to_have_text(size)
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
expect(row.locator(COL_AGGRESSOR)).to_have_text("Maker")
expect(row.locator(COL_FEE)).to_have_text(fee)
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text(fee_discount)
@pytest.mark.parametrize(
"tier, discount_program, fee",
[
(1, "volume", "9.315"),
(2, "volume", "8.28"),
(1, "referral", "8.3835"),
(2, "referral", "13.248"),
(2, "combo", "10.5984"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fills_maker_fee_tooltip_discount_program(
tier, discount_program, fee, vega_instance, page: Page, market_ids
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key)
page.goto(f"/#/markets/{market_id}")
change_keys(page, vega_instance, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
row.locator(COL_FEE).hover()
expect(page.get_by_test_id(FEE_BREAKDOWN_TOOLTIP)).to_have_text(
f"If the market was activeThe maker will receive the maker fee.If the market is active the maker will pay zero infrastructure and liquidity fees.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee-{fee} tDAITotal fees-{fee} tDAI"
)
@pytest.mark.parametrize(
"tier, discount_program, maker_fee, total_fee, infra_fee",
[
(1, "volume", "9.315", "9.36158", "0.04658"),
(2, "volume", "8.28", "8.3214", "0.0414"),
(1, "referral", "8.3835", "8.42543", "0.04193"),
(2, "referral", "13.248", "13.31424", "0.06624"),
(2, "combo", "10.5984", "10.6514", "0.053"),
],
)
@pytest.mark.usefixtures("risk_accepted", "auth", "market_ids")
def test_fills_taker_fee_tooltip_discount_program(
tier,
discount_program,
vega_instance,
maker_fee,
total_fee,
infra_fee,
page: Page,
market_ids,
):
market_ids = set_market_volume_discount(
vega_instance, tier, discount_program, market_ids
)
market_id_key = f"tier_{tier}_{discount_program}"
if discount_program == "combo":
market_id_key = "combo"
market_id = market_ids.get(market_id_key)
page.goto(f"/#/markets/{market_id}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
row.locator(COL_FEE).hover()
expect(page.get_by_test_id(FEE_BREAKDOWN_TOOLTIP)).to_have_text(
f"If the market was activeFees to be paid by the taker.Infrastructure fee{infra_fee} tDAILiquidity fee0.00 tDAIMaker fee{maker_fee} tDAITotal fees{total_fee} tDAI"
)
@@ -10,16 +10,20 @@ import logging
logger = logging.getLogger()
@pytest.fixture(scope="class")
def vega():
with init_vega() as vega:
yield vega
# we can reuse vega market-sim service and market in almost all tests
@pytest.fixture(scope="class")
def simple_market(vega: VegaService):
return setup_simple_market(vega)
class TestGetStarted:
@pytest.mark.usefixtures("page")
def test_get_started_interactive(self, vega: VegaService, page: Page):
page.goto("/")
# 0007-FUGS-001
@@ -130,7 +134,8 @@ class TestGetStarted:
# Assert dialog isn't visible
expect(page.get_by_test_id("welcome-dialog")).not_to_be_visible()
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("page", "risk_accepted")
def test_get_started_seen_already(self, simple_market, page: Page):
page.goto(f"/#/markets/{simple_market}")
get_started_locator = page.get_by_test_id("connect-vega-wallet")
@@ -143,6 +148,8 @@ class TestGetStarted:
# 0007-FUGS-007
expect(page.get_by_test_id("dialog-content").nth(1)).to_be_visible()
@pytest.mark.usefixtures("page")
def test_browser_wallet_installed(self, simple_market, page: Page):
page.add_init_script("window.vega = {}")
page.goto(f"/#/markets/{simple_market}")
@@ -152,13 +159,14 @@ class TestGetStarted:
expect(locator).to_be_visible
expect(locator).to_have_text("Connect")
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("page", "risk_accepted")
def test_get_started_deal_ticket(self,simple_market, page: Page):
page.goto(f"/#/markets/{simple_market}")
expect(page.get_by_test_id("order-connect-wallet")).to_have_text("Connect wallet")
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("page", "risk_accepted")
def test_browser_wallet_installed_deal_ticket(simple_market, page: Page):
page.add_init_script("window.vega = {}")
page.goto(f"/#/markets/{simple_market}")
@@ -166,6 +174,7 @@ class TestGetStarted:
page.wait_for_selector('[data-testid="sidebar-content"]', state="visible")
expect(page.get_by_test_id("get-started-banner")).not_to_be_visible()
@pytest.mark.usefixtures("page")
def test_redirect_default_market(self, continuous_market, vega: VegaService, page: Page):
page.goto("/")
# 0007-FUGS-012
@@ -177,6 +186,7 @@ class TestGetStarted:
expect(page.get_by_test_id("welcome-dialog")).not_to_be_visible()
class TestBrowseAll:
@pytest.mark.usefixtures("page")
def test_get_started_browse_all(self, simple_market, vega: VegaService, page: Page):
page.goto("/")
print(simple_market)
@@ -11,6 +11,7 @@ def hover_and_assert_tooltip(page: Page, element_text):
element.hover()
expect(page.get_by_role("tooltip")).to_be_visible()
class TestIcebergOrdersValidations:
@pytest.fixture(scope="class")
def vega(self, request):
@@ -21,7 +22,7 @@ class TestIcebergOrdersValidations:
def continuous_market(self, vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_iceberg_submit(self, continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id("iceberg").click()
@@ -46,7 +47,7 @@ class TestIcebergOrdersValidations:
(page.get_by_role("row").locator('[col-id="type"]')).nth(1)
).to_have_text("Limit (Iceberg)")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("vega", "page", "continuous_market", "auth", "risk_accepted")
def test_iceberg_open_order(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
@@ -17,34 +17,27 @@ def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_liquidity_provision_amendment(continuous_market, vega: VegaService, page: Page):
# TODO Refactor asserting the grid
page.goto(f"/#/liquidity/{continuous_market}")
change_keys(page, vega, "market_maker")
row = page.get_by_test_id(
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
row = page.get_by_test_id("tab-myLP").locator(".ag-center-cols-container .ag-row").first
expect(row).to_contain_text(
"Active"
)
# 5002-LIQP-006
expect(page.get_by_test_id("target-stake")
).to_have_text("Target stake5.82757 tDAI")
expect(page.get_by_test_id("target-stake")).to_have_text("Target stake5.82757 tDAI")
# 5002-LIQP-007
expect(page.get_by_test_id("supplied-stake")
).to_have_text("Supplied stake10,000.00 tDAI")
expect(page.get_by_test_id("supplied-stake")).to_have_text("Supplied stake10,000.00 tDAI")
# 5002-LIQP-008
expect(page.get_by_test_id("liquidity-supplied")
).to_have_text("Liquidity supplied 171,598.11%")
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 171,598.11%")
expect(page.get_by_test_id("fees-paid")).to_have_text("Fees paid-")
# 5002-LIQP-009
expect(page.get_by_test_id("liquidity-market-id")
).to_have_text("Market ID" + truncate_middle(continuous_market))
expect(page.get_by_test_id("liquidity-learn-more")
).to_have_text("Learn moreProviding liquidity")
expect(page.get_by_test_id("liquidity-market-id")).to_have_text("Market ID" + truncate_middle(continuous_market))
expect(page.get_by_test_id("liquidity-learn-more")).to_have_text("Learn moreProviding liquidity")
# 002-LIQP-010
expect(page.get_by_test_id("liquidity-learn-more").get_by_test_id("external-link")
).to_have_attribute("href", "https://docs.vega.xyz/testnet/concepts/liquidity/provision")
expect(page.get_by_test_id("liquidity-learn-more").get_by_test_id("external-link")).to_have_attribute("href", "https://docs.vega.xyz/testnet/concepts/liquidity/provision")
vega.submit_simple_liquidity(
key_name="market_maker",
@@ -57,32 +50,26 @@ def test_liquidity_provision_amendment(continuous_market, vega: VegaService, pag
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.reload()
row = page.get_by_test_id(
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
row = page.get_by_test_id("tab-myLP").locator(".ag-center-cols-container .ag-row").first
expect(row).to_contain_text(
"Updating next epoch"
)
next_epoch(vega=vega)
page.reload()
expect(page.get_by_test_id("supplied-stake")
).to_have_text("Supplied stake1.00001 tDAI")
expect(page.get_by_test_id("liquidity-supplied")
).to_have_text("Liquidity supplied 17.16%")
row = page.get_by_test_id(
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
expect(page.get_by_test_id("supplied-stake")).to_have_text("Supplied stake1.00001 tDAI")
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 17.16%")
row = page.get_by_test_id("tab-myLP").locator(".ag-center-cols-container .ag-row").first
expect(row).to_contain_text(
"Active"
)
@pytest.mark.skip("Waiting for the ability to cancel LP")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
def test_liquidity_provision_inactive(continuous_market, vega: VegaService, page: Page):
# TODO Refactor asserting the grid
page.goto(f"/#/liquidity/{continuous_market}")
change_keys(page, vega, "market_maker")
row = page.get_by_test_id(
"tab-myLP").locator(".ag-center-cols-container .ag-row").first
change_keys(page,vega, "market_maker")
row = page.get_by_test_id("tab-myLP").locator(".ag-center-cols-container .ag-row").first
expect(row).to_contain_text(
"Active"
)
@@ -95,3 +82,4 @@ def test_liquidity_provision_inactive(continuous_market, vega: VegaService, page
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -12,7 +12,6 @@ def vega():
with init_vega() as vega:
yield vega
@pytest.fixture(scope="class")
def create_settled_market(vega: VegaService):
market_id = setup_continuous_market(vega)
@@ -74,9 +73,8 @@ class TestSettledMarket:
# 6001-MARK-010
pattern = r"(\d+)\s+(months|hours|days)\s+ago"
date_text = row_selector.locator('[col-id="settlementDate"]').inner_text()
assert re.match(
pattern, date_text
), f"Expected text to match pattern but got {date_text}"
assert re.match(pattern, date_text), f"Expected text to match pattern but got {date_text}"
expected_pattern = re.compile(r"https://.*?/oracles/[a-f0-9]{64}")
actual_href = row_selector.locator(
@@ -89,12 +87,12 @@ class TestSettledMarket:
expect(row_selector.locator('[col-id="bestBidPrice"]')).to_have_text("0.00")
# 6001-MARK-012
expect(row_selector.locator('[col-id="bestOfferPrice"]')).to_have_text("0.00")
# 6001-MARK-013
# 6001-MARK-013
expect(row_selector.locator('[col-id="markPrice"]')).to_have_text("110.00")
# 6001-MARK-014
# 6001-MARK-015
# 6001-MARK-016
# tbd currently we have value unknown
#tbd currently we have value unknown
# expect(row_selector.locator('[col-id="settlementDataOracleId"]')).to_have_text(
# "110.00"
# )
@@ -109,9 +107,7 @@ class TestSettledMarket:
# 6001-MARK-018
expect(row_selector.locator('[col-id="settlementAsset"]')).to_have_text("tDAI")
# 6001-MARK-020
assert re.match(
pattern, date_text
), f"Expected text to match pattern but got {date_text}"
assert re.match(pattern, date_text), f"Expected text to match pattern but got {date_text}"
@pytest.mark.usefixtures("risk_accepted", "auth")
+11 -16
View File
@@ -31,7 +31,7 @@ initial_spread: float = 0.1
market_name = "BTC:DAI_2023"
@pytest.mark.usefixtures("risk_accepted", "auth")
@pytest.mark.usefixtures("vega", "page", "simple_market", "risk_accepted")
def test_price_monitoring(simple_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/all")
expect(page.locator(table_row_selector).locator(trading_mode_col)).to_have_text(
@@ -75,7 +75,7 @@ def test_price_monitoring(simple_market, vega: VegaService, page: Page):
time_in_force="TIME_IN_FORCE_GTC",
volume=99,
)
# 6002-MDET-009
#6002-MDET-009
expect(
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
).to_have_text("0.00 (0.00%)")
@@ -109,8 +109,9 @@ def test_price_monitoring(simple_market, vega: VegaService, page: Page):
expect(
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
).to_have_text("100.00 (>100%)")
vega.forward("10s")
vega.wait_fn(10)
vega.wait_fn(1)
vega.wait_for_total_catchup()
expect(
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
@@ -154,7 +155,7 @@ def test_price_monitoring(simple_market, vega: VegaService, page: Page):
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega.wait_for_total_catchup()
expect(
page.get_by_test_id(price_monitoring_bounds_row).first.get_by_text(
"135.44204 BTC"
@@ -191,28 +192,22 @@ def test_price_monitoring(simple_market, vega: VegaService, page: Page):
)
# commented out because we have an issue #4233
# expect(page.get_by_text("Opening auction")).to_be_hidden()
# 6002-MDET-009
#6002-MDET-009
expect(
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
).to_have_text("50.00 (>100%)")
COL_ID_FEE = ".ag-center-cols-container [col-id='fee'] .ag-cell-value"
@pytest.mark.usefixtures("risk_accepted", "auth")
@pytest.mark.usefixtures("vega", "page", "continuous_market", "risk_accepted", "auth")
def test_auction_uncross_fees(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id("Fills").click()
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
page.locator(COL_ID_FEE).hover()
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text(
"If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI"
)
change_keys(page, vega, "market_maker")
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text("If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI")
change_keys(page,vega, "market_maker")
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
page.locator(COL_ID_FEE).hover()
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text(
"If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI"
)
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text("If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI")
@@ -3,6 +3,7 @@ import pytest
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from fixtures.market import setup_continuous_market
from conftest import init_page, init_vega, risk_accepted_setup
market_title_test_id = "accordion-title"
@@ -14,6 +15,7 @@ def vega():
yield vega
# setting up everything in this single fixture, as all of the tests need the same setup, so no point in creating separate ones
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
@@ -120,9 +122,7 @@ def test_market_info_instrument(page: Page):
# @pytest.mark.skip("oracle test to be fixed")
def test_market_info_oracle(page: Page):
def test_market_info_oracle(page: Page, vega: VegaService):
# 6002-MDET-203
page.get_by_test_id(market_title_test_id).get_by_text("Oracle").click()
expect(
@@ -197,10 +197,10 @@ def test_market_info_risk_factors(page: Page):
fields = [
["Long", "0.05153"],
["Short", "0.05422"],
["Max Leverage Long", "19.406"],
["Max Leverage Short", "18.445"],
["Max Initial Leverage Long", "12.937"],
["Max Initial Leverage Short", "12.297"],
["Max Leverage Long", "19.036"],
["Max Leverage Short", "18.111"],
["Max Initial Leverage Long", "12.691"],
["Max Initial Leverage Short", "12.074"],
]
validate_info_section(page, fields)
@@ -2,7 +2,7 @@ import pytest
from playwright.sync_api import expect, Page
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
def test_market_selector(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
expect(page.get_by_test_id("market-selector")).not_to_be_visible()
@@ -27,7 +27,7 @@ def test_market_selector(continuous_market, page: Page):
expect(btc_market.locator('[data-testid="sparkline-svg"]')).not_to_be_visible
@pytest.mark.usefixtures("simple_market", "auth", "risk_accepted")
@pytest.mark.usefixtures("page", "continuous_market", "simple_market", "auth", "risk_accepted")
@pytest.mark.parametrize(
"simple_market",
[
@@ -1,5 +1,6 @@
import pytest
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from conftest import init_page, init_vega, risk_accepted_setup
@@ -5,7 +5,7 @@ from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from conftest import init_vega
from fixtures.market import setup_simple_market
from wallet_config import MM_WALLET
from wallet_config import MM_WALLET, MM_WALLET2, TERMINATE_WALLET, wallets
row_selector = '[data-testid="tab-proposed-markets"] .ag-center-cols-container .ag-row'
col_market_id = '[col-id="market"] [data-testid="stack-cell-primary"]'
@@ -16,7 +16,6 @@ def vega(request):
with init_vega(request) as vega:
yield vega
@pytest.fixture(scope="module")
def proposed_market(vega: VegaService):
# setup market without liquidity provided
@@ -7,7 +7,6 @@ from conftest import init_vega
from actions.utils import wait_for_toast_confirmation
from wallet_config import MM_WALLET, MM_WALLET2
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega:
@@ -18,7 +17,6 @@ def vega(request):
def simple_market(vega):
return setup_simple_market(vega)
@pytest.fixture(scope="module")
def setup_market_monitoring_auction(vega: VegaService, simple_market):
vega.submit_liquidity(
@@ -50,18 +48,12 @@ def setup_market_monitoring_auction(vega: VegaService, simple_market):
volume=99,
)
# add orders to provide liquidity
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_BUY", 1, 1)
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 1, 1)
submit_order(
vega,
MM_WALLET.name,
simple_market,
"SIDE_BUY",
1,
1 + 0.1 / 2,
)
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 1, 1 + 0.1 / 2)
submit_order(vega,MM_WALLET.name,simple_market, "SIDE_BUY",1,1 + 0.1 / 2,)
submit_order(vega,MM_WALLET.name,simple_market,"SIDE_SELL",1,1 + 0.1 / 2)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_SELL", 1, 1)
vega.forward("10s")
@@ -79,11 +71,9 @@ def setup_market_monitoring_auction(vega: VegaService, simple_market):
vega.wait_fn(1)
vega.wait_for_total_catchup()
@pytest.mark.usefixtures("risk_accepted", "auth", "setup_market_monitoring_auction")
def test_market_monitoring_auction_price_volatility_limit_order(
page: Page, simple_market, vega: VegaService
):
@pytest.mark.usefixtures("page", "risk_accepted", "simple_market", "auth", "setup_market_monitoring_auction")
def test_market_monitoring_auction_price_volatility_limit_order(page: Page, simple_market, vega: VegaService):
page.goto(f"/#/markets/{simple_market}")
page.get_by_test_id("order-size").clear()
page.get_by_test_id("order-size").type("1")
@@ -92,14 +82,10 @@ def test_market_monitoring_auction_price_volatility_limit_order(
page.get_by_test_id("order-tif").select_option("Fill or Kill (FOK)")
page.get_by_test_id("place-order").click()
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
)
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text("This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.")
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_be_visible()
expect(page.get_by_test_id("deal-ticket-warning-auction")).to_have_text(
"Any orders placed now will not trade until the auction ends"
)
expect(page.get_by_test_id("deal-ticket-warning-auction")).to_have_text("Any orders placed now will not trade until the auction ends")
expect(page.get_by_test_id("deal-ticket-warning-auction")).to_be_visible()
page.get_by_test_id("order-tif").select_option("Good 'til Cancelled (GTC)")
@@ -117,11 +103,8 @@ def test_market_monitoring_auction_price_volatility_limit_order(
"BTC:DAI_2023Futr0+1LimitActive110.00GTC"
)
@pytest.mark.usefixtures("risk_accepted", "auth", "setup_market_monitoring_auction")
def test_market_monitoring_auction_price_volatility_market_order(
page: Page, simple_market
):
@pytest.mark.usefixtures("page", "risk_accepted", "simple_market", "auth", "setup_market_monitoring_auction")
def test_market_monitoring_auction_price_volatility_market_order(page: Page, simple_market):
page.goto(f"/#/markets/{simple_market}")
page.get_by_test_id("order-type-Market").click()
page.get_by_test_id("order-size").clear()
@@ -129,12 +112,8 @@ def test_market_monitoring_auction_price_volatility_market_order(
# 7002-SORD-060
page.get_by_test_id("place-order").click()
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
)
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text("This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.")
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_be_visible()
expect(page.get_by_test_id("deal-ticket-error-message-type")).to_have_text(
"This market is in auction due to high price volatility. Only limit orders are permitted when market is in auction."
)
expect(page.get_by_test_id("deal-ticket-error-message-type")).to_have_text("This market is in auction due to high price volatility. Only limit orders are permitted when market is in auction.")
expect(page.get_by_test_id("deal-ticket-error-message-type")).to_be_visible()
@@ -9,7 +9,8 @@ from actions.utils import next_epoch
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
@pytest.mark.usefixtures("risk_accepted")
@pytest.mark.usefixtures("vega", "page", "proposed_market", "risk_accepted")
def test_market_lifecycle(proposed_market, vega: VegaService, page: Page):
# 7002-SORD-001
# 7002-SORD-002
@@ -26,12 +27,8 @@ def test_market_lifecycle(proposed_market, vega: VegaService, page: Page):
# 6002-MDET-002
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"
)
expect(
page.get_by_test_id("expiry-tooltip").first.get_by_test_id("link")
).to_have_attribute("href", re.compile(".*"))
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")
expect(page.get_by_test_id("expiry-tooltip").first.get_by_test_id("link")).to_have_attribute("href", re.compile('.*'))
# 6002-MDET-003
expect(page.get_by_test_id("market-price")).to_have_text("Mark Price0.00")
# 6002-MDET-004
@@ -39,30 +36,18 @@ def test_market_lifecycle(proposed_market, vega: VegaService, page: Page):
# 6002-MDET-005
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)-")
# 6002-MDET-008
expect(page.get_by_test_id("market-settlement-asset")).to_have_text(
"Settlement assettDAI"
)
expect(page.get_by_test_id("liquidity-supplied")).to_have_text(
"Liquidity supplied 0.00 (0.00%)"
)
expect(page.get_by_test_id("market-settlement-asset")).to_have_text("Settlement assettDAI")
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 0.00 (0.00%)")
page.get_by_test_id("liquidity-supplied").hover()
expect(page.get_by_test_id("liquidity-supplied-tooltip").first).to_have_text(
"Supplied stake0.00Target stake0.00View liquidity provision tableLearn about providing liquidity"
)
expect(
page.get_by_test_id("liquidity-supplied-tooltip")
.first.get_by_test_id("link")
.first
).to_have_text("View liquidity provision table")
expect(page.get_by_test_id("liquidity-supplied-tooltip").first).to_have_text("Supplied stake0.00Target stake0.00View liquidity provision tableLearn about providing liquidity")
expect(page.get_by_test_id("liquidity-supplied-tooltip").first.get_by_test_id("link").first).to_have_text("View liquidity provision table")
# check that market is in proposed state
# 6002-MDET-006
# 6002-MDET-007
# 7002-SORD-061
expect(trading_mode).to_have_text("No trading")
trading_mode.hover()
expect(page.get_by_test_id("trading-mode-tooltip").first).to_have_text(
"No trading enabled for this market."
)
expect(page.get_by_test_id("trading-mode-tooltip").first).to_have_text("No trading enabled for this market.")
expect(market_state).to_have_text("Proposed")
# approve market
@@ -197,4 +182,4 @@ def test_market_closing_banners(page: Page, continuous_market, vega: VegaService
will_close_pattern = r"TRADING ON MARKET BTC:DAI_2023 WILL STOP ON \d+ \w+\nYou will no longer be able to hold a position on this market when it closes in \d+ days \d+ hours\. The final price will be 107\.00 BTC\."
match_result = re.fullmatch(will_close_pattern, page.locator(".grow").inner_text())
assert match_result is not None
"""
"""

Some files were not shown because too many files have changed in this diff Show More