Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4571f60003 |
@@ -77,7 +77,6 @@
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-useless-constructor": 0,
|
||||
"curly": ["error", "multi-line"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 />
|
||||
|
||||
@@ -23,13 +23,10 @@ export const Heading = ({
|
||||
})}
|
||||
>
|
||||
<h1
|
||||
className={classNames(
|
||||
'font-alpha calt text-5xl [word-break:break-word]',
|
||||
{
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
}
|
||||
)}
|
||||
className={classNames('font-alpha calt text-5xl break-words', {
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
})}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
-3
@@ -7,10 +7,8 @@ import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
|
||||
export const ProposalAssetDetails = ({
|
||||
asset,
|
||||
originalAsset,
|
||||
}: {
|
||||
asset: AssetFieldsFragment;
|
||||
originalAsset?: AssetFieldsFragment;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showAssetDetails, setShowAssetDetails] = useState(false);
|
||||
@@ -29,7 +27,6 @@ export const ProposalAssetDetails = ({
|
||||
<div className="mb-10 pb-4">
|
||||
<AssetDetailsTable
|
||||
asset={asset}
|
||||
originalAsset={originalAsset}
|
||||
omitRows={[
|
||||
AssetDetail.STATUS,
|
||||
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
|
||||
|
||||
+3
-2
@@ -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: {
|
||||
|
||||
+7
-9
@@ -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;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
@@ -66,13 +65,10 @@ export const Proposal = ({
|
||||
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
|
||||
: undefined;
|
||||
|
||||
const originalAsset = asset;
|
||||
|
||||
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
|
||||
asset = {
|
||||
...asset,
|
||||
quantum: proposal.terms.change.quantum,
|
||||
source: { ...asset.source },
|
||||
};
|
||||
|
||||
if (asset.source.__typename === 'ERC20') {
|
||||
@@ -133,7 +129,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 */
|
||||
@@ -232,7 +228,7 @@ export const Proposal = ({
|
||||
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||
asset && (
|
||||
<div className="mb-4">
|
||||
<ProposalAssetDetails asset={asset} originalAsset={originalAsset} />
|
||||
<ProposalAssetDetails asset={asset} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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=
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { act, render, screen, waitFor, within } from '@testing-library/react';
|
||||
// import userEvent from '@testing-library/user-event';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Closed } from './closed';
|
||||
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
|
||||
@@ -27,7 +26,6 @@ import {
|
||||
marketsDataQuery,
|
||||
createMarketsDataFragment,
|
||||
} from '@vegaprotocol/mock';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
describe('Closed', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
@@ -170,11 +168,14 @@ describe('Closed', () => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
const renderComponent = async (mocks: MockedResponse[]) => {
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('renders correctly formatted and filtered rows', async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<MockedProvider
|
||||
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
@@ -184,10 +185,6 @@ describe('Closed', () => {
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
it('renders correct headers', async () => {
|
||||
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
const expectedHeaders = [
|
||||
@@ -203,10 +200,6 @@ describe('Closed', () => {
|
||||
];
|
||||
expect(headers).toHaveLength(expectedHeaders.length);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
|
||||
});
|
||||
|
||||
it('renders correctly formatted and filtered rows', async () => {
|
||||
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
|
||||
|
||||
const assetSymbol = getAsset(market).symbol;
|
||||
|
||||
@@ -280,8 +273,21 @@ describe('Closed', () => {
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await renderComponent([mixedMarketsMock, marketsDataMock, oracleDataMock]);
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
|
||||
// check that the number of rows in datagrid is 2
|
||||
const container = within(
|
||||
@@ -313,67 +319,8 @@ describe('Closed', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('display market actions', async () => {
|
||||
// Use market with a succcessor Id as the actions dropdown will optionally
|
||||
// show a link to the successor market
|
||||
const marketsWithSuccessorAndParent = [
|
||||
{
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'include-0',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
successorMarketID: 'successor',
|
||||
parentMarketID: 'parent',
|
||||
}),
|
||||
},
|
||||
];
|
||||
const mockWithSuccessorAndParent: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: marketsWithSuccessorAndParent,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
await renderComponent([
|
||||
mockWithSuccessorAndParent,
|
||||
marketsDataMock,
|
||||
oracleDataMock,
|
||||
]);
|
||||
|
||||
const actionCell = screen
|
||||
.getAllByRole('gridcell')
|
||||
.find((el) => el.getAttribute('col-id') === 'market-actions');
|
||||
|
||||
await userEvent.click(
|
||||
within(actionCell as HTMLElement).getByTestId('dropdown-menu')
|
||||
);
|
||||
|
||||
expect(screen.getByRole('menu')).toBeInTheDocument();
|
||||
|
||||
expect(
|
||||
screen.getByRole('menuitem', { name: 'Copy Market ID' })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('menuitem', { name: 'View on Explorer' })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('menuitem', { name: 'View settlement asset details' })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('menuitem', { name: 'View parent market' })
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('menuitem', { name: 'View successor market' })
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('successor market should be visible', async () => {
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('successor marked should be visible', async () => {
|
||||
const marketsWithSuccessorID = [
|
||||
{
|
||||
__typename: 'MarketEdge' as const,
|
||||
@@ -398,11 +345,21 @@ describe('Closed', () => {
|
||||
},
|
||||
};
|
||||
|
||||
await renderComponent([
|
||||
mockWithSuccessors,
|
||||
marketsDataMock,
|
||||
oracleDataMock,
|
||||
]);
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[mockWithSuccessors, marketsDataMock, oracleDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
|
||||
const container = within(
|
||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
import { act, render, screen, within } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { OpenMarkets } from './open-markets';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type {
|
||||
MarketsDataQuery,
|
||||
MarketsQuery,
|
||||
MarketCandlesQuery,
|
||||
MarketFieldsFragment,
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
MarketsDataDocument,
|
||||
MarketsDocument,
|
||||
MarketsCandlesDocument,
|
||||
} from '@vegaprotocol/markets';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
marketsQuery,
|
||||
marketsDataQuery,
|
||||
marketsCandlesQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
describe('Open', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
const mockNowTimestamp = 1672531200000;
|
||||
const pubKey = 'pubKey';
|
||||
|
||||
const marketsQueryData = marketsQuery();
|
||||
const marketsMock: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsQueryData,
|
||||
},
|
||||
};
|
||||
|
||||
const marketsCandlesQueryData = marketsCandlesQuery();
|
||||
const marketsCandlesMock: MockedResponse<MarketCandlesQuery> = {
|
||||
request: {
|
||||
query: MarketsCandlesDocument,
|
||||
variables: {
|
||||
interval: Interval.INTERVAL_I1H,
|
||||
since: '2022-12-31T00:00:00.000Z',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: marketsCandlesQueryData,
|
||||
},
|
||||
};
|
||||
|
||||
const marketsDataQueryData = marketsDataQuery();
|
||||
|
||||
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
|
||||
request: {
|
||||
query: MarketsDataDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsDataQueryData,
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
originalNow = Date.now;
|
||||
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
const renderComponent = async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[marketsMock, marketsCandlesMock, marketsDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<OpenMarkets />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
it('renders correct headers', async () => {
|
||||
await renderComponent();
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
const expectedHeaders = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Settlement asset',
|
||||
'Trading mode',
|
||||
'Status',
|
||||
'Mark price',
|
||||
'24h volume',
|
||||
'Open Interest',
|
||||
'Spread',
|
||||
'', // Action row
|
||||
];
|
||||
expect(headers).toHaveLength(expectedHeaders.length);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
|
||||
});
|
||||
|
||||
it('sort columns', async () => {
|
||||
await renderComponent();
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
const marketHeader = headers.find(
|
||||
(h) => h.getAttribute('col-id') === 'tradableInstrument.instrument.code'
|
||||
);
|
||||
if (!marketHeader) {
|
||||
throw new Error('No market header found');
|
||||
}
|
||||
expect(marketHeader).toHaveAttribute('aria-sort', 'none');
|
||||
await userEvent.click(within(marketHeader).getByText(/market/i));
|
||||
// 6001-MARK-064
|
||||
expect(marketHeader).toHaveAttribute('aria-sort', 'ascending');
|
||||
});
|
||||
|
||||
// eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect
|
||||
it('renders row', async () => {
|
||||
await renderComponent();
|
||||
|
||||
const container = within(
|
||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||
);
|
||||
|
||||
const markets = marketsQueryData.marketsConnection?.edges.map(
|
||||
(e) => e.node
|
||||
) as MarketFieldsFragment[];
|
||||
|
||||
const rows = container.getAllByRole('row');
|
||||
expect(rows).toHaveLength(markets.length);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
@@ -73,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>
|
||||
@@ -112,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"
|
||||
@@ -127,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,10 +2,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useCallback } from 'react';
|
||||
import { useRefereesQuery } from './__generated__/Referees';
|
||||
import compact from 'lodash/compact';
|
||||
import type {
|
||||
ReferralSetsQuery,
|
||||
ReferralSetsQueryVariables,
|
||||
} from './__generated__/ReferralSets';
|
||||
import type { ReferralSetsQueryVariables } from './__generated__/ReferralSets';
|
||||
import { useReferralSetsQuery } from './__generated__/ReferralSets';
|
||||
import { useStakeAvailable } from './use-stake-available';
|
||||
|
||||
@@ -121,36 +118,3 @@ export const useReferral = (args: UseReferralArgs) => {
|
||||
refetch,
|
||||
};
|
||||
};
|
||||
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -275,34 +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,
|
||||
]}
|
||||
showWarnings={false}
|
||||
>
|
||||
<ReferralStatistics />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
<MockedProvider
|
||||
mocks={[
|
||||
programMock,
|
||||
referralSetAsReferrerMock,
|
||||
noReferralSetAsRefereeMock,
|
||||
stakeAvailableMock,
|
||||
refereesMock,
|
||||
]}
|
||||
showWarnings={false}
|
||||
>
|
||||
<ReferralStatistics />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -4,10 +4,13 @@ import {
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
truncateMiddle,
|
||||
ExternalLink,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { DEFAULT_AGGREGATION_DAYS, useReferral } from './hooks/use-referral';
|
||||
import { CreateCodeContainer } from './create-code-form';
|
||||
import classNames from 'classnames';
|
||||
import { Table } from './table';
|
||||
import {
|
||||
@@ -23,39 +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 } = useReferral({
|
||||
const { data: referrer } = useReferral({
|
||||
pubKey,
|
||||
role: 'referrer',
|
||||
aggregationEpochs: program.details?.windowLength,
|
||||
});
|
||||
|
||||
const refetch = useCallback(() => {
|
||||
refereeRefetch();
|
||||
referrerRefetch();
|
||||
}, [refereeRefetch, referrerRefetch]);
|
||||
|
||||
if (referee?.code) {
|
||||
return (
|
||||
<>
|
||||
<Statistics data={referee} program={program} as="referee" />
|
||||
<Statistics data={referee} program={program} as="referee" />;
|
||||
{!referee.isEligible && <ApplyCodeForm />}
|
||||
</>
|
||||
);
|
||||
@@ -64,13 +62,13 @@ 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 = ({
|
||||
@@ -83,9 +81,7 @@ export const useStats = ({
|
||||
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 || '',
|
||||
@@ -119,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)
|
||||
@@ -178,7 +174,6 @@ export const Statistics = ({
|
||||
discountFactorValue,
|
||||
currentBenefitTierValue,
|
||||
epochsValue,
|
||||
nextBenefitTierValue,
|
||||
nextBenefitTierVolumeValue,
|
||||
nextBenefitTierEpochsValue,
|
||||
} = useStats({ data, program, as });
|
||||
@@ -212,8 +207,6 @@ export const Statistics = ({
|
||||
).toString(),
|
||||
}
|
||||
)}
|
||||
testId="base-commission-rate"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{baseCommissionValue * 100}%
|
||||
</StatTile>
|
||||
@@ -222,7 +215,6 @@ export const Statistics = ({
|
||||
const stakingMultiplierTile = (
|
||||
<StatTile
|
||||
title={t('Staking multiplier')}
|
||||
testId="staking-multiplier"
|
||||
description={
|
||||
<span
|
||||
className={classNames({
|
||||
@@ -237,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 = (
|
||||
@@ -281,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>
|
||||
@@ -293,11 +274,10 @@ export const Statistics = ({
|
||||
.reduce((all, r) => all.plus(r), new BigNumber(0));
|
||||
const totalCommissionTile = (
|
||||
<StatTile
|
||||
title={t('totalCommission', 'Total commission (last {{count}} epochs)', {
|
||||
title={t('totalCommission', 'Total commission (last {{count}}} epochs)', {
|
||||
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
|
||||
})}
|
||||
description={<QUSDTooltip />}
|
||||
testId="total-commission"
|
||||
>
|
||||
{getNumberFormat(0).format(Number(totalCommissionValue))}
|
||||
</StatTile>
|
||||
@@ -321,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}
|
||||
%
|
||||
@@ -359,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>
|
||||
);
|
||||
@@ -508,7 +461,6 @@ export const RefereesTable = ({
|
||||
count:
|
||||
details?.windowLength || DEFAULT_AGGREGATION_DAYS,
|
||||
}}
|
||||
components={[<QUSDTooltip key="qusd" />]}
|
||||
ns={ns}
|
||||
/>
|
||||
),
|
||||
@@ -540,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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
}))}
|
||||
/>
|
||||
|
||||
@@ -32,32 +32,16 @@ export const Tile = ({
|
||||
|
||||
type StatTileProps = {
|
||||
title: string;
|
||||
testId?: string;
|
||||
description?: ReactNode;
|
||||
children?: ReactNode;
|
||||
overrideWithNoProgram?: boolean;
|
||||
};
|
||||
export const StatTile = ({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
testId,
|
||||
overrideWithNoProgram = false,
|
||||
}: StatTileProps) => {
|
||||
if (overrideWithNoProgram) {
|
||||
return <NoProgramTile title={title} />;
|
||||
}
|
||||
export const StatTile = ({ title, description, children }: StatTileProps) => {
|
||||
return (
|
||||
<Tile>
|
||||
<h3
|
||||
data-testid={testId}
|
||||
className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt"
|
||||
>
|
||||
<h3 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>
|
||||
<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}
|
||||
@@ -67,20 +51,6 @@ export const StatTile = ({
|
||||
);
|
||||
};
|
||||
|
||||
export const NoProgramTile = ({ title }: Pick<StatTileProps, 'title'>) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Tile title={title}>
|
||||
<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>
|
||||
</Tile>
|
||||
);
|
||||
};
|
||||
|
||||
const FADE_OUT_STYLE = classNames(
|
||||
'after:w-5 after:h-full after:absolute after:top-0 after:right-0',
|
||||
'after:bg-gradient-to-l after:from-vega-clight-800 after:dark:from-vega-cdark-800 after:to-transparent'
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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';
|
||||
@@ -310,25 +310,16 @@ export const CurrentVolume = ({
|
||||
const t = useT();
|
||||
const nextTier = tiers[tierIndex + 1];
|
||||
const requiredForNextTier = nextTier
|
||||
? new BigNumber(nextTier.minimumRunningNotionalTakerVolume).minus(
|
||||
windowLengthVolume
|
||||
)
|
||||
: new BigNumber(0);
|
||||
const currentVolume = new BigNumber(windowLengthVolume);
|
||||
? Number(nextTier.minimumRunningNotionalTakerVolume) - windowLengthVolume
|
||||
: 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 pt-4">
|
||||
<CardStat
|
||||
value={
|
||||
currentVolume.isZero()
|
||||
? `<${formatNumberRounded(requiredForNextTier)}`
|
||||
: formatNumberRounded(currentVolume)
|
||||
}
|
||||
text={t('pastEpochs', 'Past {{count}} epochs', {
|
||||
count: windowLength,
|
||||
})}
|
||||
value={formatNumberRounded(new BigNumber(windowLengthVolume))}
|
||||
text={t('pastEpochs', 'Past {{count}} epochs', { count: windowLength })}
|
||||
/>
|
||||
{requiredForNextTier.isGreaterThan(0) && (
|
||||
{requiredForNextTier > 0 && (
|
||||
<CardStat
|
||||
value={formatNumber(requiredForNextTier)}
|
||||
text={t('Required for next tier')}
|
||||
|
||||
@@ -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';
|
||||
});
|
||||
|
||||
@@ -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,5 +1,4 @@
|
||||
import groupBy from 'lodash/groupBy';
|
||||
import uniq from 'lodash/uniq';
|
||||
import type { Account } from '@vegaprotocol/accounts';
|
||||
import { useAccounts } from '@vegaprotocol/accounts';
|
||||
import {
|
||||
@@ -32,12 +31,6 @@ import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { RewardsHistoryContainer } from './rewards-history';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useAssetsMapProvider } from '@vegaprotocol/assets';
|
||||
|
||||
const ASSETS_WITH_INCORRECT_VESTING_REWARD_DATA = [
|
||||
'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba', // USDT mainnet
|
||||
'8ba0b10971f0c4747746cd01ff05a53ae75ca91eba1d4d050b527910c983e27e', // USDT testnet
|
||||
];
|
||||
|
||||
export const RewardsContainer = () => {
|
||||
const t = useT();
|
||||
@@ -47,67 +40,34 @@ export const RewardsContainer = () => {
|
||||
NetworkParams.rewards_activityStreak_benefitTiers,
|
||||
NetworkParams.rewards_vesting_baseRate,
|
||||
]);
|
||||
|
||||
const { data: accounts, loading: accountsLoading } = useAccounts(pubKey);
|
||||
|
||||
const { data: assetMap } = useAssetsMapProvider();
|
||||
|
||||
const { data: epochData } = useRewardsEpochQuery();
|
||||
|
||||
// No need to specify the fromEpoch as it will by default give you the last
|
||||
// Note activityStreak in query will fail
|
||||
const { data: rewardsData, loading: rewardsLoading } = useRewardsPageQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
},
|
||||
// Inclusion of activity streak in query currently fails
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
if (!epochData?.epoch || !assetMap) return null;
|
||||
if (!epochData?.epoch) return null;
|
||||
|
||||
const loading = paramsLoading || accountsLoading || rewardsLoading;
|
||||
|
||||
const rewardAccounts = accounts
|
||||
? accounts
|
||||
.filter((a) =>
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
|
||||
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
|
||||
].includes(a.type)
|
||||
)
|
||||
.filter((a) => new BigNumber(a.balance).isGreaterThan(0))
|
||||
: [];
|
||||
|
||||
const rewardAccountsAssetMap = groupBy(rewardAccounts, 'asset.id');
|
||||
|
||||
const lockedBalances = rewardsData?.party?.vestingBalancesSummary
|
||||
.lockedBalances
|
||||
? rewardsData.party.vestingBalancesSummary.lockedBalances.filter((b) =>
|
||||
new BigNumber(b.balance).isGreaterThan(0)
|
||||
? accounts.filter((a) =>
|
||||
[
|
||||
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
|
||||
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
|
||||
].includes(a.type)
|
||||
)
|
||||
: [];
|
||||
const lockedAssetMap = groupBy(lockedBalances, 'asset.id');
|
||||
|
||||
const vestingBalances = rewardsData?.party?.vestingBalancesSummary
|
||||
.vestingBalances
|
||||
? rewardsData.party.vestingBalancesSummary.vestingBalances.filter((b) =>
|
||||
new BigNumber(b.balance).isGreaterThan(0)
|
||||
)
|
||||
: [];
|
||||
const vestingAssetMap = groupBy(vestingBalances, 'asset.id');
|
||||
|
||||
// each asset reward pot is made up of:
|
||||
// available to withdraw - ACCOUNT_TYPE_VESTED_REWARDS
|
||||
// vesting - vestingBalancesSummary.vestingBalances
|
||||
// locked - vestingBalancesSummary.lockedBalances
|
||||
//
|
||||
// there can be entires for the same asset in each list so we need a uniq list of assets
|
||||
const assets = uniq([
|
||||
...Object.keys(rewardAccountsAssetMap),
|
||||
...Object.keys(lockedAssetMap),
|
||||
...Object.keys(vestingAssetMap),
|
||||
]);
|
||||
const rewardAssetsMap = groupBy(
|
||||
rewardAccounts.filter((a) => a.asset.id !== params.reward_asset),
|
||||
'asset.id'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="grid auto-rows-min grid-cols-6 gap-3">
|
||||
@@ -157,72 +117,28 @@ export const RewardsContainer = () => {
|
||||
</Card>
|
||||
|
||||
{/* Show all other reward pots, most of the time users will not have other rewards */}
|
||||
{assets
|
||||
.filter((assetId) => assetId !== params.reward_asset)
|
||||
.map((assetId) => {
|
||||
const asset = assetMap ? assetMap[assetId] : null;
|
||||
|
||||
if (!asset) return null;
|
||||
|
||||
// Following code is for mitigating an issue due to a core bug where locked and vesting
|
||||
// balances were incorrectly increased for infrastructure rewards for USDT on mainnet
|
||||
//
|
||||
// We don't want to incorrectly show the wring locked/vesting values, but we DO want to
|
||||
// show the user that they have rewards available to withdraw
|
||||
if (ASSETS_WITH_INCORRECT_VESTING_REWARD_DATA.includes(asset.id)) {
|
||||
const accountsForAsset = rewardAccountsAssetMap[asset.id];
|
||||
const vestedAccount = accountsForAsset?.find(
|
||||
(a) => a.type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS
|
||||
);
|
||||
|
||||
// No vested rewards available to withdraw, so skip over USDT
|
||||
if (!vestedAccount || Number(vestedAccount.balance) <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={assetId}
|
||||
title={t('{{assetSymbol}} Reward pot', {
|
||||
assetSymbol: asset.symbol,
|
||||
})}
|
||||
className="lg:col-span-3 xl:col-span-2"
|
||||
loading={loading}
|
||||
>
|
||||
<RewardPot
|
||||
pubKey={pubKey}
|
||||
accounts={accounts}
|
||||
assetId={assetId}
|
||||
// Ensure that these values are shown as 0
|
||||
vestingBalancesSummary={{
|
||||
lockedBalances: [],
|
||||
vestingBalances: [],
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={assetId}
|
||||
title={t('{{assetSymbol}} Reward pot', {
|
||||
assetSymbol: asset.symbol,
|
||||
})}
|
||||
className="lg:col-span-3 xl:col-span-2"
|
||||
loading={loading}
|
||||
>
|
||||
<RewardPot
|
||||
pubKey={pubKey}
|
||||
accounts={accounts}
|
||||
assetId={assetId}
|
||||
vestingBalancesSummary={
|
||||
rewardsData?.party?.vestingBalancesSummary
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
{Object.keys(rewardAssetsMap).map((assetId) => {
|
||||
const asset = rewardAssetsMap[assetId][0].asset;
|
||||
return (
|
||||
<Card
|
||||
key={assetId}
|
||||
title={t('{{assetSymbol}} Reward pot', {
|
||||
assetSymbol: asset.symbol,
|
||||
})}
|
||||
className="lg:col-span-3 xl:col-span-2"
|
||||
loading={loading}
|
||||
>
|
||||
<RewardPot
|
||||
pubKey={pubKey}
|
||||
accounts={accounts}
|
||||
assetId={assetId}
|
||||
vestingBalancesSummary={
|
||||
rewardsData?.party?.vestingBalancesSummary
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
<Card
|
||||
title={t('Rewards history')}
|
||||
className="lg:col-span-full"
|
||||
@@ -231,7 +147,6 @@ export const RewardsContainer = () => {
|
||||
<RewardsHistoryContainer
|
||||
epoch={Number(epochData?.epoch.id)}
|
||||
pubKey={pubKey}
|
||||
assets={assetMap}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
@@ -398,14 +313,14 @@ export const RewardPot = ({
|
||||
export const Vesting = ({
|
||||
pubKey,
|
||||
baseRate,
|
||||
multiplier,
|
||||
multiplier = '1',
|
||||
}: {
|
||||
pubKey: string | null;
|
||||
baseRate: string;
|
||||
multiplier?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const rate = new BigNumber(baseRate).times(multiplier || 1);
|
||||
const rate = new BigNumber(baseRate).times(multiplier);
|
||||
const rateFormatted = formatPercentage(Number(rate));
|
||||
const baseRateFormatted = formatPercentage(Number(baseRate));
|
||||
|
||||
@@ -420,7 +335,7 @@ export const Vesting = ({
|
||||
{pubKey && (
|
||||
<tr>
|
||||
<CardTableTH>{t('Vesting multiplier')}</CardTableTH>
|
||||
<CardTableTD>{multiplier ? `${multiplier}x` : '-'}</CardTableTD>
|
||||
<CardTableTD>{multiplier}x</CardTableTD>
|
||||
</tr>
|
||||
)}
|
||||
</CardTable>
|
||||
@@ -430,16 +345,16 @@ export const Vesting = ({
|
||||
|
||||
export const Multipliers = ({
|
||||
pubKey,
|
||||
streakMultiplier,
|
||||
hoarderMultiplier,
|
||||
streakMultiplier = '1',
|
||||
hoarderMultiplier = '1',
|
||||
}: {
|
||||
pubKey: string | null;
|
||||
streakMultiplier?: string;
|
||||
hoarderMultiplier?: string;
|
||||
}) => {
|
||||
const t = useT();
|
||||
const combinedMultiplier = new BigNumber(streakMultiplier || 1).times(
|
||||
hoarderMultiplier || 1
|
||||
const combinedMultiplier = new BigNumber(streakMultiplier).times(
|
||||
hoarderMultiplier
|
||||
);
|
||||
|
||||
if (!pubKey) {
|
||||
@@ -460,15 +375,11 @@ export const Multipliers = ({
|
||||
<CardTable>
|
||||
<tr>
|
||||
<CardTableTH>{t('Streak reward multiplier')}</CardTableTH>
|
||||
<CardTableTD>
|
||||
{streakMultiplier ? `${streakMultiplier}x` : '-'}
|
||||
</CardTableTD>
|
||||
<CardTableTD>{streakMultiplier}x</CardTableTD>
|
||||
</tr>
|
||||
<tr>
|
||||
<CardTableTH>{t('Hoarder reward multiplier')}</CardTableTH>
|
||||
<CardTableTD>
|
||||
{hoarderMultiplier ? `${hoarderMultiplier}x` : '-'}
|
||||
</CardTableTD>
|
||||
<CardTableTD>{hoarderMultiplier}x</CardTableTD>
|
||||
</tr>
|
||||
</CardTable>
|
||||
</div>
|
||||
|
||||
@@ -61,14 +61,6 @@ const rewardSummaries = [
|
||||
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 7,
|
||||
assetId: assets.asset2.id,
|
||||
amount: '300',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const getCell = (cells: HTMLElement[], colId: string) => {
|
||||
@@ -77,7 +69,7 @@ const getCell = (cells: HTMLElement[], colId: string) => {
|
||||
);
|
||||
};
|
||||
|
||||
describe('RewardsHistoryTable', () => {
|
||||
describe('RewarsHistoryTable', () => {
|
||||
const props = {
|
||||
epochRewardSummaries: {
|
||||
edges: rewardSummaries,
|
||||
@@ -96,7 +88,7 @@ describe('RewardsHistoryTable', () => {
|
||||
loading: false,
|
||||
};
|
||||
|
||||
it('renders table with accounts summed up by asset', () => {
|
||||
it('Renders table with accounts summed up by asset', () => {
|
||||
render(<RewardHistoryTable {...props} />);
|
||||
|
||||
const container = within(
|
||||
@@ -118,27 +110,17 @@ describe('RewardsHistoryTable', () => {
|
||||
assets.asset2.name
|
||||
);
|
||||
|
||||
// First row
|
||||
const marketCreationCell = getCell(cells, 'marketCreation');
|
||||
expect(
|
||||
marketCreationCell.getByTestId('stack-cell-primary')
|
||||
).toHaveTextContent('300');
|
||||
expect(
|
||||
marketCreationCell.getByTestId('stack-cell-secondary')
|
||||
).toHaveTextContent('50.00%');
|
||||
|
||||
const infrastructureFeesCell = getCell(cells, 'infrastructureFees');
|
||||
expect(
|
||||
infrastructureFeesCell.getByTestId('stack-cell-primary')
|
||||
).toHaveTextContent('300');
|
||||
expect(
|
||||
infrastructureFeesCell.getByTestId('stack-cell-secondary')
|
||||
).toHaveTextContent('50.00%');
|
||||
).toHaveTextContent('100.00%');
|
||||
|
||||
let totalCell = getCell(cells, 'total');
|
||||
expect(totalCell.getByText('600.00')).toBeInTheDocument();
|
||||
expect(totalCell.getByText('300.00')).toBeInTheDocument();
|
||||
|
||||
// Second row
|
||||
row = within(rows[1]);
|
||||
cells = row.getAllByRole('gridcell');
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ import debounce from 'lodash/debounce';
|
||||
import { useMemo, useState } from 'react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import type { ColDef, ValueFormatterFunc } from 'ag-grid-community';
|
||||
import { type AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import {
|
||||
useAssetsMapProvider,
|
||||
type AssetFieldsFragment,
|
||||
} from '@vegaprotocol/assets';
|
||||
import {
|
||||
addDecimalsFormatNumberQuantum,
|
||||
formatNumberPercentage,
|
||||
@@ -23,17 +26,17 @@ import { useT } from '../../lib/use-t';
|
||||
export const RewardsHistoryContainer = ({
|
||||
epoch,
|
||||
pubKey,
|
||||
assets,
|
||||
}: {
|
||||
pubKey: string | null;
|
||||
epoch: number;
|
||||
assets: Record<string, AssetFieldsFragment>;
|
||||
}) => {
|
||||
const [epochVariables, setEpochVariables] = useState(() => ({
|
||||
from: epoch - 1,
|
||||
to: epoch,
|
||||
}));
|
||||
|
||||
const { data: assets } = useAssetsMapProvider();
|
||||
|
||||
// No need to specify the fromEpoch as it will by default give you the last
|
||||
const { refetch, data, loading } = useRewardsHistoryQuery({
|
||||
variables: {
|
||||
@@ -151,12 +154,10 @@ export const RewardHistoryTable = ({
|
||||
const rewardValueFormatter: ValueFormatterFunc<RewardRow> = ({
|
||||
data,
|
||||
value,
|
||||
...rest
|
||||
}) => {
|
||||
if (!value || !data) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
return addDecimalsFormatNumberQuantum(
|
||||
value,
|
||||
data.asset.decimals,
|
||||
@@ -196,11 +197,6 @@ export const RewardHistoryTable = ({
|
||||
},
|
||||
sort: 'desc',
|
||||
},
|
||||
{
|
||||
field: 'infrastructureFees',
|
||||
valueFormatter: rewardValueFormatter,
|
||||
cellRenderer: rewardCellRenderer,
|
||||
},
|
||||
{
|
||||
field: 'staking',
|
||||
valueFormatter: rewardValueFormatter,
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
import { type AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { getRewards } from './use-reward-row-data';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const asset1 = {
|
||||
id: 'asset1',
|
||||
name: 'USD (KRW)',
|
||||
symbol: 'USD-KRW',
|
||||
decimals: 6,
|
||||
quantum: '1000000',
|
||||
status: Schema.AssetStatus.STATUS_ENABLED,
|
||||
// @ts-ignore not needed
|
||||
source: {},
|
||||
} as AssetFieldsFragment;
|
||||
|
||||
const asset2 = {
|
||||
id: 'asset2',
|
||||
name: 'tDAI TEST',
|
||||
symbol: 'tDAI',
|
||||
decimals: 5,
|
||||
quantum: '1',
|
||||
status: Schema.AssetStatus.STATUS_ENABLED,
|
||||
// @ts-ignore not needed
|
||||
source: {},
|
||||
} as AssetFieldsFragment;
|
||||
|
||||
const asset3 = {
|
||||
id: 'asset3',
|
||||
name: 'Tether USD',
|
||||
symbol: 'USDT',
|
||||
decimals: 6,
|
||||
quantum: '1000000',
|
||||
status: Schema.AssetStatus.STATUS_ENABLED,
|
||||
// @ts-ignore not needed
|
||||
source: {},
|
||||
} as AssetFieldsFragment;
|
||||
|
||||
const asset4 = {
|
||||
id: 'asset4',
|
||||
name: 'USDT-T',
|
||||
symbol: 'USDT-T',
|
||||
decimals: 18,
|
||||
quantum: '1',
|
||||
status: Schema.AssetStatus.STATUS_ENABLED,
|
||||
// @ts-ignore not needed
|
||||
source: {},
|
||||
} as AssetFieldsFragment;
|
||||
|
||||
const assets: Record<string, AssetFieldsFragment> = {
|
||||
asset1,
|
||||
asset2,
|
||||
asset3,
|
||||
asset4,
|
||||
};
|
||||
|
||||
const testData = {
|
||||
rewards: [
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
assetId: 'asset1',
|
||||
amount: '31897424',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
assetId: 'asset2',
|
||||
amount: '57',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
assetId: 'asset3',
|
||||
amount: '5501',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
|
||||
assetId: 'asset3',
|
||||
amount: '5501',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
|
||||
assetId: 'asset4',
|
||||
amount: '5501',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
|
||||
assetId: 'asset4',
|
||||
amount: '456',
|
||||
},
|
||||
{
|
||||
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING,
|
||||
assetId: 'asset4',
|
||||
amount: '4565',
|
||||
},
|
||||
],
|
||||
assets,
|
||||
};
|
||||
|
||||
describe('getRewards', () => {
|
||||
it('should return the correct rewards when infra fees are included', () => {
|
||||
const rewards = getRewards(testData.rewards, testData.assets);
|
||||
expect(rewards).toEqual([
|
||||
{
|
||||
asset: asset1,
|
||||
infrastructureFees: 31897424,
|
||||
staking: 0,
|
||||
priceTaking: 0,
|
||||
priceMaking: 0,
|
||||
liquidityProvision: 0,
|
||||
marketCreation: 0,
|
||||
averagePosition: 0,
|
||||
relativeReturns: 0,
|
||||
returnsVolatility: 0,
|
||||
validatorRanking: 0,
|
||||
total: 31897424,
|
||||
},
|
||||
{
|
||||
asset: asset2,
|
||||
infrastructureFees: 57,
|
||||
staking: 0,
|
||||
priceTaking: 0,
|
||||
priceMaking: 0,
|
||||
liquidityProvision: 0,
|
||||
marketCreation: 0,
|
||||
averagePosition: 0,
|
||||
relativeReturns: 0,
|
||||
returnsVolatility: 0,
|
||||
validatorRanking: 0,
|
||||
total: 57,
|
||||
},
|
||||
{
|
||||
asset: asset3,
|
||||
infrastructureFees: 5501,
|
||||
staking: 0,
|
||||
priceTaking: 0,
|
||||
priceMaking: 0,
|
||||
liquidityProvision: 0,
|
||||
marketCreation: 0,
|
||||
averagePosition: 5501,
|
||||
relativeReturns: 0,
|
||||
returnsVolatility: 0,
|
||||
validatorRanking: 0,
|
||||
total: 11002,
|
||||
},
|
||||
{
|
||||
asset: asset4,
|
||||
infrastructureFees: 0,
|
||||
staking: 0,
|
||||
priceTaking: 0,
|
||||
priceMaking: 5501,
|
||||
liquidityProvision: 456,
|
||||
marketCreation: 0,
|
||||
averagePosition: 0,
|
||||
relativeReturns: 0,
|
||||
returnsVolatility: 0,
|
||||
validatorRanking: 4565,
|
||||
total: 10522,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -16,10 +16,9 @@ const REWARD_ACCOUNT_TYPES = [
|
||||
AccountType.ACCOUNT_TYPE_REWARD_RELATIVE_RETURN,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY,
|
||||
AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING,
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
];
|
||||
|
||||
export const getRewards = (
|
||||
const getRewards = (
|
||||
rewards: Array<{
|
||||
rewardType: AccountType;
|
||||
assetId: string;
|
||||
@@ -57,9 +56,6 @@ export const getRewards = (
|
||||
|
||||
return {
|
||||
asset,
|
||||
infrastructureFees: totals.get(
|
||||
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE
|
||||
),
|
||||
staking: totals.get(AccountType.ACCOUNT_TYPE_GLOBAL_REWARD),
|
||||
priceTaking: totals.get(AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES),
|
||||
priceMaking: totals.get(
|
||||
@@ -105,8 +101,7 @@ export const useRewardsRowData = ({
|
||||
assetId: r.asset.id,
|
||||
amount: r.amount,
|
||||
}));
|
||||
const result = getRewards(rewards, assets);
|
||||
return result;
|
||||
return getRewards(rewards, assets);
|
||||
}
|
||||
|
||||
const rewards = removePaginationWrapper(epochRewardSummaries?.edges);
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 +1,2 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.73.8
|
||||
VEGA_VERSION=v0.73.6
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.73.8
|
||||
VEGA_VERSION=v0.73.6
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:main
|
||||
VEGA_VERSION=v0.73.8
|
||||
VEGA_VERSION=v0.73.6
|
||||
|
||||
@@ -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,34 +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
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
@@ -12,7 +12,6 @@ from contextlib import contextmanager
|
||||
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
|
||||
from fixtures.market import (
|
||||
setup_simple_market,
|
||||
setup_opening_auction_market,
|
||||
@@ -79,7 +78,6 @@ def init_vega(request=None):
|
||||
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(
|
||||
|
||||
@@ -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,4 +225,4 @@ def setup_perps_market(
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
return market_id
|
||||
return market_id
|
||||
Generated
+4
-4
@@ -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 = "e93f7dfa8463c59cfd0e299362b845511cebeef6"
|
||||
|
||||
[[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"
|
||||
|
||||
@@ -9,7 +9,7 @@ packages = [{include = "trading market-sim e2e"}]
|
||||
[tool.poetry.dependencies]
|
||||
python = ">=3.9,<3.11"
|
||||
psutil = "^5.9.5"
|
||||
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/", branch = "fix/genesis_panic"}
|
||||
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git"}
|
||||
pytest-playwright = "^0.4.2"
|
||||
docker = "^6.1.3"
|
||||
pytest-xdist = "^3.3.1"
|
||||
|
||||
@@ -58,6 +58,7 @@ class TestSettledMarket:
|
||||
def test_settled_rows(self, page: Page, create_settled_market):
|
||||
page.goto(f"/#/markets/all")
|
||||
page.get_by_test_id("Closed markets").click()
|
||||
|
||||
row_selector = page.locator(
|
||||
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row'
|
||||
).first
|
||||
@@ -71,7 +72,7 @@ class TestSettledMarket:
|
||||
# 6001-MARK-009
|
||||
# 6001-MARK-008
|
||||
# 6001-MARK-010
|
||||
pattern = r"(\d+)\s+(months|hours|days)\s+ago"
|
||||
pattern = r"(\d+)\s+months\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}"
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.service import VegaService
|
||||
from actions.vega import submit_order
|
||||
from actions.utils import change_keys
|
||||
from wallet_config import MM_WALLET, MM_WALLET2
|
||||
import logging
|
||||
|
||||
@@ -31,7 +30,7 @@ initial_spread: float = 0.1
|
||||
market_name = "BTC:DAI_2023"
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("vega", "page", "simple_market", "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(
|
||||
@@ -109,8 +108,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)
|
||||
@@ -196,17 +196,3 @@ 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("50.00 (>100%)")
|
||||
|
||||
COL_ID_FEE = ".ag-center-cols-container [col-id='fee'] .ag-cell-value"
|
||||
|
||||
@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.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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from fixtures.market import setup_continuous_market
|
||||
|
||||
from conftest import init_vega
|
||||
|
||||
market_names = ["ETHBTC.QM21", "BTCUSD.MF21", "SOLUSD", "AAPL.MF21"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega():
|
||||
with init_vega() as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def create_markets(vega):
|
||||
for market_name in market_names:
|
||||
setup_continuous_market(vega, custom_market_name=market_name)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_table_headers(page: Page, create_markets):
|
||||
page.goto(f"/#/markets/all")
|
||||
headers = [
|
||||
"Market",
|
||||
"Description",
|
||||
"Settlement asset",
|
||||
"Trading mode",
|
||||
"Status",
|
||||
"Mark price",
|
||||
"24h volume",
|
||||
"Open Interest",
|
||||
"Spread",
|
||||
"",
|
||||
]
|
||||
page.wait_for_selector('[data-testid="tab-open-markets"]', state="visible")
|
||||
page_headers = (
|
||||
page.get_by_test_id("tab-open-markets").locator(".ag-header-cell-text").all()
|
||||
)
|
||||
for i, header in enumerate(headers):
|
||||
expect(page_headers[i]).to_have_text(header)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_markets_tab(page: Page, create_markets):
|
||||
page.goto(f"/#/markets/all")
|
||||
expect(page.get_by_test_id("Open markets")).to_have_attribute(
|
||||
"data-state", "active"
|
||||
)
|
||||
expect(page.get_by_test_id("Proposed markets")).to_have_attribute(
|
||||
"data-state", "inactive"
|
||||
)
|
||||
expect(page.get_by_test_id("Closed markets")).to_have_attribute(
|
||||
"data-state", "inactive"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_markets_content(page: Page, create_markets):
|
||||
page.goto(f"/#/markets/all")
|
||||
row_selector = page.locator(
|
||||
'[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row'
|
||||
).first
|
||||
instrument_code_locator = '[col-id="tradableInstrument.instrument.code"] [data-testid="stack-cell-primary"]'
|
||||
# 6001-MARK-035
|
||||
expect(row_selector.locator(instrument_code_locator)).to_have_text("ETHBTC.QM21")
|
||||
|
||||
# 6001-MARK-073
|
||||
expect(row_selector.locator('[title="Future"]')).to_have_text("Futr")
|
||||
|
||||
# 6001-MARK-036
|
||||
expect(
|
||||
row_selector.locator('[col-id="tradableInstrument.instrument.name"]')
|
||||
).to_have_text("ETHBTC.QM21")
|
||||
|
||||
# 6001-MARK-037
|
||||
expect(row_selector.locator('[col-id="tradingMode"]')).to_have_text("Continuous")
|
||||
|
||||
# 6001-MARK-038
|
||||
expect(row_selector.locator('[col-id="state"]')).to_have_text("Active")
|
||||
|
||||
# 6001-MARK-039
|
||||
expect(row_selector.locator('[col-id="data.markPrice"]')).to_have_text("107.50")
|
||||
|
||||
# 6001-MARK-040
|
||||
expect(row_selector.locator('[col-id="data.candles"]')).to_have_text("0.00")
|
||||
|
||||
# 6001-MARK-042
|
||||
expect(
|
||||
row_selector.locator(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]'
|
||||
)
|
||||
).to_have_text("tDAI")
|
||||
|
||||
expect(row_selector.locator('[col-id="data.bestBidPrice"]')).to_have_text("2")
|
||||
|
||||
# 6001-MARK-043
|
||||
row_selector.locator(
|
||||
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"] button'
|
||||
).click()
|
||||
expect(page.get_by_test_id("dialog-title")).to_have_text("Asset details - tDAI")
|
||||
# 6001-MARK-019
|
||||
page.get_by_test_id("close-asset-details-dialog").click()
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_market_actions(page: Page, create_markets):
|
||||
# 6001-MARK-044
|
||||
# 6001-MARK-045
|
||||
# 6001-MARK-046
|
||||
# 6001-MARK-047
|
||||
page.goto(f"/#/markets/all")
|
||||
page.locator(
|
||||
'.ag-pinned-right-cols-container [col-id="market-actions"]'
|
||||
).first.locator("button").click()
|
||||
|
||||
actions = [
|
||||
"Copy Market ID",
|
||||
"View on Explorer",
|
||||
"View settlement asset details",
|
||||
]
|
||||
action_elements = (
|
||||
page.get_by_test_id("market-actions-content").get_by_role("menuitem").all()
|
||||
)
|
||||
|
||||
for i, action in enumerate(actions):
|
||||
expect(action_elements[i]).to_have_text(action)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_sort_markets(page: Page, create_markets):
|
||||
# 6001-MARK-064
|
||||
|
||||
page.goto(f"/#/markets/all")
|
||||
sorted_market_names = [
|
||||
"AAPL.MF21",
|
||||
"BTCUSD.MF21",
|
||||
"ETHBTC.QM21",
|
||||
"SOLUSD",
|
||||
]
|
||||
page.locator('.ag-header-row [col-id="tradableInstrument.instrument.code"]').click()
|
||||
for i, market_name in enumerate(sorted_market_names):
|
||||
expect(
|
||||
page.locator(
|
||||
f'[row-index="{i}"] [col-id="tradableInstrument.instrument.name"]'
|
||||
)
|
||||
).to_have_text(market_name)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("risk_accepted")
|
||||
def test_drag_and_drop_column(page: Page, create_markets):
|
||||
# 6001-MARK-065
|
||||
page.goto(f"/#/markets/all")
|
||||
col_instrument_code = '.ag-header-row [col-id="tradableInstrument.instrument.code"]'
|
||||
|
||||
page.locator(col_instrument_code).drag_to(
|
||||
page.locator('.ag-header-row [col-id="data.bestBidPrice"]')
|
||||
)
|
||||
expect(page.locator(col_instrument_code)).to_have_attribute("aria-colindex", "9")
|
||||
@@ -5,7 +5,6 @@ from playwright.sync_api import Page, expect
|
||||
from vega_sim.service import VegaService, PeggedOrder
|
||||
import vega_sim.api.governance as governance
|
||||
from actions.vega import submit_order
|
||||
from actions.utils import next_epoch
|
||||
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
|
||||
|
||||
|
||||
@@ -59,9 +58,9 @@ def test_market_lifecycle(proposed_market, vega: VegaService, page: Page):
|
||||
|
||||
# "wait" for market to be approved and enacted
|
||||
vega.forward("60s")
|
||||
vega.wait_fn(10)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
next_epoch(vega=vega)
|
||||
|
||||
# check that market is in pending state
|
||||
expect(trading_mode).to_have_text("Opening auction")
|
||||
expect(market_state).to_have_text("Pending")
|
||||
|
||||
@@ -1,142 +0,0 @@
|
||||
import pytest
|
||||
import re
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.service import VegaService
|
||||
from vega_sim.service import MarketStateUpdateType
|
||||
from datetime import datetime, timedelta
|
||||
from conftest import init_vega
|
||||
from actions.utils import change_keys
|
||||
from actions.vega import submit_multiple_orders
|
||||
from fixtures.market import setup_perps_market
|
||||
from wallet_config import MM_WALLET, MM_WALLET2, TERMINATE_WALLET
|
||||
|
||||
row_selector = '[data-testid="tab-funding-payments"] .ag-center-cols-container .ag-row'
|
||||
col_amount = '[col-id="amount"]'
|
||||
|
||||
class TestPerpetuals:
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def vega(self, request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def perps_market(self, vega: VegaService):
|
||||
perps_market = setup_perps_market(vega)
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET.name, perps_market, "SIDE_SELL", [[1, 110], [1, 105]]
|
||||
)
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET2.name, perps_market, "SIDE_BUY", [[1, 90], [1, 95]]
|
||||
)
|
||||
vega.submit_settlement_data(
|
||||
settlement_key=TERMINATE_WALLET.name,
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET.name, perps_market, "SIDE_SELL", [[1, 110], [1, 105]]
|
||||
)
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET2.name, perps_market, "SIDE_BUY", [[1, 112], [1, 115]]
|
||||
)
|
||||
vega.submit_settlement_data(
|
||||
settlement_key=TERMINATE_WALLET.name,
|
||||
settlement_price=110,
|
||||
market_id=perps_market,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
return perps_market
|
||||
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth")
|
||||
def test_funding_payment_profit(self, perps_market, page: Page):
|
||||
page.goto(f"/#/markets/{perps_market}")
|
||||
page.get_by_test_id("Funding payments").click()
|
||||
row = page.locator(row_selector)
|
||||
expect(row.locator(col_amount)).to_have_text("9.00 tDAI")
|
||||
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth")
|
||||
def test_funding_payment_loss(self, perps_market, page: Page, vega):
|
||||
page.goto(f"/#/markets/{perps_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
page.get_by_test_id("Funding payments").click()
|
||||
row = page.locator(row_selector)
|
||||
expect(row.locator(col_amount)).to_have_text("-27.00 tDAI")
|
||||
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth")
|
||||
def test_funding_header(self, perps_market, page: Page):
|
||||
page.goto(f"/#/markets/{perps_market}")
|
||||
expect(page.get_by_test_id("market-funding")).to_contain_text("Funding Rate / Countdown-8.1818%")
|
||||
expect(page.get_by_test_id("index-price")).to_have_text("Index Price110.00")
|
||||
|
||||
@pytest.mark.skip("Skipped due to issue #5421")
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth")
|
||||
def test_funding_payment_history(perps_market, page: Page, vega):
|
||||
page.goto(f"/#/markets/{perps_market}")
|
||||
change_keys(page, vega, "market_maker")
|
||||
page.get_by_test_id("Funding history").click()
|
||||
element = page.get_by_test_id("tab-funding-history")
|
||||
# Get the bounding box of the element
|
||||
bounding_box = element.bounding_box()
|
||||
if bounding_box:
|
||||
bottom_right_x = bounding_box["x"] + bounding_box["width"]
|
||||
bottom_right_y = bounding_box["y"] + bounding_box["height"]
|
||||
|
||||
# Hover over the bottom-right corner of the element
|
||||
element.hover(position={"x": bottom_right_x, "y": bottom_right_y})
|
||||
else:
|
||||
print("Bounding box not found for the element")
|
||||
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth")
|
||||
def test_perps_market_termination_proposed(page: Page, vega: VegaService):
|
||||
perpetual_market = setup_perps_market(vega)
|
||||
page.goto(f"/#/markets/{perpetual_market}")
|
||||
vega.update_market_state(
|
||||
proposal_key=MM_WALLET.name,
|
||||
market_id=perpetual_market,
|
||||
market_state=MarketStateUpdateType.Terminate,
|
||||
price=100,
|
||||
vote_closing_time = datetime.now() + timedelta(seconds=15),
|
||||
vote_enactment_time = datetime.now() + timedelta(seconds=60),
|
||||
approve_proposal = True,
|
||||
forward_time_to_enactment = False,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
banner_text = page.get_by_test_id(f"termination-warning-banner-{perpetual_market}").text_content()
|
||||
pattern = re.compile(
|
||||
r"Trading on Market BTC:DAI_Perpetual may stop on \d{2} [A-Za-z]+\. There is open proposal to close this market\.Proposed final price is 100\.00 BTC\.View proposal"
|
||||
)
|
||||
assert pattern.search(banner_text), f"Text did not match pattern. Text was: {banner_text}"
|
||||
|
||||
@pytest.mark.usefixtures("page","risk_accepted", "auth" )
|
||||
def test_perps_market_terminated(page: Page, vega: VegaService):
|
||||
perpetual_market = setup_perps_market(vega)
|
||||
vega.update_market_state(
|
||||
proposal_key=MM_WALLET.name,
|
||||
market_id=perpetual_market,
|
||||
market_state=MarketStateUpdateType.Terminate,
|
||||
price=100,
|
||||
approve_proposal = True,
|
||||
forward_time_to_enactment = True,
|
||||
)
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
page.goto(f"/#/markets/{perpetual_market}")
|
||||
expect(page.get_by_test_id("market-price")).to_have_text("Mark Price100.00")
|
||||
expect(page.get_by_test_id("market-change")).to_have_text("Change (24h)-")
|
||||
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)-")
|
||||
expect(page.get_by_test_id("market-trading-mode")).to_have_text("Trading modeNo trading")
|
||||
expect(page.get_by_test_id("market-state")).to_have_text("StatusClosed")
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 0.00 (0.00%)")
|
||||
expect(page.get_by_test_id("market-funding")).to_contain_text("Funding Rate / Countdown")
|
||||
expect(page.get_by_test_id("index-price")).to_contain_text("Index Price")
|
||||
expect(page.get_by_test_id("deal-ticket-error-message-summary")).to_have_text("This market is closed and not accepting orders")
|
||||
@@ -1,175 +0,0 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.service import VegaService
|
||||
from conftest import init_vega
|
||||
from fixtures.market import setup_continuous_market, setup_simple_market
|
||||
from actions.utils import change_keys, create_and_faucet_wallet, forward_time, selector_contains_text
|
||||
from actions.vega import submit_order, submit_liquidity
|
||||
from wallet_config import MM_WALLET, PARTY_A, PARTY_B
|
||||
|
||||
SELL_ORDERS = [[1, 111], [1, 111], [1, 112], [1, 112], [
|
||||
1, 113], [1, 113], [1, 114], [1, 114], [1, 115], [1, 115]]
|
||||
BUY_ORDERS = [[1, 106], [1, 107], [1, 108]]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega(request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def continuous_market(vega):
|
||||
market = setup_simple_market(vega, custom_quantum=100000)
|
||||
return setup_continuous_market(vega, market, BUY_ORDERS, SELL_ORDERS, add_liquidity=False)
|
||||
|
||||
|
||||
def generate_referrer_expected_value_dic(expected_base_commission, expected_staking_multiplier, expected_final_commission_rate, expected_volume, expected_num_traders, expected_total_commission):
|
||||
return {
|
||||
'[data-testid=my-volume-value]': expected_volume,
|
||||
'[data-testid=total-commission-value]': expected_total_commission,
|
||||
'[data-testid=base-commission-rate-value]': expected_base_commission,
|
||||
'[data-testid=number-of-traders-value]': expected_num_traders,
|
||||
'[data-testid=final-commission-rate-value]': expected_final_commission_rate,
|
||||
'[data-testid=staking-multiplier-value]': expected_staking_multiplier
|
||||
}
|
||||
|
||||
|
||||
def generate_referral_expected_value_dic(expected_volume, expected_tier, expected_discount, expected_epochs, expected_epochs_to_next_tier):
|
||||
return {
|
||||
'[data-testid=combined-volume-value]': expected_volume,
|
||||
'[data-testid=current-tier-value]': expected_tier,
|
||||
'[data-testid=discount-value]': expected_discount,
|
||||
'[data-testid=epochs-in-set-value]': expected_epochs,
|
||||
'[data-testid=epochs-to-next-tier-value]': expected_epochs_to_next_tier
|
||||
}
|
||||
|
||||
|
||||
def check_tile_values(page: Page, expected_results: dict):
|
||||
if "referrals" in page.url:
|
||||
page.reload()
|
||||
else:
|
||||
page.goto("/#/referrals/")
|
||||
|
||||
for selector, expected_text in expected_results.items():
|
||||
assert selector_contains_text(
|
||||
page, selector, expected_text), f"Expected text '{expected_text}' not found in selector '{selector}'"
|
||||
|
||||
|
||||
def create_benefit_tier(minimum_running_notional_taker_volume, minimum_epochs, referral_reward_factor, referral_discount_factor):
|
||||
return {
|
||||
"minimum_running_notional_taker_volume": minimum_running_notional_taker_volume,
|
||||
"minimum_epochs": minimum_epochs,
|
||||
"referral_reward_factor": referral_reward_factor,
|
||||
"referral_discount_factor": referral_discount_factor,
|
||||
}
|
||||
|
||||
|
||||
def create_staking_tier(minimum_staked_tokens, referral_reward_multiplier):
|
||||
return {
|
||||
"minimum_staked_tokens": minimum_staked_tokens,
|
||||
"referral_reward_multiplier": referral_reward_multiplier,
|
||||
}
|
||||
|
||||
|
||||
def setup_market_and_referral_scheme(vega: VegaService, continuous_market: str, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
|
||||
create_and_faucet_wallet(vega=vega, wallet=PARTY_A)
|
||||
create_and_faucet_wallet(vega=vega, wallet=PARTY_B)
|
||||
forward_time(vega)
|
||||
|
||||
benefit_tiers = []
|
||||
staking_tiers = []
|
||||
for i in range(1, 4):
|
||||
benefit_tiers.append(create_benefit_tier(
|
||||
i * 100, i, i * 0.01, i * 0.01))
|
||||
staking_tiers.append(create_staking_tier(
|
||||
i * 100, i))
|
||||
|
||||
vega.update_referral_program(
|
||||
proposal_key=MM_WALLET.name,
|
||||
benefit_tiers=benefit_tiers,
|
||||
staking_tiers=staking_tiers,
|
||||
window_length=1,
|
||||
)
|
||||
forward_time(vega, True)
|
||||
|
||||
vega.create_referral_set(key_name=PARTY_A.name)
|
||||
forward_time(vega, True)
|
||||
|
||||
referral_set_id = list(vega.list_referral_sets().keys())[0]
|
||||
vega.apply_referral_code(key_name=PARTY_B.name, id=referral_set_id)
|
||||
|
||||
tdai_id = vega.find_asset_id(symbol="tDAI")
|
||||
vega.mint(
|
||||
"Key 1",
|
||||
asset=tdai_id,
|
||||
amount=10e6,
|
||||
)
|
||||
vega.mint(
|
||||
PARTY_B.name,
|
||||
asset=tdai_id,
|
||||
amount=10e6,
|
||||
)
|
||||
|
||||
submit_liquidity(vega, MM_WALLET.name, continuous_market, 100, 100)
|
||||
forward_time(vega)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
|
||||
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaService, page: Page):
|
||||
setup_market_and_referral_scheme(vega, continuous_market, page)
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"110", "1", "1%", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"1%", "1", "1%", "0", "1", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"221", "2", "2%", "2", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"2%", "1", "2%", "0", "1", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 3, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"331", "3", "3%", "3", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"3%", "1", "3%", "0", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"110", "1", "1%", "4", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"1%", "1", "1%", "0", "1", "1"))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
|
||||
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaService, page: Page):
|
||||
setup_market_and_referral_scheme(vega, continuous_market, page)
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"221", "1", "1%", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"2%", "1", "2%", "0", "1", "0"))
|
||||
@@ -7,9 +7,6 @@ WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
|
||||
MM_WALLET = WalletConfig("market_maker", "pin")
|
||||
MM_WALLET2 = WalletConfig("market_maker_2", "pin2")
|
||||
TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
GOVERNANCE_WALLET = WalletConfig(
|
||||
"FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
PARTY_A = WalletConfig("party_a", "party_a")
|
||||
PARTY_B = WalletConfig("party_b", "party_b")
|
||||
GOVERNANCE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
|
||||
wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET, GOVERNANCE_WALLET]
|
||||
|
||||
@@ -4,18 +4,9 @@ export default {
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
|
||||
'^.+\\.[tj]sx?$': [
|
||||
'babel-jest',
|
||||
{
|
||||
presets: ['@nx/next/babel'],
|
||||
// required for pennant to work in jest, due to having untranspiled exports
|
||||
plugins: [['@babel/plugin-proposal-private-methods']],
|
||||
},
|
||||
],
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/apps/trading',
|
||||
setupFilesAfterEnv: ['./setup-tests.ts'],
|
||||
// dont ignore pennant from transpilation
|
||||
transformIgnorePatterns: ['<rootDir>/node_modules/pennant'],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useRouterConfig } from '../../pages/client-router';
|
||||
import { routerConfig } from '../../pages/client-router';
|
||||
import { matchRoutes, useLocation } from 'react-router-dom';
|
||||
|
||||
export const useGetCurrentRouteId = () => {
|
||||
const location = useLocation();
|
||||
const matches = matchRoutes(useRouterConfig(), location);
|
||||
const matches = matchRoutes(routerConfig, location);
|
||||
const lastRoute = matches ? matches[matches.length - 1] : undefined;
|
||||
if (lastRoute) {
|
||||
const id = lastRoute.route.id;
|
||||
|
||||
@@ -75,7 +75,6 @@ i18n
|
||||
'positions',
|
||||
'trades',
|
||||
'trading',
|
||||
'trading-view',
|
||||
'ui-toolkit',
|
||||
'utils',
|
||||
'wallet',
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
@@ -19,17 +18,13 @@ if (typeof window !== 'undefined') {
|
||||
view = new ViewConnector();
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ApplyCodeFormContainer } from '../client-pages/referrals/apply-code-for
|
||||
import { CreateCodeContainer } from '../client-pages/referrals/create-code-form';
|
||||
import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary';
|
||||
import { compact } from 'lodash';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { LiquidityHeader } from '../components/liquidity-header';
|
||||
import { MarketHeader } from '../components/market-header';
|
||||
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
|
||||
@@ -44,147 +44,144 @@ const NotFound = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useRouterConfig = (): RouteObject[] => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return compact([
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
id: AppRoutes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'disclaimer',
|
||||
element: <LayoutCentered />,
|
||||
id: AppRoutes.DISCLAIMER,
|
||||
children: [{ index: true, element: <Disclaimer /> }],
|
||||
},
|
||||
// Referrals routing (the pages should be available if the feature flag is on)
|
||||
featureFlags.REFERRALS
|
||||
? {
|
||||
path: AppRoutes.REFERRALS,
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
element: (
|
||||
<LayoutWithSky>
|
||||
<Referrals />
|
||||
</LayoutWithSky>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ReferralStatistics />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeContainer />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_APPLY_CODE,
|
||||
element: <ApplyCodeFormContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <ReferralNotFound />,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
path: 'fees/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Fees />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'rewards/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Rewards />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'markets/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<MarketHeader />}
|
||||
sidebar={<MarketsSidebar />}
|
||||
/>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
id: AppRoutes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: 'all',
|
||||
element: <Navigate to="/markets" />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
id: AppRoutes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Portfolio />,
|
||||
id: AppRoutes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Assets />,
|
||||
id: AppRoutes.ASSETS,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="deposit" /> },
|
||||
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
|
||||
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
|
||||
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
export const routerConfig: RouteObject[] = compact([
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
id: AppRoutes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'disclaimer',
|
||||
element: <LayoutCentered />,
|
||||
id: AppRoutes.DISCLAIMER,
|
||||
children: [{ index: true, element: <Disclaimer /> }],
|
||||
},
|
||||
// Referrals routing (the pages should be available if the feature flag is on)
|
||||
FLAGS.REFERRALS
|
||||
? {
|
||||
path: AppRoutes.REFERRALS,
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
element: (
|
||||
<LayoutWithSky>
|
||||
<Referrals />
|
||||
</LayoutWithSky>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ReferralStatistics />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeContainer />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_APPLY_CODE,
|
||||
element: <ApplyCodeFormContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <ReferralNotFound />,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
path: 'fees/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Fees />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'rewards/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Rewards />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'markets/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<MarketHeader />}
|
||||
sidebar={<MarketsSidebar />}
|
||||
/>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
id: AppRoutes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: 'all',
|
||||
element: <Navigate to="/markets" />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
id: AppRoutes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Portfolio />,
|
||||
id: AppRoutes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Assets />,
|
||||
id: AppRoutes.ASSETS,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="deposit" /> },
|
||||
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
|
||||
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
|
||||
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: 'liquidity/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<LiquidityHeader />}
|
||||
sidebar={<LiquiditySidebar />}
|
||||
/>
|
||||
),
|
||||
id: AppRoutes.LIQUIDITY,
|
||||
children: [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <Liquidity />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFound />,
|
||||
},
|
||||
]);
|
||||
};
|
||||
{
|
||||
path: 'liquidity/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<LiquidityHeader />}
|
||||
sidebar={<LiquiditySidebar />}
|
||||
/>
|
||||
),
|
||||
id: AppRoutes.LIQUIDITY,
|
||||
children: [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <Liquidity />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFound />,
|
||||
},
|
||||
]);
|
||||
|
||||
export const ClientRouter = () => {
|
||||
const routes = useRoutes(useRouterConfig());
|
||||
const routes = useRoutes(routerConfig);
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/assets';
|
||||
import { VegaConnectDialog, ViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { useConnectors } from '../lib/vega-connectors';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import {
|
||||
Web3ConnectUncontrolledDialog,
|
||||
WithdrawalApprovalDialogContainer,
|
||||
@@ -13,14 +13,13 @@ import { RiskMessage } from '../components/welcome-dialog';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
|
||||
const connectors = useConnectors();
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={connectors}
|
||||
connectors={Connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
<ViewAsDialog connector={Connectors.view} />
|
||||
<AssetDetailsDialog
|
||||
assetId={id}
|
||||
trigger={trigger || null}
|
||||
|
||||
@@ -7,14 +7,13 @@ import {
|
||||
useVegaWallet,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useGlobalStore } from '../stores';
|
||||
import { useConnectors } from '../lib/vega-connectors';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
||||
|
||||
export const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const connectors = useConnectors();
|
||||
const eagerConnecting = useVegaEagerConnect(connectors);
|
||||
const eagerConnecting = useVegaEagerConnect(Connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
@@ -24,7 +23,7 @@ export const MaybeConnectEagerly = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(connectors.view);
|
||||
connect(Connectors['view']);
|
||||
}
|
||||
useEffect(() => {
|
||||
update({ eagerConnecting });
|
||||
|
||||
@@ -18,7 +18,7 @@ type Rows = {
|
||||
key: AssetDetail;
|
||||
label: string;
|
||||
tooltip: string;
|
||||
value: (asset: Asset, orignalAsset?: Asset) => ReactNode | undefined;
|
||||
value: (asset: Asset) => ReactNode | undefined;
|
||||
valueTooltip?: (asset: Asset) => string | null | undefined;
|
||||
}[];
|
||||
|
||||
@@ -52,21 +52,6 @@ const num = (asset: Asset, n: string | undefined | null) => {
|
||||
return addDecimalsFormatNumber(n, asset.decimals);
|
||||
};
|
||||
|
||||
const Diff = ({
|
||||
oldValue,
|
||||
newValue,
|
||||
}: {
|
||||
oldValue: ReactNode;
|
||||
newValue: ReactNode;
|
||||
}) => (
|
||||
<span className="flex gap-1">
|
||||
<span className="line-through bg-vega-red-300 dark:bg-vega-red-600">
|
||||
{oldValue}
|
||||
</span>
|
||||
<span className="bg-vega-green-300 dark:bg-vega-green-600">{newValue}</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
export const useRows = () => {
|
||||
const t = useT();
|
||||
const AssetTypeMapping = useAssetTypeMapping();
|
||||
@@ -118,14 +103,7 @@ export const useRows = () => {
|
||||
key: AssetDetail.QUANTUM,
|
||||
label: t('Quantum'),
|
||||
tooltip: t('The minimum economically meaningful amount of the asset'),
|
||||
value: (asset, originalAsset) => {
|
||||
const value = num(asset, asset.quantum);
|
||||
if (originalAsset && originalAsset.quantum !== asset.quantum) {
|
||||
const original = num(originalAsset, originalAsset.quantum);
|
||||
return <Diff oldValue={original} newValue={value} />;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
value: (asset) => num(asset, asset.quantum),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.STATUS,
|
||||
@@ -165,24 +143,8 @@ export const useRows = () => {
|
||||
tooltip: t('WITHDRAW_THRESHOLD_TOOLTIP_TEXT', {
|
||||
defaultValue: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
|
||||
}),
|
||||
value: (asset, originalAsset) => {
|
||||
const value = num(
|
||||
asset,
|
||||
(asset.source as Schema.ERC20).withdrawThreshold
|
||||
);
|
||||
if (
|
||||
originalAsset &&
|
||||
(originalAsset.source as Schema.ERC20).withdrawThreshold !==
|
||||
(asset.source as Schema.ERC20).withdrawThreshold
|
||||
) {
|
||||
const original = num(
|
||||
asset,
|
||||
(originalAsset.source as Schema.ERC20).withdrawThreshold
|
||||
);
|
||||
return <Diff oldValue={original} newValue={value} />;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.LIFETIME_LIMIT,
|
||||
@@ -190,26 +152,8 @@ export const useRows = () => {
|
||||
tooltip: t(
|
||||
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance'
|
||||
),
|
||||
value: (asset, originalAsset) => {
|
||||
const value = num(
|
||||
asset,
|
||||
(asset.source as Schema.ERC20).lifetimeLimit
|
||||
);
|
||||
|
||||
if (
|
||||
originalAsset &&
|
||||
(originalAsset.source as Schema.ERC20).lifetimeLimit !==
|
||||
(asset.source as Schema.ERC20).lifetimeLimit
|
||||
) {
|
||||
const original = num(
|
||||
asset,
|
||||
(originalAsset.source as Schema.ERC20).lifetimeLimit
|
||||
);
|
||||
return <Diff oldValue={original} newValue={value} />;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
value: (asset) =>
|
||||
num(asset, (asset.source as Schema.ERC20).lifetimeLimit),
|
||||
},
|
||||
{
|
||||
key: AssetDetail.MAX_FAUCET_AMOUNT_MINT,
|
||||
@@ -317,13 +261,10 @@ export const testId = (detail: AssetDetail, field: 'label' | 'value') =>
|
||||
|
||||
export type AssetDetailsTableProps = {
|
||||
asset: Asset;
|
||||
originalAsset?: Asset;
|
||||
|
||||
omitRows?: AssetDetail[];
|
||||
} & Omit<KeyValueTableRowProps, 'children'>;
|
||||
export const AssetDetailsTable = ({
|
||||
asset,
|
||||
originalAsset,
|
||||
omitRows = [],
|
||||
...props
|
||||
}: AssetDetailsTableProps) => {
|
||||
@@ -334,7 +275,7 @@ export const AssetDetailsTable = ({
|
||||
|
||||
const details = useRows().map((r) => ({
|
||||
...r,
|
||||
value: r.value(asset, originalAsset),
|
||||
value: r.value(asset),
|
||||
valueTooltip: r.valueTooltip?.(asset),
|
||||
}));
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user