Compare commits
183
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83fd2aa12f | ||
|
|
adf0e26df6 | ||
|
|
a7f95aa953 | ||
|
|
3583f22067 | ||
|
|
2ac5471710 | ||
|
|
98ce1d0a25 | ||
|
|
053775bef6 | ||
|
|
0660eda334 | ||
|
|
f22a3bc2d2 | ||
|
|
fde77ebccb | ||
|
|
e309669736 | ||
|
|
b1621d1191 | ||
|
|
39907f07db | ||
|
|
3dab5f3d9b | ||
|
|
3cf9ae7582 | ||
|
|
557894e2ef | ||
|
|
baf9875c69 | ||
|
|
51199b02ce | ||
|
|
bb826c88f0 | ||
|
|
0da20b750f | ||
|
|
e16c447564 | ||
|
|
a2170d27d6 | ||
|
|
00d840b7b3 | ||
|
|
2a4a05630f | ||
|
|
97f1f40f2c | ||
|
|
a8e6963521 | ||
|
|
a2bffa1dfd | ||
|
|
9253e8067a | ||
|
|
58972f0a11 | ||
|
|
a3c55fd7c3 | ||
|
|
c63cba1071 | ||
|
|
bc9d87fe30 | ||
|
|
6a21862378 | ||
|
|
0fb4348e9c | ||
|
|
b751bcf17d | ||
|
|
d32f27fcb1 | ||
|
|
d05cd6a2ed | ||
|
|
c003e5fa30 | ||
|
|
f62d3289ab | ||
|
|
a3d3d18c5c | ||
|
|
77ca101781 | ||
|
|
d238c37d0c | ||
|
|
79feb485f6 | ||
|
|
6aa5c3b6e3 | ||
|
|
82abc13fda | ||
|
|
933f07cddf | ||
|
|
c8067669c2 | ||
|
|
78f5a9c520 | ||
|
|
462066959d | ||
|
|
f12f5ab961 | ||
|
|
98b2460d8b | ||
|
|
67c182f3de | ||
|
|
68e0009ab0 | ||
|
|
ad74b12908 | ||
|
|
7fff33e493 | ||
|
|
e7cb2c8276 | ||
|
|
4adaeea40a | ||
|
|
d47d894147 | ||
|
|
515932d401 | ||
|
|
723ff2805d | ||
|
|
424cced4be | ||
|
|
b2c7ee82c5 | ||
|
|
4cc63be4f9 | ||
|
|
9fbb7a13e6 | ||
|
|
1874095222 | ||
|
|
e459e92127 | ||
|
|
4dd65b4923 | ||
|
|
5d792d2458 | ||
|
|
59b2f75b13 | ||
|
|
95775679ca | ||
|
|
4cffee29f8 | ||
|
|
bd70b0c233 | ||
|
|
145792f216 | ||
|
|
10add7c236 | ||
|
|
362a2031c7 | ||
|
|
5aaeb87059 | ||
|
|
d240dddab5 | ||
|
|
0af1d0ba82 | ||
|
|
665ab6693a | ||
|
|
902ee2696c | ||
|
|
3beeb0140c | ||
|
|
4b208e90bf | ||
|
|
ec837854cf | ||
|
|
8b94a75ba4 | ||
|
|
08c57b6759 | ||
|
|
bb2184498f | ||
|
|
8b07ce3024 | ||
|
|
8a110584dd | ||
|
|
b82615a3d8 | ||
|
|
f178b85846 | ||
|
|
0796f2b31f | ||
|
|
67be224138 | ||
|
|
51c426ef4b | ||
|
|
fa28d31ef3 | ||
|
|
201a586b05 | ||
|
|
7d96d9bcd1 | ||
|
|
345be81142 | ||
|
|
2221e9faca | ||
|
|
7c0139e107 | ||
|
|
a157ff1e05 | ||
|
|
2e9fa0c52e | ||
|
|
c0790c1e93 | ||
|
|
13c1044f8e | ||
|
|
cf9f313e4c | ||
|
|
f56d34fe6e | ||
|
|
1379366caa | ||
|
|
8958c12fe3 | ||
|
|
fef13874db | ||
|
|
40f02ecf89 | ||
|
|
41cd6b1455 | ||
|
|
3cd393dac0 | ||
|
|
a52e60d6a2 | ||
|
|
bc13f1b359 | ||
|
|
eb81f4ae44 | ||
|
|
51ab02a2e2 | ||
|
|
ffada1b93d | ||
|
|
9dda3f712b | ||
|
|
df20dbeee0 | ||
|
|
cdfd8a2d00 | ||
|
|
1e5c523bc4 | ||
|
|
37cd69ba6e | ||
|
|
2c11045dd9 | ||
|
|
9aef41a119 | ||
|
|
80ab8821d0 | ||
|
|
8a3657a9b9 | ||
|
|
7100b0e9fc | ||
|
|
614a83b7d6 | ||
|
|
3dc77b0eff | ||
|
|
a59f7dfd29 | ||
|
|
61471228aa | ||
|
|
127e784ceb | ||
|
|
70d748fb15 | ||
|
|
e06f4818fc | ||
|
|
8182da3b31 | ||
|
|
c8c56307bb | ||
|
|
a8cd7f157f | ||
|
|
4f18caa486 | ||
|
|
5c7c626bbc | ||
|
|
e4c4c20631 | ||
|
|
0697302d07 | ||
|
|
2d926c0ce0 | ||
|
|
f57d6a7c7b | ||
|
|
15f905046f | ||
|
|
4f7918f64e | ||
|
|
52ab0562b0 | ||
|
|
4e2b0d1b1d | ||
|
|
0b0bcad9b3 | ||
|
|
bcf17bb34e | ||
|
|
a2b9b0da05 | ||
|
|
7588d0cd11 | ||
|
|
5ee1748495 | ||
|
|
73a118978f | ||
|
|
eac26c1966 | ||
|
|
d615587564 | ||
|
|
ba4ce1ce88 | ||
|
|
3bbacc1aa0 | ||
|
|
de5371435d | ||
|
|
12cb5e10b6 | ||
|
|
ee2909cc84 | ||
|
|
068d6abf1b | ||
|
|
e5d4d2b0b8 | ||
|
|
d4e801cfc6 | ||
|
|
964deb2f23 | ||
|
|
6669125dd3 | ||
|
|
129b6c4e89 | ||
|
|
ca418cabfe | ||
|
|
192af844c4 | ||
|
|
6a841f226a | ||
|
|
76426baa2a | ||
|
|
6fdac2419c | ||
|
|
87807d2088 | ||
|
|
a5f2533a66 | ||
|
|
1e9251c9c0 | ||
|
|
2a27e124d7 | ||
|
|
4ee85720ab | ||
|
|
a19da4c408 | ||
|
|
65ad4fffda | ||
|
|
11317cdb2e | ||
|
|
c98870f27d | ||
|
|
c0c56e4c48 | ||
|
|
d9dc43b359 | ||
|
|
06a6fe6d67 | ||
|
|
a9ca215276 |
@@ -77,6 +77,7 @@
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-useless-constructor": 0,
|
||||
"curly": ["error", "multi-line"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* text eol=lf
|
||||
*.png binary
|
||||
*.ico binary
|
||||
*.woff2 binary
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
inputs:
|
||||
console-test-branch:
|
||||
type: choice
|
||||
description: 'main: v0.72.14, develop: v0.73.4'
|
||||
description: 'main: v0.73.5, develop: v0.73.5'
|
||||
options:
|
||||
- main
|
||||
- develop
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
create-docker-image:
|
||||
name: Create docker image for console-test
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# check-out frontend-monorepo
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
name: run-tests
|
||||
runs-on: 8-cores
|
||||
needs: [create-docker-image, console-test-branch]
|
||||
timeout-minutes: 20
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
#----------------------------------------------
|
||||
# load docker image
|
||||
@@ -205,7 +205,7 @@ jobs:
|
||||
# run tests
|
||||
#----------------------------------------------
|
||||
- name: Run tests
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
|
||||
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 1 --dist loadfile --durations=45
|
||||
working-directory: apps/trading/e2e
|
||||
#----------------------------------------------
|
||||
# upload traces
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-trace
|
||||
path: ./traces/
|
||||
path: apps/trading/e2e/traces/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload logs -----
|
||||
@@ -227,3 +227,46 @@ jobs:
|
||||
name: worker-logs
|
||||
path: ./logs/
|
||||
retention-days: 15
|
||||
#----------------------------------------------
|
||||
# ----- upload market-sim logs -----
|
||||
#----------------------------------------------
|
||||
- name: Prepare and Zip market-sim-logs
|
||||
if: always()
|
||||
run: |
|
||||
parent_dir="/tmp/market-sim-logs"
|
||||
echo "Creating parent directory at $parent_dir"
|
||||
mkdir -p "$parent_dir"
|
||||
|
||||
echo "Waiting for vega-sim-* folders to be created..."
|
||||
sleep 10 # Waits 10 seconds to ensure all folders are created
|
||||
|
||||
echo "Before searching for vega-sim-* folders in /tmp..."
|
||||
folders=$(find /tmp -mindepth 1 -type d -name 'vega-sim-*' -print) || echo "Find command failed with exit code $?"
|
||||
|
||||
echo "After searching for vega-sim-* folders in /tmp..."
|
||||
|
||||
if [ -z "$folders" ]; then
|
||||
echo "No vega-sim-* folders found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Moving vega-sim-* folders to $parent_dir"
|
||||
echo "$folders" | xargs -I {} mv {} "$parent_dir/"
|
||||
|
||||
echo "Checking if $parent_dir is not empty..."
|
||||
if [ "$(ls -A $parent_dir)" ]; then
|
||||
echo "Zipping the parent directory..."
|
||||
zip -r market-sim-logs.zip "$parent_dir" && echo "Zip file created successfully."
|
||||
else
|
||||
echo "$parent_dir is empty. No zip file created."
|
||||
exit 0
|
||||
fi
|
||||
shell: /usr/bin/bash -e {0}
|
||||
|
||||
- name: Upload market-sim-logs
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: market-sim-logs
|
||||
path: market-sim-logs.zip
|
||||
retention-days: 15
|
||||
|
||||
+1
-2
@@ -59,5 +59,4 @@ apps/trading/e2e/logs/
|
||||
apps/trading/e2e/.pytest_cache/
|
||||
apps/trading/e2e/traces/
|
||||
|
||||
.nx/cache
|
||||
|
||||
.nx/
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#!/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
|
||||
yarn nx format:check
|
||||
# Lint all staged files - this brings more value as pre-commit
|
||||
# yarn nx format:check
|
||||
|
||||
# Test all projects with changes
|
||||
yarn nx affected -t test --exclude trading
|
||||
# yarn nx affected -t test --exclude trading
|
||||
|
||||
@@ -4,7 +4,7 @@ The front-end monorepo provides a toolkit for building apps that interact with V
|
||||
|
||||
This repository is managed using [Nx](https://nx.dev).
|
||||
|
||||
# 🔎 Applications in this repo
|
||||
## 🔎 Applications in this repo
|
||||
|
||||
### [Block explorer](./apps/explorer)
|
||||
|
||||
@@ -30,7 +30,7 @@ Hosting for static content being shared across apps, for example fonts.
|
||||
|
||||
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
|
||||
|
||||
# 🧱 Libraries in this repo
|
||||
## 🧱 Libraries in this repo
|
||||
|
||||
### [UI toolkit](./libs/ui-toolkit)
|
||||
|
||||
@@ -53,7 +53,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
|
||||
|
||||
Generic react helpers that can be used across multiple applications, along with other utilities.
|
||||
|
||||
# 💻 Develop
|
||||
## 💻 Develop
|
||||
|
||||
### Set up
|
||||
|
||||
@@ -103,7 +103,7 @@ In CI linting, formatting and also run. These checks can be seen in the [CI work
|
||||
|
||||
Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more.
|
||||
|
||||
# 🐋 Hosting a console
|
||||
## 🐋 Hosting a console
|
||||
|
||||
To host a console there are two possible build scenarios for running the frontends: nx performed **outside** or **inside** docker build. For specific build instructions follow [build instructions](#build-instructions).
|
||||
|
||||
@@ -226,6 +226,6 @@ Note: The script is only needed if capsule was built for first time or fresh. To
|
||||
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
|
||||
```
|
||||
|
||||
# 📑 License
|
||||
## 📑 License
|
||||
|
||||
[MIT](./LICENSE)
|
||||
|
||||
@@ -44,7 +44,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('icon-cross').click();
|
||||
});
|
||||
|
||||
it.skip('Proposal page displayed on mobile', function () {
|
||||
it('Proposal page displayed on mobile', function () {
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
@@ -55,7 +55,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to view new asset proposal', function () {
|
||||
it.skip('Able to view new asset proposal', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
const newAssetProposalBody = getNewAssetTxBody();
|
||||
cy.VegaWalletSubmitProposal(newAssetProposalBody);
|
||||
|
||||
@@ -12,7 +12,8 @@ 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 { router } from './routes/router-config';
|
||||
import { useRouterConfig } from './routes/router-config';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
@@ -38,7 +39,10 @@ function App() {
|
||||
}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
<RouterProvider
|
||||
router={createBrowserRouter(useRouterConfig())}
|
||||
fallbackElement={splashLoading}
|
||||
/>
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
|
||||
@@ -12,7 +12,8 @@ import { type VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
|
||||
type AssetsTableProps = {
|
||||
data: AssetFieldsFragment[] | null;
|
||||
@@ -23,7 +24,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
ref.current?.columnApi.setColumnsVisible(
|
||||
ref.current?.api.setColumnsVisible(
|
||||
['id', 'type', 'status'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -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 { routerConfig } from '../../routes/router-config';
|
||||
import { useRouterConfig } from '../../routes/router-config';
|
||||
import { useMemo } from 'react';
|
||||
import compact from 'lodash/compact';
|
||||
import { Search } from '../search';
|
||||
@@ -26,6 +26,7 @@ const routeToNavigationItem = (r: Navigable) => (
|
||||
);
|
||||
|
||||
export const Header = () => {
|
||||
const routerConfig = useRouterConfig();
|
||||
const isHome = Boolean(useMatch(Routes.HOME));
|
||||
const pages = routerConfig[0].children || [];
|
||||
const mainItems = compact(
|
||||
|
||||
@@ -60,7 +60,7 @@ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="whitespace-nowrap">
|
||||
<span>
|
||||
{useName && <Icon size={4} name="cube" className="mr-2" />}
|
||||
<Link
|
||||
className="underline font-mono"
|
||||
|
||||
@@ -29,7 +29,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
['id', 'state', 'asset'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
BORDER_COLOURS,
|
||||
NestedDataList,
|
||||
sortNestedDataByChildren,
|
||||
} from './nested-data-list';
|
||||
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
|
||||
const mockData = {
|
||||
@@ -61,38 +57,6 @@ describe('NestedDataList', () => {
|
||||
expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2');
|
||||
});
|
||||
|
||||
it('should repeat the border colours in the correct order', () => {
|
||||
const colourMockData = {
|
||||
t0: {
|
||||
t1: {
|
||||
t2: {
|
||||
t3: {
|
||||
t4: {
|
||||
t5: {
|
||||
t6: {
|
||||
t7: {
|
||||
t8: {
|
||||
hello: 'world',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const tree = render(<NestedDataList data={colourMockData} />);
|
||||
const { getByTestId } = tree;
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const item = getByTestId(`T${i}`);
|
||||
const expected = BORDER_COLOURS.light[i % 5];
|
||||
expect(item.style.borderColor.toUpperCase()).toBe(expected);
|
||||
}
|
||||
});
|
||||
|
||||
it('should sort the data by values with children', () => {
|
||||
const mockData = {
|
||||
nonce: '5980890939790185837',
|
||||
|
||||
@@ -6,6 +6,7 @@ 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;
|
||||
@@ -59,7 +60,7 @@ const DeterministicOrderDetails = ({
|
||||
const o = data.orderByID;
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mb-12 lg:mb-0">
|
||||
<div className="mb-0">
|
||||
<div className="relative block px-3 py-6 md:px-6 lg:-mr-7">
|
||||
<h2 className="text-3xl font-bold mb-4 display-5">
|
||||
<abbr title={tifFull[o.timeInForce]} className="bb-dotted mr-2">
|
||||
@@ -69,7 +70,7 @@ const DeterministicOrderDetails = ({
|
||||
<span className="mx-5 text-base">@</span>
|
||||
<PriceInMarket price={o.price} marketId={o.market.id} />
|
||||
</h2>
|
||||
<p className="text-gray-200">
|
||||
<p className="text-gray-400 dark:text-gray-600">
|
||||
In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />.
|
||||
</p>
|
||||
{o.peggedOrder ? (
|
||||
@@ -83,15 +84,14 @@ 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-4 gap-x-6 mt-4">
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-x-6 mt-4">
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
{t('Status')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0 capitalize">
|
||||
@@ -99,21 +99,33 @@ const DeterministicOrderDetails = ({
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">{t('Size')}</h2>
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
{t('Size')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
<SizeInMarket size={o.size} marketId={o.market.id} />
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
{t('Version')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
{o.version}
|
||||
</h5>
|
||||
</div>
|
||||
{o.type ? (
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md: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>
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
type VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
@@ -43,11 +44,11 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
['voting', 'cDate', 'eDate', 'type'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
gridRef.current?.columnApi.setColumnWidth(
|
||||
gridRef.current?.api.setColumnWidth(
|
||||
'actions',
|
||||
window.innerWidth > BREAKPOINT_MD ? 221 : 80
|
||||
);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { useState } from 'react';
|
||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
interface SignatureProps {
|
||||
signature: BlockExplorerTransactionResult['signature'];
|
||||
}
|
||||
|
||||
const valueClass =
|
||||
'font-mono px-2.5 py-0.5 text-xs max-w-[200px] cursor-pointer';
|
||||
const valueClassClosed = 'text-ellipsis overflow-hidden';
|
||||
const valueClassOpen = 'break-words text-left';
|
||||
|
||||
/**
|
||||
* Viewer component for a vega signature. Featuers copy and pasting, truncation
|
||||
*
|
||||
* @param signature
|
||||
*/
|
||||
export const Signature = ({ signature }: SignatureProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
if (!signature || !signature.value || !signature.version || !signature.algo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="inline-flex border rounded signature-component relative pr-[20px]">
|
||||
<div
|
||||
className="bg-gray-100 px-2.5 py-0.5 text-xs text-gray-500 select-none cursor-default"
|
||||
title={`${signature.algo}`}
|
||||
>
|
||||
<span>v{signature.version}</span>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isOpen
|
||||
? `${valueClass} ${valueClassOpen}`
|
||||
: `${valueClass} ${valueClassClosed}`
|
||||
}
|
||||
>
|
||||
<CopyWithTooltip text={signature.value}>
|
||||
<span title={signature.value}>{signature.value}</span>
|
||||
</CopyWithTooltip>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="absolute top-[-3px] right-0 pr-2"
|
||||
title={t('Show full signature')}
|
||||
>
|
||||
<VegaIcon name={isOpen ? VegaIconNames.EYE_OFF : VegaIconNames.EYE} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerReferralCodeOwnerQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerReferralCodeOwnerQuery = { __typename?: 'Query', referralSets: { __typename?: 'ReferralSetConnection', edges: Array<{ __typename?: 'ReferralSetEdge', node: { __typename?: 'ReferralSet', createdAt: any, updatedAt: any, referrer: string } } | null> } };
|
||||
|
||||
|
||||
export const ExplorerReferralCodeOwnerDocument = gql`
|
||||
query ExplorerReferralCodeOwner($id: ID!) {
|
||||
referralSets(id: $id) {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
updatedAt
|
||||
referrer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerReferralCodeOwnerQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerReferralCodeOwnerQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerReferralCodeOwnerQuery` 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 } = useExplorerReferralCodeOwnerQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerReferralCodeOwnerQuery(baseOptions: Apollo.QueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
|
||||
}
|
||||
export function useExplorerReferralCodeOwnerLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>(ExplorerReferralCodeOwnerDocument, options);
|
||||
}
|
||||
export type ExplorerReferralCodeOwnerQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerQuery>;
|
||||
export type ExplorerReferralCodeOwnerLazyQueryHookResult = ReturnType<typeof useExplorerReferralCodeOwnerLazyQuery>;
|
||||
export type ExplorerReferralCodeOwnerQueryResult = Apollo.QueryResult<ExplorerReferralCodeOwnerQuery, ExplorerReferralCodeOwnerQueryVariables>;
|
||||
@@ -0,0 +1,11 @@
|
||||
query ExplorerReferralCodeOwner($id: ID!) {
|
||||
referralSets(id: $id) {
|
||||
edges {
|
||||
node {
|
||||
createdAt
|
||||
updatedAt
|
||||
referrer
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { ReferralCodeOwner } from './referral-code-owner';
|
||||
import type { ReferralCodeOwnerProps } from './referral-code-owner';
|
||||
import { ExplorerReferralCodeOwnerDocument } from './__generated__/code-owner';
|
||||
const renderComponent = (
|
||||
props: ReferralCodeOwnerProps,
|
||||
mocks: MockedResponse[]
|
||||
) => {
|
||||
return render(
|
||||
<MockedProvider mocks={mocks}>
|
||||
<MemoryRouter>
|
||||
<ReferralCodeOwner {...props} />
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('ReferralCodeOwner', () => {
|
||||
it('should render loading state', () => {
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
expect(getByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render error state', async () => {
|
||||
const errorMessage = 'Error fetching referrer: ABC123';
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
error: new Error('nope'),
|
||||
},
|
||||
];
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText(errorMessage)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render link to referring party', async () => {
|
||||
const referrerId = 'DEF789';
|
||||
|
||||
const mocks = [
|
||||
{
|
||||
request: {
|
||||
query: ExplorerReferralCodeOwnerDocument,
|
||||
variables: {
|
||||
id: 'ABC123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
referralSets: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'ReferralSet',
|
||||
referrer: referrerId,
|
||||
createdAt: '123',
|
||||
updatedAt: '456',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const { getByText } = renderComponent({ code: 'ABC123' }, mocks);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(getByText(referrerId)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,26 @@
|
||||
import { TableCell } from '../../../table';
|
||||
import { useExplorerReferralCodeOwnerQuery } from './__generated__/code-owner';
|
||||
import { PartyLink } from '../../../links';
|
||||
|
||||
export interface ReferralCodeOwnerProps {
|
||||
code: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the owner of a referral code
|
||||
*/
|
||||
export const ReferralCodeOwner = ({ code }: ReferralCodeOwnerProps) => {
|
||||
const { data, error, loading } = useExplorerReferralCodeOwnerQuery({
|
||||
variables: {
|
||||
id: code,
|
||||
},
|
||||
});
|
||||
const referrer = data?.referralSets.edges[0]?.node.referrer || '';
|
||||
return (
|
||||
<TableCell>
|
||||
{loading && 'Loading...'}
|
||||
{error && `Error fetching referrer: ${code}`}
|
||||
{referrer.length > 0 && <PartyLink id={referrer} />}
|
||||
</TableCell>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,93 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { ReferralTeam } from './team';
|
||||
import type { CreateReferralSet } from './team';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
describe('ReferralTeam', () => {
|
||||
const team = {
|
||||
name: 'Test Team',
|
||||
teamUrl: 'https://example.com/team',
|
||||
avatarUrl: 'https://example.com/avatar',
|
||||
closed: false,
|
||||
};
|
||||
|
||||
const mockTx: CreateReferralSet = {
|
||||
team,
|
||||
};
|
||||
|
||||
const mockId = '123456';
|
||||
const mockCreator = 'JohnDoe';
|
||||
|
||||
it('should render the team name', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Test Team')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the team ID', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Id')).toBeInTheDocument();
|
||||
expect(getByText(mockId)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the creator', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Creator')).toBeInTheDocument();
|
||||
expect(getByText(mockCreator)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the team URL', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Team URL')).toBeInTheDocument();
|
||||
expect(getByText(team.teamUrl)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the avatar URL', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByText('Avatar')).toBeInTheDocument();
|
||||
expect(getByText(team.avatarUrl)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the open status as a tick if closed is falsy', () => {
|
||||
const { getByTestId } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={mockTx} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByTestId('open-yes')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the open status as a cross if it is truthy', () => {
|
||||
const m = {
|
||||
team: {
|
||||
closed: true,
|
||||
},
|
||||
};
|
||||
|
||||
const { getByTestId } = render(
|
||||
<MockedProvider>
|
||||
<ReferralTeam tx={m} id={mockId} creator={mockCreator} />
|
||||
</MockedProvider>
|
||||
);
|
||||
expect(getByTestId('open-no')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,73 @@
|
||||
import {
|
||||
VegaIcon,
|
||||
Icon,
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { components } from '../../../../../types/explorer';
|
||||
import Hash from '../../../links/hash';
|
||||
import { t } from 'i18next';
|
||||
import { PartyLink } from '../../../links';
|
||||
|
||||
export type CreateReferralSet = components['schemas']['v1CreateReferralSet'];
|
||||
export type ReferralTeam = CreateReferralSet['team'];
|
||||
|
||||
export interface ReferralTeamProps {
|
||||
tx: CreateReferralSet;
|
||||
id: string;
|
||||
creator: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the details for a team in a CreateReferralSet or UpdateReferralSet transaction.
|
||||
*
|
||||
* Intentionally does not render the avatar image or link to the team url.
|
||||
*/
|
||||
export const ReferralTeam = ({ tx, id, creator }: ReferralTeamProps) => {
|
||||
if (!tx.team) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div className="inline-block mr-2 leading-none">
|
||||
<VegaIcon name={VegaIconNames.TEAM} />
|
||||
</div>
|
||||
{tx.team.name && (
|
||||
<h3 className="inline-block leading-loose">{tx.team.name}</h3>
|
||||
)}
|
||||
|
||||
<div className="min-w-fit max-w-2xl block">
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
{t('Id')}
|
||||
<Hash text={id} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('Creator')}
|
||||
<PartyLink id={creator} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
{tx.team.teamUrl && (
|
||||
<KeyValueTableRow>
|
||||
{t('Team URL')}
|
||||
<Hash text={tx.team.teamUrl} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
{tx.team.avatarUrl && (
|
||||
<KeyValueTableRow>
|
||||
{t('Avatar')}
|
||||
<Hash text={tx.team.avatarUrl} truncate={false} />
|
||||
</KeyValueTableRow>
|
||||
)}
|
||||
<KeyValueTableRow>
|
||||
{t('Open')}
|
||||
<span data-testid={!tx.team.closed ? 'open-yes' : 'open-no'}>
|
||||
{!tx.team.closed ? <Icon name="tick" /> : <Icon name="cross" />}
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -9,6 +9,7 @@ import { Time } from '../../../time';
|
||||
import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
|
||||
import { TxDataView } from '../../tx-data-view';
|
||||
import Hash from '../../../links/hash';
|
||||
import { Signature } from '../../../signature/signature';
|
||||
|
||||
interface TxDetailsSharedProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -26,7 +27,8 @@ export const sharedHeaderProps = {
|
||||
className: 'align-top',
|
||||
};
|
||||
|
||||
const Labels: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
// The incoming type field is usually the right thing to show. Exceptions are listed here
|
||||
const LabelOverrides: Record<BlockExplorerTransactionResult['type'], string> = {
|
||||
'Stop Orders Submission': 'Stop Order',
|
||||
'Stop Orders Cancellation': 'Cancel Stop Order',
|
||||
};
|
||||
@@ -49,7 +51,7 @@ export const TxDetailsShared = ({
|
||||
const time: string = blockData?.result.block.header.time || '';
|
||||
const height: string = blockData?.result.block.header.height || txData.block;
|
||||
|
||||
const type = Labels[txData.type] || txData.type;
|
||||
const type = LabelOverrides[txData.type] || txData.type;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -75,6 +77,12 @@ export const TxDetailsShared = ({
|
||||
<BlockLink height={height} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Signature')}</TableCell>
|
||||
<TableCell>
|
||||
<Signature signature={txData.signature} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
|
||||
<TableCell>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { ReferralCodeOwner } from './referrals/referral-code-owner';
|
||||
|
||||
interface TxDetailsApplyReferralCodeProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsApplyReferralCode = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsApplyReferralCodeProps) => {
|
||||
if (!txData || !txData.command.applyReferralCode || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const referralCode = txData.command.applyReferralCode.id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Applied Code')}</TableCell>
|
||||
<TableCell>{referralCode}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Referrer')}</TableCell>
|
||||
<ReferralCodeOwner code={referralCode} />
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import { ReferralTeam } from './referrals/team';
|
||||
|
||||
interface TxDetailsCreateReferralProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsCreateReferralSet = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsCreateReferralProps) => {
|
||||
if (!txData || !txData.command.createReferralSet || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
||||
|
||||
const isTeam = txData.command.createReferralSet.isTeam || false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
|
||||
<TableCell>{id}</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
|
||||
<ReferralTeam
|
||||
tx={txData.command.createReferralSet}
|
||||
id={id}
|
||||
creator={txData.submitter}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,10 @@ import { TxProposal } from './tx-proposal';
|
||||
import { TxDetailsTransfer } from './tx-transfer';
|
||||
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
import { TxDetailsCreateReferralSet } from './tx-create-referral-set';
|
||||
import { TxDetailsApplyReferralCode } from './tx-apply-referral-code';
|
||||
import { TxDetailsUpdateReferralSet } from './tx-update-referral-set';
|
||||
import { TxDetailsJoinTeam } from './tx-join-team';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -121,6 +125,14 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
return TxDetailsStopOrderSubmission;
|
||||
case 'Transfer Funds':
|
||||
return TxDetailsTransfer;
|
||||
case 'Create Referral Set':
|
||||
return TxDetailsCreateReferralSet;
|
||||
case 'Update Referral Set':
|
||||
return TxDetailsUpdateReferralSet;
|
||||
case 'Apply Referral Code':
|
||||
return TxDetailsApplyReferralCode;
|
||||
case 'Join Team':
|
||||
return TxDetailsJoinTeam;
|
||||
default:
|
||||
return TxDetailsGeneric;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { ReferralCodeOwner } from './referrals/referral-code-owner';
|
||||
|
||||
interface TxDetailsJoinTeamProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The signature can be turned in to an id with txSignatureToDeterministicId but
|
||||
*/
|
||||
export const TxDetailsJoinTeam = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsJoinTeamProps) => {
|
||||
if (!txData || !txData.command.joinTeam || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const team = txData.command.joinTeam.id;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Team')}</TableCell>
|
||||
<TableCell>{team}</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Referrer')}</TableCell>
|
||||
<ReferralCodeOwner code={team} />
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||
import { ReferralTeam } from './referrals/team';
|
||||
|
||||
interface TxDetailsUpdateReferralProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of create referral set, effectively.
|
||||
* Updating a referral set without a team doesn't make sense,
|
||||
* but is valid, so this component ignores sense.
|
||||
*/
|
||||
export const TxDetailsUpdateReferralSet = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsUpdateReferralProps) => {
|
||||
if (!txData || !txData.command.updateReferralSet || !txData.signature) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const id = txSignatureToDeterministicId(txData.signature.value);
|
||||
|
||||
const isTeam = txData.command.updateReferralSet.isTeam || false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
<TxDetailsShared
|
||||
txData={txData}
|
||||
pubKey={pubKey}
|
||||
blockData={blockData}
|
||||
/>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{isTeam ? t('Team ID') : t('Referral code')}</TableCell>
|
||||
<TableCell>{id}</TableCell>
|
||||
</TableRow>
|
||||
</TableWithTbody>
|
||||
|
||||
<ReferralTeam
|
||||
tx={txData.command.updateReferralSet}
|
||||
id={id}
|
||||
creator={txData.submitter}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -28,6 +28,7 @@ export type FilterOption =
|
||||
| 'Delegate'
|
||||
| 'Ethereum Key Rotate Submission'
|
||||
| 'Issue Signatures'
|
||||
| 'Join Team'
|
||||
| 'Key Rotate Submission'
|
||||
| 'Liquidity Provision Order'
|
||||
| 'Node Signature'
|
||||
@@ -47,45 +48,46 @@ export type FilterOption =
|
||||
| 'Vote on Proposal'
|
||||
| 'Withdraw';
|
||||
|
||||
// Alphabetised list of transaction types to appear at the top level
|
||||
export const PrimaryFilterOptions: FilterOption[] = [
|
||||
'Amend LiquidityProvision Order',
|
||||
'Amend Order',
|
||||
'Batch Market Instructions',
|
||||
'Cancel LiquidityProvision Order',
|
||||
'Cancel Order',
|
||||
'Cancel Transfer Funds',
|
||||
'Delegate',
|
||||
'Liquidity Provision Order',
|
||||
'Proposal',
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Oracle Data',
|
||||
'Submit Order',
|
||||
'Transfer Funds',
|
||||
'Undelegate',
|
||||
'Vote on Proposal',
|
||||
'Withdraw',
|
||||
];
|
||||
export const filterOptions: Record<string, FilterOption[]> = {
|
||||
'Market Instructions': [
|
||||
'Amend LiquidityProvision Order',
|
||||
'Amend Order',
|
||||
'Batch Market Instructions',
|
||||
'Cancel LiquidityProvision Order',
|
||||
'Cancel Order',
|
||||
'Liquidity Provision Order',
|
||||
'Stop Orders Submission',
|
||||
'Stop Orders Cancellation',
|
||||
'Submit Order',
|
||||
],
|
||||
'Transfers and Withdrawals': [
|
||||
'Transfer Funds',
|
||||
'Cancel Transfer Funds',
|
||||
'Withdraw',
|
||||
],
|
||||
Governance: ['Delegate', 'Undelegate', 'Vote on Proposal', 'Proposal'],
|
||||
Referrals: [
|
||||
'Apply Referral Code',
|
||||
'Create Referral Set',
|
||||
'Join Team',
|
||||
'Update Referral Set',
|
||||
],
|
||||
'External Data': ['Chain Event', 'Submit Oracle Data'],
|
||||
Validators: [
|
||||
'Ethereum Key Rotate Submission',
|
||||
'Issue Signatures',
|
||||
'Key Rotate Submission',
|
||||
'Node Signature',
|
||||
'Node Vote',
|
||||
'Protocol Upgrade',
|
||||
'Register new Node',
|
||||
'State Variable Proposal',
|
||||
'Validator Heartbeat',
|
||||
],
|
||||
};
|
||||
|
||||
// Alphabetised list of transaction types to nest under a 'More...' submenu
|
||||
export const SecondaryFilterOptions: FilterOption[] = [
|
||||
'Chain Event',
|
||||
'Ethereum Key Rotate Submission',
|
||||
'Issue Signatures',
|
||||
'Key Rotate Submission',
|
||||
'Node Signature',
|
||||
'Node Vote',
|
||||
'Protocol Upgrade',
|
||||
'Register new Node',
|
||||
'State Variable Proposal',
|
||||
'Validator Heartbeat',
|
||||
];
|
||||
|
||||
export const AllFilterOptions: FilterOption[] = [
|
||||
...PrimaryFilterOptions,
|
||||
...SecondaryFilterOptions,
|
||||
];
|
||||
export const AllFilterOptions: FilterOption[] =
|
||||
Object.values(filterOptions).flat();
|
||||
|
||||
export interface TxFilterProps {
|
||||
filters: Set<FilterOption>;
|
||||
@@ -122,46 +124,33 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
|
||||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
{PrimaryFilterOptions.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={() => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
|
||||
{Object.entries(filterOptions).map(([key, value]) => (
|
||||
<DropdownMenuSub key={key}>
|
||||
<DropdownMenuSubTrigger>
|
||||
{t(key)}
|
||||
<Icon name="chevron-right" />
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
{value.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={(checked) => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" className="inline" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
))}
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
{t('More Types')}
|
||||
<Icon name="chevron-right" />
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuSubContent>
|
||||
{SecondaryFilterOptions.map((f) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={f}
|
||||
checked={filters.has(f)}
|
||||
onCheckedChange={(checked) => {
|
||||
// NOTE: These act like radio buttons until the API supports multiple filters
|
||||
setFilters(new Set([f]));
|
||||
}}
|
||||
id={`radio-${f}`}
|
||||
>
|
||||
{f}
|
||||
<DropdownMenuItemIndicator>
|
||||
<Icon name="tick-circle" className="inline" />
|
||||
</DropdownMenuItemIndicator>
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuSub>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -48,6 +48,8 @@ const displayString: StringMap = {
|
||||
StopOrdersSubmission: 'Stop',
|
||||
StopOrdersCancellation: 'Cancel stop',
|
||||
'Stop Orders Cancellation': 'Cancel stop',
|
||||
'Apply Referral Code': 'Referral',
|
||||
'Create Referral Set': 'Create referral',
|
||||
};
|
||||
|
||||
export function getLabelForStopOrderType(
|
||||
@@ -80,6 +82,12 @@ 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';
|
||||
}
|
||||
|
||||
@@ -98,6 +98,8 @@ describe('TxDetailsTransfer', () => {
|
||||
},
|
||||
},
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value:
|
||||
'610c2e196a7d4fed4413b9e82af267b1ff3e30e943df3a3d28096fd60604d430d752fbaf6dd4f84d496be78885bb6118f40560bff7832c06bd7a3d67b718b700',
|
||||
},
|
||||
|
||||
@@ -20,6 +20,8 @@ const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
|
||||
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
|
||||
type: 'Submit Order',
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value: '123',
|
||||
},
|
||||
code: 0,
|
||||
|
||||
@@ -54,7 +54,7 @@ const Block = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
{blockData && (
|
||||
{blockData && 'result' in blockData && (
|
||||
<>
|
||||
<TableWithTbody className="mb-8">
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
@@ -73,7 +73,7 @@ export const Layout = () => {
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<main className="p-4">
|
||||
<main className="md:p-4">
|
||||
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -18,7 +18,6 @@ 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';
|
||||
@@ -29,7 +28,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import RestrictedPage from './restricted';
|
||||
|
||||
export type Navigable = {
|
||||
@@ -60,311 +59,315 @@ type Route = RouteItem & {
|
||||
children?: RouteItem[];
|
||||
};
|
||||
|
||||
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 />,
|
||||
},
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
export const useRouterConfig = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
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>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Parties />,
|
||||
},
|
||||
{
|
||||
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>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
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 assetsRoutes: Route[] = FLAGS.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 />,
|
||||
]
|
||||
: [];
|
||||
|
||||
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 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
|
||||
? [
|
||||
{
|
||||
path: ':assetId',
|
||||
element: <AssetPage />,
|
||||
path: Routes.NETWORK_PARAMETERS,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<AssetLink assetId={params.assetId as string} />
|
||||
name: t('NetworkParameters'),
|
||||
text: t('Network Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.NETWORK_PARAMETERS}>
|
||||
{t('Network Parameters')}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
element: <NetworkParameters />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
]
|
||||
: [];
|
||||
|
||||
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 />,
|
||||
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
||||
? [
|
||||
{
|
||||
path: Routes.VALIDATORS,
|
||||
handle: {
|
||||
name: t('Validators'),
|
||||
text: t('Validators'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<MarketLink id={params.marketId as string} />
|
||||
),
|
||||
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.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>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
index: true,
|
||||
element: <TxsList />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.BLOCKS,
|
||||
handle: {
|
||||
name: t('Blocks'),
|
||||
text: t('Blocks'),
|
||||
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
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: <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>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
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>
|
||||
),
|
||||
{
|
||||
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>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
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>,
|
||||
{
|
||||
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,
|
||||
],
|
||||
},
|
||||
errorElement: <ErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
},
|
||||
{
|
||||
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>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
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'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(routerConfig);
|
||||
];
|
||||
return routerConfig;
|
||||
};
|
||||
|
||||
@@ -31,6 +31,10 @@ const Tx = () => {
|
||||
}
|
||||
}
|
||||
|
||||
if (!data || !data?.transaction) {
|
||||
errorMessage = 'Transaction not found';
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<PageHeader
|
||||
@@ -49,7 +53,7 @@ const Tx = () => {
|
||||
<TxDetails
|
||||
className="mb-28"
|
||||
txData={data?.transaction}
|
||||
pubKey={data?.transaction.submitter}
|
||||
pubKey={data?.transaction?.submitter}
|
||||
/>
|
||||
</RenderFetched>
|
||||
</section>
|
||||
|
||||
@@ -23,6 +23,8 @@ const txData: BlockExplorerTransactionResult = {
|
||||
type: 'type',
|
||||
command: {} as ValidatorHeartbeat,
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value: '123',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,8 +11,8 @@ interface TxDetailsProps {
|
||||
export const txDetailsTruncateLength = 30;
|
||||
|
||||
export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
if (!txData || !pubKey) {
|
||||
return <>{t('Transaction could not be found')}</>;
|
||||
}
|
||||
return (
|
||||
<section className="mb-10" key={txData.hash}>
|
||||
|
||||
@@ -11,6 +11,8 @@ export interface BlockExplorerTransactionResult {
|
||||
cursor: string;
|
||||
command: components['schemas']['blockexplorerv1transaction'];
|
||||
signature: {
|
||||
version: string;
|
||||
algo: string;
|
||||
value: string;
|
||||
};
|
||||
error?: string;
|
||||
|
||||
+2
-2
@@ -8,13 +8,13 @@ type Without<T, U> = { [P in Exclude<keyof T, keyof U>]?: never };
|
||||
type XOR<T, U> = T | U extends object
|
||||
? (Without<T, U> & U) | (Without<U, T> & T)
|
||||
: T | U;
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
type OneOf<T extends any[]> = T extends [infer Only]
|
||||
? Only
|
||||
: T extends [infer A, infer B, ...infer Rest]
|
||||
? OneOf<[XOR<A, B>, ...Rest]>
|
||||
: never;
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||
|
||||
export interface paths {
|
||||
'/info': {
|
||||
|
||||
@@ -47,7 +47,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
.and('contain.text', 'USDC (fake)');
|
||||
});
|
||||
|
||||
it('Unable to submit proposal with public key', function () {
|
||||
it.skip('Unable to submit proposal with public key', function () {
|
||||
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
@@ -55,7 +55,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
cy.getByTestId('dialog-content')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'have.text',
|
||||
'Transaction failed'
|
||||
);
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -112,6 +112,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
||||
performanceHysteresisEpochs: 2,
|
||||
slaCompetitionFactor: '0.1',
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
quadraticSlippageFactor: '0',
|
||||
instrument: {
|
||||
name: 'Token test market',
|
||||
@@ -196,6 +197,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
@@ -240,6 +242,7 @@ export function createSuccessorMarketProposalTxBody(
|
||||
decimalPlaces: '5',
|
||||
positionDecimalPlaces: '5',
|
||||
linearSlippageFactor: '0.001',
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
quadraticSlippageFactor: '0',
|
||||
liquiditySlaParameters: {
|
||||
priceRange: '0.5',
|
||||
@@ -334,6 +337,7 @@ export function createSuccessorMarketProposalTxBody(
|
||||
timeWindow: '3600',
|
||||
scalingFactor: 10,
|
||||
},
|
||||
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
|
||||
triggeringRatio: '0.7',
|
||||
auctionExtension: '1',
|
||||
},
|
||||
|
||||
@@ -32,7 +32,7 @@ CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -23,7 +23,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -22,7 +22,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -21,7 +21,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -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 { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React, { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -15,21 +15,23 @@ import {
|
||||
} from './contexts/app-state/app-state-context';
|
||||
import { useContracts } from './contexts/contracts/contracts-context';
|
||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||
import { Connectors } from './lib/vega-connectors';
|
||||
import { useConnectors } from './lib/vega-connectors';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const useVegaWalletEagerConnect = () => {
|
||||
const vegaConnecting = useEagerConnect(Connectors);
|
||||
const connectors = useConnectors();
|
||||
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();
|
||||
@@ -79,10 +81,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (!featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
run();
|
||||
}
|
||||
}, [token, appDispatch, staking, vesting]);
|
||||
}, [
|
||||
token,
|
||||
appDispatch,
|
||||
staking,
|
||||
vesting,
|
||||
featureFlags.GOVERNANCE_NETWORK_DOWN,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account && pubKey) {
|
||||
@@ -147,16 +155,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 (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
|
||||
if (featureFlags.GOVERNANCE_NETWORK_LIMITS) {
|
||||
getNetworkLimits();
|
||||
}
|
||||
|
||||
return () => {
|
||||
stopPoll();
|
||||
};
|
||||
}, [appDispatch, VEGA_URL, t]);
|
||||
}, [appDispatch, VEGA_URL, t, featureFlags.GOVERNANCE_NETWORK_LIMITS]);
|
||||
|
||||
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashError />
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -96,7 +96,9 @@ const cache: InMemoryCacheConfig = {
|
||||
const Web3Container = ({
|
||||
chainId,
|
||||
}: {
|
||||
/** Ethereum chain id */
|
||||
chainId: number;
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
@@ -123,6 +125,9 @@ const Web3Container = ({
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
return initializeConnectors(
|
||||
@@ -157,7 +162,8 @@ const Web3Container = ({
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
@@ -169,6 +175,7 @@ const Web3Container = ({
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
|
||||
@@ -23,10 +23,13 @@ export const Heading = ({
|
||||
})}
|
||||
>
|
||||
<h1
|
||||
className={classNames('font-alpha calt text-5xl break-words', {
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
})}
|
||||
className={classNames(
|
||||
'font-alpha calt text-5xl [word-break:break-word]',
|
||||
{
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
}
|
||||
)}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
@@ -7,16 +7,16 @@ import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { Connectors } from '../../lib/vega-connectors';
|
||||
import { useConnectors } 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,12 +49,8 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
? activeProvider
|
||||
: defaultProvider;
|
||||
|
||||
if (
|
||||
account &&
|
||||
activeProvider &&
|
||||
typeof activeProvider.getSigner === 'function'
|
||||
) {
|
||||
signer = provider.getSigner();
|
||||
if (account && provider && typeof provider.getSigner === 'function') {
|
||||
signer = provider.getSigner(account);
|
||||
}
|
||||
|
||||
const tokenVestingAddress =
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
import {
|
||||
PARTY_NOT_FOUND,
|
||||
filterAcceptableGraphqlErrors,
|
||||
isPartyNotFoundError,
|
||||
} from './party';
|
||||
import type { GraphQLError } from 'graphql';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param message
|
||||
* @returns GraphQLError
|
||||
*/
|
||||
function createMockApolloErrors(message: string): GraphQLError {
|
||||
return {
|
||||
message,
|
||||
extensions: {
|
||||
code: message.toUpperCase().replace(/ /g, '_'),
|
||||
},
|
||||
locations: [],
|
||||
originalError: new Error(message),
|
||||
path: [],
|
||||
nodes: [],
|
||||
positions: [1],
|
||||
name: message,
|
||||
source: {
|
||||
body: message,
|
||||
name: message,
|
||||
locationOffset: {
|
||||
line: 1,
|
||||
column: 1,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
describe('filterAcceptableGraphqlErrors', () => {
|
||||
it('should return undefined if the error is a party not found error', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [createMockApolloErrors('failed to get party for ID')],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the error if it is not a party not found error', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [createMockApolloErrors('Some other error')],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return the error if there are multiple errors', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [
|
||||
createMockApolloErrors('failed to get party for ID'),
|
||||
createMockApolloErrors('Some other error'),
|
||||
],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return the error if there are no errors', () => {
|
||||
const error: Partial<ApolloError> = {
|
||||
graphQLErrors: [],
|
||||
};
|
||||
|
||||
const result = filterAcceptableGraphqlErrors(error as ApolloError);
|
||||
|
||||
expect(result).toEqual(error);
|
||||
});
|
||||
|
||||
it('should return undefined if the error is undefined', () => {
|
||||
const result = filterAcceptableGraphqlErrors(undefined);
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPartyNotFoundError', () => {
|
||||
it('should return true if the error message includes PARTY_NOT_FOUND', () => {
|
||||
const error = { message: 'failed to get party for ID' };
|
||||
|
||||
const result = isPartyNotFoundError(error);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the error message does not include PARTY_NOT_FOUND', () => {
|
||||
const error = { message: 'Some other error' };
|
||||
|
||||
const result = isPartyNotFoundError(error);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
// Will trip if the error message changes, which should not be a problem, but there
|
||||
// might be logic that depends on it
|
||||
it('expects party not found error to remain consistent', () => {
|
||||
const error = 'failed to get party for ID';
|
||||
|
||||
expect(PARTY_NOT_FOUND).toStrictEqual(error);
|
||||
});
|
||||
});
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { ApolloError } from '@apollo/client';
|
||||
|
||||
export const PARTY_NOT_FOUND = 'failed to get party for ID';
|
||||
|
||||
export const isPartyNotFoundError = (error: { message: string }) => {
|
||||
@@ -6,3 +8,23 @@ export const isPartyNotFoundError = (error: { message: string }) => {
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* If a party has no accounts or data, then this GraphQL query believes it does not exist
|
||||
* Not having any rewards is a valid state, so in some cases we can filter this error out.
|
||||
*
|
||||
* @param error ApolloError | undefined
|
||||
* @returns ApolloError | undefined
|
||||
*/
|
||||
export function filterAcceptableGraphqlErrors(
|
||||
error?: ApolloError
|
||||
): ApolloError | undefined {
|
||||
// Currently the only error we expect is when a party has no accounts
|
||||
if (error && error.graphQLErrors.length === 1) {
|
||||
if (isPartyNotFoundError(error.graphQLErrors[0])) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
@@ -13,13 +14,17 @@ export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = FLAGS.METAMASK_SNAPS
|
||||
? new SnapConnector(DEFAULT_SNAP_ID)
|
||||
: undefined;
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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, FLAGS } from '@vegaprotocol/environment';
|
||||
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
@@ -175,6 +175,7 @@ export const ValidatorDetailsLink = ({
|
||||
};
|
||||
|
||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -186,9 +187,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+3
@@ -7,8 +7,10 @@ import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
|
||||
export const ProposalAssetDetails = ({
|
||||
asset,
|
||||
originalAsset,
|
||||
}: {
|
||||
asset: AssetFieldsFragment;
|
||||
originalAsset?: AssetFieldsFragment;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showAssetDetails, setShowAssetDetails] = useState(false);
|
||||
@@ -27,6 +29,7 @@ export const ProposalAssetDetails = ({
|
||||
<div className="mb-10 pb-4">
|
||||
<AssetDetailsTable
|
||||
asset={asset}
|
||||
originalAsset={originalAsset}
|
||||
omitRows={[
|
||||
AssetDetail.STATUS,
|
||||
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
|
||||
|
||||
+3
-4
@@ -19,7 +19,7 @@ import {
|
||||
mockWalletContext,
|
||||
createUserVoteQueryMock,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
@@ -62,8 +62,7 @@ describe('Proposal header', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('Renders New market proposal', () => {
|
||||
const mockedFlags = jest.mocked(FLAGS);
|
||||
mockedFlags.SUCCESSOR_MARKETS = true;
|
||||
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
@@ -125,7 +124,7 @@ describe('Proposal header', () => {
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Market change: MarketId'
|
||||
'Update to market ID: MarketId'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
+50
-10
@@ -1,5 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
Lozenge,
|
||||
Tooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -12,7 +18,12 @@ import {
|
||||
useNewTransferProposalDetails,
|
||||
useSuccessorMarketProposalDetails,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import {
|
||||
CONSOLE_MARKET_PAGE,
|
||||
DApp,
|
||||
useFeatureFlags,
|
||||
useLinks,
|
||||
} from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
@@ -28,9 +39,12 @@ export const ProposalHeader = ({
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
const consoleLink = useLinks(DApp.Console);
|
||||
|
||||
let details: ReactNode;
|
||||
let proposalType = '';
|
||||
let fallbackTitle = '';
|
||||
@@ -54,13 +68,14 @@ export const ProposalHeader = ({
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType =
|
||||
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
|
||||
featureFlags.PRODUCT_PERPETUALS &&
|
||||
change?.instrument?.product?.__typename
|
||||
? `NewMarket${change?.instrument?.product?.__typename}`
|
||||
: 'NewMarket';
|
||||
fallbackTitle = t('NewMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<SuccessorCode proposalId={proposal?.id} />
|
||||
)}
|
||||
<span>
|
||||
@@ -82,13 +97,13 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateMarketState': {
|
||||
proposalType =
|
||||
FLAGS.UPDATE_MARKET_STATE && change?.updateType
|
||||
featureFlags.UPDATE_MARKET_STATE && change?.updateType
|
||||
? t(change.updateType)
|
||||
: 'UpdateMarketState';
|
||||
fallbackTitle = t('UpdateMarketStateProposal');
|
||||
details = (
|
||||
<span>
|
||||
{FLAGS.UPDATE_MARKET_STATE &&
|
||||
{featureFlags.UPDATE_MARKET_STATE &&
|
||||
change?.market?.id &&
|
||||
change.updateType ? (
|
||||
<>
|
||||
@@ -104,8 +119,33 @@ export const ProposalHeader = ({
|
||||
fallbackTitle = t('UpdateMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
<span>{t('MarketChange')}:</span>{' '}
|
||||
<span>{truncateMiddle(change.marketId)}</span>
|
||||
<span>{t('UpdateToMarket')}:</span>{' '}
|
||||
<span className="inline-flex items-start gap-2">
|
||||
<span className="break-all">{change.marketId} </span>
|
||||
<span className="inline-flex items-end gap-0">
|
||||
<CopyWithTooltip
|
||||
text={change.marketId}
|
||||
description={t('copyToClipboard')}
|
||||
>
|
||||
<button className="inline-block px-1">
|
||||
<VegaIcon size={20} name={VegaIconNames.COPY} />
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
<Tooltip description={t('OpenInConsole')} align="center">
|
||||
<button
|
||||
className="inline-block px-1"
|
||||
onClick={() => {
|
||||
const marketPageLink = consoleLink(
|
||||
CONSOLE_MARKET_PAGE.replace(':marketId', change.marketId)
|
||||
);
|
||||
window.open(marketPageLink, '_blank');
|
||||
}}
|
||||
>
|
||||
<VegaIcon size={20} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
break;
|
||||
@@ -177,14 +217,14 @@ export const ProposalHeader = ({
|
||||
case 'NewTransfer':
|
||||
proposalType = 'NewTransfer';
|
||||
fallbackTitle = t('NewTransferProposal');
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||
<NewTransferSummary proposalId={proposal?.id} />
|
||||
) : null;
|
||||
break;
|
||||
case 'CancelTransfer':
|
||||
proposalType = 'CancelTransfer';
|
||||
fallbackTitle = t('CancelTransferProposal');
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
details = featureFlags.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;
|
||||
const stakingTiers = proposal?.terms?.change?.stakingTiers;
|
||||
const benefitTiers = proposal?.terms?.change?.benefitTiers.slice();
|
||||
const stakingTiers = proposal?.terms?.change?.stakingTiers.slice();
|
||||
const windowLength = proposal?.terms?.change?.windowLength;
|
||||
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
ProposalCancelTransferDetails,
|
||||
ProposalTransferDetails,
|
||||
} from '../proposal-transfer';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||
|
||||
export interface ProposalProps {
|
||||
@@ -53,6 +53,7 @@ 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);
|
||||
@@ -65,10 +66,13 @@ 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') {
|
||||
@@ -129,7 +133,7 @@ export const Proposal = ({
|
||||
}
|
||||
|
||||
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
|
||||
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
|
||||
const governanceTransferDetails = featureFlags.GOVERNANCE_TRANSFERS && (
|
||||
<>
|
||||
{proposal.terms.change.__typename === 'NewTransfer' && (
|
||||
/** Governance New Transfer Details */
|
||||
@@ -228,7 +232,7 @@ export const Proposal = ({
|
||||
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||
asset && (
|
||||
<div className="mb-4">
|
||||
<ProposalAssetDetails asset={asset} />
|
||||
<ProposalAssetDetails asset={asset} originalAsset={originalAsset} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
+3
-2
@@ -1,7 +1,7 @@
|
||||
import {
|
||||
getProposalDialogIcon,
|
||||
getProposalDialogIntent,
|
||||
getProposalDialogTitle,
|
||||
useGetProposalDialogTitle,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import type { DialogProps } from '@vegaprotocol/proposals';
|
||||
@@ -15,6 +15,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
finalizedProposal,
|
||||
TransactionDialog,
|
||||
}: ProposalFormTransactionDialogProps) => {
|
||||
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
||||
// Render a custom complete UI if the proposal was rejected otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const completeContent = finalizedProposal?.rejectionReason ? (
|
||||
@@ -24,7 +25,7 @@ export const ProposalFormTransactionDialog = ({
|
||||
return (
|
||||
<div data-testid="proposal-transaction-dialog">
|
||||
<TransactionDialog
|
||||
title={getProposalDialogTitle(finalizedProposal?.state)}
|
||||
title={title}
|
||||
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
||||
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
||||
content={{
|
||||
|
||||
+22
-3
@@ -8,7 +8,7 @@ import {
|
||||
networkParamsQueryMock,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { VoteBreakdown } from './vote-breakdown';
|
||||
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import {
|
||||
@@ -281,8 +281,8 @@ describe('VoteBreakdown', () => {
|
||||
});
|
||||
|
||||
it('Progress bar displays status - LP majority', () => {
|
||||
const yesVotesLP = 800;
|
||||
const noVotesLP = 200;
|
||||
const yesVotesLP = 0.8;
|
||||
const noVotesLP = 0.2;
|
||||
const expectedProgress = (yesVotesLP / (yesVotesLP + noVotesLP)) * 100; // 80%
|
||||
|
||||
renderComponent(
|
||||
@@ -346,3 +346,22 @@ describe('VoteBreakdown', () => {
|
||||
expect(style.width).toBe(`${expectedProgress}%`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CompactVotes', () => {
|
||||
it.each([
|
||||
[0, '0'],
|
||||
[1, '1'],
|
||||
[12, '12'],
|
||||
[123, '123'],
|
||||
[1234, '1.2K'],
|
||||
[12345, '12.3K'],
|
||||
[123456, '123.5K'],
|
||||
[1234567, '1.2M'],
|
||||
[12345678, '12.3M'],
|
||||
[123456789, '123.5M'],
|
||||
[1234567890, '1.2B'],
|
||||
])('compacts %s to %s', (input, output) => {
|
||||
const { getByTestId } = render(<CompactVotes number={BigNumber(input)} />);
|
||||
expect(getByTestId('compact-number').textContent).toBe(output);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,11 +3,21 @@ import BigNumber from 'bignumber.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
<CompactNumber
|
||||
number={number}
|
||||
decimals={number.isGreaterThan(1000) ? 1 : 0}
|
||||
compactAbove={1000}
|
||||
compactDisplay="short"
|
||||
/>
|
||||
);
|
||||
|
||||
interface VoteBreakdownProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
@@ -95,8 +105,6 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
yesLPPercentage,
|
||||
yesTokens,
|
||||
noTokens,
|
||||
yesEquityLikeShareWeight,
|
||||
noEquityLikeShareWeight,
|
||||
totalEquityLikeShareWeight,
|
||||
requiredMajorityPercentage,
|
||||
requiredMajorityLPPercentage,
|
||||
@@ -125,6 +133,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
.multipliedBy(100),
|
||||
new BigNumber(100)
|
||||
);
|
||||
|
||||
const willPass = willPassByTokenVote || willPassByLPVote;
|
||||
const updateMarketVotePassMethod = willPassByTokenVote
|
||||
? t('byTokenVote')
|
||||
@@ -192,56 +201,24 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{t('liquidityProviderVotesFor')}:</span>
|
||||
<Tooltip
|
||||
description={formatNumber(
|
||||
yesEquityLikeShareWeight,
|
||||
defaultDP
|
||||
)}
|
||||
description={
|
||||
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
>
|
||||
<button>
|
||||
{yesEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
</button>
|
||||
<button>{yesLPPercentage.toFixed(1)}%</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
(
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
>
|
||||
<button>{yesLPPercentage.toFixed(0)}%</button>
|
||||
</Tooltip>
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
||||
<Tooltip
|
||||
description={formatNumber(
|
||||
noEquityLikeShareWeight,
|
||||
defaultDP
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
{noEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
(
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
>
|
||||
<button>{noLPPercentage.toFixed(0)}%</button>
|
||||
<button>{noLPPercentage.toFixed(1)}%</button>
|
||||
</Tooltip>
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -278,16 +255,8 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
defaultDP
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
{totalEquityLikeShareWeight
|
||||
.dividedBy(toBigNum(10 ** 6, 0))
|
||||
.toFixed(1)}
|
||||
M
|
||||
</button>
|
||||
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
||||
</Tooltip>
|
||||
<span>
|
||||
({totalEquityLikeShareWeight.toFixed(defaultDP)}%)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -321,7 +290,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('tokenVotesFor')}:</span>
|
||||
<Tooltip description={formatNumber(yesTokens, defaultDP)}>
|
||||
<button data-testid="num-votes-for">
|
||||
{yesTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={yesTokens} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -341,7 +310,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('tokenVotesAgainst')}:</span>
|
||||
<Tooltip description={formatNumber(noTokens, defaultDP)}>
|
||||
<button data-testid="num-votes-against">
|
||||
{noTokens.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={noTokens} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
@@ -384,7 +353,7 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<span>{t('totalTokensVoted')}:</span>
|
||||
<Tooltip description={formatNumber(totalTokensVoted, defaultDP)}>
|
||||
<button data-testid="total-voted">
|
||||
{totalTokensVoted.dividedBy(toBigNum(10 ** 6, 0)).toFixed(1)}M
|
||||
<CompactVotes number={totalTokensVoted} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span data-testid="total-voted-percentage">
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { filterAcceptableGraphqlErrors } from '../../../../lib/party';
|
||||
|
||||
interface VoteButtonsContainerProps {
|
||||
voteState: VoteState | null;
|
||||
@@ -42,8 +43,10 @@ export const VoteButtonsContainer = (props: VoteButtonsContainerProps) => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<AsyncRenderer loading={loading} error={filteredErrors} data={data}>
|
||||
<VoteButtons
|
||||
{...props}
|
||||
currentStakeAvailable={toBigNum(
|
||||
|
||||
@@ -54,8 +54,8 @@ describe('use-vote-information', () => {
|
||||
it('returns all required vote information', () => {
|
||||
const yesVotes = 40;
|
||||
const noVotes = 60;
|
||||
const yesEquityLikeShareWeight = '30';
|
||||
const noEquityLikeShareWeight = '70';
|
||||
const yesEquityLikeShareWeight = '0.30';
|
||||
const noEquityLikeShareWeight = '0.70';
|
||||
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
|
||||
const fixedTokenValue = 1000000000000000000;
|
||||
|
||||
@@ -195,10 +195,10 @@ describe('use-vote-information', () => {
|
||||
});
|
||||
|
||||
it('correctly shows whether an update market proposal will pass by token or LP vote - both failing', () => {
|
||||
const yesVotes = 20;
|
||||
const noVotes = 70;
|
||||
const yesEquityLikeShareWeight = '30';
|
||||
const noEquityLikeShareWeight = '60';
|
||||
const yesVotes = 0.2;
|
||||
const noVotes = 0.7;
|
||||
const yesEquityLikeShareWeight = '0.30';
|
||||
const noEquityLikeShareWeight = '0.60';
|
||||
const fixedTokenValue = 1000000000000000000;
|
||||
|
||||
const proposal = generateProposal({
|
||||
|
||||
@@ -61,7 +61,7 @@ export const useVoteInformation = ({
|
||||
const noEquityLikeShareWeight = !proposal?.votes.no
|
||||
.totalEquityLikeShareWeight
|
||||
? new BigNumber(0)
|
||||
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight);
|
||||
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight).times(100);
|
||||
|
||||
const yesTokens = new BigNumber(
|
||||
addDecimal(proposal?.votes.yes.totalTokens ?? 0, decimals)
|
||||
@@ -70,7 +70,7 @@ export const useVoteInformation = ({
|
||||
const yesEquityLikeShareWeight = !proposal?.votes.yes
|
||||
.totalEquityLikeShareWeight
|
||||
? new BigNumber(0)
|
||||
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight);
|
||||
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight).times(100);
|
||||
|
||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||
|
||||
@@ -81,12 +81,7 @@ export const useVoteInformation = ({
|
||||
const yesPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
|
||||
const yesLPPercentage = totalEquityLikeShareWeight.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesEquityLikeShareWeight
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalEquityLikeShareWeight);
|
||||
const yesLPPercentage = yesEquityLikeShareWeight;
|
||||
|
||||
const noPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
@@ -103,9 +98,7 @@ export const useVoteInformation = ({
|
||||
);
|
||||
|
||||
const participationLPMet = requiredParticipationLP
|
||||
? totalEquityLikeShareWeight.isGreaterThan(
|
||||
totalSupply.multipliedBy(requiredParticipationLP)
|
||||
)
|
||||
? totalEquityLikeShareWeight.isGreaterThan(requiredParticipationLP)
|
||||
: false;
|
||||
|
||||
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
||||
@@ -120,9 +113,7 @@ export const useVoteInformation = ({
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalSupply);
|
||||
|
||||
const totalLPTokensPercentage = totalEquityLikeShareWeight
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalSupply);
|
||||
const totalLPTokensPercentage = totalEquityLikeShareWeight;
|
||||
|
||||
const willPassByTokenVote =
|
||||
participationMet &&
|
||||
|
||||
@@ -205,7 +205,6 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
@@ -213,7 +212,6 @@ query Proposal(
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
quadraticSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
@@ -366,7 +364,6 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -15,10 +15,11 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const [
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
setMostRecentlyEnactedAssociatedMarketProposal,
|
||||
@@ -59,9 +60,9 @@ export const ProposalContainer = () => {
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
proposalId: params.proposalId || '',
|
||||
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductField: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!featureFlags.REFERRALS,
|
||||
},
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
@@ -120,7 +121,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: marketData?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
skip: !featureFlags.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -133,7 +134,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: parentMarketId?.market?.parentMarketID || '',
|
||||
skip:
|
||||
!FLAGS.SUCCESSOR_MARKETS ||
|
||||
!featureFlags.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 { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
|
||||
return flow([
|
||||
@@ -43,15 +43,16 @@ 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: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ export const ProtocolUpgradeProposalContainer = () => {
|
||||
time={
|
||||
pending && time ? (
|
||||
convertToCountdownString(time, '0:00:00:00')
|
||||
) : blockInfo?.result ? (
|
||||
) : blockInfo && 'result' in blockInfo && blockInfo?.result ? (
|
||||
<span title={blockInfo.result.block.header.time}>
|
||||
{formatDateWithLocalTimezone(
|
||||
new Date(blockInfo.result.block.header.time)
|
||||
|
||||
@@ -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 { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
@@ -33,15 +33,16 @@ 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: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -116,7 +116,8 @@ export const generateYesVotes = (
|
||||
fixedTokenValue?: number,
|
||||
totalEquityLikeShareWeight?: string
|
||||
): Votes => {
|
||||
const votes = Array.from(Array(numberOfVotes)).map(() => {
|
||||
const votes = [];
|
||||
for (let i = 0; i < numberOfVotes; i++) {
|
||||
const vote: Vote = {
|
||||
__typename: 'Vote',
|
||||
value: Schema.VoteValue.VALUE_YES,
|
||||
@@ -152,8 +153,9 @@ export const generateYesVotes = (
|
||||
datetime: faker.date.past().toISOString(),
|
||||
};
|
||||
|
||||
return vote;
|
||||
});
|
||||
votes.push(vote);
|
||||
}
|
||||
|
||||
return {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: votes.length.toString(),
|
||||
@@ -172,7 +174,8 @@ export const generateNoVotes = (
|
||||
fixedTokenValue?: number,
|
||||
totalEquityLikeShareWeight?: string
|
||||
): Votes => {
|
||||
const votes = Array.from(Array(numberOfVotes)).map(() => {
|
||||
const votes = [];
|
||||
for (let i = 0; i < numberOfVotes; i++) {
|
||||
const vote: Vote = {
|
||||
__typename: 'Vote',
|
||||
value: Schema.VoteValue.VALUE_NO,
|
||||
@@ -207,8 +210,9 @@ export const generateNoVotes = (
|
||||
},
|
||||
datetime: faker.date.past().toISOString(),
|
||||
};
|
||||
return vote;
|
||||
});
|
||||
votes.push(vote);
|
||||
}
|
||||
|
||||
return {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: votes.length.toString(),
|
||||
|
||||
+23
-13
@@ -10,6 +10,7 @@ import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
import { useNetworkParam } from '@vegaprotocol/network-parameters';
|
||||
import { filterAcceptableGraphqlErrors } from '../../../lib/party';
|
||||
|
||||
const EPOCHS_PAGE_SIZE = 10;
|
||||
|
||||
@@ -99,17 +100,24 @@ export const EpochIndividualRewards = ({
|
||||
prevEpochIdRef.current = epochId;
|
||||
}, [epochId, refetchData]);
|
||||
|
||||
// Workarounds for the error handling of AsyncRenderer
|
||||
const filteredErrors = filterAcceptableGraphqlErrors(error);
|
||||
const filteredData = data || [];
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
error={filteredErrors}
|
||||
data={filteredData}
|
||||
render={() => (
|
||||
<div>
|
||||
<p data-testid="connected-vega-key" className="mb-10">
|
||||
{t('Connected Vega key')}:{' '}
|
||||
<span className="text-white">{pubKey}</span>
|
||||
</p>
|
||||
{epochIndividualRewardSummaries.length === 0 && (
|
||||
<p>{t('No rewards for key')}</p>
|
||||
)}
|
||||
{epochIndividualRewardSummaries.map(
|
||||
(epochIndividualRewardSummary) => (
|
||||
<EpochIndividualRewardsTable
|
||||
@@ -118,17 +126,19 @@ export const EpochIndividualRewards = ({
|
||||
/>
|
||||
)
|
||||
)}
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
{epochIndividualRewardSummaries.length > 0 && (
|
||||
<Pagination
|
||||
isLoading={loading}
|
||||
hasPrevPage={page > 1}
|
||||
hasNextPage={page < totalPages}
|
||||
onBack={() => refetchData(page - 1)}
|
||||
onNext={() => refetchData(page + 1)}
|
||||
onFirst={() => refetchData(1)}
|
||||
onLast={() => refetchData(totalPages)}
|
||||
>
|
||||
{t('Page')} {page}
|
||||
</Pagination>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
+2
-2
@@ -16,7 +16,7 @@ import type { ValidatorsView } from './validator-tables';
|
||||
const nodeFactory = (
|
||||
overrides?: PartialDeep<NodesFragmentFragment>
|
||||
): NodesFragmentFragment => {
|
||||
const defaultNode = {
|
||||
const defaultNode: NodesFragmentFragment = {
|
||||
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
|
||||
name: 'high',
|
||||
avatarUrl: 'https://upload.wikimedia.org/wikipedia/en/2/25/Marvin-TV-3.jpg',
|
||||
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
|
||||
|
||||
expect(
|
||||
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
||||
).toHaveTextContent('10.07%');
|
||||
).toHaveTextContent('13.16%');
|
||||
|
||||
expect(
|
||||
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
||||
|
||||
+11
-2
@@ -185,6 +185,15 @@ export const ConsensusValidatorsTable = ({
|
||||
const { rawValidatorScore: previousEpochValidatorScore } =
|
||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||
|
||||
const overstakingPenalty = calculateOverallPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
const totalPenalty = calculateOverstakedPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
@@ -213,11 +222,11 @@ export const ConsensusValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
overstakingPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
totalPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
+11
-2
@@ -124,6 +124,15 @@ export const StandbyPendingValidatorsTable = ({
|
||||
}
|
||||
}
|
||||
|
||||
const overstakingPenalty = calculateOverallPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
const totalPenalty = calculateOverstakedPenalty(
|
||||
id,
|
||||
allNodesInPreviousEpoch
|
||||
);
|
||||
|
||||
return {
|
||||
id,
|
||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||
@@ -154,11 +163,11 @@ export const StandbyPendingValidatorsTable = ({
|
||||
2
|
||||
),
|
||||
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
|
||||
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
|
||||
overstakingPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
|
||||
calculateOverallPenalty(id, allNodesInPreviousEpoch),
|
||||
totalPenalty,
|
||||
2
|
||||
),
|
||||
[ValidatorFields.PENDING_STAKE]: pendingStake,
|
||||
|
||||
@@ -266,7 +266,9 @@ export const ValidatorTable = ({
|
||||
|
||||
<Tooltip description={t('OverstakedPenaltyDescription')}>
|
||||
<span data-testid="overstaking-penalty">
|
||||
{formatNumberPercentage(penalties.overstaked, 2)}
|
||||
{penalties.overstaked
|
||||
? formatNumberPercentage(penalties.overstaked, 2)
|
||||
: '-'}
|
||||
</span>
|
||||
</Tooltip>
|
||||
</KeyValueTableRow>
|
||||
@@ -285,7 +287,9 @@ export const ValidatorTable = ({
|
||||
</span>
|
||||
<span data-testid="total-penalties">
|
||||
<strong>
|
||||
{formatNumberPercentage(penalties.overall, 2)}
|
||||
{penalties.overall
|
||||
? formatNumberPercentage(penalties.overall, 2)
|
||||
: '-'}
|
||||
</strong>
|
||||
</span>
|
||||
</KeyValueTableRow>
|
||||
|
||||
@@ -3,11 +3,11 @@ import {
|
||||
getLastEpochScoreAndPerformance,
|
||||
getNormalisedVotingPower,
|
||||
getUnnormalisedVotingPower,
|
||||
getOverstakingPenalty,
|
||||
getFormattedPerformanceScore,
|
||||
getPerformancePenalty,
|
||||
getTotalPenalties,
|
||||
getStakePercentage,
|
||||
calculateOverallPenalty,
|
||||
calculateOverstakedPenalty,
|
||||
} from './shared';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
@@ -106,38 +106,6 @@ describe('getUnnormalisedVotingPower', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getOverstakingPenalty', () => {
|
||||
it('returns "0%" when both arguments are null or undefined', () => {
|
||||
expect(getOverstakingPenalty(null, null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, null)).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns "0%" when one argument is null or undefined', () => {
|
||||
expect(getOverstakingPenalty('10', null)).toBe('0%');
|
||||
expect(getOverstakingPenalty(null, '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', undefined)).toBe('0%');
|
||||
expect(getOverstakingPenalty(undefined, '20')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns "0%" when validatorScore or stakeScore is zero', () => {
|
||||
expect(getOverstakingPenalty('0', '20')).toBe('0%');
|
||||
expect(getOverstakingPenalty('10', '0')).toBe('0%');
|
||||
});
|
||||
|
||||
it('returns the correct overstaking penalty', () => {
|
||||
expect(getOverstakingPenalty('0.18', '0.2')).toBe('10.00%');
|
||||
expect(getOverstakingPenalty('0.2', '0.2')).toBe('0.00%');
|
||||
expect(getOverstakingPenalty('0.04', '0.2')).toBe('80.00%');
|
||||
});
|
||||
|
||||
it('handles string numbers with decimals', () => {
|
||||
expect(getOverstakingPenalty('7.5', '15')).toBe('50.00%');
|
||||
expect(getOverstakingPenalty('12.5', '25')).toBe('50.00%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFormattedPerformanceScore', () => {
|
||||
it('should return the formatted performance score', () => {
|
||||
expect(getFormattedPerformanceScore('0.25')).toEqual(new BigNumber(0.25));
|
||||
@@ -152,17 +120,6 @@ describe('getPerformancePenalty', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalPenalties', () => {
|
||||
it('should return the total penalties', () => {
|
||||
expect(getTotalPenalties('0.25', '1', '5000', '10000')).toEqual('50.00%');
|
||||
expect(getTotalPenalties('0.25', '0.5', '5000', '10000')).toEqual('75.00%');
|
||||
});
|
||||
|
||||
it('should return 0 if the total penalties is negative', () => {
|
||||
expect(getTotalPenalties('0.25', '0.5', '1000', '10000')).toEqual('0.00%');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStakePercentage', () => {
|
||||
it('should return the stake percentage', () => {
|
||||
expect(
|
||||
@@ -182,3 +139,107 @@ describe('getStakePercentage', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateOverallPenalty', () => {
|
||||
it('returns null if rewardScore is null', () => {
|
||||
const res = calculateOverallPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
rewardScore: null,
|
||||
stakedTotal: '',
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if rewardScore.rawValidatorScore is null (should not happen)', () => {
|
||||
const res = calculateOverallPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
stakedTotal: '',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
multisigScore: '',
|
||||
validatorScore: null as unknown as string,
|
||||
normalisedScore: '',
|
||||
validatorStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('calculateOverstakedPenalty', () => {
|
||||
it('returns null if rewardScore is null', () => {
|
||||
const res = calculateOverstakedPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
rewardScore: null,
|
||||
stakedTotal: '',
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null if rewardScore.rawValidatorScore is null (should not happen)', () => {
|
||||
const res = calculateOverstakedPenalty('1', [
|
||||
{
|
||||
id: '1',
|
||||
stakedTotal: '',
|
||||
rewardScore: {
|
||||
rawValidatorScore: null as unknown as string,
|
||||
performanceScore: '0.75',
|
||||
multisigScore: '',
|
||||
validatorScore: '',
|
||||
normalisedScore: '',
|
||||
validatorStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
},
|
||||
rankingScore: {
|
||||
stakeScore: '0.25',
|
||||
performanceScore: '0.75',
|
||||
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
previousStatus:
|
||||
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
|
||||
rankingScore: '',
|
||||
votingPower: '',
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
expect(res).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import type { LastArrayElement } from 'type-fest';
|
||||
import isNull from 'lodash/isNull';
|
||||
|
||||
type Node = NonNullable<
|
||||
LastArrayElement<
|
||||
@@ -21,7 +22,10 @@ type Node = NonNullable<
|
||||
* @returns Theoretical stake score for given node based on the staked total
|
||||
* of all node of the same type (status)
|
||||
*/
|
||||
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||
const calculateTheoreticalStakeScore = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
if (!node) {
|
||||
return new BigNumber(0);
|
||||
@@ -42,14 +46,25 @@ const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
export const calculateOverallPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (!node || tts.isZero()) {
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
!node.rewardScore ||
|
||||
(node.rewardScore && isNull(node.rewardScore.validatorScore))
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
|
||||
.minus(new BigNumber(node.rewardScore.validatorScore).dividedBy(tts))
|
||||
.times(100);
|
||||
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
|
||||
};
|
||||
@@ -60,10 +75,21 @@ export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
|
||||
* @returns %
|
||||
*/
|
||||
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
export const calculateOverstakedPenalty = (
|
||||
nodeId: string,
|
||||
nodes: Node[]
|
||||
): BigNumber | null => {
|
||||
const node = nodes.find((n) => n.id === nodeId);
|
||||
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
|
||||
if (!node || tts.isZero()) {
|
||||
if (
|
||||
!node ||
|
||||
isNull(tts) ||
|
||||
isNull(node.rewardScore) ||
|
||||
(node.rewardScore && node.rewardScore.rawValidatorScore === null)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
if (tts.isZero()) {
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const penalty = new BigNumber(1)
|
||||
@@ -78,7 +104,9 @@ export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
|
||||
* Calculates performance penalty based on the given performance score.
|
||||
* @returns %
|
||||
*/
|
||||
export const calculatesPerformancePenalty = (performanceScore: string) => {
|
||||
export const calculatesPerformancePenalty = (
|
||||
performanceScore: string
|
||||
): BigNumber => {
|
||||
const penalty = new BigNumber(1)
|
||||
.minus(new BigNumber(performanceScore))
|
||||
.times(100);
|
||||
@@ -123,60 +151,6 @@ export const getPerformancePenalty = (performanceScore?: string) =>
|
||||
2
|
||||
);
|
||||
|
||||
export const getOverstakingPenalty = (
|
||||
validatorScore: string | null | undefined,
|
||||
stakeScore: string | null | undefined
|
||||
) => {
|
||||
if (!validatorScore || !stakeScore) {
|
||||
return '0%';
|
||||
}
|
||||
|
||||
// avoid division by zero
|
||||
if (
|
||||
new BigNumber(validatorScore).isZero() ||
|
||||
new BigNumber(stakeScore).isZero()
|
||||
) {
|
||||
return '0%';
|
||||
}
|
||||
|
||||
return formatNumberPercentage(
|
||||
BigNumber.max(
|
||||
new BigNumber(1)
|
||||
.minus(
|
||||
new BigNumber(validatorScore).dividedBy(new BigNumber(stakeScore))
|
||||
)
|
||||
.times(100),
|
||||
new BigNumber(0)
|
||||
),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
export const getTotalPenalties = (
|
||||
rawValidatorScore: string | null | undefined,
|
||||
performanceScore: string | undefined,
|
||||
stakedOnNode: string,
|
||||
totalStake: string
|
||||
) => {
|
||||
const calc =
|
||||
rawValidatorScore &&
|
||||
performanceScore &&
|
||||
new BigNumber(totalStake).isGreaterThan(0)
|
||||
? new BigNumber(1).minus(
|
||||
new BigNumber(performanceScore)
|
||||
.times(new BigNumber(rawValidatorScore))
|
||||
.dividedBy(
|
||||
new BigNumber(stakedOnNode).dividedBy(new BigNumber(totalStake))
|
||||
)
|
||||
)
|
||||
: new BigNumber(0);
|
||||
|
||||
return formatNumberPercentage(
|
||||
calc.isPositive() ? calc.times(100) : new BigNumber(0),
|
||||
2
|
||||
);
|
||||
};
|
||||
|
||||
export const getStakePercentage = (total: BigNumber, stakedOnNode: BigNumber) =>
|
||||
total.isEqualTo(0) || stakedOnNode.isEqualTo(0)
|
||||
? '0%'
|
||||
|
||||
@@ -104,6 +104,10 @@
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.react-markdown-container a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta,
|
||||
.jsondiffpatch-delta pre {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
|
||||
@@ -1,14 +1,50 @@
|
||||
import { ToastsContainer, useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Intent,
|
||||
ToastsContainer,
|
||||
TradingButton,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
useToasts,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useEthereumTransactionToasts,
|
||||
useEthereumWithdrawApprovalsToasts,
|
||||
useVegaTransactionToasts,
|
||||
useWalletDisconnectToastActions,
|
||||
useWalletDisconnectedToasts,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<TradingButton
|
||||
data-testid="connect-vega-wallet"
|
||||
onClick={() => {
|
||||
hideToast();
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
size="small"
|
||||
intent={Intent.Danger}
|
||||
icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />}
|
||||
>
|
||||
<span className="whitespace-nowrap uppercase">{t('Connect')}</span>
|
||||
</TradingButton>
|
||||
</p>
|
||||
);
|
||||
};
|
||||
|
||||
export const ToastsManager = () => {
|
||||
useVegaTransactionToasts();
|
||||
useEthereumTransactionToasts();
|
||||
useEthereumWithdrawApprovalsToasts();
|
||||
useWalletDisconnectedToasts(<WalletDisconnectAdditionalContent />);
|
||||
|
||||
const toasts = useToasts((store) => store.toasts);
|
||||
return <ToastsContainer order="desc" toasts={toasts} />;
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
const marketInfoBtn = 'Info';
|
||||
const marketInfoSubtitle = 'accordion-title';
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
const marketExpiry = 'market-expiry';
|
||||
const marketPrice = 'market-price';
|
||||
const marketChange = 'market-change';
|
||||
const marketVolume = 'market-volume';
|
||||
const marketMode = 'market-trading-mode';
|
||||
const marketSettlement = 'market-settlement-asset';
|
||||
const percentageValue = 'price-change-percentage';
|
||||
const priceChangeValue = 'price-change';
|
||||
const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
const marketListContent = 'popover-content';
|
||||
|
||||
describe(
|
||||
'Console - market info - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
});
|
||||
const titles = ['Market data', 'Market specification', 'Market governance'];
|
||||
const subtitles = [
|
||||
'Current fees',
|
||||
'Market price',
|
||||
'Market volume',
|
||||
'Insurance pool',
|
||||
'Key details',
|
||||
'Instrument',
|
||||
'Settlement asset',
|
||||
'Metadata',
|
||||
'Risk model',
|
||||
'Risk parameters',
|
||||
'Risk factors',
|
||||
'Price monitoring bounds 1',
|
||||
'Liquidity monitoring parameters',
|
||||
'Liquidity',
|
||||
'Liquidity price range',
|
||||
'Oracle',
|
||||
'Proposal',
|
||||
];
|
||||
|
||||
it('market info titles are displayed', () => {
|
||||
cy.getByTestId('split-view-view')
|
||||
.find('.text-lg')
|
||||
.each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('market info subtitles are displayed', () => {
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
cy.contains('[data-testid="link"]', 'AAVEDAI.MF21').click();
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.getByTestId(marketInfoSubtitle).each((element, index) => {
|
||||
cy.wrap(element).should('have.text', subtitles[index]);
|
||||
});
|
||||
});
|
||||
|
||||
it('renders correctly liquidity in trading tab', () => {
|
||||
cy.getByTestId('Liquidity').click();
|
||||
cy.contains('Loading').should('not.exist');
|
||||
cy.contains('Something went wrong').should('not.exist');
|
||||
cy.contains('Application error').should('not.exist');
|
||||
cy.getByTestId('tab-liquidity').within(() => {
|
||||
cy.get('[col-id="partyId"]').eq(1).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe(
|
||||
'Console - market summary - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.visit('/');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
it('must display market name', () => {
|
||||
cy.getByTestId('popover-trigger').should('not.be.empty');
|
||||
});
|
||||
|
||||
it('must see market expiry', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market price', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market change', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market volume', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market mode', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe(
|
||||
'Console - markets table - live env',
|
||||
{ tags: '@live', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('renders markets correctly', () => {
|
||||
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
|
||||
cy.getByTestId('price').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('settlement-asset').should('not.be.empty');
|
||||
cy.getByTestId('price-change-percentage').should('not.be.empty');
|
||||
cy.getByTestId('price-change').should('not.be.empty');
|
||||
cy.getByTestId('sparkline-svg').should('be.visible');
|
||||
});
|
||||
|
||||
it('renders market list drop down', () => {
|
||||
openMarketDropDown();
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="price"]')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="trading-mode-col"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="taker-fee"]')
|
||||
.should('contain.text', '%');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-volume"]')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId(marketListContent)
|
||||
.find('[data-testid="market-name"]')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Able to select market from dropdown', () => {
|
||||
cy.getByTestId('popover-trigger')
|
||||
.invoke('text')
|
||||
.then((marketName) => {
|
||||
openMarketDropDown();
|
||||
cy.get('[data-testid^=market-link]').eq(1).click();
|
||||
cy.getByTestId('popover-trigger').should('not.be.equal', marketName);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function openMarketDropDown() {
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.getByTestId('link').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.getByTestId('popover-trigger').click();
|
||||
cy.contains('Loading market data...').should('not.exist');
|
||||
}
|
||||
@@ -1,311 +0,0 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const liquidityTab = 'Liquidity';
|
||||
const rowSelector =
|
||||
'[data-testid="tab-liquidity"] .ag-center-cols-container .ag-row';
|
||||
const rowSelectorLiquidityActive =
|
||||
'[data-testid="tab-active"] .ag-center-cols-container .ag-row';
|
||||
const rowSelectorLiquidityInactive =
|
||||
'[data-testid="tab-inactive"] .ag-center-cols-container .ag-row';
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
const itemValue = 'item-value';
|
||||
const itemHeader = 'item-header';
|
||||
const colCommitmentAmount = '[col-id="commitmentAmount"]';
|
||||
const colEquityLikeShare = '[col-id="feeShare.equityLikeShare"]';
|
||||
const colFee = '[col-id="fee"]';
|
||||
const colCommitmentAmount_1 = '[col-id="commitmentAmount_1"]';
|
||||
const colBalance = '[col-id="balance"]';
|
||||
const colStatus = '[col-id="status"]';
|
||||
const colCreatedAt = '[col-id="createdAt"] button';
|
||||
const colUpdatedAt = '[col-id="updatedAt"] button';
|
||||
|
||||
const headers = [
|
||||
'Party',
|
||||
'Status',
|
||||
'Commitment (tDAI)',
|
||||
'Obligation',
|
||||
'Fee',
|
||||
'Adjusted stake share',
|
||||
'Share',
|
||||
'Live supplied liquidity',
|
||||
'Fees accrued this epoch',
|
||||
'Live time on book',
|
||||
'Live liquidity quality score (%)',
|
||||
'Last time on the book',
|
||||
'Last fee penalty',
|
||||
'Last bond penalty',
|
||||
'Created',
|
||||
'Updated',
|
||||
];
|
||||
|
||||
describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockSubscription();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(liquidityTab).click();
|
||||
cy.wait('@LiquidityProvisions');
|
||||
});
|
||||
|
||||
it('can see table headers', () => {
|
||||
// 5002-LIQP-001
|
||||
cy.getByTestId('tab-liquidity').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders liquidity table correctly', () => {
|
||||
// 5002-LIQP-002
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="partyId"]')
|
||||
.should('have.text', '69464e…dc6f');
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colCommitmentAmount)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colEquityLikeShare)
|
||||
.should('have.text', '100.00%');
|
||||
|
||||
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colCommitmentAmount_1)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colBalance)
|
||||
.scrollIntoView()
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelector).first().find(colStatus).should('have.text', 'Active');
|
||||
|
||||
cy.get(rowSelector).first().find(colCreatedAt).should('not.be.empty');
|
||||
cy.get(rowSelector).first().find(colUpdatedAt).should('not.be.empty');
|
||||
});
|
||||
|
||||
it('liquidity status column should be sorted properly', () => {
|
||||
// 5002-LIQP-003
|
||||
const liquidityColDefault = ['Active', 'Pending'];
|
||||
const liquidityColAsc = ['Active', 'Pending'];
|
||||
const liquidityColDesc = ['Pending', 'Active'];
|
||||
checkSorting(
|
||||
'status',
|
||||
liquidityColDefault,
|
||||
liquidityColAsc,
|
||||
liquidityColDesc
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockSubscription();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.visit('/#/liquidity/market-0');
|
||||
cy.wait('@LiquidityProvisions');
|
||||
});
|
||||
|
||||
it('can see header title', () => {
|
||||
// 5002-LIQP-004
|
||||
// 5002-LIQP-005
|
||||
cy.getByTestId('header-title').should(
|
||||
'contain.text',
|
||||
'BTCUSD.MF21 liquidity provision'
|
||||
);
|
||||
});
|
||||
|
||||
it('can see target stake', () => {
|
||||
// 5002-LIQP-006
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('target-stake').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Target stake');
|
||||
cy.getByTestId(itemValue).should('have.text', '10.00 tDAI').realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
`The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.The market's liquidity requirement which is derived from the maximum open interest observed over a rolling time window.`
|
||||
);
|
||||
});
|
||||
|
||||
it('can see supplied stake', () => {
|
||||
// 5002-LIQP-007
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('supplied-stake').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Supplied stake');
|
||||
cy.getByTestId(itemValue).should('have.text', '0.01 tDAI').realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
'The current amount of liquidity supplied for this market.'
|
||||
);
|
||||
});
|
||||
|
||||
it('can see liquidity supplied', () => {
|
||||
// 5002-LIQP-008
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('liquidity-supplied').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
|
||||
cy.getByTestId('indicator').should('be.visible');
|
||||
cy.getByTestId(itemValue).should('have.text', ' 0.10%').realHover();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can see market id', () => {
|
||||
// 5002-LIQP-009
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('liquidity-market-id').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Market ID');
|
||||
cy.getByTestId(itemValue).should('have.text', 'market-0');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can see market id', () => {
|
||||
// 5002-LIQP-010
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('liquidity-learn-more').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Learn more');
|
||||
cy.getByTestId(itemValue).should('have.text', 'Providing liquidity');
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href')
|
||||
.and(
|
||||
'include',
|
||||
'https://docs.vega.xyz/testnet/concepts/liquidity/provision'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
it('can see table headers', () => {
|
||||
cy.getByTestId('tab-active').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('renders liquidity active table correctly', () => {
|
||||
// 5002-LIQP-011
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find('[col-id="partyId"]')
|
||||
.should('have.text', '69464e…dc6f');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colCommitmentAmount)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colEquityLikeShare)
|
||||
.should('have.text', '100.00%');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colFee)
|
||||
.should('have.text', '0.09%');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colCommitmentAmount_1)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colBalance)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colStatus)
|
||||
.should('have.text', 'Active');
|
||||
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colCreatedAt)
|
||||
.should('not.be.empty');
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colUpdatedAt)
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('renders liquidity inactive table correctly', () => {
|
||||
// 5002-LIQP-012
|
||||
cy.getByTestId('Inactive').click();
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find('[col-id="partyId"]')
|
||||
.should('have.text', 'cc464e…dc6f');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colCommitmentAmount)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colEquityLikeShare)
|
||||
.should('have.text', '100.00%');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colFee)
|
||||
.should('have.text', '0.40%');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colCommitmentAmount_1)
|
||||
.should('have.text', '4,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colBalance)
|
||||
.should('have.text', '2,000.00');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colStatus)
|
||||
.should('have.text', 'Pending');
|
||||
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colCreatedAt)
|
||||
.should('not.be.empty');
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
.find(colUpdatedAt)
|
||||
.should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { proposalListQuery, marketUpdateProposal } from '@vegaprotocol/mock';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
|
||||
describe('Market proposal notification', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'ProposalsList',
|
||||
proposalListQuery({
|
||||
proposalsConnection: {
|
||||
edges: [{ node: marketUpdateProposal }],
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
it('should display market proposal notification if proposal found', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('market-proposal-notification').should(
|
||||
'contain.text',
|
||||
'Changes have been proposed for this market'
|
||||
);
|
||||
cy.getByTestId('market-proposal-notification').within(() => {
|
||||
cy.getByTestId('external-link').should(
|
||||
'have.attr',
|
||||
'href',
|
||||
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/123`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,216 +0,0 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const expirtyTooltip = 'expiry-tooltip';
|
||||
const externalLink = 'external-link';
|
||||
const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
const link = 'link';
|
||||
const liquidityLink = 'view-liquidity-link';
|
||||
const liquiditySupplied = 'liquidity-supplied';
|
||||
const liquiditySuppliedTooltip = 'liquidity-supplied-tooltip';
|
||||
const marketChange = 'market-change';
|
||||
const marketExpiry = 'market-expiry';
|
||||
const marketMode = 'market-trading-mode';
|
||||
const marketName = 'header-title';
|
||||
const marketPrice = 'market-price';
|
||||
const marketSettlement = 'market-settlement-asset';
|
||||
const marketState = 'market-state';
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
const marketVolume = 'market-volume';
|
||||
const percentageValue = 'price-change-percentage';
|
||||
const priceChangeValue = 'price-change';
|
||||
const tradingModeTooltip = 'trading-mode-tooltip';
|
||||
|
||||
describe('Market trading page', () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
describe('Market summary', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-001
|
||||
// 7002-SORD-002
|
||||
it('must display market name', () => {
|
||||
// 6002-MDET-001
|
||||
cy.getByTestId(marketName).should('not.be.empty');
|
||||
});
|
||||
|
||||
it('must see market expiry', () => {
|
||||
// 6002-MDET-002
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market price', () => {
|
||||
// 6002-MDET-003
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Mark Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market change', () => {
|
||||
// 6002-MDET-004
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market volume', () => {
|
||||
// 6002-MDET-005
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market mode', () => {
|
||||
// 6002-MDET-006
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Trading mode');
|
||||
cy.getByTestId(itemValue).should(
|
||||
'have.text',
|
||||
'Monitoring auction - liquidity (target not met)'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market status', () => {
|
||||
// 6002-MDET-007
|
||||
// 7002-SORD-061
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketState).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Status');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
// 6002-MDET-008
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
it('must see market liquidity supplied', () => {
|
||||
// 6002-MDET-009
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(liquiditySupplied).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Market tooltips', { tags: '@smoke' }, () => {
|
||||
it('should see expiry tooltip', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemValue)
|
||||
.should('have.text', 'Not time-based')
|
||||
.realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(expirtyTooltip)
|
||||
.eq(0)
|
||||
.should(
|
||||
'contain.text',
|
||||
'This market expires when triggered by its oracle, not on a set date.'
|
||||
)
|
||||
.within(() => {
|
||||
cy.getByTestId(link)
|
||||
.should('have.attr', 'href')
|
||||
.and('include', Cypress.env('EXPLORER_URL'));
|
||||
});
|
||||
});
|
||||
|
||||
it('should see trading conditions tooltip', () => {
|
||||
const toolTipLabel = 'tooltip-label';
|
||||
const toolTipValue = 'tooltip-value';
|
||||
const auctionToolTipLabels = [
|
||||
'Auction start',
|
||||
'Est. auction end',
|
||||
'Target liquidity',
|
||||
'Current liquidity',
|
||||
'Est. uncrossing price',
|
||||
'Est. uncrossing vol',
|
||||
];
|
||||
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemValue)
|
||||
.should('contain.text', 'Monitoring auction')
|
||||
.and('contain.text', 'liquidity')
|
||||
.realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(tradingModeTooltip)
|
||||
.should(
|
||||
'contain.text',
|
||||
'This market is in auction until it reaches sufficient liquidity.'
|
||||
)
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.getByTestId(externalLink)
|
||||
.should('have.attr', 'href')
|
||||
.and('include', Cypress.env('TRADING_MODE_LINK'));
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
cy.getByTestId(toolTipLabel)
|
||||
.eq(i)
|
||||
.should('have.text', auctionToolTipLabels[i]);
|
||||
cy.getByTestId(toolTipValue).eq(i).should('not.be.empty');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should see liquidity supplied tooltip', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(liquiditySupplied).within(() => {
|
||||
cy.getByTestId(itemValue).realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(liquiditySuppliedTooltip)
|
||||
.should('contain.text', 'Supplied stake')
|
||||
.and('contain.text', 'Target stake')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(liquidityLink).should(
|
||||
'have.text',
|
||||
'View liquidity provision table'
|
||||
);
|
||||
cy.getByTestId(externalLink).should(
|
||||
'have.text',
|
||||
'Learn about providing liquidity'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,103 +0,0 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe(
|
||||
'vega wallet - prompt',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
describe('must submit order', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-039
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
it('must see a prompt to check connected vega wallet to approve transaction', () => {
|
||||
// 0003-WTXN-002
|
||||
cy.mockVegaWalletTransaction(1000);
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'Please go to your Vega wallet application and approve or reject the transaction.'
|
||||
);
|
||||
});
|
||||
|
||||
it('must show error returned by wallet ', () => {
|
||||
// 0003-WTXN-009
|
||||
// 0003-WTXN-011
|
||||
// 0002-WCON-016
|
||||
// 0003-WTXN-008
|
||||
|
||||
//trigger error from the wallet
|
||||
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
jsonrpc: '2.0',
|
||||
id: '1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'The connection to your Vega Wallet has been lost.'
|
||||
);
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
});
|
||||
|
||||
it('must see that the order was rejected by the connected wallet', () => {
|
||||
// 0003-WTXN-007
|
||||
|
||||
//trigger rejection error from the wallet
|
||||
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
|
||||
req.alias = 'client.send_transaction';
|
||||
req.reply({
|
||||
statusCode: 400,
|
||||
body: {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: 3001,
|
||||
data: 'the user rejected the wallet connection',
|
||||
message: 'User error',
|
||||
},
|
||||
id: '0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'Error occurredthe user rejected the wallet connection'
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -26,18 +26,6 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
cy.getByTestId('tab-deposits').should('not.be.empty');
|
||||
});
|
||||
|
||||
it.skip('should see QR code modal for WalletConnect', () => {
|
||||
// 0004-EWAL-003
|
||||
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
||||
cy.getByTestId('web3-connector-list').should('exist');
|
||||
cy.getByTestId('web3-connector-WalletConnect').click();
|
||||
// testing if exists rather than visible because of the long loading time
|
||||
cy.get('#w3m-modal').should('exist');
|
||||
});
|
||||
|
||||
it('able to disconnect eth wallet', () => {
|
||||
// 0004-EWAL-004
|
||||
// 0004-EWAL-005
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
import {
|
||||
mockConnectWallet,
|
||||
mockConnectWalletWithUserError,
|
||||
} from '@vegaprotocol/cypress';
|
||||
|
||||
const connectVegaBtn = 'connect-vega-wallet';
|
||||
const manageVegaBtn = 'manage-vega-wallet';
|
||||
const dialogContent = 'dialog-content';
|
||||
|
||||
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
it('can connect', () => {
|
||||
// 0002-WCON-002
|
||||
// 0002-WCON-005
|
||||
// 0002-WCON-007
|
||||
// 0002-WCON-009
|
||||
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
.click();
|
||||
cy.wait('@walletReq');
|
||||
cy.getByTestId(dialogContent).should(
|
||||
'contain.text',
|
||||
'Approve the connection from your Vega wallet app.'
|
||||
);
|
||||
cy.getByTestId(dialogContent).should('not.exist');
|
||||
cy.getByTestId(manageVegaBtn).should('exist');
|
||||
});
|
||||
|
||||
it('can not connect', () => {
|
||||
// 0002-WCON-002
|
||||
// 0002-WCON-005
|
||||
// 0002-WCON-007
|
||||
// 0002-WCON-015
|
||||
|
||||
mockConnectWalletWithUserError();
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
.click();
|
||||
cy.getByTestId('dialog-content')
|
||||
.should('contain.text', 'User error')
|
||||
.and('contain.text', 'the user rejected the wallet connection');
|
||||
});
|
||||
|
||||
it('can change selected public key and disconnect', () => {
|
||||
// 0002-WCON-022
|
||||
// 0002-WCON-023
|
||||
// 0002-WCON-025
|
||||
// 0002-WCON-026
|
||||
// 0002-WCON-021
|
||||
// 0002-WCON-027
|
||||
// 0002-WCON-030
|
||||
// 0002-WCON-029
|
||||
// 0002-WCON-008
|
||||
// 0002-WCON-035
|
||||
// 0002-WCON-014
|
||||
// 0002-WCON-010
|
||||
// 0003-WTXN-004
|
||||
|
||||
mockConnectWallet();
|
||||
const key2 = Cypress.env('VEGA_PUBLIC_KEY2');
|
||||
const truncatedKey2 = Cypress.env('TRUNCATED_VEGA_PUBLIC_KEY2');
|
||||
cy.connectVegaWallet();
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('keypair-list').should('exist');
|
||||
cy.getByTestId(`key-${key2}`).should('contain.text', truncatedKey2);
|
||||
cy.getByTestId(`key-${key2}`)
|
||||
.find('[data-testid="copy-vega-public-key"]')
|
||||
.should('be.visible');
|
||||
cy.get(`[data-testid="key-${key2}"] > .mr-2`).click();
|
||||
cy.getByTestId('keypair-list')
|
||||
.find('[data-state="checked"]')
|
||||
.should('be.visible');
|
||||
cy.getByTestId('disconnect').click();
|
||||
cy.getByTestId('connect-vega-wallet').should('exist');
|
||||
cy.getByTestId('manage-vega-wallet').should('not.exist');
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.contains('Enter a custom wallet location');
|
||||
});
|
||||
});
|
||||
+7
-1
@@ -22,6 +22,12 @@ 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_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
# NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -22,9 +22,12 @@ 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=false
|
||||
NX_REFERRALS=true
|
||||
# NX_DISABLE_CLOSE_POSITION=false
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
# NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -24,4 +24,7 @@ NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=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=
|
||||
|
||||
@@ -23,9 +23,11 @@ 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=
|
||||
|
||||
@@ -54,3 +54,5 @@ To run the UI automation tests with a mocked API, run:
|
||||
```bash
|
||||
yarn nx run trading-e2e:e2e
|
||||
```
|
||||
|
||||
To run tests with market sim please read [the readme](e2e/README.md).
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
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';
|
||||
|
||||
export const Fees = () => {
|
||||
const t = useT();
|
||||
const title = t('Fees');
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
updateTitle(titlefy([title]));
|
||||
}, [updateTitle, title]);
|
||||
|
||||
return (
|
||||
<div className="container p-4 mx-auto">
|
||||
<h1 className="px-4 pb-4 text-2xl">{t('Fees')}</h1>
|
||||
<FeesContainer />
|
||||
</div>
|
||||
<ErrorBoundary feature="fees">
|
||||
<div className="container p-4 mx-auto">
|
||||
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
|
||||
<FeesContainer />
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,34 +1,11 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useTopTradedMarkets } from '../../lib/hooks/use-top-traded-markets';
|
||||
import { Links } from '../../lib/links';
|
||||
import { useNavigateToLastMarket } from '../../lib/hooks/use-navigate-to-last-market';
|
||||
|
||||
// The home pages only purpose is to redirect to the users last market,
|
||||
// the top traded if they are new, or fall back to the list of markets.
|
||||
// Thats why we just render a loader here
|
||||
export const Home = () => {
|
||||
const navigate = useNavigate();
|
||||
const { data } = useTopTradedMarkets();
|
||||
const marketId = useGlobalStore((store) => store.marketId);
|
||||
|
||||
useEffect(() => {
|
||||
if (marketId) {
|
||||
navigate(Links.MARKET(marketId), {
|
||||
replace: true,
|
||||
});
|
||||
} else if (data) {
|
||||
const marketDataId = data[0]?.id;
|
||||
if (marketDataId) {
|
||||
navigate(Links.MARKET(marketDataId), {
|
||||
replace: true,
|
||||
});
|
||||
} else {
|
||||
navigate(Links.MARKETS());
|
||||
}
|
||||
}
|
||||
}, [marketId, data, navigate]);
|
||||
useNavigateToLastMarket();
|
||||
|
||||
return (
|
||||
<Splash>
|
||||
|
||||
@@ -6,6 +6,7 @@ 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',
|
||||
@@ -58,19 +59,28 @@ export const LiquidityViewContainer = ({
|
||||
name={t('My liquidity provision')}
|
||||
hidden={!pubKey}
|
||||
>
|
||||
<LiquidityContainer
|
||||
marketId={marketId}
|
||||
filter={{ partyId: pubKey || undefined }}
|
||||
/>
|
||||
<ErrorBoundary feature="liquidity-party">
|
||||
<LiquidityContainer
|
||||
marketId={marketId}
|
||||
filter={{ partyId: pubKey || undefined }}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Active} name={t('Active')}>
|
||||
<LiquidityContainer marketId={marketId} filter={{ active: true }} />
|
||||
<ErrorBoundary feature="liquidity-active">
|
||||
<LiquidityContainer
|
||||
marketId={marketId}
|
||||
filter={{ active: true }}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id={LiquidityTabs.Inactive} name={t('Inactive')}>
|
||||
<LiquidityContainer
|
||||
marketId={marketId}
|
||||
filter={{ active: false }}
|
||||
/>
|
||||
<ErrorBoundary feature="liquidity-inactive">
|
||||
<LiquidityContainer
|
||||
marketId={marketId}
|
||||
filter={{ active: false }}
|
||||
/>
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { DocsLinks, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ButtonLink, ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketProposalNotification } from '@vegaprotocol/proposals';
|
||||
import type { Market } from '@vegaprotocol/markets';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
@@ -145,7 +144,6 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
||||
/>
|
||||
</HeaderStat>
|
||||
)}
|
||||
<MarketProposalNotification marketId={market.id} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -190,12 +188,11 @@ const useNow = () => {
|
||||
return now;
|
||||
};
|
||||
|
||||
const useEvery = (marketId: string) => {
|
||||
const { data: marketTradingMode } = useMarketTradingMode(marketId);
|
||||
const useEvery = (marketId: string, skip: boolean) => {
|
||||
const { data: marketInfo } = useDataProvider({
|
||||
dataProvider: marketInfoProvider,
|
||||
variables: { marketId },
|
||||
skip: !marketTradingMode || isMarketInAuction(marketTradingMode),
|
||||
skip,
|
||||
});
|
||||
let every: number | undefined = undefined;
|
||||
const sourceType =
|
||||
@@ -213,8 +210,10 @@ const useEvery = (marketId: string) => {
|
||||
return every;
|
||||
};
|
||||
|
||||
const useStartTime = (marketId: string) => {
|
||||
const useStartTime = (marketId: string, skip: boolean) => {
|
||||
const { data: fundingPeriods } = useFundingPeriodsQuery({
|
||||
pollInterval: 5000,
|
||||
skip,
|
||||
variables: {
|
||||
marketId: marketId,
|
||||
pagination: { first: 1 },
|
||||
@@ -248,8 +247,10 @@ const useFormatCountdown = (
|
||||
|
||||
export const FundingCountdown = ({ marketId }: { marketId: string }) => {
|
||||
const now = useNow();
|
||||
const startTime = useStartTime(marketId);
|
||||
const every = useEvery(marketId);
|
||||
const { data: marketTradingMode } = useMarketTradingMode(marketId);
|
||||
const skip = !marketTradingMode || isMarketInAuction(marketTradingMode);
|
||||
const startTime = useStartTime(marketId, skip);
|
||||
const every = useEvery(marketId, skip);
|
||||
|
||||
return (
|
||||
<div data-testid="funding-countdown">
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user