Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
86d234b6eb | ||
|
|
587065beb4 | ||
|
|
3a1522bbf5 | ||
|
|
fc0ae5478e | ||
|
|
cf89c4f902 | ||
|
|
5e9009eaf1 | ||
|
|
7f77fceeab |
@@ -82,10 +82,6 @@ jobs:
|
||||
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
|
||||
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
|
||||
|
||||
- name: Print logs files
|
||||
if: ${{ always() }}
|
||||
run: ls -alsh /home/runner/.vegacapsule/testnet/logs/
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: ${{ always() }}
|
||||
with:
|
||||
|
||||
@@ -1,57 +1,150 @@
|
||||
context('Asset page', { tags: '@regression' }, () => {
|
||||
const columns = ['symbol', 'name', 'id', 'type', 'status', 'actions'];
|
||||
const hiddenOnMobile = ['id', 'type', 'status'];
|
||||
describe('Verify elements on page', () => {
|
||||
before('Navigate to assets page', () => {
|
||||
cy.visit('/assets');
|
||||
context('Asset page', { tags: '@regression' }, function () {
|
||||
before('gather system asset information', function () {
|
||||
cy.get_asset_information().as('assetsInfo');
|
||||
});
|
||||
|
||||
describe('Verify elements on page', function () {
|
||||
const assetsNavigation = 'a[href="/assets"]';
|
||||
const assetHeader = '[data-testid="asset-header"]';
|
||||
const jsonSection = '.language-json';
|
||||
|
||||
before('Navigate to assets page', function () {
|
||||
cy.visit('/');
|
||||
cy.get(assetsNavigation).click();
|
||||
|
||||
// Check we have enough enough assets
|
||||
cy.getAssets().then((assets) => {
|
||||
assert.isAtLeast(
|
||||
Object.keys(assets).length,
|
||||
5,
|
||||
'Ensuring we have at least 5 assets to test'
|
||||
);
|
||||
const assetNames = Object.keys(this.assetsInfo);
|
||||
assert.isAtLeast(
|
||||
assetNames.length,
|
||||
5,
|
||||
'Ensuring we have at least 5 assets to test'
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to see assets page sections', function () {
|
||||
const assetNames = Object.keys(this.assetsInfo);
|
||||
assetNames.forEach((assetName) => {
|
||||
cy.get(assetHeader)
|
||||
.contains(assetName)
|
||||
.should('be.visible')
|
||||
.next()
|
||||
.within(() => {
|
||||
cy.get(jsonSection).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to see full assets list', () => {
|
||||
cy.getAssets().then((assets) => {
|
||||
Object.values(assets).forEach((asset) => {
|
||||
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
|
||||
});
|
||||
});
|
||||
columns.forEach((col) => {
|
||||
cy.get(`[col-id="${col}"]`).should('be.visible');
|
||||
it('should be able to see all asset details displayed in JSON', function () {
|
||||
const assetNames = Object.keys(this.assetsInfo);
|
||||
assetNames.forEach((assetName) => {
|
||||
cy.get(assetHeader)
|
||||
.contains(assetName)
|
||||
.next()
|
||||
.within(() => {
|
||||
cy.get(jsonSection)
|
||||
.invoke('text')
|
||||
.convert_string_json_to_js_object()
|
||||
.then((assetsListedInJson) => {
|
||||
const assetInfo = this.assetsInfo[assetName];
|
||||
|
||||
assert.equal(assetsListedInJson.name, assetInfo.node.name);
|
||||
assert.equal(assetsListedInJson.id, assetInfo.node.id);
|
||||
assert.equal(
|
||||
assetsListedInJson.decimals,
|
||||
assetInfo.node.decimals
|
||||
);
|
||||
assert.equal(assetsListedInJson.symbol, assetInfo.node.symbol);
|
||||
assert.equal(
|
||||
assetsListedInJson.source.__typename,
|
||||
assetInfo.node.source.__typename
|
||||
);
|
||||
|
||||
if (assetInfo.node.source.__typename == 'ERC20') {
|
||||
assert.equal(
|
||||
assetsListedInJson.source.contractAddress,
|
||||
assetInfo.node.source.contractAddress
|
||||
);
|
||||
}
|
||||
|
||||
if (assetInfo.node.source.__typename == 'BuiltinAsset') {
|
||||
assert.equal(
|
||||
assetsListedInJson.source.maxFaucetAmountMint,
|
||||
assetInfo.node.source.maxFaucetAmountMint
|
||||
);
|
||||
}
|
||||
|
||||
let knownAssetTypes = ['BuiltinAsset', 'ERC20'];
|
||||
assert.include(
|
||||
knownAssetTypes,
|
||||
assetInfo.node.source.__typename,
|
||||
`Checking that current asset type of ${assetInfo.node.source.__typename} /
|
||||
is one of: ${knownAssetTypes}: /
|
||||
If fail then we need to add extra tests for un-encountered asset types`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to see assets page displayed in mobile', () => {
|
||||
cy.switchToMobile();
|
||||
it('should be able to switch assets between light and dark mode', function () {
|
||||
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
|
||||
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
|
||||
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
|
||||
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
|
||||
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
|
||||
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
|
||||
const themeSwitcher = '[data-testid="theme-switcher"]';
|
||||
const jsonFields = '.hljs';
|
||||
const sideMenuBackground = '.absolute';
|
||||
|
||||
hiddenOnMobile.forEach((col) => {
|
||||
cy.get(`[col-id="${col}"]`).should('have.length', 0);
|
||||
});
|
||||
|
||||
cy.getAssets().then((assets) => {
|
||||
Object.values(assets).forEach((asset) => {
|
||||
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
|
||||
// Engage dark mode if not allready set
|
||||
cy.get(sideMenuBackground)
|
||||
.should('have.css', 'background-color')
|
||||
.then((background_color) => {
|
||||
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
|
||||
cy.get(themeSwitcher).click();
|
||||
});
|
||||
});
|
||||
|
||||
// Engage white mode
|
||||
cy.get(themeSwitcher).click();
|
||||
|
||||
// White Mode
|
||||
cy.get(assetsNavigation)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', whiteThemeSelectedMenuOptionColor);
|
||||
cy.get(jsonFields)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', whiteThemeJsonFieldBackColor);
|
||||
cy.get(sideMenuBackground)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', whiteThemeSideMenuBackgroundColor);
|
||||
|
||||
// Dark Mode
|
||||
cy.get(themeSwitcher).click();
|
||||
cy.get(assetsNavigation)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', darkThemeSelectedMenuOptionColor);
|
||||
cy.get(jsonFields)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', darkThemeJsonFieldBackColor);
|
||||
cy.get(sideMenuBackground)
|
||||
.should('have.css', 'background-color')
|
||||
.and('include', darkThemeSideMenuBackgroundColor);
|
||||
});
|
||||
|
||||
it('should open details dialog when clicked on "View details"', () => {
|
||||
cy.getAssets().then((assets) => {
|
||||
Object.values(assets).forEach((asset) => {
|
||||
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
|
||||
.eq(0)
|
||||
.should('contain.text', 'View details');
|
||||
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
|
||||
.eq(0)
|
||||
.click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
it('should be able to see assets page displayed in mobile', function () {
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
cy.get(assetsNavigation).click();
|
||||
|
||||
const assetNames = Object.keys(this.assetsInfo);
|
||||
assetNames.forEach((assetName) => {
|
||||
cy.get(assetHeader)
|
||||
.contains(assetName)
|
||||
.should('be.visible')
|
||||
.next()
|
||||
.within(() => {
|
||||
cy.get(jsonSection).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
||||
.convert_string_json_to_js_object()
|
||||
.get_party_accounts_data_from_js_object()
|
||||
.then((accountsListedInJson) => {
|
||||
cy.getAssets().then((assetsInfo) => {
|
||||
cy.get_asset_information().then((assetsInfo) => {
|
||||
const assetInfo =
|
||||
assetsInfo[accountsListedInJson[assetInTest].asset.name];
|
||||
|
||||
@@ -205,7 +205,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('get_asset_decimals', (assetID) => {
|
||||
cy.getAssets().then((assetsInfo) => {
|
||||
cy.get_asset_information().then((assetsInfo) => {
|
||||
const assetDecimals = assetsInfo[assetData[assetID].name].decimals;
|
||||
let decimals = '';
|
||||
for (let i = 0; i < assetDecimals; i++) decimals += '0';
|
||||
|
||||
@@ -21,10 +21,6 @@ Cypress.Commands.add(
|
||||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add('switchToMobile', () => {
|
||||
cy.viewport('iphone-x');
|
||||
});
|
||||
|
||||
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
|
||||
cy.viewport('iphone-x');
|
||||
cy.visit('/');
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# App configuration variables
|
||||
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/tm
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.be.devnet1.vega.xyz/websocket
|
||||
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
|
||||
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
|
||||
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
|
||||
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet1-network.json
|
||||
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
|
||||
@@ -9,23 +9,6 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { Footer } from './components/footer/footer';
|
||||
import { AnnouncementBanner, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
AssetDetailsDialog,
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/assets';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
|
||||
return (
|
||||
<AssetDetailsDialog
|
||||
assetId={id}
|
||||
trigger={trigger || null}
|
||||
asJson={asJson}
|
||||
open={isOpen}
|
||||
onChange={setOpen}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
function App() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
@@ -60,9 +43,9 @@ function App() {
|
||||
<NetworkLoader cache={cacheConfig}>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">Mainnet sim 2 coming in March!</span>
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Learn more
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
@@ -73,8 +56,6 @@ function App() {
|
||||
<Main />
|
||||
<Footer />
|
||||
</div>
|
||||
|
||||
<DialogsContainer />
|
||||
</NetworkLoader>
|
||||
</TendermintWebsocketProvider>
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useAssetDataProvider } from '@vegaprotocol/assets';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
|
||||
import { AssetLink } from '../links';
|
||||
import { useExplorerAssetQuery } from '../links/asset-link/__generated__/Asset';
|
||||
|
||||
export type AssetBalanceProps = {
|
||||
assetId: string;
|
||||
@@ -17,17 +17,21 @@ const AssetBalance = ({
|
||||
price,
|
||||
showAssetLink = true,
|
||||
}: AssetBalanceProps) => {
|
||||
const { data: asset } = useAssetDataProvider(assetId);
|
||||
const { data } = useExplorerAssetQuery({
|
||||
variables: { id: assetId },
|
||||
});
|
||||
|
||||
const label =
|
||||
asset && asset.decimals
|
||||
? addDecimalsFormatNumber(price, asset.decimals)
|
||||
data && data.asset?.decimals
|
||||
? addDecimalsFormatNumber(price, data.asset.decimals)
|
||||
: price;
|
||||
|
||||
return (
|
||||
<div className="inline-block">
|
||||
<span>{label}</span>{' '}
|
||||
{showAssetLink && asset?.id ? <AssetLink assetId={assetId} /> : null}
|
||||
{showAssetLink && data?.asset?.id ? (
|
||||
<AssetLink id={data.asset.id} />
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { assetsList } from '../../mocks/assets';
|
||||
import { AssetsTable } from './assets-table';
|
||||
|
||||
describe('AssetsTable', () => {
|
||||
it('shows loading message on first render', async () => {
|
||||
const res = render(<AssetsTable data={null} />);
|
||||
expect(await res.findByText('Loading...')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows no data message if no assets found', async () => {
|
||||
const res = render(<AssetsTable data={[]} />);
|
||||
expect(
|
||||
await res.findByText('This chain has no assets')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows a table/list with all the assets', async () => {
|
||||
const res = render(<AssetsTable data={assetsList} />);
|
||||
await waitFor(() => {
|
||||
const rowA1 = res.container.querySelector('[row-id="123"]');
|
||||
expect(rowA1).toBeInTheDocument();
|
||||
|
||||
const rowA2 = res.container.querySelector('[row-id="456"]');
|
||||
expect(rowA2).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,115 +0,0 @@
|
||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/ui-toolkit';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
|
||||
type AssetsTableProps = {
|
||||
data: AssetFieldsFragment[] | null;
|
||||
};
|
||||
export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const openAssetDetailsDialog = useAssetDetailsDialogStore(
|
||||
(state) => state.open
|
||||
);
|
||||
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
ref.current?.columnApi.setColumnsVisible(
|
||||
['id', 'type', 'status'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
};
|
||||
|
||||
useLayoutEffect(() => {
|
||||
window.addEventListener('resize', showColumnsOnDesktop);
|
||||
return () => {
|
||||
window.removeEventListener('resize', showColumnsOnDesktop);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
ref={ref}
|
||||
rowData={data}
|
||||
getRowId={({ data }: { data: AssetFieldsFragment }) => data.id}
|
||||
overlayNoRowsTemplate={t('This chain has no assets')}
|
||||
domLayout="autoHeight"
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
filter: true,
|
||||
filterParams: { buttons: ['reset'] },
|
||||
autoHeight: true,
|
||||
}}
|
||||
suppressCellFocus={true}
|
||||
onGridReady={() => {
|
||||
showColumnsOnDesktop();
|
||||
}}
|
||||
>
|
||||
<AgGridColumn headerName={t('Symbol')} field="symbol" />
|
||||
<AgGridColumn headerName={t('Name')} field="name" />
|
||||
<AgGridColumn flex="2" headerName={t('ID')} field="id" />
|
||||
<AgGridColumn
|
||||
colId="type"
|
||||
headerName={t('Type')}
|
||||
field="source.__typename"
|
||||
valueFormatter={({ value }: { value?: string }) =>
|
||||
value && AssetTypeMapping[value].value
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
headerName={t('Status')}
|
||||
field="status"
|
||||
valueFormatter={({ value }: { value?: string }) =>
|
||||
value && AssetStatusMapping[value].value
|
||||
}
|
||||
/>
|
||||
<AgGridColumn
|
||||
colId="actions"
|
||||
headerName=""
|
||||
sortable={false}
|
||||
filter={false}
|
||||
resizable={false}
|
||||
wrapText={true}
|
||||
field="id"
|
||||
cellRenderer={({
|
||||
value,
|
||||
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
|
||||
value ? (
|
||||
<div className="pb-1">
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
openAssetDetailsDialog(value, e.target as HTMLElement);
|
||||
}}
|
||||
>
|
||||
{t('View details')}
|
||||
</ButtonLink>{' '}
|
||||
<span className="max-md:hidden">
|
||||
<ButtonLink
|
||||
onClick={(e) => {
|
||||
openAssetDetailsDialog(
|
||||
value,
|
||||
e.target as HTMLElement,
|
||||
true
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t('View JSON')}
|
||||
</ButtonLink>
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
)
|
||||
}
|
||||
/>
|
||||
</AgGrid>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
query ExplorerAsset($id: ID!) {
|
||||
asset(id: $id) {
|
||||
id
|
||||
name
|
||||
status
|
||||
decimals
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerAssetQueryVariables = Types.Exact<{
|
||||
id: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerAssetQuery = { __typename?: 'Query', asset?: { __typename?: 'Asset', id: string, name: string, status: Types.AssetStatus, decimals: number } | null };
|
||||
|
||||
|
||||
export const ExplorerAssetDocument = gql`
|
||||
query ExplorerAsset($id: ID!) {
|
||||
asset(id: $id) {
|
||||
id
|
||||
name
|
||||
status
|
||||
decimals
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerAssetQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerAssetQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerAssetQuery` 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 } = useExplorerAssetQuery({
|
||||
* variables: {
|
||||
* id: // value for 'id'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerAssetQuery(baseOptions: Apollo.QueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
|
||||
}
|
||||
export function useExplorerAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
|
||||
}
|
||||
export type ExplorerAssetQueryHookResult = ReturnType<typeof useExplorerAssetQuery>;
|
||||
export type ExplorerAssetLazyQueryHookResult = ReturnType<typeof useExplorerAssetLazyQuery>;
|
||||
export type ExplorerAssetQueryResult = Apollo.QueryResult<ExplorerAssetQuery, ExplorerAssetQueryVariables>;
|
||||
@@ -1,37 +1,63 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { render, waitFor } from '@testing-library/react';
|
||||
import { AssetLink } from './asset-link';
|
||||
import { mockAssetA1 } from '../../../mocks/assets';
|
||||
import { render } from '@testing-library/react';
|
||||
import AssetLink from './asset-link';
|
||||
import { ExplorerAssetDocument } from './__generated__/Asset';
|
||||
|
||||
function renderComponent(id: string, mock: MockedResponse[]) {
|
||||
return (
|
||||
<MockedProvider mocks={mock} addTypename={false}>
|
||||
<MockedProvider mocks={mock}>
|
||||
<MemoryRouter>
|
||||
<AssetLink assetId={id} />
|
||||
<AssetLink id={id} />
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('AssetLink', () => {
|
||||
it('renders the asset id when not found and makes the button disabled', async () => {
|
||||
describe('Asset link component', () => {
|
||||
it('Renders the ID at first', () => {
|
||||
const res = render(renderComponent('123', []));
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
expect(await res.findByTestId('asset-link')).toBeDisabled();
|
||||
await waitFor(async () => {
|
||||
expect(await res.queryByText('A ONE')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders the asset name when found and make the button enabled', async () => {
|
||||
const res = render(renderComponent('123', [mockAssetA1]));
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
it('Renders the asset name when the query returns a result', async () => {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerAssetDocument,
|
||||
variables: {
|
||||
id: '123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
asset: {
|
||||
id: '123',
|
||||
name: 'test-label',
|
||||
status: 'irrelevant-test-data',
|
||||
decimals: 18,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(await res.findByText('A ONE')).toBeInTheDocument();
|
||||
expect(await res.findByTestId('asset-link')).not.toBeDisabled();
|
||||
});
|
||||
const res = render(renderComponent('123', [mock]));
|
||||
expect(res.getByText('123')).toBeInTheDocument();
|
||||
expect(await res.findByText('test-label')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Leaves the asset id when the asset is not found', async () => {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerAssetDocument,
|
||||
variables: {
|
||||
id: '123',
|
||||
},
|
||||
},
|
||||
error: new Error('No such asset'),
|
||||
};
|
||||
|
||||
const res = render(renderComponent('123', [mock]));
|
||||
expect(await res.findByText('123')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,35 +1,36 @@
|
||||
import React from 'react';
|
||||
import { Routes } from '../../../routes/route-names';
|
||||
import { useExplorerAssetQuery } from './__generated__/Asset';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import type { ComponentProps } from 'react';
|
||||
import Hash from '../hash';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useAssetDataProvider,
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/assets';
|
||||
|
||||
export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
|
||||
assetId: string;
|
||||
export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||
id: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Given an asset ID, it will fetch the asset name and show that,
|
||||
* with a link to the assets modal. If the name does not come back
|
||||
* with a link to the assets list. If the name does not come back
|
||||
* it will use the ID instead.
|
||||
*/
|
||||
export const AssetLink = ({ assetId, ...props }: AssetLinkProps) => {
|
||||
const { data: asset } = useAssetDataProvider(assetId);
|
||||
const AssetLink = ({ id, ...props }: AssetLinkProps) => {
|
||||
const { data } = useExplorerAssetQuery({
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
let label: string = id;
|
||||
|
||||
if (data?.asset?.name) {
|
||||
label = data.asset.name;
|
||||
}
|
||||
|
||||
const open = useAssetDetailsDialogStore((state) => state.open);
|
||||
const label = asset?.name ? asset.name : assetId;
|
||||
return (
|
||||
<ButtonLink
|
||||
data-testid="asset-link"
|
||||
disabled={!asset}
|
||||
onClick={(e) => {
|
||||
open(assetId, e.target as HTMLElement);
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<Link className="underline" {...props} to={`/${Routes.ASSETS}#${id}`}>
|
||||
<Hash text={label} />
|
||||
</ButtonLink>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
export default AssetLink;
|
||||
|
||||
@@ -2,4 +2,4 @@ export { default as BlockLink } from './block-link/block-link';
|
||||
export { default as PartyLink } from './party-link/party-link';
|
||||
export { default as NodeLink } from './node-link/node-link';
|
||||
export { default as MarketLink } from './market-link/market-link';
|
||||
export * from './asset-link/asset-link';
|
||||
export { default as AssetLink } from './asset-link/asset-link';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useExplorerDeterministicOrderQuery } from '../order-details/__generated__/Order';
|
||||
import PriceInMarket from '../price-in-market/price-in-market';
|
||||
import { sideText } from '../order-details/lib/order-labels';
|
||||
import SizeInMarket from '../size-in-market/size-in-market';
|
||||
|
||||
// Note: Edited has no style currently
|
||||
export type OrderSummaryModifier = 'cancelled' | 'edited';
|
||||
@@ -42,8 +41,7 @@ const OrderSummary = ({ id, modifier }: OrderSummaryProps) => {
|
||||
return (
|
||||
<div data-testid="order-summary" className={getClassName(modifier)}>
|
||||
<span>{sideText[order.side]}</span>
|
||||
<SizeInMarket marketId={order.market.id} size={order.size} />
|
||||
<i>@</i>
|
||||
<span>{order.size}</span> <i>@</i>
|
||||
<PriceInMarket marketId={order.market.id} price={order.price} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -112,14 +112,13 @@ describe('Order TX Summary component', () => {
|
||||
const res = renderComponent(o, [mock]);
|
||||
expect(res.queryByTestId('order-summary')).toBeInTheDocument();
|
||||
expect(res.getByText('Buy')).toBeInTheDocument();
|
||||
|
||||
// Initially renders price and size unformatted
|
||||
expect(res.getByText('333')).toBeInTheDocument();
|
||||
expect(res.getByText('10')).toBeInTheDocument();
|
||||
|
||||
// Initially renders price alone
|
||||
expect(res.getByText('333')).toBeInTheDocument();
|
||||
|
||||
// After fetch renders formatted price and asset quotename
|
||||
expect(await res.findByText('3.33')).toBeInTheDocument();
|
||||
expect(await res.findByText('TEST')).toBeInTheDocument();
|
||||
expect(await res.getByText('0.10')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { components } from '../../../types/explorer';
|
||||
|
||||
import PriceInMarket from '../price-in-market/price-in-market';
|
||||
import { sideText } from '../order-details/lib/order-labels';
|
||||
import SizeInMarket from '../size-in-market/size-in-market';
|
||||
|
||||
export type OrderSummaryProps = {
|
||||
order: components['schemas']['v1OrderSubmission'];
|
||||
@@ -30,12 +29,7 @@ const OrderTxSummary = ({ order }: OrderSummaryProps) => {
|
||||
return (
|
||||
<div data-testid="order-summary">
|
||||
<span>{sideText[order.side]}</span>
|
||||
{order.size ? (
|
||||
<SizeInMarket size={order.size} marketId={order.marketId} />
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
<i className="text-xs">@</i>
|
||||
<span>{order.size}</span> <i className="text-xs">@</i>
|
||||
<PriceInMarket
|
||||
marketId={order.marketId}
|
||||
price={order.price}
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { DecimalSource } from './size-in-market';
|
||||
import { ExplorerMarketDocument } from '../links/market-link/__generated__/Market';
|
||||
|
||||
function renderComponent(
|
||||
size: string | undefined,
|
||||
price: string,
|
||||
marketId: string,
|
||||
mocks: MockedResponse[],
|
||||
decimalSource: DecimalSource = 'MARKET'
|
||||
@@ -17,7 +17,7 @@ function renderComponent(
|
||||
<MemoryRouter>
|
||||
<SizeInMarket
|
||||
marketId={marketId}
|
||||
size={size}
|
||||
size={price}
|
||||
decimalSource={decimalSource}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
@@ -57,11 +57,6 @@ const fullMock = {
|
||||
};
|
||||
|
||||
describe('Size in Market component', () => {
|
||||
it('Renders a dash size when there is no size', () => {
|
||||
const res = render(renderComponent(undefined, '123', []));
|
||||
expect(res.getByText('-')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders the raw size when there is no market data', () => {
|
||||
const res = render(renderComponent('100', '123', []));
|
||||
expect(res.getByText('100')).toBeInTheDocument();
|
||||
|
||||
@@ -5,7 +5,7 @@ export type DecimalSource = 'MARKET';
|
||||
|
||||
export type PriceInMarketProps = {
|
||||
marketId: string;
|
||||
size?: string | number;
|
||||
size: string | number;
|
||||
decimalSource?: DecimalSource;
|
||||
};
|
||||
|
||||
@@ -22,9 +22,6 @@ const SizeInMarket = ({
|
||||
variables: { id: marketId },
|
||||
fetchPolicy: 'cache-first',
|
||||
});
|
||||
if (!size) {
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
let label = size;
|
||||
|
||||
|
||||
+4
-2
@@ -76,7 +76,9 @@ describe('Chain Event: Builtin asset deposit', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,7 +34,7 @@ export const TxDetailsChainEventBuiltinDeposit = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={deposit.vegaAssetId} /> ({t('built in asset')})
|
||||
<AssetLink id={deposit.vegaAssetId} /> ({t('built in asset')})
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
+4
-2
@@ -82,7 +82,9 @@ describe('Chain Event: Builtin asset withdrawal', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,8 +39,8 @@ export const TxDetailsChainEventBuiltinWithdrawal = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={withdrawal.vegaAssetId || ''} /> (
|
||||
{t('built in asset')})
|
||||
<AssetLink id={withdrawal.vegaAssetId || ''} /> ({t('built in asset')}
|
||||
)
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
+4
-2
@@ -63,7 +63,9 @@ describe('Chain Event: ERC20 Asset Delist', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ export const TxDetailsChainEventErc20AssetDelist = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Removed Vega asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={assetDelist.vegaAssetId || ''} />
|
||||
<AssetLink id={assetDelist.vegaAssetId || ''} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
|
||||
+4
-2
@@ -79,8 +79,10 @@ describe('Chain Event: ERC20 Asset limits updated', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
|
||||
expect(screen.getByText(t('ERC20 asset'))).toBeInTheDocument();
|
||||
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ export const TxDetailsChainEventErc20AssetLimitsUpdated = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Vega asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={assetLimitsUpdated.vegaAssetId} />
|
||||
<AssetLink id={assetLimitsUpdated.vegaAssetId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
+4
-2
@@ -65,8 +65,10 @@ describe('Chain Event: ERC20 Asset List', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
|
||||
expect(screen.getByText(t('Source'))).toBeInTheDocument();
|
||||
const ethLink = screen.getByText(`${fullMock.assetSource}`);
|
||||
|
||||
@@ -41,7 +41,7 @@ export const TxDetailsChainEventErc20AssetList = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Added Vega asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={assetList.vegaAssetId} />
|
||||
<AssetLink id={assetList.vegaAssetId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
|
||||
@@ -75,8 +75,10 @@ describe('Chain Event: ERC20 asset deposit', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
|
||||
expect(screen.getByText(t('Source'))).toBeInTheDocument();
|
||||
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
|
||||
|
||||
@@ -51,7 +51,7 @@ export const TxDetailsChainEventDeposit = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={deposit.vegaAssetId} />
|
||||
<AssetLink id={deposit.vegaAssetId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
+4
-2
@@ -60,8 +60,10 @@ describe('Chain Event: ERC20 asset deposit', () => {
|
||||
if (!assetLink.parentElement) {
|
||||
throw new Error('Asset link does not exist');
|
||||
}
|
||||
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
|
||||
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
|
||||
expect(assetLink.parentElement.tagName).toEqual('A');
|
||||
expect(assetLink.parentElement.getAttribute('href')).toEqual(
|
||||
`/assets#${fullMock.vegaAssetId}`
|
||||
);
|
||||
|
||||
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
|
||||
const ethLink = screen.getByText(`${fullMock.targetEthereumAddress}`);
|
||||
|
||||
@@ -45,7 +45,7 @@ export const TxDetailsChainEventWithdrawal = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Asset')}</TableCell>
|
||||
<TableCell>
|
||||
<AssetLink assetId={withdrawal.vegaAssetId} />
|
||||
<AssetLink id={withdrawal.vegaAssetId} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</>
|
||||
|
||||
@@ -21,7 +21,6 @@ import { TxDetailsDataSubmission } from './tx-data-submission';
|
||||
import { TxProposalVote } from './tx-proposal-vote';
|
||||
import { TxDetailsProtocolUpgrade } from './tx-details-protocol-upgrade';
|
||||
import { TxDetailsIssueSignatures } from './tx-issue-signatures';
|
||||
import { TxDetailsNodeAnnounce } from './tx-node-announce';
|
||||
import { TxDetailsStateVariable } from './tx-state-variable-proposal';
|
||||
|
||||
interface TxDetailsWrapperProps {
|
||||
@@ -70,8 +69,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
||||
|
||||
// These come from https://github.com/vegaprotocol/vega/blob/develop/core/txn/command.go#L72-L98
|
||||
switch (txData.type) {
|
||||
case 'Register new Node':
|
||||
return TxDetailsNodeAnnounce;
|
||||
case 'Issue Signatures':
|
||||
return TxDetailsIssueSignatures;
|
||||
case 'Submit Order':
|
||||
|
||||
@@ -1,80 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 type { components } from '../../../../types/explorer';
|
||||
import {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../links/eth-explorer-link/eth-explorer-link';
|
||||
import { BlockLink } from '../../links';
|
||||
|
||||
type EthKeyRotate = components['schemas']['v1EthereumKeyRotateSubmission'];
|
||||
interface TxDetailsEthKeyRotateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A node is changing ethereum key
|
||||
*/
|
||||
export const TxDetailsEthKeyRotate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsEthKeyRotateProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const k: EthKeyRotate = txData.command;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{k.targetBlock ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Target block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={k.targetBlock} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Old Address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.currentAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.newAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New Address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.newAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.submitterAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Submitter address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={k.submitterAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -1,67 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 type { components } from '../../../../types/explorer';
|
||||
import { BlockLink, PartyLink } from '../../links';
|
||||
|
||||
type KeyRotate = components['schemas']['v1KeyRotateSubmission'];
|
||||
interface TxDetailsKeyRotateProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A node is changing Vega key
|
||||
*/
|
||||
export const TxDetailsKeyRotate = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsKeyRotateProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const k: KeyRotate = txData.command;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{k.targetBlock ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Target block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={k.targetBlock} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentPubKeyHash ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Old Address')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={k.currentPubKeyHash} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.currentPubKeyHash ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('New Address')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={k.currentPubKeyHash} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{k.newPubKeyIndex ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Key index')}</TableCell>
|
||||
<TableCell>
|
||||
<code>{k.newPubKeyIndex}</code>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -1,112 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
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 { TableRow, TableCell, TableWithTbody } from '../../table';
|
||||
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import {
|
||||
EthExplorerLink,
|
||||
EthExplorerLinkTypes,
|
||||
} from '../../links/eth-explorer-link/eth-explorer-link';
|
||||
import { PartyLink } from '../../links';
|
||||
import Hash from '../../links/hash';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type Command = components['schemas']['v1AnnounceNode'];
|
||||
|
||||
interface TxDetailsNodeAnnounceProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
pubKey: string | undefined;
|
||||
blockData: TendermintBlocksResponse | undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* When a new potential validator node comes online, it announces
|
||||
* itself with this transaction.
|
||||
*
|
||||
* Design decisions:
|
||||
* - Signatures are not rendered. You can still access them via the
|
||||
* TX details. This is consistent with explorers for other chains
|
||||
* - The avatar icon is rendered as a link rather than embedding
|
||||
*/
|
||||
export const TxDetailsNodeAnnounce = ({
|
||||
txData,
|
||||
pubKey,
|
||||
blockData,
|
||||
}: TxDetailsNodeAnnounceProps) => {
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
|
||||
const cmd: Command = txData.command.announceNode;
|
||||
|
||||
return (
|
||||
<TableWithTbody className="mb-8">
|
||||
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||
{cmd.name ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Name')}</TableCell>
|
||||
<TableCell>
|
||||
<span>{cmd.name}</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.id ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('ID')}</TableCell>
|
||||
<TableCell>
|
||||
<Hash text={cmd.id} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.chainPubKey ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Chain public key')}</TableCell>
|
||||
<TableCell>
|
||||
<Hash text={cmd.chainPubKey} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.ethereumAddress ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Ethereum Address')}</TableCell>
|
||||
<TableCell>
|
||||
<EthExplorerLink
|
||||
type={EthExplorerLinkTypes.address}
|
||||
id={cmd.ethereumAddress}
|
||||
/>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.vegaPubKey ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Vega public key')}</TableCell>
|
||||
<TableCell>
|
||||
<PartyLink id={cmd.vegaPubKey} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.avatarUrl ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Avatar URL')}</TableCell>
|
||||
<TableCell>
|
||||
<ExternalLink href={cmd.avatarUrl} rel="noreferrer noopener">
|
||||
{cmd.avatarUrl}
|
||||
</ExternalLink>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
{cmd.infoUrl ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Info link')}</TableCell>
|
||||
<TableCell>
|
||||
<ExternalLink href={cmd.infoUrl} rel="noreferrer noopener">
|
||||
{cmd.infoUrl}
|
||||
</ExternalLink>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
);
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Equivalent of tailwind's `md` modifier
|
||||
*/
|
||||
export const BREAKPOINT_MD = 768;
|
||||
@@ -1,134 +0,0 @@
|
||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
import { AssetDocument } from '@vegaprotocol/assets';
|
||||
import { AssetsDocument } from '@vegaprotocol/assets';
|
||||
import { AssetStatus } from '@vegaprotocol/types';
|
||||
|
||||
const A1: AssetFieldsFragment = {
|
||||
__typename: 'Asset',
|
||||
id: '123',
|
||||
name: 'A ONE',
|
||||
symbol: 'A1',
|
||||
decimals: 0,
|
||||
quantum: '',
|
||||
status: AssetStatus.STATUS_ENABLED,
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '',
|
||||
},
|
||||
infrastructureFeeAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
globalRewardPoolAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
lpFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
makerFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
marketProposerRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
takerFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
};
|
||||
|
||||
const A2: AssetFieldsFragment = {
|
||||
__typename: 'Asset',
|
||||
id: '456',
|
||||
name: 'A TWO',
|
||||
symbol: 'A2',
|
||||
decimals: 0,
|
||||
quantum: '',
|
||||
status: AssetStatus.STATUS_ENABLED,
|
||||
source: {
|
||||
__typename: 'BuiltinAsset',
|
||||
maxFaucetAmountMint: '',
|
||||
},
|
||||
infrastructureFeeAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
globalRewardPoolAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
lpFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
makerFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
marketProposerRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
takerFeeRewardAccount: {
|
||||
__typename: 'AccountBalance',
|
||||
balance: '',
|
||||
},
|
||||
};
|
||||
|
||||
export const assetsList = [A1, A2];
|
||||
|
||||
export const mockAssetsList = {
|
||||
request: {
|
||||
query: AssetsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
assetsConnection: {
|
||||
__typename: 'AssetsConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'AssetEdge',
|
||||
node: A1,
|
||||
},
|
||||
{
|
||||
__typename: 'AssetEdge',
|
||||
node: A2,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const mockEmptyAssetsList = {
|
||||
request: {
|
||||
query: AssetsDocument,
|
||||
},
|
||||
result: { data: null },
|
||||
};
|
||||
|
||||
export const mockAssetA1 = {
|
||||
request: {
|
||||
query: AssetDocument,
|
||||
variables: {
|
||||
assetId: '123',
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
assetsConnection: {
|
||||
__typename: 'AssetsConnection',
|
||||
edges: [
|
||||
{
|
||||
__typename: 'AssetEdge',
|
||||
node: A1,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,32 @@
|
||||
fragment AssetsFields on Asset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
}
|
||||
|
||||
infrastructureFeeAccount {
|
||||
type
|
||||
balance
|
||||
market {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query ExplorerAssets {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
...AssetsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type AssetsFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string }, infrastructureFeeAccount?: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, market?: { __typename?: 'Market', id: string } | null } | null };
|
||||
|
||||
export type ExplorerAssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerAssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string }, infrastructureFeeAccount?: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, market?: { __typename?: 'Market', id: string } | null } | null } } | null> | null } | null };
|
||||
|
||||
export const AssetsFieldsFragmentDoc = gql`
|
||||
fragment AssetsFields on Asset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
source {
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
}
|
||||
infrastructureFeeAccount {
|
||||
type
|
||||
balance
|
||||
market {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const ExplorerAssetsDocument = gql`
|
||||
query ExplorerAssets {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
...AssetsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${AssetsFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useExplorerAssetsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerAssetsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerAssetsQuery` 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 } = useExplorerAssetsQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerAssetsQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>(ExplorerAssetsDocument, options);
|
||||
}
|
||||
export function useExplorerAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>(ExplorerAssetsDocument, options);
|
||||
}
|
||||
export type ExplorerAssetsQueryHookResult = ReturnType<typeof useExplorerAssetsQuery>;
|
||||
export type ExplorerAssetsLazyQueryHookResult = ReturnType<typeof useExplorerAssetsLazyQuery>;
|
||||
export type ExplorerAssetsQueryResult = Apollo.QueryResult<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>;
|
||||
@@ -1,30 +0,0 @@
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import { useAssetsDataProvider } from '@vegaprotocol/assets';
|
||||
import { AssetsTable } from '../../components/assets/assets-table';
|
||||
|
||||
export const Assets = () => {
|
||||
useDocumentTitle(['Assets']);
|
||||
useScrollToLocation();
|
||||
|
||||
const { data, loading, error } = useAssetsDataProvider();
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
|
||||
<AsyncRenderer
|
||||
noDataMessage={t('This chain has no assets')}
|
||||
data={data}
|
||||
loading={loading}
|
||||
error={error}
|
||||
>
|
||||
<div className="h-full relative">
|
||||
<AssetsTable data={data} />
|
||||
</div>
|
||||
</AsyncRenderer>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,44 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import Assets from './index';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { ExplorerAssetDocument } from '../../components/links/asset-link/__generated__/Asset';
|
||||
|
||||
function renderComponent(mock: MockedResponse[]) {
|
||||
return (
|
||||
<MemoryRouter>
|
||||
<MockedProvider mocks={mock}>
|
||||
<Assets />
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
}
|
||||
|
||||
describe('Assets index', () => {
|
||||
it('Renders loader when loading', async () => {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerAssetDocument,
|
||||
},
|
||||
result: {
|
||||
data: {},
|
||||
},
|
||||
};
|
||||
const res = render(renderComponent([mock]));
|
||||
expect(await res.findByTestId('loader')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders EmptyList when loading completes and there are no results', async () => {
|
||||
const mock = {
|
||||
request: {
|
||||
query: ExplorerAssetDocument,
|
||||
},
|
||||
result: {
|
||||
data: {},
|
||||
},
|
||||
};
|
||||
const res = render(renderComponent([mock]));
|
||||
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1 +1,53 @@
|
||||
export * from './assets';
|
||||
import { getNodes, t } from '@vegaprotocol/react-helpers';
|
||||
import React from 'react';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { SubHeading } from '../../components/sub-heading';
|
||||
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { useExplorerAssetsQuery } from './__generated__/Assets';
|
||||
import type { AssetsFieldsFragment } from './__generated__/Assets';
|
||||
import { useScrollToLocation } from '../../hooks/scroll-to-location';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import EmptyList from '../../components/empty-list/empty-list';
|
||||
|
||||
const Assets = () => {
|
||||
const { data, loading } = useExplorerAssetsQuery();
|
||||
useDocumentTitle(['Assets']);
|
||||
|
||||
useScrollToLocation();
|
||||
|
||||
const assets = getNodes<AssetsFieldsFragment>(data?.assetsConnection);
|
||||
|
||||
if (!assets || assets.length === 0) {
|
||||
if (!loading) {
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
|
||||
<EmptyList
|
||||
heading={t('This chain has no assets')}
|
||||
label={t('0 assets')}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
} else {
|
||||
return <Loader />;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
|
||||
{assets.map((a) => {
|
||||
return (
|
||||
<React.Fragment key={a.id}>
|
||||
<SubHeading data-testid="asset-header" id={a.id}>
|
||||
{a.name} ({a.symbol})
|
||||
</SubHeading>
|
||||
<SyntaxHighlighter data={a} />
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Assets;
|
||||
|
||||
@@ -71,7 +71,7 @@ export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
|
||||
/>
|
||||
</td>
|
||||
<td className="text-md">
|
||||
<AssetLink assetId={account.asset.id} />
|
||||
<AssetLink id={account.asset.id} />
|
||||
</td>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Assets } from './assets';
|
||||
import Assets from './assets';
|
||||
import BlockPage from './blocks';
|
||||
import Governance from './governance';
|
||||
import Home from './home';
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const { join } = require('path');
|
||||
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
|
||||
const theme = require('../../libs/tailwindcss-config/src/theme');
|
||||
const {
|
||||
VegaColours,
|
||||
} = require('../../libs/tailwindcss-config/src/vega-colours');
|
||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||
|
||||
module.exports = {
|
||||
@@ -11,7 +14,12 @@ module.exports = {
|
||||
],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: theme,
|
||||
extend: {
|
||||
...theme,
|
||||
colors: {
|
||||
vega: VegaColours,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [vegaCustomClasses],
|
||||
};
|
||||
|
||||
+4
-2
@@ -39,8 +39,10 @@ export const MarketList = () => {
|
||||
|
||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
||||
|
||||
const localData = data?.markets;
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<AsyncRenderer loading={loading} error={error} data={localData}>
|
||||
<div
|
||||
className="grow w-full"
|
||||
style={{ minHeight: 500, overflow: 'hidden' }}
|
||||
@@ -55,7 +57,7 @@ export const MarketList = () => {
|
||||
);
|
||||
},
|
||||
}}
|
||||
rowData={data}
|
||||
rowData={localData}
|
||||
defaultColDef={{
|
||||
resizable: true,
|
||||
sortable: true,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -866,10 +866,6 @@ context(
|
||||
|
||||
cy.get(stakeTokenSubmitButton).should('contain', 'Add 1 $VEGA tokens');
|
||||
});
|
||||
|
||||
after('teardown wallet', function () {
|
||||
cy.vega_wallet_teardown();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -7,9 +7,26 @@ const navWithdraw = '[href="/token/withdraw"]';
|
||||
const navGovernance = '[href="/proposals"]';
|
||||
const navRedeem = '[href="/token/redeem"]';
|
||||
|
||||
const tokenDetailsTable = '.token-details';
|
||||
const address = '[data-testid="token-address"]';
|
||||
const contract = '[data-testid="token-contract"]';
|
||||
const totalSupply = '[data-testid="total-supply"]';
|
||||
const circulatingSupply = '[data-testid="circulating-supply"]';
|
||||
const staked = '[data-testid="staked"]';
|
||||
const tranchesLink = '[data-testid="tranches-link"]';
|
||||
const redeemBtn = '[data-testid="check-vesting-page-btn"]';
|
||||
const getVegaWalletLink = '[data-testid="get-vega-wallet-link"]';
|
||||
const associateVegaLink =
|
||||
'[data-testid="associate-vega-tokens-link-on-homepage"]';
|
||||
const stakingBtn = '[data-testid="staking-button-on-homepage"]';
|
||||
const governanceBtn = '[data-testid="governance-button-on-homepage"]';
|
||||
|
||||
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
|
||||
context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
before('visit token home page', function () {
|
||||
cy.visit('/');
|
||||
cy.visit('/token');
|
||||
});
|
||||
|
||||
describe('with wallets disconnected', function () {
|
||||
@@ -37,7 +54,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
describe('Token dropdown', function () {
|
||||
before('click on token dropdown', function () {
|
||||
cy.get(navSection).within(() => {
|
||||
cy.getByTestId('state-trigger').realClick();
|
||||
cy.getByTestId('state-trigger').click();
|
||||
});
|
||||
});
|
||||
it('should have token dropdown', function () {
|
||||
@@ -55,73 +72,80 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Links and buttons', function () {
|
||||
it('should have link for proposal page', function () {
|
||||
cy.getByTestId('home-proposals').within(() => {
|
||||
cy.get('[href="/proposals"]')
|
||||
.should('exist')
|
||||
.and('have.text', 'Browse, vote, and propose');
|
||||
describe('THE $VEGA TOKEN table', function () {
|
||||
it('should have TOKEN ADDRESS', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(address)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenAddress);
|
||||
});
|
||||
});
|
||||
it('should have external link for governance', function () {
|
||||
cy.getByTestId('home-proposals').within(() => {
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://vega.xyz/governance');
|
||||
it('should have VESTING CONTRACT', function () {
|
||||
// 1004-ASSO-001
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(contract)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenContractAddress);
|
||||
});
|
||||
});
|
||||
it('should have link for validator page', function () {
|
||||
cy.getByTestId('home-validators').within(() => {
|
||||
cy.get('[href="/validators"]')
|
||||
.first()
|
||||
.should('exist')
|
||||
.and('have.text', 'Browse, and stake');
|
||||
it('should have TOTAL SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(totalSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have external link for validators', function () {
|
||||
cy.getByTestId('home-validators').within(() => {
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href')
|
||||
.and(
|
||||
'contain',
|
||||
'https://community.vega.xyz/c/mainnet-validator-candidates'
|
||||
);
|
||||
it('should have CIRCULATING SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(circulatingSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have information on active nodes', function () {
|
||||
cy.getByTestId('node-information')
|
||||
.first()
|
||||
.should('contain.text', '2')
|
||||
.and('contain.text', 'active nodes');
|
||||
});
|
||||
it('should have information on consensus nodes', function () {
|
||||
cy.getByTestId('node-information')
|
||||
.last()
|
||||
.should('contain.text', '2')
|
||||
.and('contain.text', 'consensus nodes');
|
||||
});
|
||||
it('should contain link to specific validators', function () {
|
||||
cy.getByTestId('validators')
|
||||
.should('have.length', '2')
|
||||
.each(($validator) => {
|
||||
cy.wrap($validator).find('a').should('have.attr', 'href');
|
||||
});
|
||||
});
|
||||
it('should have link for rewards page', function () {
|
||||
cy.getByTestId('home-rewards').within(() => {
|
||||
cy.get('[href="/rewards"]')
|
||||
.first()
|
||||
.should('exist')
|
||||
.and('have.text', 'See rewards');
|
||||
it('should have STAKED $VEGA', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(staked).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have link for withdrawal page', function () {
|
||||
cy.getByTestId('home-vega-token').within(() => {
|
||||
cy.get('[href="/token/withdraw"]')
|
||||
.first()
|
||||
.should('exist')
|
||||
.and('have.text', 'Manage tokens');
|
||||
});
|
||||
});
|
||||
|
||||
describe('links and buttons', function () {
|
||||
it('should have TRANCHES link', function () {
|
||||
cy.get(tranchesLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/tranches');
|
||||
});
|
||||
it('should have REDEEM button', function () {
|
||||
cy.get(redeemBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/token/redeem');
|
||||
});
|
||||
it('should have GET VEGA WALLET link', function () {
|
||||
cy.get(getVegaWalletLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', 'https://vega.xyz/wallet');
|
||||
});
|
||||
it('should have ASSOCIATE VEGA TOKENS link', function () {
|
||||
cy.get(associateVegaLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/associate');
|
||||
});
|
||||
it('should have STAKING button', function () {
|
||||
cy.get(stakingBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/validators');
|
||||
});
|
||||
it('should have GOVERNANCE button', function () {
|
||||
cy.get(governanceBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/proposals');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const viewToggle = '[data-testid="epoch-reward-view-toggle-total"]';
|
||||
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
const warning = '[data-testid="callout"]';
|
||||
|
||||
context(
|
||||
@@ -15,7 +15,7 @@ context(
|
||||
});
|
||||
|
||||
it('should have rewards header visible', function () {
|
||||
cy.verify_page_header('Rewards and fees');
|
||||
cy.verify_page_header('Rewards');
|
||||
});
|
||||
|
||||
it('should have epoch warning', function () {
|
||||
@@ -27,8 +27,10 @@ context(
|
||||
);
|
||||
});
|
||||
|
||||
it('should have toggle for seeing total vs individual rewards', function () {
|
||||
cy.get(viewToggle).should('be.visible');
|
||||
it('should have connect Vega wallet button', function () {
|
||||
cy.get(connectToVegaBtn)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,16 +10,6 @@ const pendingStake = '[data-testid="pending-stake"]';
|
||||
const stakedByOperator = '[data-testid="staked-by-operator"]';
|
||||
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
|
||||
const stakeShare = '[data-testid="stake-percentage"]';
|
||||
const stakedByOperatorToolTip = "[data-testid='staked-operator-tooltip']";
|
||||
const stakedByDelegatesToolTip = "[data-testid='staked-delegates-tooltip']";
|
||||
const totalStakedToolTip = "[data-testid='total-staked-tooltip']";
|
||||
const unnormalisedVotingPowerToolTip =
|
||||
"[data-testid='unnormalised-voting-power-tooltip']";
|
||||
const normalisedVotingPowerToolTip =
|
||||
"[data-testid='normalised-voting-power-tooltip']";
|
||||
const performancePenaltyToolTip = "[data-testid='performance-penalty-tooltip']";
|
||||
const overstakedPenaltyToolTip = "[data-testid='overstaked-penalty-tooltip']";
|
||||
const totalPenaltyToolTip = "[data-testid='total-penalty-tooltip']";
|
||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||
const stakeNumberRegex = /^\d*\.?\d*$/;
|
||||
|
||||
@@ -66,70 +56,37 @@ context('Staking Page - verify elements on page', function () {
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake', function () {
|
||||
cy.get('[col-id="stake"] > div > span > span')
|
||||
cy.get('[col-id="stake"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($stake) => {
|
||||
cy.wrap($stake).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator stake tooltip', function () {
|
||||
cy.get('[col-id="stake"] > div > span > span').first().realHover();
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 0.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 0.00');
|
||||
});
|
||||
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator voting power tooltip', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
|
||||
.first()
|
||||
.realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 0.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 0.10%');
|
||||
it('Should be able to see validator normalised voting power', function () {
|
||||
cy.get('[col-id="normalisedVotingPower"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($vPower) => {
|
||||
cy.wrap($vPower).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator total penalties', function () {
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span')
|
||||
cy.get('[col-id="totalPenalties"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($penalties) => {
|
||||
cy.wrap($penalties).should('contain.text', '0%');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see validator penalties tooltip', function () {
|
||||
cy.get('[col-id="totalPenalties"] > div > span > span').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 100.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
|
||||
cy.get(totalPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total penalties: 0.00%');
|
||||
});
|
||||
|
||||
it('Should be able to see validator pending stake', function () {
|
||||
cy.get('[col-id="pendingStake"] > div > span')
|
||||
.should('have.length.at.least', 1)
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
const tokenDetailsTable = '.token-details';
|
||||
const address = '[data-testid="token-address"]';
|
||||
const contract = '[data-testid="token-contract"]';
|
||||
const totalSupply = '[data-testid="total-supply"]';
|
||||
const circulatingSupply = '[data-testid="circulating-supply"]';
|
||||
const staked = '[data-testid="staked"]';
|
||||
const tranchesLink = '[data-testid="tranches-link"]';
|
||||
const redeemBtn = '[data-testid="check-vesting-page-btn"]';
|
||||
const getVegaWalletLink = '[data-testid="get-vega-wallet-link"]';
|
||||
const associateVegaLink =
|
||||
'[data-testid="associate-vega-tokens-link-on-homepage"]';
|
||||
const stakingBtn = '[data-testid="staking-button-on-homepage"]';
|
||||
const governanceBtn = '[data-testid="governance-button-on-homepage"]';
|
||||
|
||||
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
|
||||
context('Verify elements on Token page', { tags: '@smoke' }, function () {
|
||||
before('Visit token page', function () {
|
||||
cy.visit('/');
|
||||
cy.navigate_to('token');
|
||||
});
|
||||
describe('THE $VEGA TOKEN table', function () {
|
||||
it('should have TOKEN ADDRESS', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(address)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenAddress);
|
||||
});
|
||||
});
|
||||
it('should have VESTING CONTRACT', function () {
|
||||
// 1004-ASSO-001
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(contract)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenContractAddress);
|
||||
});
|
||||
});
|
||||
it('should have TOTAL SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(totalSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have CIRCULATING SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(circulatingSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have STAKED $VEGA', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(staked).should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('links and buttons', function () {
|
||||
it('should have TRANCHES link', function () {
|
||||
cy.get(tranchesLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/tranches');
|
||||
});
|
||||
it('should have REDEEM button', function () {
|
||||
cy.get(redeemBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/token/redeem');
|
||||
});
|
||||
it('should have GET VEGA WALLET link', function () {
|
||||
cy.get(getVegaWalletLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', 'https://vega.xyz/wallet');
|
||||
});
|
||||
it('should have ASSOCIATE VEGA TOKENS link', function () {
|
||||
cy.get(associateVegaLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/associate');
|
||||
});
|
||||
it('should have STAKING button', function () {
|
||||
cy.get(stakingBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/validators');
|
||||
});
|
||||
it('should have GOVERNANCE button', function () {
|
||||
cy.get(governanceBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/proposals');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,4 @@
|
||||
import '@vegaprotocol/cypress';
|
||||
import 'cypress-real-events/support';
|
||||
|
||||
import './common.functions.js';
|
||||
import './staking.functions.js';
|
||||
|
||||
@@ -95,15 +95,8 @@ Cypress.Commands.add('faucet_asset', function (assetEthAddress) {
|
||||
});
|
||||
|
||||
Cypress.Commands.add('vega_wallet_teardown', function () {
|
||||
cy.get('[data-testid="associated-amount"]')
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.as('associatedAmount');
|
||||
cy.get('body').then(($body) => {
|
||||
if (
|
||||
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
|
||||
this.associatedAmount != '0.00'
|
||||
) {
|
||||
if ($body.find('[data-testid="eth-wallet-associated-balances"]').length) {
|
||||
cy.vega_wallet_teardown_vesting(this.vestingContract);
|
||||
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node", "cypress-real-events", "@cypress/grep"]
|
||||
"types": ["cypress", "node", "@cypress/grep"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js"]
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
<>
|
||||
<AnnouncementBanner>
|
||||
<div className="font-alpha calt uppercase text-center text-lg text-white">
|
||||
<span className="pr-4">Mainnet sim 2 coming in March!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">Learn more</ExternalLink>
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
<Nav navbarTheme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||
@@ -30,9 +32,7 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||
) : null}
|
||||
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
|
||||
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
|
||||
{children}
|
||||
</main>
|
||||
<main className="col-start-1 p-4">{children}</main>
|
||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||
{sidebar.map((Component, i) => (
|
||||
<section className="mb-4 last:mb-0" key={i}>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
import type { ObservableQuery } from '@apollo/client';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const useRefreshAfterEpoch = (
|
||||
export const useRefreshValidators = (
|
||||
epochExpiry: string | undefined,
|
||||
refetch: ObservableQuery['refetch']
|
||||
) => {
|
||||
@@ -14,7 +14,7 @@
|
||||
"pageTitleProposals": "Proposals",
|
||||
"pageTitleDepositLp": "Deposit liquidity token for $VEGA rewards",
|
||||
"pageTitleWithdrawLp": "Withdraw SLP and Rewards",
|
||||
"pageTitleRewards": "Rewards and fees",
|
||||
"pageTitleRewards": "Rewards",
|
||||
"pageTitleRejectedProposals": "Rejected proposals",
|
||||
"pageTitleValidators": "Validators",
|
||||
"Vesting": "Vesting",
|
||||
@@ -197,6 +197,10 @@
|
||||
"STATE_PASSED": "Passed",
|
||||
"STATE_OPEN": "Open",
|
||||
"STATE_WAITING_FOR_NODE_VOTE": "Waiting for node vote",
|
||||
"NewMarket": "New market",
|
||||
"UpdateMarket": "Update market",
|
||||
"NewAsset": "New asset",
|
||||
"UpdateAsset": "Update asset",
|
||||
"UpdateNetworkParameter": "Network parameter",
|
||||
"NewFreeform": "Freeform",
|
||||
"tokenVotes": "Token votes",
|
||||
@@ -432,32 +436,12 @@
|
||||
"associatedWithVegaKeys": "Associated with Vega keys",
|
||||
"thisEpoch": "This Epoch",
|
||||
"nextEpoch": "Next epoch",
|
||||
"rewardsIntro": "Earn rewards and infrastructure fees for trading and maintaining the network.",
|
||||
"rewardsPara1": "Rewards are paid out from the treasury at the end of an epoch.",
|
||||
"rewardsPara2": "This page lists all the rewards that your Vega key has received.",
|
||||
"rewardsPara3": "This delay is set by a network parameter",
|
||||
"rewardsCallout": "Rewards are credited {{duration}} after the epoch ends.",
|
||||
"rewardsCalloutDetail": "This delay is set by a network parameter",
|
||||
"noRewards": "The Vega key has not been credited any rewards since the previous network checkpoint.",
|
||||
"seeHowRewardsAreCalculated": "See how rewards are calculated",
|
||||
"rewardType": "Reward type",
|
||||
"rewardsAndFeesReceived": "Rewards and fees received",
|
||||
"ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders": "This does not include fees received for makers or liquidity providers",
|
||||
"totalDistributed": "TOTAL DISTRIBUTED",
|
||||
"earnedByMe": "EARNED BY ME",
|
||||
"noRewardsHaveBeenDistributedYet": "NO REWARDS HAVE BEEN DISTRIBUTED YET",
|
||||
"rewardsColAssetHeader": "ASSET",
|
||||
"rewardsColStakingHeader": "STAKING",
|
||||
"rewardsColStakingTooltip": "Staking rewards supplement infrastructure fees in the early stages of the network, rewarding validators and those who stake them for maintaining the network",
|
||||
"rewardsColInfraHeader": "INFRA FEES",
|
||||
"rewardsColInfraTooltip": "Infrastructure fees are incurred across the network during trading. They are distributed to validators according to their share of total stake on the network, and passed onto those who stake them, proportionate to their own stake after validator commission is taken",
|
||||
"rewardsColPriceTakingHeader": "PRICE TAKING",
|
||||
"rewardsColPriceTakingTooltip": "Price taking rewards are based on the proportion of the total maker fees you paid while trading, on markets where there is a funded reward",
|
||||
"rewardsColPriceMakingHeader": "PRICE MAKING",
|
||||
"rewardsColPriceMakingTooltip": "Price making rewards are based on the proportion of the total maker fees you received while trading, on markets where there is a funded reward",
|
||||
"rewardsColLiquidityProvisionHeader": "LIQUIDITY PROVISION",
|
||||
"rewardsColLiquidityProvisionTooltip": "Liquidity provision rewards are distributed based on how much you have earned in liquidity fees, funded by a liquidity reward pool for that market",
|
||||
"rewardsColMarketCreationHeader": "MARKET CREATION",
|
||||
"rewardsColMarketCreationTooltip": "Market creation rewards are paid out to the creator of any market that exceeds a set threshold of cumulative volume in a given epoch, currently [rewards.marketCreationQuantumMultiple]",
|
||||
"rewardsColTotalHeader": "TOTAL",
|
||||
"checkBackSoon": "Check back soon",
|
||||
"yourStake": "Your stake",
|
||||
"reward": "Reward",
|
||||
"shareOfReward": "Share of reward",
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { useRefreshValidators } from '../../hooks/use-refresh-validators';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import {
|
||||
@@ -94,10 +94,7 @@ const HomeNodes = ({
|
||||
<div key={index} className="col-span-3">
|
||||
<Link to={Routes.VALIDATORS}>
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<div
|
||||
data-testid="node-information"
|
||||
className="flex flex-col items-center m-[-1rem] px-4 py-6 hover:bg-neutral-800"
|
||||
>
|
||||
<div className="flex flex-col items-center m-[-1rem] px-4 py-6 hover:bg-neutral-800">
|
||||
<span className="text-5xl">{length}</span>
|
||||
<span className="text-sm uppercase text-neutral-400">
|
||||
{title}
|
||||
@@ -109,7 +106,7 @@ const HomeNodes = ({
|
||||
))}
|
||||
|
||||
{trimmedActiveNodes.map(({ id, avatarUrl, name }) => (
|
||||
<div key={id} data-testid="validators" className="col-span-2">
|
||||
<div key={id} className="col-span-2">
|
||||
<Link to={`${Routes.VALIDATORS}/${id}`}>
|
||||
<RoundedWrapper paddingBottom={true} border={false}>
|
||||
<div className="flex items-center justify-center m-[-1rem] p-4 bg-neutral-900 hover:bg-neutral-800">
|
||||
@@ -159,7 +156,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
refetch,
|
||||
} = useNodesQuery();
|
||||
|
||||
useRefreshAfterEpoch(validatorsData?.epoch.timestamps.expiry, refetch);
|
||||
useRefreshValidators(validatorsData?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const proposals = useMemo(
|
||||
() =>
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
|
||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { ENV } from '../../../config';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { RewardTable } from './reward-table';
|
||||
|
||||
export const RewardInfo = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { delegationsPagination } = ENV;
|
||||
|
||||
const { data, loading, error } = useRewardsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
const rewards = useMemo(() => {
|
||||
if (!data?.party || !data.party.rewardsConnection?.edges?.length) return [];
|
||||
|
||||
return removePaginationWrapper(data.party.rewardsConnection.edges);
|
||||
}, [data]);
|
||||
|
||||
const delegations = useMemo(() => {
|
||||
if (!data?.party || !data.party.delegationsConnection?.edges?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return removePaginationWrapper(data.party.delegationsConnection.edges);
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
render={() => (
|
||||
<div>
|
||||
<p>
|
||||
{t('Connected Vega key')}: {pubKey}
|
||||
</p>
|
||||
{rewards.length ? (
|
||||
rewards.map((reward, i) => {
|
||||
if (!reward) return null;
|
||||
return (
|
||||
<RewardTable
|
||||
key={i}
|
||||
reward={reward}
|
||||
delegations={delegations || []}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p>{t('noRewards')}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEpochAssetsRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
||||
import { NoRewards } from '../no-rewards';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
|
||||
export const EpochRewards = () => {
|
||||
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
|
||||
variables: {
|
||||
epochRewardSummariesPagination: {
|
||||
first: 10,
|
||||
},
|
||||
},
|
||||
});
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const epochRewardSummaries = generateEpochTotalRewardsList(data) || [];
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data}
|
||||
render={() => (
|
||||
<div
|
||||
className="max-w-full overflow-auto"
|
||||
data-testid="epoch-rewards-total"
|
||||
>
|
||||
{epochRewardSummaries.length === 0 ? (
|
||||
<NoRewards />
|
||||
) : (
|
||||
<>
|
||||
{epochRewardSummaries.map((aggregatedEpochSummary) => (
|
||||
<EpochTotalRewardsTable data={aggregatedEpochSummary} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
const mockData = {
|
||||
epoch: 4431,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId:
|
||||
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
|
||||
name: 'tDAI TEST',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '295',
|
||||
},
|
||||
],
|
||||
totalAmount: '295',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('EpochTotalRewardsTable', () => {
|
||||
it('should render correctly', () => {
|
||||
const { getByTestId } = render(<EpochTotalRewardsTable data={mockData} />);
|
||||
expect(getByTestId('epoch-total-rewards-table')).toBeInTheDocument();
|
||||
expect(getByTestId('asset')).toBeInTheDocument();
|
||||
expect(getByTestId('global')).toBeInTheDocument();
|
||||
expect(getByTestId('infra')).toBeInTheDocument();
|
||||
expect(getByTestId('taker')).toBeInTheDocument();
|
||||
expect(getByTestId('maker')).toBeInTheDocument();
|
||||
expect(getByTestId('liquidity')).toBeInTheDocument();
|
||||
expect(getByTestId('market-maker')).toBeInTheDocument();
|
||||
expect(getByTestId('total')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -1,212 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatNumber } from '@vegaprotocol/react-helpers';
|
||||
import { Tooltip, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
import { SubHeading } from '../../../components/heading';
|
||||
import type { AggregatedEpochSummary } from './generate-epoch-total-rewards-list';
|
||||
|
||||
interface EpochTotalRewardsGridProps {
|
||||
data: AggregatedEpochSummary;
|
||||
}
|
||||
|
||||
interface ColumnHeaderProps {
|
||||
title: string;
|
||||
tooltipContent?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
interface RewardItemProps {
|
||||
value: string;
|
||||
dataTestId: string;
|
||||
last?: boolean;
|
||||
}
|
||||
|
||||
const displayReward = (reward: string) => {
|
||||
if (Number(reward) === 0) {
|
||||
return <span className="text-vega-dark-300">0</span>;
|
||||
}
|
||||
|
||||
if (reward.split('.')[1] && reward.split('.')[1].length > 4) {
|
||||
return (
|
||||
<Tooltip description={formatNumber(reward)}>
|
||||
<button>{formatNumber(Number(reward).toFixed(4))}</button>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
return <span>{formatNumber(reward)}</span>;
|
||||
};
|
||||
|
||||
const gridStyles = classNames(
|
||||
'grid grid-cols-[repeat(8,minmax(100px,auto))] max-w-full overflow-auto',
|
||||
`border-t border-vega-dark-200`,
|
||||
'text-sm'
|
||||
);
|
||||
|
||||
const headerGridItemStyles = (last = false) =>
|
||||
classNames('border-r border-b border-b-vega-dark-200', 'py-3 px-5', {
|
||||
'border-r-vega-dark-150': !last,
|
||||
'border-r-0': last,
|
||||
});
|
||||
|
||||
const rowGridItemStyles = (last = false) =>
|
||||
classNames('relative', 'border-r border-b border-b-vega-dark-150', {
|
||||
'border-r-vega-dark-150': !last,
|
||||
'border-r-0': last,
|
||||
});
|
||||
|
||||
const ColumnHeader = ({
|
||||
title,
|
||||
tooltipContent,
|
||||
className,
|
||||
}: ColumnHeaderProps) => (
|
||||
<div className={className}>
|
||||
<h2 className="mb-1 text-sm text-vega-dark-300">{title}</h2>
|
||||
{tooltipContent && (
|
||||
<Tooltip description={tooltipContent}>
|
||||
<button>
|
||||
<Icon name={'info-sign'} className="text-vega-dark-200" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
const RewardItem = ({ value, dataTestId, last }: RewardItemProps) => (
|
||||
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
|
||||
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
|
||||
<div className="overflow-auto p-5">{displayReward(value)}</div>
|
||||
<div className="h-full w-5 absolute left-0 top-0 bg-gradient-to-l from-transparent to-black pointer-events-none" />
|
||||
</div>
|
||||
);
|
||||
|
||||
export const EpochTotalRewardsTable = ({
|
||||
data,
|
||||
}: EpochTotalRewardsGridProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const rowData = data.assetRewards.map(({ name, rewards, totalAmount }) => ({
|
||||
name,
|
||||
ACCOUNT_TYPE_GLOBAL_REWARD:
|
||||
rewards
|
||||
.filter((r) => r.rewardType === AccountType.ACCOUNT_TYPE_GLOBAL_REWARD)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
ACCOUNT_TYPE_FEES_INFRASTRUCTURE:
|
||||
rewards
|
||||
.filter(
|
||||
(r) => r.rewardType === AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE
|
||||
)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES:
|
||||
rewards
|
||||
.filter(
|
||||
(r) =>
|
||||
r.rewardType === AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES
|
||||
)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES:
|
||||
rewards
|
||||
.filter(
|
||||
(r) =>
|
||||
r.rewardType === AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES
|
||||
)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
ACCOUNT_TYPE_FEES_LIQUIDITY:
|
||||
rewards
|
||||
.filter((r) => r.rewardType === AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS:
|
||||
rewards
|
||||
.filter(
|
||||
(r) =>
|
||||
r.rewardType === AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS
|
||||
)
|
||||
.map((r) => r.amount)[0] || '0',
|
||||
totalAmount: totalAmount,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div data-testid="epoch-total-rewards-table" className="mb-12">
|
||||
<SubHeading title={`EPOCH ${data.epoch}`} />
|
||||
|
||||
<div className={gridStyles}>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColAssetHeader')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColStakingHeader')}
|
||||
tooltipContent={t('rewardsColStakingTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColInfraHeader')}
|
||||
tooltipContent={t('rewardsColInfraTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColPriceTakingHeader')}
|
||||
tooltipContent={t('rewardsColPriceTakingTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColPriceMakingHeader')}
|
||||
tooltipContent={t('rewardsColPriceMakingTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColLiquidityProvisionHeader')}
|
||||
tooltipContent={t('rewardsColLiquidityProvisionTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColMarketCreationHeader')}
|
||||
tooltipContent={t('rewardsColMarketCreationTooltip')}
|
||||
className={headerGridItemStyles()}
|
||||
/>
|
||||
<ColumnHeader
|
||||
title={t('rewardsColTotalHeader')}
|
||||
className={headerGridItemStyles(true)}
|
||||
/>
|
||||
|
||||
{rowData.map((row, i) => (
|
||||
<div className="contents" key={i}>
|
||||
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
|
||||
{row.name}
|
||||
</div>
|
||||
<RewardItem
|
||||
dataTestId="global"
|
||||
value={row.ACCOUNT_TYPE_GLOBAL_REWARD}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="infra"
|
||||
value={row.ACCOUNT_TYPE_FEES_INFRASTRUCTURE}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="taker"
|
||||
value={row.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="maker"
|
||||
value={row.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="liquidity"
|
||||
value={row.ACCOUNT_TYPE_FEES_LIQUIDITY}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="market-maker"
|
||||
value={row.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS}
|
||||
/>
|
||||
<RewardItem
|
||||
dataTestId="total"
|
||||
value={row.totalAmount}
|
||||
last={true}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
-259
@@ -1,259 +0,0 @@
|
||||
import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-list';
|
||||
import { AccountType } from '@vegaprotocol/types';
|
||||
|
||||
describe('generateEpochAssetRewardsList', () => {
|
||||
it('should return an empty array if data is undefined', () => {
|
||||
const result = generateEpochTotalRewardsList(undefined);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if empty data is provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [],
|
||||
},
|
||||
epoch: {
|
||||
timestamps: {
|
||||
expiry: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if no epochRewardSummaries are provided', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '1',
|
||||
name: 'Asset 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '2',
|
||||
name: 'Asset 2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [],
|
||||
},
|
||||
epoch: {
|
||||
timestamps: {
|
||||
expiry: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an array of unnamed assets if no assets are provided (should not happen)', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '123',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
epoch: {
|
||||
timestamps: {
|
||||
expiry: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '123',
|
||||
},
|
||||
],
|
||||
totalAmount: '123',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId: '1',
|
||||
name: '',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
],
|
||||
totalAmount: '5',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an array of aggregated epoch summaries', () => {
|
||||
const epochData = {
|
||||
assetsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: '1',
|
||||
name: 'Asset 1',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '2',
|
||||
name: 'Asset 2',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
epochRewardSummaries: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '123',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '2',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '17.9873',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 1,
|
||||
assetId: '2',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '1',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
epoch: 2,
|
||||
assetId: '1',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
epoch: {
|
||||
timestamps: {
|
||||
expiry: null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = generateEpochTotalRewardsList(epochData);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
epoch: 1,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
|
||||
amount: '123',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '100',
|
||||
},
|
||||
],
|
||||
totalAmount: '223',
|
||||
},
|
||||
{
|
||||
assetId: '2',
|
||||
name: 'Asset 2',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
|
||||
amount: '17.9873',
|
||||
},
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '1',
|
||||
},
|
||||
],
|
||||
totalAmount: '18.9873',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
epoch: 2,
|
||||
assetRewards: [
|
||||
{
|
||||
assetId: '1',
|
||||
name: 'Asset 1',
|
||||
rewards: [
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
|
||||
amount: '5',
|
||||
},
|
||||
],
|
||||
totalAmount: '5',
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
-103
@@ -1,103 +0,0 @@
|
||||
import type {
|
||||
EpochAssetsRewardsQuery,
|
||||
EpochRewardSummaryFieldsFragment,
|
||||
} from '../home/__generated__/Rewards';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
|
||||
|
||||
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface AggregatedEpochRewardSummary {
|
||||
assetId: EpochRewardSummaryFieldsFragment['assetId'];
|
||||
name: EpochSummaryWithNamedReward['name'];
|
||||
rewards: {
|
||||
rewardType: EpochRewardSummaryFieldsFragment['rewardType'];
|
||||
amount: EpochRewardSummaryFieldsFragment['amount'];
|
||||
}[];
|
||||
totalAmount: string;
|
||||
}
|
||||
|
||||
export interface AggregatedEpochSummary {
|
||||
epoch: EpochRewardSummaryFieldsFragment['epoch'];
|
||||
assetRewards: AggregatedEpochRewardSummary[];
|
||||
}
|
||||
|
||||
export const generateEpochTotalRewardsList = (
|
||||
epochData: EpochAssetsRewardsQuery | undefined
|
||||
) => {
|
||||
const epochRewardSummaries = removePaginationWrapper(
|
||||
epochData?.epochRewardSummaries?.edges
|
||||
);
|
||||
|
||||
const assets = removePaginationWrapper(epochData?.assetsConnection?.edges);
|
||||
|
||||
// Because the epochRewardSummaries don't have the asset name, we need to find it in the assets list
|
||||
const epochSummariesWithNamedReward: EpochSummaryWithNamedReward[] =
|
||||
epochRewardSummaries.map((epochReward) => ({
|
||||
...epochReward,
|
||||
name:
|
||||
assets.find((asset) => asset.id === epochReward.assetId)?.name || '',
|
||||
}));
|
||||
|
||||
// Aggregating the epoch summaries by epoch number
|
||||
const aggregatedEpochSummariesByEpochNumber =
|
||||
epochSummariesWithNamedReward.reduce((acc, epochReward) => {
|
||||
const epoch = epochReward.epoch;
|
||||
const epochSummaryIndex = acc.findIndex(
|
||||
(epochSummary) => epochSummary[0].epoch === epoch
|
||||
);
|
||||
|
||||
if (epochSummaryIndex === -1) {
|
||||
acc.push([epochReward]);
|
||||
} else {
|
||||
acc[epochSummaryIndex].push(epochReward);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as EpochSummaryWithNamedReward[][]);
|
||||
|
||||
// Now aggregate the array of arrays of epoch summaries by asset rewards.
|
||||
const aggregatedEpochSummaries: AggregatedEpochSummary[] =
|
||||
aggregatedEpochSummariesByEpochNumber.map((epochSummaries) => {
|
||||
const assetRewards = epochSummaries.reduce((acc, epochSummary) => {
|
||||
const assetRewardIndex = acc.findIndex(
|
||||
(assetReward) =>
|
||||
assetReward.assetId === epochSummary.assetId &&
|
||||
assetReward.name === epochSummary.name
|
||||
);
|
||||
|
||||
if (assetRewardIndex === -1) {
|
||||
acc.push({
|
||||
assetId: epochSummary.assetId,
|
||||
name: epochSummary.name,
|
||||
rewards: [
|
||||
{
|
||||
rewardType: epochSummary.rewardType,
|
||||
amount: epochSummary.amount,
|
||||
},
|
||||
],
|
||||
totalAmount: epochSummary.amount,
|
||||
});
|
||||
} else {
|
||||
acc[assetRewardIndex].rewards.push({
|
||||
rewardType: epochSummary.rewardType,
|
||||
amount: epochSummary.amount,
|
||||
});
|
||||
acc[assetRewardIndex].totalAmount = (
|
||||
Number(acc[assetRewardIndex].totalAmount) +
|
||||
Number(epochSummary.amount)
|
||||
).toString();
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as AggregatedEpochRewardSummary[]);
|
||||
|
||||
return {
|
||||
epoch: epochSummaries[0].epoch,
|
||||
assetRewards,
|
||||
};
|
||||
});
|
||||
|
||||
return aggregatedEpochSummaries;
|
||||
};
|
||||
@@ -47,48 +47,3 @@ query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
epoch
|
||||
assetId
|
||||
amount
|
||||
rewardType
|
||||
}
|
||||
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment EpochFields on Epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
|
||||
query Epoch {
|
||||
epoch {
|
||||
...EpochFields
|
||||
}
|
||||
}
|
||||
|
||||
+1
-121
@@ -15,22 +15,6 @@ export type RewardsQueryVariables = Types.Exact<{
|
||||
|
||||
export type RewardsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, rewardsConnection?: { __typename?: 'RewardsConnection', edges?: Array<{ __typename?: 'RewardEdge', node: { __typename?: 'Reward', rewardType: Types.AccountType, amount: string, percentageOfTotal: string, receivedAt: any, asset: { __typename?: 'Asset', id: string, symbol: string }, party: { __typename?: 'Party', id: string }, epoch: { __typename?: 'Epoch', id: string } } } | null> | null } | null, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
|
||||
|
||||
export type EpochRewardSummaryFieldsFragment = { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType };
|
||||
|
||||
export type EpochAssetsRewardsQueryVariables = Types.Exact<{
|
||||
epochRewardSummariesPagination?: Types.InputMaybe<Types.Pagination>;
|
||||
}>;
|
||||
|
||||
|
||||
export type EpochAssetsRewardsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string } } | null> | null } | null, epochRewardSummaries?: { __typename?: 'EpochRewardSummaryConnection', edges?: Array<{ __typename?: 'EpochRewardSummaryEdge', node: { __typename?: 'EpochRewardSummary', epoch: number, assetId: string, amount: string, rewardType: Types.AccountType } } | null> | null } | null, epoch: { __typename?: 'Epoch', timestamps: { __typename?: 'EpochTimestamps', expiry?: any | null } } };
|
||||
|
||||
export type EpochFieldsFragment = { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } };
|
||||
|
||||
export type EpochQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type EpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } } };
|
||||
|
||||
export const RewardFieldsFragmentDoc = gql`
|
||||
fragment RewardFields on Reward {
|
||||
rewardType
|
||||
@@ -55,24 +39,6 @@ export const DelegationFieldsFragmentDoc = gql`
|
||||
epoch
|
||||
}
|
||||
`;
|
||||
export const EpochRewardSummaryFieldsFragmentDoc = gql`
|
||||
fragment EpochRewardSummaryFields on EpochRewardSummary {
|
||||
epoch
|
||||
assetId
|
||||
amount
|
||||
rewardType
|
||||
}
|
||||
`;
|
||||
export const EpochFieldsFragmentDoc = gql`
|
||||
fragment EpochFields on Epoch {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
expiry
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const RewardsDocument = gql`
|
||||
query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
|
||||
party(id: $partyId) {
|
||||
@@ -131,90 +97,4 @@ export function useRewardsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<Re
|
||||
}
|
||||
export type RewardsQueryHookResult = ReturnType<typeof useRewardsQuery>;
|
||||
export type RewardsLazyQueryHookResult = ReturnType<typeof useRewardsLazyQuery>;
|
||||
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
||||
export const EpochAssetsRewardsDocument = gql`
|
||||
query EpochAssetsRewards($epochRewardSummariesPagination: Pagination) {
|
||||
assetsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
epochRewardSummaries(pagination: $epochRewardSummariesPagination) {
|
||||
edges {
|
||||
node {
|
||||
...EpochRewardSummaryFields
|
||||
}
|
||||
}
|
||||
}
|
||||
epoch {
|
||||
timestamps {
|
||||
expiry
|
||||
}
|
||||
}
|
||||
}
|
||||
${EpochRewardSummaryFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useEpochAssetsRewardsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useEpochAssetsRewardsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useEpochAssetsRewardsQuery` 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 } = useEpochAssetsRewardsQuery({
|
||||
* variables: {
|
||||
* epochRewardSummariesPagination: // value for 'epochRewardSummariesPagination'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEpochAssetsRewardsQuery(baseOptions?: Apollo.QueryHookOptions<EpochAssetsRewardsQuery, EpochAssetsRewardsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EpochAssetsRewardsQuery, EpochAssetsRewardsQueryVariables>(EpochAssetsRewardsDocument, options);
|
||||
}
|
||||
export function useEpochAssetsRewardsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EpochAssetsRewardsQuery, EpochAssetsRewardsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EpochAssetsRewardsQuery, EpochAssetsRewardsQueryVariables>(EpochAssetsRewardsDocument, options);
|
||||
}
|
||||
export type EpochAssetsRewardsQueryHookResult = ReturnType<typeof useEpochAssetsRewardsQuery>;
|
||||
export type EpochAssetsRewardsLazyQueryHookResult = ReturnType<typeof useEpochAssetsRewardsLazyQuery>;
|
||||
export type EpochAssetsRewardsQueryResult = Apollo.QueryResult<EpochAssetsRewardsQuery, EpochAssetsRewardsQueryVariables>;
|
||||
export const EpochDocument = gql`
|
||||
query Epoch {
|
||||
epoch {
|
||||
...EpochFields
|
||||
}
|
||||
}
|
||||
${EpochFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useEpochQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useEpochQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useEpochQuery` 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 } = useEpochQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useEpochQuery(baseOptions?: Apollo.QueryHookOptions<EpochQuery, EpochQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<EpochQuery, EpochQueryVariables>(EpochDocument, options);
|
||||
}
|
||||
export function useEpochLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<EpochQuery, EpochQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<EpochQuery, EpochQueryVariables>(EpochDocument, options);
|
||||
}
|
||||
export type EpochQueryHookResult = ReturnType<typeof useEpochQuery>;
|
||||
export type EpochLazyQueryHookResult = ReturnType<typeof useEpochLazyQuery>;
|
||||
export type EpochQueryResult = Apollo.QueryResult<EpochQuery, EpochQueryVariables>;
|
||||
export type RewardsQueryResult = Apollo.QueryResult<RewardsQuery, RewardsQueryVariables>;
|
||||
+60
-9
@@ -1,15 +1,66 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||
import { format } from 'date-fns';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../lib/date-formats';
|
||||
import type {
|
||||
DelegationFieldsFragment,
|
||||
RewardsQuery,
|
||||
RewardFieldsFragment,
|
||||
} from '../home/__generated__/Rewards';
|
||||
DelegationFieldsFragment,
|
||||
} from './__generated__/Rewards';
|
||||
import {
|
||||
formatNumber,
|
||||
removePaginationWrapper,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
|
||||
interface RewardInfoProps {
|
||||
data: RewardsQuery | undefined;
|
||||
currVegaKey: string;
|
||||
}
|
||||
|
||||
export const RewardInfo = ({ data, currVegaKey }: RewardInfoProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const rewards = React.useMemo(() => {
|
||||
if (!data?.party || !data.party.rewardsConnection?.edges?.length) return [];
|
||||
|
||||
return removePaginationWrapper(data.party.rewardsConnection.edges);
|
||||
}, [data]);
|
||||
|
||||
const delegations = React.useMemo(() => {
|
||||
if (!data?.party || !data.party.delegationsConnection?.edges?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return removePaginationWrapper(data.party.delegationsConnection.edges);
|
||||
}, [data]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
{t('Connected Vega key')}: {currVegaKey}
|
||||
</p>
|
||||
{rewards.length ? (
|
||||
rewards.map((reward, i) => {
|
||||
if (!reward) return null;
|
||||
return (
|
||||
<RewardTable
|
||||
key={i}
|
||||
reward={reward}
|
||||
delegations={delegations || []}
|
||||
/>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<p>{t('noRewards')}</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
interface RewardTableProps {
|
||||
reward: RewardFieldsFragment;
|
||||
@@ -23,7 +74,7 @@ export const RewardTable = ({ reward, delegations }: RewardTableProps) => {
|
||||
} = useAppState();
|
||||
|
||||
// Get your stake for epoch in which you have rewards
|
||||
const stakeForEpoch = useMemo(() => {
|
||||
const stakeForEpoch = React.useMemo(() => {
|
||||
if (!delegations.length) return '0';
|
||||
|
||||
const delegationsForEpoch = delegations
|
||||
@@ -1,64 +1,47 @@
|
||||
import { Button, Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatDistance } from 'date-fns';
|
||||
// @ts-ignore No types available for duration-js
|
||||
import Duration from 'duration-js';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { formatDistance } from 'date-fns';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Button,
|
||||
Callout,
|
||||
Intent,
|
||||
AsyncRenderer,
|
||||
Toggle,
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useNetworkParams,
|
||||
NetworkParams,
|
||||
createDocsLinks,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { ENV } from '../../../config';
|
||||
|
||||
import { EpochCountdown } from '../../../components/epoch-countdown';
|
||||
import { Heading } from '../../../components/heading';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../../contexts/app-state/app-state-context';
|
||||
import { useEpochQuery } from './__generated__/Rewards';
|
||||
|
||||
import { EpochCountdown } from '../../../components/epoch-countdown';
|
||||
import { Heading, SubHeading } from '../../../components/heading';
|
||||
import { RewardInfo } from '../epoch-individual-awards/reward-info';
|
||||
import { EpochRewards } from '../epoch-total-rewards/epoch-rewards';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
type RewardsView = 'total' | 'individual';
|
||||
import { RewardInfo } from './reward-info';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useNetworkParams, NetworkParams } from '@vegaprotocol/react-helpers';
|
||||
import { useRewardsQuery } from './__generated__/Rewards';
|
||||
|
||||
export const RewardsPage = () => {
|
||||
const { t } = useTranslation();
|
||||
const { VEGA_DOCS_URL } = useEnvironment();
|
||||
const { pubKey, pubKeys } = useVegaWallet();
|
||||
const [toggleRewardsView, setToggleRewardsView] =
|
||||
useState<RewardsView>('total');
|
||||
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const { appDispatch } = useAppState();
|
||||
const { delegationsPagination } = ENV;
|
||||
const { data, loading, error } = useRewardsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.reward_staking_delegation_payoutDelay,
|
||||
]);
|
||||
|
||||
const {
|
||||
params,
|
||||
loading: paramsLoading,
|
||||
error: paramsError,
|
||||
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
|
||||
|
||||
const {
|
||||
data: epochData,
|
||||
loading: epochLoading,
|
||||
error: epochError,
|
||||
refetch,
|
||||
} = useEpochQuery();
|
||||
useRefreshAfterEpoch(epochData?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
const payoutDuration = useMemo(() => {
|
||||
const payoutDuration = React.useMemo(() => {
|
||||
if (!params) {
|
||||
return 0;
|
||||
}
|
||||
@@ -67,112 +50,76 @@ export const RewardsPage = () => {
|
||||
).milliseconds();
|
||||
}, [params]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<section>
|
||||
<p>{t('Something went wrong')}</p>
|
||||
{error && <pre>{error.message}</pre>}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
if (loading || !params) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashLoader />
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={paramsLoading || epochLoading}
|
||||
error={paramsError || epochError}
|
||||
data={epochData}
|
||||
render={() => (
|
||||
<section className="rewards">
|
||||
<Heading title={t('pageTitleRewards')} />
|
||||
<p className="mb-12">
|
||||
{t('rewardsIntro')}{' '}
|
||||
{VEGA_DOCS_URL && (
|
||||
<ExternalLink
|
||||
href={createDocsLinks(VEGA_DOCS_URL).REWARDS_GUIDE}
|
||||
target="_blank"
|
||||
data-testid="rewards-guide-link"
|
||||
className="text-white"
|
||||
>
|
||||
{t('seeHowRewardsAreCalculated')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</p>
|
||||
|
||||
{payoutDuration ? (
|
||||
<div className="my-8">
|
||||
<Callout
|
||||
title={t('rewardsCallout', {
|
||||
duration: formatDistance(new Date(0), payoutDuration),
|
||||
})}
|
||||
headingLevel={3}
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p className="mb-0">{t('rewardsCalloutDetail')}</p>
|
||||
</Callout>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{epochData &&
|
||||
epochData.epoch.id &&
|
||||
epochData.epoch.timestamps.start &&
|
||||
epochData.epoch.timestamps.expiry && (
|
||||
<section className="mb-16">
|
||||
<EpochCountdown
|
||||
id={epochData.epoch.id}
|
||||
startDate={new Date(epochData.epoch.timestamps.start)}
|
||||
endDate={new Date(epochData.epoch.timestamps.expiry)}
|
||||
/>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="grid xl:grid-cols-2 gap-12 items-center mb-8">
|
||||
<div>
|
||||
<SubHeading title={t('rewardsAndFeesReceived')} />
|
||||
<p>
|
||||
{t(
|
||||
'ThisDoesNotIncludeFeesReceivedForMakersOrLiquidityProviders'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="max-w-[600px]">
|
||||
<Toggle
|
||||
name="epoch-reward-view-toggle"
|
||||
toggles={[
|
||||
{
|
||||
label: t('totalDistributed'),
|
||||
value: 'total',
|
||||
},
|
||||
{
|
||||
label: t('earnedByMe'),
|
||||
value: 'individual',
|
||||
},
|
||||
]}
|
||||
checkedValue={toggleRewardsView}
|
||||
onChange={(e) =>
|
||||
setToggleRewardsView(e.target.value as RewardsView)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<section className="rewards">
|
||||
<Heading title={t('pageTitleRewards')} />
|
||||
<p>{t('rewardsPara1')}</p>
|
||||
<p>{t('rewardsPara2')}</p>
|
||||
{payoutDuration ? (
|
||||
<div className="my-8">
|
||||
<Callout
|
||||
title={t('rewardsCallout', {
|
||||
duration: formatDistance(new Date(0), payoutDuration),
|
||||
})}
|
||||
headingLevel={3}
|
||||
intent={Intent.Warning}
|
||||
>
|
||||
<p className="mb-0">{t('rewardsPara3')}</p>
|
||||
</Callout>
|
||||
</div>
|
||||
) : null}
|
||||
{!loading &&
|
||||
data &&
|
||||
!error &&
|
||||
data.epoch.timestamps.start &&
|
||||
data.epoch.timestamps.expiry && (
|
||||
<section className="mb-8">
|
||||
<EpochCountdown
|
||||
// eslint-disable-next-line
|
||||
id={data!.epoch.id}
|
||||
startDate={new Date(data.epoch.timestamps.start)}
|
||||
// eslint-disable-next-line
|
||||
endDate={new Date(data.epoch.timestamps.expiry!)}
|
||||
/>
|
||||
</section>
|
||||
|
||||
{toggleRewardsView === 'total' ? (
|
||||
<EpochRewards />
|
||||
) : (
|
||||
<section>
|
||||
{pubKey && pubKeys?.length ? (
|
||||
<RewardInfo />
|
||||
) : (
|
||||
<div>
|
||||
<Button
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
onClick={() => {
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
});
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
>
|
||||
{t('connectVegaWallet')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<section>
|
||||
{pubKey && pubKeys?.length ? (
|
||||
<RewardInfo currVegaKey={pubKey} data={data} />
|
||||
) : (
|
||||
<div>
|
||||
<Button
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
onClick={() => {
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
|
||||
isOpen: true,
|
||||
});
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
>
|
||||
{t('connectVegaWallet')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SubHeading } from '../../components/heading';
|
||||
|
||||
export const NoRewards = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const classes = classNames(
|
||||
'flex flex-col items-center justify-center h-[300px] w-full',
|
||||
'border border-vega-dark-200'
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<SubHeading title={t('noRewardsHaveBeenDistributedYet')} />
|
||||
<p className="font-alpha text-xl">{t('checkBackSoon')}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { EpochCountdown } from '../../../components/epoch-countdown';
|
||||
import { useNodesQuery } from './__generated___/Nodes';
|
||||
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
import { ValidatorTables } from './validator-tables';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useRefreshValidators } from '../../../hooks/use-refresh-validators';
|
||||
|
||||
export const EpochData = () => {
|
||||
// errorPolicy due to vegaprotocol/vega issue 5898
|
||||
@@ -15,7 +15,7 @@ export const EpochData = () => {
|
||||
skip: !data?.epoch.id,
|
||||
});
|
||||
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
useRefreshValidators(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
|
||||
@@ -126,10 +126,10 @@ export const VotingPowerRenderer = ({ data }: VotingPowerRendererProps) => {
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div data-testid="unnormalised-voting-power-tooltip">
|
||||
<div>
|
||||
{t('unnormalisedVotingPower')}: {data.unnormalisedVotingPower}
|
||||
</div>
|
||||
<div data-testid="normalised-voting-power-tooltip">
|
||||
<div>
|
||||
{t('normalisedVotingPower')}: {data.normalisedVotingPower}
|
||||
</div>
|
||||
</>
|
||||
@@ -155,13 +155,13 @@ export const TotalStakeRenderer = ({ data }: TotalStakeRendererProps) => {
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div data-testid="staked-operator-tooltip">
|
||||
<div>
|
||||
{t('stakedByOperator')}: {data.stakedByOperator.toString()}
|
||||
</div>
|
||||
<div data-testid="staked-delegates-tooltip">
|
||||
<div>
|
||||
{t('stakedByDelegates')}: {data.stakedByDelegates.toString()}
|
||||
</div>
|
||||
<div data-testid="total-staked-tooltip">
|
||||
<div>
|
||||
{t('totalStake')}: <span className="font-bold">{data.stake}</span>
|
||||
</div>
|
||||
</>
|
||||
@@ -191,13 +191,13 @@ export const TotalPenaltiesRenderer = ({
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<div data-testid="performance-penalty-tooltip">
|
||||
<div>
|
||||
{t('performancePenalty')}: {data.performancePenalty}
|
||||
</div>
|
||||
<div data-testid="overstaked-penalty-tooltip">
|
||||
<div>
|
||||
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
||||
</div>
|
||||
<div data-testid="total-penalty-tooltip">
|
||||
<div>
|
||||
{t('totalPenalties')}:{' '}
|
||||
<span className="font-bold">{data.totalPenalties}</span>
|
||||
</div>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ENV } from '../../../config';
|
||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useRefreshValidators } from '../../../hooks/use-refresh-validators';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { useStakingQuery } from './__generated__/Staking';
|
||||
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
|
||||
@@ -45,7 +45,7 @@ export const NodeContainer = ({
|
||||
skip: !data?.epoch.id,
|
||||
});
|
||||
|
||||
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
|
||||
useRefreshValidators(data?.epoch.timestamps.expiry, refetch);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
||||
@@ -32,14 +32,11 @@ const usdcSymbol = 'fUSDC';
|
||||
const toastContent = 'toast-content';
|
||||
const ordersTab = 'Orders';
|
||||
const depositsTab = 'Deposits';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
const price = '390';
|
||||
const size = '0.0005';
|
||||
const newPrice = '200';
|
||||
const completeWithdrawalBtn = 'complete-withdrawal';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const transferForm = 'transfer-form';
|
||||
|
||||
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
|
||||
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
@@ -108,39 +105,12 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('can key to key transfers', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
||||
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId('open-transfer-dialog').click();
|
||||
cy.getByTestId('transfer-form').should('be.visible');
|
||||
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
||||
cy.get('select option')
|
||||
.contains('BTC')
|
||||
.invoke('index')
|
||||
.then((index) => {
|
||||
cy.get(assetSelectField).select(index, { force: true });
|
||||
});
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.focus()
|
||||
.type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Transfer completeYour transaction has been confirmed TransferTo 7f9cf0…c255351.00 tBTC'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('can not withdrawal because of no MultiSign', function () {
|
||||
// 1002-WITH-022
|
||||
// 1002-WITH-023
|
||||
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
@@ -148,7 +118,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
|
||||
cy.getByTestId('tab-withdrawals').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
@@ -165,7 +135,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
'contain.text',
|
||||
'Error occurredprocessing response error'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId(completeWithdrawalBtn).should(
|
||||
'contain.text',
|
||||
'Complete withdrawal'
|
||||
@@ -258,8 +227,8 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
// comment because of bug #2695
|
||||
it.skip('can edit order', function () {
|
||||
|
||||
it('can edit order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('edit').first().should('be.visible').click();
|
||||
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
||||
@@ -284,8 +253,8 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
|
||||
});
|
||||
});
|
||||
// comment because of bug #2695
|
||||
it.skip('can cancel order', function () {
|
||||
|
||||
it('can cancel order', function () {
|
||||
cy.getByTestId(ordersTab).click();
|
||||
cy.getByTestId('cancel').first().click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
@@ -332,6 +301,7 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
|
||||
cy.getByTestId('tab-withdrawals').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
@@ -348,19 +318,8 @@ describe('capsule', { tags: '@slow' }, () => {
|
||||
'contain.text',
|
||||
'Transaction confirmed'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
|
||||
cy.wrap(null).then(() => {
|
||||
try {
|
||||
cy.getByTestId(completeWithdrawalBtn)
|
||||
.eq(0, txTimeout)
|
||||
.should('not.exist');
|
||||
} catch (error) {
|
||||
console.log(
|
||||
'Assertion failed, but we are continuing because this is our wait to complete transaction'
|
||||
);
|
||||
}
|
||||
});
|
||||
cy.getByTestId(completeWithdrawalBtn).eq(0, txTimeout).should('not.exist');
|
||||
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
|
||||
@@ -59,9 +59,11 @@ describe('home', { tags: '@regression' }, () => {
|
||||
describe('default market found', () => {
|
||||
it('redirects to a default market with the landing dialog open', () => {
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
|
||||
cy.get('main[data-testid^="/markets/"]');
|
||||
cy.get('main', { timeout: 20000 }).then((el) => {
|
||||
expect(el.attr('data-testid')?.startsWith('/market')).to.equal(true);
|
||||
}); // Wait for page to be rendered to before checking url
|
||||
|
||||
// Overlay should be shown
|
||||
cy.getByTestId(selectMarketOverlay).should('exist');
|
||||
@@ -99,7 +101,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-1');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -123,7 +125,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
aliasGQLQuery(req, 'MarketsData', data);
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId(selectMarketOverlay)
|
||||
.get('table')
|
||||
.invoke('outerWidth')
|
||||
@@ -231,7 +233,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-1');
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.location('hash').should('equal', '#/markets/market-1');
|
||||
cy.getByTestId('dialog-content').should('not.exist');
|
||||
});
|
||||
@@ -244,7 +246,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
aliasGQLQuery(req, 'Market', null);
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.location('hash').should('equal', '#/markets/market-not-existing');
|
||||
cy.getByTestId('dialog-content').should('not.exist');
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId(marketInfoBtn).click();
|
||||
cy.wait('@MarketInfo');
|
||||
});
|
||||
@@ -72,9 +72,6 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
|
||||
it('settlement asset displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Settlement asset').click();
|
||||
cy.window().then((win) => {
|
||||
cy.stub(win, 'prompt').returns('DISABLED WINDOW PROMPT');
|
||||
});
|
||||
validateMarketDataRow(0, 'ID', 'asset-id');
|
||||
validateMarketDataRow(1, 'Type', 'ERC20');
|
||||
validateMarketDataRow(2, 'Name', 'Euro');
|
||||
|
||||
@@ -237,6 +237,7 @@ describe('market states not accepting orders', { tags: '@smoke' }, function () {
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
it('must display that market is not accepting orders', function () {
|
||||
cy.getByTestId('place-order').click();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsQuery } from '@vegaprotocol/mock';
|
||||
import { marketQuery } from '@vegaprotocol/mock';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||
|
||||
describe('markets table', { tags: '@smoke' }, () => {
|
||||
@@ -13,6 +13,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
cy.wait('@Market');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.wait('@MarketsCandles');
|
||||
@@ -122,24 +123,17 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
);
|
||||
cy.mockGQL((req) => {
|
||||
const override = {
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: `opening auction MARKET`,
|
||||
},
|
||||
},
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode:
|
||||
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
},
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: `opening auction MARKET`,
|
||||
},
|
||||
],
|
||||
},
|
||||
state: Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
||||
},
|
||||
};
|
||||
const market = marketsQuery(override);
|
||||
const market = marketQuery(override);
|
||||
aliasGQLQuery(req, 'Market', market);
|
||||
aliasGQLQuery(req, 'ProposalOfMarket', {
|
||||
proposal: { terms: { enactmentDatetime: '2023-01-31 12:00:01' } },
|
||||
|
||||
@@ -4,15 +4,14 @@ before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
|
||||
describe('Desktop view', { tags: '@smoke' }, () => {
|
||||
describe('Navbar', () => {
|
||||
const links = ['Markets', 'Trading', 'Portfolio'];
|
||||
const hashes = ['#/markets/all', '#/markets/market-1', '#/portfolio'];
|
||||
const hashes = ['#/markets/all', '#/markets/market-0', '#/portfolio'];
|
||||
|
||||
links.forEach((link, index) => {
|
||||
it(`${link} should be correctly rendered`, () => {
|
||||
@@ -68,7 +67,7 @@ describe('Mobile view', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').within((el) => {
|
||||
cy.wrap(el).getByTestId('Trading').click();
|
||||
cy.location('hash').should('equal', '#/markets/market-1');
|
||||
cy.location('hash').should('equal', '#/markets/market-0');
|
||||
});
|
||||
});
|
||||
it('Portfolio should be correctly rendered', () => {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockWeb3Provider();
|
||||
@@ -38,96 +36,6 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="deposited"]')
|
||||
.should('have.text', '1,001.00');
|
||||
});
|
||||
describe('sorting by ag-grid columns should work well', () => {
|
||||
it('sorting by asset', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = ['tBTC', 'AST0', 'tEURO', 'tDAI', 'tBTC'];
|
||||
const marketsSortedAsc = ['AST0', 'tBTC', 'tBTC', 'tDAI', 'tEURO'];
|
||||
const marketsSortedDesc = ['tEURO', 'tDAI', 'tBTC', 'tBTC', 'AST0'];
|
||||
checkSorting(
|
||||
'asset.symbol',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
|
||||
it('sorting by total', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = [
|
||||
'1,000.00002',
|
||||
'1,001.00',
|
||||
'1,000.01',
|
||||
'1,000.01',
|
||||
'1,000.00001',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'1,000.00001',
|
||||
'1,000.00002',
|
||||
'1,000.01',
|
||||
'1,000.01',
|
||||
'1,001.00',
|
||||
];
|
||||
const marketsSortedDesc = [
|
||||
'1,001.00',
|
||||
'1,000.01',
|
||||
'1,000.01',
|
||||
'1,000.00002',
|
||||
'1,000.00001',
|
||||
];
|
||||
checkSorting(
|
||||
'deposited',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
|
||||
it('sorting by used', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = ['0.00', '1.00', '0.01', '0.01', '0.00'];
|
||||
const marketsSortedAsc = ['0.00', '0.00', '0.01', '0.01', '1.00'];
|
||||
const marketsSortedDesc = ['1.00', '0.01', '0.01', '0.00', '0.00'];
|
||||
checkSorting(
|
||||
'used',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
|
||||
it('sorting by available', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = [
|
||||
'1,000.00002',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
'1,000.00002',
|
||||
];
|
||||
const marketsSortedDesc = [
|
||||
'1,000.00002',
|
||||
'1,000.00001',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
];
|
||||
|
||||
checkSorting(
|
||||
'available',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
.should('have.text', '1,000.00');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('time in force default values', () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('must have market order set up to IOC by default', function () {
|
||||
@@ -64,7 +64,7 @@ describe('must submit order', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -162,7 +162,7 @@ describe(
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -231,7 +231,7 @@ describe(
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -300,7 +300,7 @@ describe(
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -360,7 +360,7 @@ describe('deal ticket validation', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('must not place an order if wallet is not connected', () => {
|
||||
@@ -405,7 +405,7 @@ describe('deal ticket size validation', { tags: '@smoke' }, function () {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('must warn if order size input has too many digits after the decimal place', function () {
|
||||
@@ -440,7 +440,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
});
|
||||
|
||||
@@ -452,7 +452,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
|
||||
//7002-SORD-018
|
||||
cy.getByTestId(orderPriceField)
|
||||
.siblings('label')
|
||||
.should('have.text', 'Price (DAI)');
|
||||
.should('have.text', 'Price (BTC)');
|
||||
});
|
||||
|
||||
it('must see warning when placing an order with expiry date in past', () => {
|
||||
@@ -532,7 +532,7 @@ describe('market order validations', { tags: '@smoke' }, () => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
cy.getByTestId(toggleMarket).click();
|
||||
});
|
||||
|
||||
@@ -586,7 +586,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -648,7 +648,7 @@ describe('account validation', { tags: '@regression' }, () => {
|
||||
market: null,
|
||||
asset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-0',
|
||||
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -660,7 +660,7 @@ describe('account validation', { tags: '@regression' }, () => {
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('should show an error if your balance is zero', () => {
|
||||
@@ -670,7 +670,7 @@ describe('account validation', { tags: '@regression' }, () => {
|
||||
//7002-SORD-003
|
||||
cy.getByTestId('dealticket-error-message-zero-balance').should(
|
||||
'have.text',
|
||||
'Insufficient balance. Deposit ' + 'tDAI'
|
||||
'Insufficient balance. Deposit ' + 'tBTC'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
|
||||
});
|
||||
@@ -696,7 +696,7 @@ describe('account validation', { tags: '@regression' }, () => {
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@Market');
|
||||
});
|
||||
|
||||
it('should display info and button for deposit', () => {
|
||||
@@ -708,7 +708,7 @@ describe('account validation', { tags: '@regression' }, () => {
|
||||
);
|
||||
cy.getByTestId('dealticket-warning-margin').should(
|
||||
'contain.text',
|
||||
'9,999.99 tDAI currently required, 1,000.00 tDAI available'
|
||||
'9,999.99 tBTC currently required, 1,000.00 tBTC available'
|
||||
);
|
||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||
cy.getByTestId('dialog-content')
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsDataQuery } from '@vegaprotocol/mock';
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
@@ -21,95 +17,6 @@ describe('positions', { tags: '@smoke' }, () => {
|
||||
validatePositionsDisplayed();
|
||||
});
|
||||
|
||||
it('renders position among some graphql errors', () => {
|
||||
const errors = [
|
||||
{
|
||||
message: 'no market data for market: market-2',
|
||||
path: ['market', 'data'],
|
||||
extensions: {
|
||||
code: 13,
|
||||
type: 'Internal',
|
||||
},
|
||||
},
|
||||
];
|
||||
const marketData = marketsDataQuery();
|
||||
const edges = marketData.marketsConnection?.edges.map((market) => {
|
||||
const replace =
|
||||
market.node.data?.market.id === 'market-2' ? null : market.node.data;
|
||||
return { ...market, node: { ...market.node, data: replace } };
|
||||
});
|
||||
const overrides = {
|
||||
...marketData,
|
||||
marketsConnection: { ...marketData.marketsConnection, edges },
|
||||
};
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', overrides, errors);
|
||||
});
|
||||
cy.visit('/#/markets/market-0');
|
||||
const emptyCells = [
|
||||
'notional',
|
||||
'markPrice',
|
||||
'liquidationPrice',
|
||||
'currentLeverage',
|
||||
'averageEntryPrice',
|
||||
];
|
||||
cy.getByTestId('tab-positions').within(() => {
|
||||
cy.get('[row-id="market-2"]').within(() => {
|
||||
emptyCells.forEach((cell) => {
|
||||
cy.get(`[col-id="${cell}"]`).should('contain.text', '-');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sorting by ag-grid columns should work well', () => {
|
||||
it('sorting by Market', () => {
|
||||
cy.visit('/#/markets/market-0');
|
||||
const marketsSortedDefault = [
|
||||
'ACTIVE MARKET',
|
||||
'Apple Monthly (30 Jun 2022)',
|
||||
];
|
||||
const marketsSortedAsc = ['ACTIVE MARKET', 'Apple Monthly (30 Jun 2022)'];
|
||||
const marketsSortedDesc = [
|
||||
'Apple Monthly (30 Jun 2022)',
|
||||
'ACTIVE MARKET',
|
||||
];
|
||||
cy.getByTestId('Positions').click();
|
||||
checkSorting(
|
||||
'marketName',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
it('sorting by notional', () => {
|
||||
cy.visit('/#/markets/market-0');
|
||||
const marketsSortedDefault = ['276,761.40348', '46,126.90058'];
|
||||
const marketsSortedAsc = ['46,126.90058', '276,761.40348'];
|
||||
const marketsSortedDesc = ['276,761.40348', '46,126.90058'];
|
||||
cy.getByTestId('Positions').click();
|
||||
checkSorting(
|
||||
'notional',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
it('sorting by unrealisedPNL', () => {
|
||||
cy.visit('/#/markets/market-0');
|
||||
const marketsSortedDefault = ['8.95', '-0.22519'];
|
||||
const marketsSortedAsc = ['-0.22519', '8.95'];
|
||||
const marketsSortedDesc = ['8.95', '-0.22519'];
|
||||
cy.getByTestId('Positions').click();
|
||||
checkSorting(
|
||||
'unrealisedPNL',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
function validatePositionsDisplayed() {
|
||||
cy.getByTestId('tab-positions').should('be.visible');
|
||||
cy.getByTestId('tab-positions').within(() => {
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const formFieldError = 'input-error-text';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const amountField = 'input[name="amount"]';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const transferForm = 'transfer-form';
|
||||
const errorText = 'input-error-text';
|
||||
const openTransferDialog = 'open-transfer-dialog';
|
||||
const closeDialog = 'dialog-close';
|
||||
const dialogTransferText = 'dialog-transfer-text';
|
||||
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
const ASSET_EURO = 1;
|
||||
|
||||
const toastContent = 'toast-content';
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
|
||||
describe(
|
||||
'transfer form validation and transfer from options',
|
||||
{ tags: '@smoke' },
|
||||
() => {
|
||||
before(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
// only 2 despite 3 fields because the ethereum address will be auto populated
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Value is below minimum'
|
||||
);
|
||||
});
|
||||
it('max amount', () => {
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from vega wallet', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('wallet-transfer').should('have.text', 'Transfer').click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
|
||||
it('can start transfer from trading collateral table', () => {
|
||||
cy.getByTestId(closeDialog).click();
|
||||
cy.getByTestId('Trading').first().click();
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
cy.getByTestId(dialogTransferText).should(
|
||||
'contain.text',
|
||||
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(collateralTab).click();
|
||||
cy.getByTestId(openTransferDialog).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('key to key transfers by select key', function () {
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.getByTestId(transferForm).find(toAddressField).select(1);
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
|
||||
it('key to key transfers by enter manual key', function () {
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(toastContent).should(
|
||||
'contain.text',
|
||||
'Awaiting confirmation'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
});
|
||||
});
|
||||
@@ -66,7 +66,7 @@ describe('withdraw form validation', { tags: '@smoke' }, () => {
|
||||
// 1002-WITH-004
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(useMaximumAmount).click();
|
||||
cy.get(amountField).should('have.value', '1000.00001');
|
||||
cy.get(amountField).should('have.value', '1000.00000');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -102,10 +102,7 @@ describe('withdraw actions', { tags: '@regression' }, () => {
|
||||
'contain.text',
|
||||
'Balance available'
|
||||
);
|
||||
cy.getByTestId('BALANCE_AVAILABLE_value').should(
|
||||
'have.text',
|
||||
'1,000.00001'
|
||||
);
|
||||
cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '1,000.00');
|
||||
cy.getByTestId('WITHDRAWAL_THRESHOLD_label').should(
|
||||
'contain.text',
|
||||
'Delayed withdrawal threshold'
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
marketDataQuery,
|
||||
marketDepthQuery,
|
||||
marketInfoQuery,
|
||||
marketQuery,
|
||||
marketsCandlesQuery,
|
||||
marketsDataQuery,
|
||||
marketsQuery,
|
||||
@@ -27,7 +28,7 @@ import {
|
||||
withdrawalsQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
|
||||
import type { MarketDataQuery, MarketQuery } from '@vegaprotocol/market-list';
|
||||
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
|
||||
|
||||
type MarketPageMockData = {
|
||||
@@ -54,18 +55,17 @@ const marketDataOverride = (
|
||||
},
|
||||
});
|
||||
|
||||
const marketsDataOverride = (
|
||||
const marketQueryOverride = (
|
||||
data: MarketPageMockData
|
||||
): PartialDeep<MarketsQuery> => ({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
tradingMode: data.tradingMode,
|
||||
state: data.state,
|
||||
},
|
||||
): PartialDeep<MarketQuery> => ({
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
name: `${data.state?.toUpperCase()} MARKET`,
|
||||
},
|
||||
],
|
||||
},
|
||||
state: data.state,
|
||||
tradingMode: data.tradingMode,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -91,9 +91,10 @@ const mockTradingPage = (
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'Markets',
|
||||
marketsQuery(marketsDataOverride({ state, tradingMode, trigger }))
|
||||
'Market',
|
||||
marketQuery(marketQueryOverride({ state, tradingMode, trigger }))
|
||||
);
|
||||
aliasGQLQuery(req, 'Markets', marketsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketData',
|
||||
|
||||
@@ -59,7 +59,7 @@ export const LiquidityContainer = ({
|
||||
marketId: string | undefined;
|
||||
}) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: market } = useMarket(marketId);
|
||||
const market = useMarket(marketId);
|
||||
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
|
||||
|
||||
// To be removed when liquidityProvision subscriptions are working
|
||||
@@ -129,8 +129,8 @@ export const LiquidityViewContainer = ({
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: market } = useMarket(marketId);
|
||||
const { data: marketData } = useStaticMarketData(marketId);
|
||||
const market = useMarket(marketId);
|
||||
const marketData = useStaticMarketData(marketId);
|
||||
|
||||
const dataRef = useRef<LiquidityProvisionData[] | null>(null);
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import type {
|
||||
SingleMarketFieldsFragment,
|
||||
MarketData,
|
||||
Candle,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
|
||||
@@ -25,6 +27,11 @@ const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||
: '-';
|
||||
};
|
||||
|
||||
export interface SingleMarketData extends SingleMarketFieldsFragment {
|
||||
candles: Candle[];
|
||||
data: MarketData;
|
||||
}
|
||||
|
||||
const TitleUpdater = ({
|
||||
marketId,
|
||||
marketName,
|
||||
@@ -82,9 +89,12 @@ export const MarketPage = () => {
|
||||
[marketId, navigate]
|
||||
);
|
||||
|
||||
const { data, error, loading } = useDataProvider({
|
||||
const { data, error, loading } = useDataProvider<
|
||||
SingleMarketFieldsFragment,
|
||||
never
|
||||
>({
|
||||
dataProvider: marketProvider,
|
||||
variables: { marketId: marketId || '' },
|
||||
variables: useMemo(() => ({ marketId: marketId || '' }), [marketId]),
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
@@ -105,7 +115,7 @@ export const MarketPage = () => {
|
||||
<Splash>
|
||||
<span className="flex flex-col items-center gap-2">
|
||||
<p className="text-sm justify-center">
|
||||
{t('This market URL is not available any more.')}
|
||||
{t('This market URL is not available anymore.')}
|
||||
</p>
|
||||
<p className="text-sm justify-center">
|
||||
{t(`Please choose another market from the`)}{' '}
|
||||
@@ -119,7 +129,7 @@ export const MarketPage = () => {
|
||||
}
|
||||
|
||||
return (
|
||||
<AsyncRenderer
|
||||
<AsyncRenderer<SingleMarketFieldsFragment>
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={data || undefined}
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { TradeMarketHeader } from './trade-market-header';
|
||||
import { NO_MARKET } from './constants';
|
||||
@@ -63,7 +63,7 @@ const TradingViews = {
|
||||
type TradingView = keyof typeof TradingViews;
|
||||
|
||||
interface TradeGridProps {
|
||||
market: Market | null;
|
||||
market: SingleMarketFieldsFragment | null;
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
@@ -207,7 +207,7 @@ const TradeGridChild = ({ children }: TradeGridChildProps) => {
|
||||
};
|
||||
|
||||
interface TradePanelsProps {
|
||||
market: Market | null;
|
||||
market: SingleMarketFieldsFragment | null;
|
||||
onSelect: (marketId: string) => void;
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketProposalNotification } from '@vegaprotocol/governance';
|
||||
import type { Market } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
getExpiryDate,
|
||||
getMarketExpiryDate,
|
||||
t,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import type { SingleMarketFieldsFragment } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
ColumnKind,
|
||||
SelectMarketPopover,
|
||||
@@ -24,7 +24,7 @@ import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
||||
import { MarketState as State } from '@vegaprotocol/types';
|
||||
|
||||
interface TradeMarketHeaderProps {
|
||||
market: Market | null;
|
||||
market: SingleMarketFieldsFragment | null;
|
||||
onSelect: (marketId: string) => void;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ export const TradeMarketHeader = ({
|
||||
};
|
||||
|
||||
type ExpiryLabelProps = {
|
||||
market: Market | null;
|
||||
market: SingleMarketFieldsFragment | null;
|
||||
};
|
||||
|
||||
const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
||||
@@ -140,7 +140,7 @@ const ExpiryLabel = ({ market }: ExpiryLabelProps) => {
|
||||
};
|
||||
|
||||
type ExpiryTooltipContentProps = {
|
||||
market: Market;
|
||||
market: SingleMarketFieldsFragment;
|
||||
explorerUrl?: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -43,11 +43,7 @@ export const AccountsContainer = () => {
|
||||
</div>
|
||||
{!isReadOnly && (
|
||||
<div className="flex gap-2 justify-end p-2 px-[11px]">
|
||||
<Button
|
||||
size="sm"
|
||||
data-testid="open-transfer-dialog"
|
||||
onClick={() => openTransferDialog()}
|
||||
>
|
||||
<Button size="sm" onClick={() => openTransferDialog()}>
|
||||
{t('Transfer')}
|
||||
</Button>
|
||||
<Button size="sm" onClick={() => openDepositDialog()}>
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import React from 'react';
|
||||
|
||||
export const Banner = () => {
|
||||
const { update, shouldDisplayAnnouncementBanner } = useGlobalStore(
|
||||
@@ -29,8 +28,10 @@ export const Banner = () => {
|
||||
<Icon name="cross" className="w-6 h-6" ariaLabel="dismiss" />
|
||||
</button>
|
||||
<div>
|
||||
<span className="pr-4">Mainnet sim 2 coming in March!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">Learn more</ExternalLink>
|
||||
<span className="pr-4">The Mainnet sims are live!</span>
|
||||
<ExternalLink href="https://fairground.wtf/">
|
||||
Come help stress test the network
|
||||
</ExternalLink>
|
||||
</div>
|
||||
</div>
|
||||
</AnnouncementBanner>
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
import type {
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
SingleMarketFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
@@ -43,12 +44,12 @@ export const MarketLiquiditySupplied = ({
|
||||
|
||||
const variables = useMemo(
|
||||
() => ({
|
||||
marketId: marketId || '',
|
||||
marketId: marketId,
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
|
||||
const { data } = useDataProvider({
|
||||
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
|
||||
@@ -2,7 +2,7 @@ import throttle from 'lodash/throttle';
|
||||
import type {
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
Market,
|
||||
SingleMarketFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
@@ -11,7 +11,11 @@ import { HeaderStat } from '../header';
|
||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
||||
import * as constants from '../constants';
|
||||
|
||||
export const MarketState = ({ market }: { market: Market | null }) => {
|
||||
export const MarketState = ({
|
||||
market,
|
||||
}: {
|
||||
market: SingleMarketFieldsFragment | null;
|
||||
}) => {
|
||||
const [marketState, setMarketState] = useState<Schema.MarketState | null>(
|
||||
null
|
||||
);
|
||||
|
||||
@@ -1,24 +1,37 @@
|
||||
import type { RefObject } from 'react';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useMemo } from 'react';
|
||||
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { TradingModeTooltip } from '@vegaprotocol/deal-ticket';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { HeaderStat } from '../header';
|
||||
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { useStaticMarketData } from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider } from '@vegaprotocol/market-list';
|
||||
|
||||
// This will cause often re-rendering
|
||||
// Here it may not be a problem because the component is not very complex
|
||||
// In general, we should avoid using this marketData hook without any throttling
|
||||
const useMarketData = (marketId?: string, skip?: boolean) => {
|
||||
const variables = useMemo(() => ({ marketId }), [marketId]);
|
||||
const { data } = useDataProvider({
|
||||
dataProvider: marketDataProvider,
|
||||
variables,
|
||||
skip: skip || !marketId,
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
const getTradingModeLabel = (
|
||||
marketTradingMode?: Schema.MarketTradingMode,
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger
|
||||
) => {
|
||||
return (
|
||||
(marketTradingMode ===
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
(tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION &&
|
||||
trigger &&
|
||||
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
||||
? `${Schema.MarketTradingModeMapping[marketTradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`
|
||||
? `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`
|
||||
: Schema.MarketTradingModeMapping[
|
||||
marketTradingMode as Schema.MarketTradingMode
|
||||
tradingMode as Schema.MarketTradingMode
|
||||
]) || '-'
|
||||
);
|
||||
};
|
||||
@@ -36,8 +49,8 @@ export const HeaderStatMarketTradingMode = ({
|
||||
initialTradingMode,
|
||||
initialTrigger,
|
||||
}: HeaderStatMarketTradingModeProps) => {
|
||||
const { data } = useStaticMarketData(marketId);
|
||||
const marketTradingMode = data?.marketTradingMode ?? initialTradingMode;
|
||||
const data = useMarketData(marketId);
|
||||
const tradingMode = data?.marketTradingMode ?? initialTradingMode;
|
||||
const trigger = data?.trigger ?? initialTrigger;
|
||||
|
||||
return (
|
||||
@@ -48,7 +61,7 @@ export const HeaderStatMarketTradingMode = ({
|
||||
}
|
||||
testId="market-trading-mode"
|
||||
>
|
||||
<div>{getTradingModeLabel(marketTradingMode, trigger)}</div>
|
||||
<div>{getTradingModeLabel(tradingMode, trigger)}</div>
|
||||
</HeaderStat>
|
||||
);
|
||||
};
|
||||
@@ -62,7 +75,7 @@ export const MarketTradingMode = ({
|
||||
inViewRoot?: RefObject<Element>;
|
||||
}) => {
|
||||
const [ref, inView] = useInView({ root: inViewRoot?.current });
|
||||
const { data } = useStaticMarketData(marketId, !inView);
|
||||
const data = useMarketData(marketId, !inView);
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
import type {
|
||||
MarketData,
|
||||
MarketDataUpdateFieldsFragment,
|
||||
SingleMarketFieldsFragment,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { marketDataProvider, marketProvider } from '@vegaprotocol/market-list';
|
||||
import { HeaderStat } from '../header';
|
||||
@@ -21,7 +22,7 @@ export const MarketVolume = ({ marketId }: { marketId: string }) => {
|
||||
}),
|
||||
[marketId]
|
||||
);
|
||||
const { data } = useDataProvider({
|
||||
const { data } = useDataProvider<SingleMarketFieldsFragment, never>({
|
||||
dataProvider: marketProvider,
|
||||
variables,
|
||||
skip: !marketId,
|
||||
|
||||
@@ -14,7 +14,10 @@ import {
|
||||
import { Link as UILink, Sparkline, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import isNil from 'lodash/isNil';
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/market-list';
|
||||
import type {
|
||||
MarketWithData,
|
||||
MarketWithCandles,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MarketMarkPrice } from '../market-mark-price';
|
||||
import { Last24hPriceChange } from '../last-24h-price-change';
|
||||
@@ -22,6 +25,8 @@ import { MarketTradingMode } from '../market-trading-mode';
|
||||
import { Last24hVolume } from '../last-24h-volume';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
type Market = MarketWithData & MarketWithCandles;
|
||||
|
||||
const ellipsisClasses = 'whitespace-nowrap overflow-hidden text-ellipsis';
|
||||
export const cellClassNames = `py-1 first:text-left text-right ${ellipsisClasses}`;
|
||||
|
||||
@@ -166,7 +171,7 @@ export type OnCellClickHandler = (
|
||||
) => void;
|
||||
|
||||
export const columns = (
|
||||
market: MarketMaybeWithDataAndCandles,
|
||||
market: Market,
|
||||
onSelect: (id: string) => void,
|
||||
onCellClick: OnCellClickHandler,
|
||||
inViewRoot?: RefObject<HTMLElement>
|
||||
@@ -354,7 +359,7 @@ export const columns = (
|
||||
};
|
||||
|
||||
export const columnsPositionMarkets = (
|
||||
market: MarketMaybeWithDataAndCandles,
|
||||
market: Market,
|
||||
onSelect: (id: string) => void,
|
||||
inViewRoot?: RefObject<HTMLElement>,
|
||||
openVolume?: string,
|
||||
|
||||
@@ -4,13 +4,13 @@ import * as Schema from '@vegaprotocol/types';
|
||||
import { SelectAllMarketsTableBody } from './select-market';
|
||||
|
||||
import type {
|
||||
MarketMaybeWithCandles,
|
||||
MarketMaybeWithData,
|
||||
MarketWithCandles,
|
||||
MarketWithData,
|
||||
MarketData,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
type Market = MarketMaybeWithCandles & MarketMaybeWithData;
|
||||
type Market = MarketWithCandles & MarketWithData;
|
||||
|
||||
type PartialMarket = Partial<
|
||||
Omit<Market, 'data'> & { data: Partial<MarketData> }
|
||||
@@ -34,13 +34,9 @@ const MARKET_A: PartialMarket = {
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-ABC',
|
||||
name: '',
|
||||
decimals: 2,
|
||||
symbol: 'ABC',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
@@ -110,13 +106,9 @@ const MARKET_B: PartialMarket = {
|
||||
settlementAsset: {
|
||||
__typename: 'Asset',
|
||||
id: 'asset-XYZ',
|
||||
name: 'asset-XYZ',
|
||||
decimals: 2,
|
||||
symbol: 'XYZ',
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: '',
|
||||
},
|
||||
},
|
||||
metadata: {
|
||||
__typename: 'InstrumentMetadata',
|
||||
|
||||
@@ -17,7 +17,10 @@ import {
|
||||
SelectMarketTableRowSplash,
|
||||
} from './select-market-table';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/market-list';
|
||||
import type {
|
||||
MarketWithCandles,
|
||||
MarketWithData,
|
||||
} from '@vegaprotocol/market-list';
|
||||
import type { PositionFieldsFragment } from '@vegaprotocol/positions';
|
||||
import type { Column, OnCellClickHandler } from './select-market-columns';
|
||||
import {
|
||||
@@ -27,6 +30,8 @@ import {
|
||||
} from '@vegaprotocol/environment';
|
||||
import { HeaderTitle } from '../header';
|
||||
|
||||
export type Market = MarketWithCandles & MarketWithData;
|
||||
|
||||
export const SelectAllMarketsTableBody = ({
|
||||
markets,
|
||||
positions,
|
||||
@@ -36,14 +41,14 @@ export const SelectAllMarketsTableBody = ({
|
||||
headers = columnHeaders,
|
||||
tableColumns = (market) => columns(market, onSelect, onCellClick, inViewRoot),
|
||||
}: {
|
||||
markets?: MarketMaybeWithDataAndCandles[] | null;
|
||||
markets?: Market[] | null;
|
||||
positions?: PositionFieldsFragment[];
|
||||
title?: string;
|
||||
onSelect: (id: string) => void;
|
||||
onCellClick: OnCellClickHandler;
|
||||
headers?: Column[];
|
||||
tableColumns?: (
|
||||
market: MarketMaybeWithDataAndCandles,
|
||||
market: Market,
|
||||
inViewRoot?: RefObject<HTMLDivElement>,
|
||||
openVolume?: string
|
||||
) => Column[];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user