Compare commits

..
Author SHA1 Message Date
Dariusz Majcherczyk cb4aeb6d8d test: added check if the market not exist 2023-02-17 10:05:01 +01:00
Dariusz Majcherczyk e5bcc1dc96 test: wait for hash 2023-02-17 09:44:37 +01:00
Dariusz Majcherczyk 2a8094ed6d test: added wait for market 2023-02-17 09:16:41 +01:00
122 changed files with 2246 additions and 4666 deletions
-4
View File
@@ -18,10 +18,6 @@ jobs:
- name: Checkout
uses: actions/checkout@v2
- uses: actions/setup-node@v3
with:
node-version: 16
- name: Run Cypress tests
uses: cypress-io/github-action@v4
with:
+12 -3
View File
@@ -4,6 +4,15 @@ context('Asset page', { tags: '@regression' }, () => {
describe('Verify elements on page', () => {
before('Navigate to assets page', () => {
cy.visit('/assets');
// 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'
);
});
});
it('should be able to see full assets list', () => {
@@ -31,7 +40,7 @@ context('Asset page', { tags: '@regression' }, () => {
});
});
it('should open details page when clicked on "View details"', () => {
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`)
@@ -40,8 +49,8 @@ context('Asset page', { tags: '@regression' }, () => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
.eq(0)
.click();
cy.getByTestId('asset-header').should('have.text', asset.name);
cy.go('back');
cy.getByTestId('dialog-content').should('be.visible');
cy.getByTestId('dialog-close').click();
});
});
});
@@ -1,35 +1,22 @@
import { render, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { assetsList } from '../../mocks/assets';
import { AssetsTable } from './assets-table';
describe('AssetsTable', () => {
it('shows loading message on first render', async () => {
const res = render(
<MemoryRouter>
<AssetsTable data={null} />
</MemoryRouter>
);
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(
<MemoryRouter>
<AssetsTable data={[]} />
</MemoryRouter>
);
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(
<MemoryRouter>
<AssetsTable data={assetsList} />
</MemoryRouter>
);
const res = render(<AssetsTable data={assetsList} />);
await waitFor(() => {
const rowA1 = res.container.querySelector('[row-id="123"]');
expect(rowA1).toBeInTheDocument();
@@ -1,4 +1,5 @@
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';
@@ -8,14 +9,15 @@ 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';
import { useNavigate } from 'react-router-dom';
import type { RowClickedEvent } from 'ag-grid-community';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
};
export const AssetsTable = ({ data }: AssetsTableProps) => {
const navigate = useNavigate();
const openAssetDetailsDialog = useAssetDetailsDialogStore(
(state) => state.open
);
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
ref.current?.columnApi.setColumnsVisible(
@@ -47,23 +49,17 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
autoHeight: true,
}}
suppressCellFocus={true}
onRowClicked={({ data }: RowClickedEvent) => {
navigate(data.id);
onGridReady={() => {
showColumnsOnDesktop();
}}
>
<AgGridColumn headerName={t('Symbol')} field="symbol" />
<AgGridColumn headerName={t('Name')} field="name" />
<AgGridColumn
flex="2"
headerName={t('ID')}
field="id"
hide={window.innerWidth < BREAKPOINT_MD}
/>
<AgGridColumn flex="2" headerName={t('ID')} field="id" />
<AgGridColumn
colId="type"
headerName={t('Type')}
field="source.__typename"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetTypeMapping[value].value
}
@@ -71,7 +67,6 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
<AgGridColumn
headerName={t('Status')}
field="status"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetStatusMapping[value].value
}
@@ -88,13 +83,28 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<ButtonLink
onClick={(e) => {
navigate(value);
}}
>
{t('View details')}
</ButtonLink>
<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>
) : (
''
)
@@ -1,24 +1,17 @@
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
import { t, useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react';
import { ENV } from '../../config/env';
import { t } from '@vegaprotocol/react-helpers';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useState } from 'react';
export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
const [nodeSwitcherOpen, setNodeSwitcherOpen] = useState(false);
const { screenSize } = useScreenDimensions();
const showFullFeedbackLabel = useMemo(
() => ['lg', 'xl'].includes(screenSize),
[screenSize]
);
return (
<>
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] text-xs md:text-md md:flex md:col-span-2 px-4 py-2 gap-4 border-t border-neutral-700 dark:border-neutral-300">
<div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
{GIT_COMMIT_HASH && (
<p data-testid="git-commit-hash">
{t('Version')}:{' '}
<Link
@@ -32,21 +25,15 @@ export const Footer = () => {
{GIT_COMMIT_HASH}
</Link>
</p>
</div>
)}
)}
</div>
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
<div className="flex pl-2 content-center">
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
{t('Change')}
</Link>
</div>
<div className="flex pl-2 content-center">
<ExternalLink href={ENV.addresses.feedback}>
{showFullFeedbackLabel ? t('Share your feedback') : t('Feedback')}
</ExternalLink>
</div>
</div>
</footer>
<NodeSwitcherDialog
@@ -5,12 +5,9 @@ import {
useAssetDataProvider,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
import { useNavigate } from 'react-router-dom';
import { Routes } from '../../../routes/route-names';
export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
assetId: string;
asDialog?: boolean;
};
/**
@@ -18,22 +15,17 @@ export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
* with a link to the assets modal. If the name does not come back
* it will use the ID instead.
*/
export const AssetLink = ({ assetId, asDialog, ...props }: AssetLinkProps) => {
export const AssetLink = ({ assetId, ...props }: AssetLinkProps) => {
const { data: asset } = useAssetDataProvider(assetId);
const open = useAssetDetailsDialogStore((state) => state.open);
const navigate = useNavigate();
const label = asset?.name ? asset.name : assetId;
return (
<ButtonLink
data-testid="asset-link"
disabled={!asset}
onClick={(e) => {
if (asDialog) {
open(assetId, e.target as HTMLElement);
} else {
navigate(`${Routes.ASSETS}/${asset?.id}`);
}
open(assetId, e.target as HTMLElement);
}}
{...props}
>
@@ -1,227 +0,0 @@
import type { ProposalListFieldsFragment } from '@vegaprotocol/governance';
import { VoteProgress } from '@vegaprotocol/governance';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react';
import type {
VegaICellRendererParams,
VegaValueFormatterParams,
} from '@vegaprotocol/ui-toolkit';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { RowClickedEvent } from 'ag-grid-community';
import {
getDateTimeFormat,
NetworkParams,
t,
useNetworkParams,
} from '@vegaprotocol/react-helpers';
import { ProposalStateMapping } from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
import { DApp, TOKEN_PROPOSAL, useLinks } from '@vegaprotocol/environment';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { JsonViewerDialog } from '../dialogs/json-viewer-dialog';
type ProposalTermsDialog = {
open: boolean;
title: string;
content: unknown;
};
type ProposalsTableProps = {
data: ProposalListFieldsFragment[] | null;
};
export const ProposalsTable = ({ data }: ProposalsTableProps) => {
const { params } = useNetworkParams([
NetworkParams.governance_proposal_market_requiredMajority,
]);
const tokenLink = useLinks(DApp.Token);
const requiredMajorityPercentage = useMemo(() => {
const requiredMajority =
params?.governance_proposal_market_requiredMajority ?? 1;
return new BigNumber(requiredMajority).times(100);
}, [params?.governance_proposal_market_requiredMajority]);
const gridRef = useRef<AgGridReact>(null);
useLayoutEffect(() => {
const showColumnsOnDesktop = () => {
gridRef.current?.columnApi.setColumnsVisible(
['voting', 'cDate', 'eDate', 'type'],
window.innerWidth > BREAKPOINT_MD
);
gridRef.current?.columnApi.setColumnWidth(
'actions',
window.innerWidth > BREAKPOINT_MD ? 221 : 80
);
};
window.addEventListener('resize', showColumnsOnDesktop);
return () => {
window.removeEventListener('resize', showColumnsOnDesktop);
};
}, []);
const [dialog, setDialog] = useState<ProposalTermsDialog>({
open: false,
title: '',
content: null,
});
return (
<>
<AgGrid
ref={gridRef}
rowData={data}
getRowId={({ data }: { data: ProposalListFieldsFragment }) =>
data.id || data.rationale.title
}
overlayNoRowsTemplate={t('This chain has no markets')}
domLayout="autoHeight"
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
suppressCellFocus={true}
onRowClicked={({ data, event }: RowClickedEvent) => {
if (
(event?.target as HTMLElement).tagName.toUpperCase() !== 'BUTTON'
) {
const proposalPage = tokenLink(
TOKEN_PROPOSAL.replace(':id', data.id)
);
window.open(proposalPage, '_blank');
}
}}
>
<AgGridColumn
colId="title"
headerName={t('Title')}
field="rationale.title"
flex={2}
wrapText={true}
/>
<AgGridColumn
colId="type"
maxWidth={180}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Type')}
field="terms.change.__typename"
/>
<AgGridColumn
maxWidth={100}
headerName={t('State')}
field="state"
valueFormatter={({
value,
}: VegaValueFormatterParams<ProposalListFieldsFragment, 'state'>) => {
return value ? ProposalStateMapping[value] : '-';
}}
/>
<AgGridColumn
colId="voting"
maxWidth={100}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Voting')}
cellRenderer={({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
if (data) {
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
const noTokens = new BigNumber(data.votes.no.totalTokens);
const totalTokensVoted = yesTokens.plus(noTokens);
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="uppercase flex h-full items-center justify-center pt-2">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</div>
);
}
return '-';
}}
/>
<AgGridColumn
colId="cDate"
maxWidth={150}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Closing date')}
field="terms.closingDatetime"
valueFormatter={({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.closingDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
}}
/>
<AgGridColumn
colId="eDate"
maxWidth={150}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Enactment date')}
field="terms.enactmentDatetime"
valueFormatter={({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.enactmentDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
}}
/>
<AgGridColumn
colId="actions"
minWidth={window.innerWidth > BREAKPOINT_MD ? 221 : 80}
maxWidth={221}
sortable={false}
filter={false}
resizable={false}
cellRenderer={({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
const proposalPage = tokenLink(
TOKEN_PROPOSAL.replace(':id', data?.id || '')
);
const openDialog = () => {
if (!data) return;
setDialog({
open: true,
title: data.rationale.title,
content: data.terms,
});
};
return (
<div className="pb-1">
<button
className="underline max-md:hidden"
onClick={openDialog}
>
{t('View terms')}
</button>{' '}
<ExternalLink className="max-md:hidden" href={proposalPage}>
{t('Open in Governance')}
</ExternalLink>
<ExternalLink className="md:hidden" href={proposalPage}>
{t('Open')}
</ExternalLink>
</div>
);
}}
/>
</AgGrid>
<JsonViewerDialog
open={dialog.open}
onChange={(isOpen) => setDialog({ ...dialog, open: isOpen })}
title={dialog.title}
content={dialog.content}
/>
</>
);
};
-3
View File
@@ -33,7 +33,4 @@ export const ENV = {
parties: truthy.includes(windowOrDefault('NX_EXPLORER_PARTIES')),
validators: truthy.includes(windowOrDefault('NX_EXPLORER_VALIDATORS')),
},
addresses: {
feedback: windowOrDefault('NX_GITHUB_FEEDBACK_URL'),
},
};
@@ -1,50 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetDetailsTable, useAssetDataProvider } from '@vegaprotocol/assets';
import { useParams } from 'react-router-dom';
import { JsonViewerDialog } from '../../components/dialogs/json-viewer-dialog';
import { useState } from 'react';
export const AssetPage = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
const { assetId } = useParams<{ assetId: string }>();
const { data, loading, error } = useAssetDataProvider(assetId || '');
const title = data ? data.name : error ? t('Asset not found') : '';
const [dialogOpen, setDialogOpen] = useState<boolean>(false);
return (
<>
<section className="relative">
<RouteTitle data-testid="asset-header">{title}</RouteTitle>
<AsyncRenderer
noDataMessage={t('Asset not found')}
data={data}
loading={loading}
error={error}
>
<div className="absolute top-0 right-0">
<Button size="xs" onClick={() => setDialogOpen(true)}>
{t('View JSON')}
</Button>
</div>
<div className="h-full relative">
<AssetDetailsTable asset={data as AssetFieldsFragment} />
</div>
</AsyncRenderer>
</section>
<JsonViewerDialog
open={dialogOpen}
onChange={(isOpen) => setDialogOpen(isOpen)}
title={data?.name || ''}
content={data}
/>
</>
);
};
@@ -6,7 +6,7 @@ import { useDocumentTitle } from '../../hooks/use-document-title';
import { useAssetsDataProvider } from '@vegaprotocol/assets';
import { AssetsTable } from '../../components/assets/assets-table';
export const AssetsPage = () => {
export const Assets = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
@@ -1,2 +1 @@
export * from './assets-page';
export * from './asset-page';
export * from './assets';
@@ -0,0 +1,82 @@
query ExplorerProposals {
proposalsConnection {
edges {
node {
id
rationale {
title
description
}
reference
state
datetime
rejectionReason
party {
id
}
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
instrument {
name
}
}
... on UpdateMarket {
marketId
}
... on NewAsset {
__typename
symbol
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on ERC20 {
contractAddress
}
}
}
... on UpdateNetworkParameter {
networkParameter {
key
value
}
}
}
}
votes {
yes {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
}
}
no {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
}
}
}
}
}
}
}
@@ -0,0 +1,122 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', symbol: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string } } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, votes?: Array<{ __typename?: 'Vote', value: Types.VoteValue, datetime: any, party: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string } } }> | null }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, votes?: Array<{ __typename?: 'Vote', value: Types.VoteValue, datetime: any, party: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string } } }> | null } } } } | null> | null } | null };
export const ExplorerProposalsDocument = gql`
query ExplorerProposals {
proposalsConnection {
edges {
node {
id
rationale {
title
description
}
reference
state
datetime
rejectionReason
party {
id
}
terms {
closingDatetime
enactmentDatetime
change {
... on NewMarket {
instrument {
name
}
}
... on UpdateMarket {
marketId
}
... on NewAsset {
__typename
symbol
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on ERC20 {
contractAddress
}
}
}
... on UpdateNetworkParameter {
networkParameter {
key
value
}
}
}
}
votes {
yes {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
}
}
no {
totalTokens
totalNumber
votes {
value
party {
id
stakingSummary {
currentStakeAvailable
}
}
datetime
}
}
}
}
}
}
}
`;
/**
* __useExplorerProposalsQuery__
*
* To run a query within a React component, call `useExplorerProposalsQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerProposalsQuery` 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 } = useExplorerProposalsQuery({
* variables: {
* },
* });
*/
export function useExplorerProposalsQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerProposalsQuery, ExplorerProposalsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerProposalsQuery, ExplorerProposalsQueryVariables>(ExplorerProposalsDocument, options);
}
export function useExplorerProposalsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerProposalsQuery, ExplorerProposalsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerProposalsQuery, ExplorerProposalsQueryVariables>(ExplorerProposalsDocument, options);
}
export type ExplorerProposalsQueryHookResult = ReturnType<typeof useExplorerProposalsQuery>;
export type ExplorerProposalsLazyQueryHookResult = ReturnType<typeof useExplorerProposalsLazyQuery>;
export type ExplorerProposalsQueryResult = Apollo.QueryResult<ExplorerProposalsQuery, ExplorerProposalsQueryVariables>;
@@ -1 +1,63 @@
export * from './proposals-page';
import { 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 { useExplorerProposalsQuery } from './__generated__/Proposals';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Governance = () => {
const { data, loading } = useExplorerProposalsQuery({
errorPolicy: 'ignore',
});
useDocumentTitle();
if (!data || !data.proposalsConnection || !data.proposalsConnection.edges) {
if (!loading) {
return (
<section>
<RouteTitle data-testid="governance-header">
{t('Governance Proposals')}
</RouteTitle>
<EmptyList
heading={t('This chain has no proposals')}
label={t('0 proposals')}
/>
</section>
);
} else {
return <Loader />;
}
}
const proposals = data?.proposalsConnection?.edges.map((e) => {
return e?.node;
});
return (
<section>
<RouteTitle data-testid="governance-header">
{t('Governance Proposals')}
</RouteTitle>
{proposals.map((p) => {
if (!p || !p.id) {
return null;
}
return (
<React.Fragment key={p.id}>
<SubHeading>
{p.rationale.title || p.rationale.description}
</SubHeading>
<SyntaxHighlighter data={p} />
</React.Fragment>
);
})}
</section>
);
};
export default Governance;
@@ -1,33 +0,0 @@
import { proposalsDataProvider } from '@vegaprotocol/governance';
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { ProposalsTable } from '../../components/proposals/proposals-table';
import { RouteTitle } from '../../components/route-title';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
export const Proposals = () => {
useScrollToLocation();
const { data, loading, error } = useDataProvider({
dataProvider: proposalsDataProvider,
});
useDocumentTitle([t('Governance Proposals')]);
return (
<section>
<RouteTitle data-testid="proposals-heading">
{t('Governance proposals')}
</RouteTitle>
<AsyncRenderer
noDataMessage={t('This chain has no proposals')}
data={data}
loading={loading}
error={error}
>
<ProposalsTable data={data} />
</AsyncRenderer>
</section>
);
};
+4 -13
View File
@@ -1,6 +1,6 @@
import { AssetPage, AssetsPage } from './assets';
import { Assets } from './assets';
import BlockPage from './blocks';
import { Proposals } from './governance';
import Governance from './governance';
import Home from './home';
import OraclePage from './oracles';
import Oracles from './oracles/home';
@@ -53,16 +53,7 @@ const assetsRoutes: Route[] = flags.assets
path: Routes.ASSETS,
text: t('Assets'),
name: 'Assets',
children: [
{
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
},
],
element: <Assets />,
},
]
: [];
@@ -84,7 +75,7 @@ const governanceRoutes: Route[] = flags.governance
path: Routes.GOVERNANCE,
name: 'Governance proposals',
text: t('Governance Proposals'),
element: <Proposals />,
element: <Governance />,
},
]
: [];
+66 -335
View File
@@ -2761,7 +2761,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "68026.0050809900766642912",
"locked_amount": "69212.7941588870528164928",
"deposits": [
{
"amount": "86666.297",
@@ -2827,7 +2827,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1366.694488960114",
"locked_amount": "1435.351483007733",
"deposits": [
{
"amount": "2500",
@@ -3214,8 +3214,8 @@
"tranche_start": "2023-02-01T00:00:00.000Z",
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "3009.936598125",
"locked_amount": "33251.80085558624625",
"total_removed": "2836.415649675",
"locked_amount": "34287.34557243708",
"deposits": [
{
"amount": "7500",
@@ -3244,11 +3244,6 @@
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
"tx": "0x78fd0cf1ca8de93d15eee1424146bcd70b84cc168b4c1ea1ac50020b8d67bcc1"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3284,12 +3279,6 @@
"tranche_id": 34,
"tx": "0x5bfef799be13741d89872cc604df1f35b41069397e681577fc15c039866d8707"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3304,8 +3293,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "734.823415425",
"remaining_tokens": "6765.176584575"
"withdrawn_tokens": "561.302466975",
"remaining_tokens": "6938.697533025"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -3337,7 +3326,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "67963.9399107138444471",
"locked_amount": "69149.646192904764493335",
"deposits": [
{
"amount": "129999.45",
@@ -3403,7 +3392,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "33185.77934424150344",
"locked_amount": "34043.00988077117846",
"deposits": [
{
"amount": "10000",
@@ -3596,7 +3585,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2842.40217529173",
"locked_amount": "2910.871067985794",
"deposits": [
{
"amount": "5000",
@@ -3807,7 +3796,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "9888.4955479431840552672",
"locked_amount": "11051.5629572361427967976",
"deposits": [
{
"amount": "97499.58",
@@ -3840,7 +3829,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "13515.866236501447216654821204",
"locked_amount": "15105.57858979342307160273426",
"deposits": [
{
"amount": "135173.4239508",
@@ -3886,7 +3875,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "4159.9193118355352539772",
"locked_amount": "4649.201685825244862006",
"deposits": [
{
"amount": "32499.86",
@@ -3919,7 +3908,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1354.013369100967212235",
"locked_amount": "1513.2700339508446292286",
"deposits": [
{
"amount": "10833.29",
@@ -3952,7 +3941,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "4720.860935375",
"locked_amount": "5061.6043871236901291745",
"locked_amount": "5656.9413696662315119734",
"deposits": [
{
"amount": "6500",
@@ -4103,8 +4092,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "4546.97235645",
"locked_amount": "8514.6164249539599",
"total_removed": "4373.451408",
"locked_amount": "9135.943255064457",
"deposits": [
{
"amount": "7500",
@@ -4128,11 +4117,6 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4a7c7462e3bb6abe2d33dfe5a1bf1e4211e52e5e03239c02554988f7d3635523"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4228,12 +4212,6 @@
"tranche_id": 33,
"tx": "0x4a7c7462e3bb6abe2d33dfe5a1bf1e4211e52e5e03239c02554988f7d3635523"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4320,8 +4298,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "4546.97235645",
"remaining_tokens": "2953.02764355"
"withdrawn_tokens": "4373.451408",
"remaining_tokens": "3126.548592"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -4346,7 +4324,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "534119.9961293638279815094",
"locked_amount": "560684.945746241749007341",
"deposits": [
{
"amount": "1852091.69",
@@ -7320,15 +7298,10 @@
"tranche_id": 10,
"tranche_start": "2021-07-15T23:37:11.000Z",
"tranche_end": "2021-07-15T23:37:11.000Z",
"total_added": "6259302.299000000000000001",
"total_removed": "6213483.280000000000000001",
"total_added": "6159302.299000000000000001",
"total_removed": "6113483.280000000000000001",
"locked_amount": "0",
"deposits": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0xa4417547c32fb4936e0dd6ee338ba31b2fd622388fe8a3906d9e7020d5e8371b"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -7851,11 +7824,6 @@
}
],
"withdrawals": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tx": "0xa4417547c32fb4936e0dd6ee338ba31b2fd622388fe8a3906d9e7020d5e8371b"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -8321,12 +8289,6 @@
{
"address": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"deposits": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0xa4417547c32fb4936e0dd6ee338ba31b2fd622388fe8a3906d9e7020d5e8371b"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -8509,12 +8471,6 @@
}
],
"withdrawals": [
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
"tranche_id": 10,
"tx": "0xa4417547c32fb4936e0dd6ee338ba31b2fd622388fe8a3906d9e7020d5e8371b"
},
{
"amount": "100000",
"user": "0xb2d6DEC77558Cf8EdB7c428d23E70Eab0688544f",
@@ -8690,8 +8646,8 @@
"tx": "0xac16a4ce688d40a482a59914d68c3a676592f8804ee8f0781b66a4ba5ccfbdfc"
}
],
"total_tokens": "3056651",
"withdrawn_tokens": "3056651",
"total_tokens": "2956651",
"withdrawn_tokens": "2956651",
"remaining_tokens": "0"
},
{
@@ -36637,8 +36593,8 @@
"tranche_start": "2022-03-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "609657.626547646980493",
"locked_amount": "845257.797948570775371065827",
"total_removed": "592998.0503546212334",
"locked_amount": "886078.876576593193858643006",
"deposits": [
{
"amount": "1998.95815",
@@ -36807,11 +36763,6 @@
}
],
"withdrawals": [
{
"amount": "16659.576193025747093",
"user": "0xB523235B6c7C74DDB26b10E78bFb2d0Cb63Ae289",
"tx": "0x2c5cfeee95fba21323fdbfde219501aeafe8d7f236d1283b4f8f7c7760e4401c"
},
{
"amount": "144779.049152",
"user": "0x1da69E9C22d77Ef8Ccbf5a1F2d83eDBc5Dcc20fA",
@@ -37911,17 +37862,10 @@
"tx": "0x8cc5159f3d665dd33a2bdac6e990cf1b65dc18b6b5e37a07b37dd54495a8d33c"
}
],
"withdrawals": [
{
"amount": "16659.576193025747093",
"user": "0xB523235B6c7C74DDB26b10E78bFb2d0Cb63Ae289",
"tranche_id": 1,
"tx": "0x2c5cfeee95fba21323fdbfde219501aeafe8d7f236d1283b4f8f7c7760e4401c"
}
],
"withdrawals": [],
"total_tokens": "21666.5743",
"withdrawn_tokens": "16659.576193025747093",
"remaining_tokens": "5006.998106974252907"
"withdrawn_tokens": "0",
"remaining_tokens": "21666.5743"
},
{
"address": "0x7227e17101E6C70F4dAfC7DDB77BB7D83DdfC1C8",
@@ -37975,8 +37919,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "572826.70366554650274952",
"locked_amount": "8296917.466428724395809164572318349628278",
"total_removed": "567626.84057226362274952",
"locked_amount": "8441666.3902799341022910153716735157718303",
"deposits": [
{
"amount": "16249.93",
@@ -38510,31 +38454,6 @@
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tx": "0x4870598253a2a664d9c96af37904f08ac772ee9620b5975560ef7e7fb51c9a7f"
},
{
"amount": "2820.55961",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tx": "0x039c7d1eb6b18a13c3fb91589403d43710e12b319e7d40936280a576502f80fe"
},
{
"amount": "1073.5102073953235",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x75d6ba37c5621a2bfd39153fdeb79265a5fd36872774966fff58a7a0c476b99b"
},
{
"amount": "404.073696023421875",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xa0d7984104142e0bfbd90d11fbb3006941b04976943427b1e248514e0d6dbcce"
},
{
"amount": "574.414613957721625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x702ab149d41128eefe14cffbca4297575ef4b88a30a15c864af13bf9621e000f"
},
{
"amount": "327.304965906413",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xe5fb2af901c286e061280cff77fcf8056e52dffb23b9fc7bba3dfb15abee8abd"
},
{
"amount": "856.08784586478614",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -40243,30 +40162,6 @@
"tranche_id": 2,
"tx": "0x307e498402679f0f8ee11dcd3dfc684db5558203469f07ce58e98a4f87f0fa62"
},
{
"amount": "1073.5102073953235",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x75d6ba37c5621a2bfd39153fdeb79265a5fd36872774966fff58a7a0c476b99b"
},
{
"amount": "404.073696023421875",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xa0d7984104142e0bfbd90d11fbb3006941b04976943427b1e248514e0d6dbcce"
},
{
"amount": "574.414613957721625",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x702ab149d41128eefe14cffbca4297575ef4b88a30a15c864af13bf9621e000f"
},
{
"amount": "327.304965906413",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xe5fb2af901c286e061280cff77fcf8056e52dffb23b9fc7bba3dfb15abee8abd"
},
{
"amount": "966.75883976995675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -41409,8 +41304,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "123657.945723010546375",
"remaining_tokens": "136340.941776989453625"
"withdrawn_tokens": "121278.642239727666375",
"remaining_tokens": "138720.245260272333625"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -42070,12 +41965,6 @@
}
],
"withdrawals": [
{
"amount": "2820.55961",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tranche_id": 2,
"tx": "0x039c7d1eb6b18a13c3fb91589403d43710e12b319e7d40936280a576502f80fe"
},
{
"amount": "1412.763584",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
@@ -42276,8 +42165,8 @@
}
],
"total_tokens": "200000",
"withdrawn_tokens": "93634.576066",
"remaining_tokens": "106365.423934"
"withdrawn_tokens": "90814.016456",
"remaining_tokens": "109185.983544"
},
{
"address": "0x1b956E6c00E238194B331eddEFF72Cb5f28A8d01",
@@ -43629,8 +43518,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "4396450.802668984389787156",
"locked_amount": "1938216.002398478950673792862823089",
"total_removed": "3926967.723059737571037032",
"locked_amount": "2071847.284667310624156645942280506",
"deposits": [
{
"amount": "129284.449",
@@ -43864,31 +43753,6 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x532c270b8a50b8e2923a96b038129a203e01eba1b84faf238dd9b48aeceb1c91"
},
{
"amount": "1488.49150551791738425",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x6bacc9b91153b8977b9cabc510975f9d249be098bf8dc841ca72408513d9fbb1"
},
{
"amount": "560.9020402316959745",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x6dfd5e6e697a6db058ed45cd16d19ee2bb68b5e3dfecdc52b7829d85e9adbaa0"
},
{
"amount": "797.50404161762011275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xa7b60b86ca0234d8bcfeac24131315f65b0f041aebd74593e73a86c75ea2cce4"
},
{
"amount": "451.82760560232519575",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x5fcd1783bbd5f8b519d94782580777ff37d84df742f617baf632fc693615f39a"
},
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tx": "0x43c34302d1895983b4f5b28ad186ea31662a045f3c7c63075a1c9db4b059e42d"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -46785,30 +46649,6 @@
"tranche_id": 3,
"tx": "0x532c270b8a50b8e2923a96b038129a203e01eba1b84faf238dd9b48aeceb1c91"
},
{
"amount": "1488.49150551791738425",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x6bacc9b91153b8977b9cabc510975f9d249be098bf8dc841ca72408513d9fbb1"
},
{
"amount": "560.9020402316959745",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x6dfd5e6e697a6db058ed45cd16d19ee2bb68b5e3dfecdc52b7829d85e9adbaa0"
},
{
"amount": "797.50404161762011275",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xa7b60b86ca0234d8bcfeac24131315f65b0f041aebd74593e73a86c75ea2cce4"
},
{
"amount": "451.82760560232519575",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x5fcd1783bbd5f8b519d94782580777ff37d84df742f617baf632fc693615f39a"
},
{
"amount": "1333.9237119810715295",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -49253,8 +49093,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "310868.25548215842910375",
"remaining_tokens": "48255.21409284157089625"
"withdrawn_tokens": "307569.5302891888704365",
"remaining_tokens": "51553.9392858111295635"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -50132,12 +49972,6 @@
}
],
"withdrawals": [
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tranche_id": 3,
"tx": "0x43c34302d1895983b4f5b28ad186ea31662a045f3c7c63075a1c9db4b059e42d"
},
{
"amount": "32828.670452546475795519",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
@@ -50146,8 +49980,8 @@
}
],
"total_tokens": "575536.79731735",
"withdrawn_tokens": "499013.024868823735878393",
"remaining_tokens": "76523.772448526264121607"
"withdrawn_tokens": "32828.670452546475795519",
"remaining_tokens": "542708.126864803524204481"
},
{
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
@@ -50598,7 +50432,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2749067.463242913023296295",
"locked_amount": "448897.461240245945578932424695999",
"locked_amount": "501695.989059927736982047096440314",
"deposits": [
{
"amount": "552496.6455",
@@ -52624,8 +52458,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "34093.0379154332685",
"locked_amount": "133935.9297845122141846487163876",
"total_removed": "33643.6376997992685",
"locked_amount": "140404.26303635577567951130086252",
"deposits": [
{
"amount": "3000",
@@ -59309,41 +59143,6 @@
"user": "0x4F4A140E9B4a8403792970C6b5535ed0AA32c8A4",
"tx": "0x181a9afe9b3592427f1ee10f205e01f34ae7be383294e8d93e397766092bf6df"
},
{
"amount": "18.399238968",
"user": "0xD3ec605d078326B0a636ca90d496Ebb5Eb457a27",
"tx": "0xa82e451753cbced76b064af62868d3170dff7afca557680e657c27ebba5c7beb"
},
{
"amount": "141.230663368",
"user": "0xb7df334217A208B5B35a3824D1CDdBCa85BD352B",
"tx": "0x41e9fa9b5003c1d6edf1e9423bd6ae7ce6e4e48fcbce0b47ba17968a67da4d48"
},
{
"amount": "12.107001524",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tx": "0xa2e745ec65b1caad05b18aa7c730d0550a263df2208ba10cecee5a30f815447b"
},
{
"amount": "24.023287672",
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
"tx": "0xbb25fa75431a6463909c9efde31d110e1aa5c945d0363fcc4bd5c46f869cabac"
},
{
"amount": "94.45296804",
"user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392",
"tx": "0x10a149356dc25da53994746b06fc8922faa23be9fb781541e91357d7d4f4d36a"
},
{
"amount": "142.126249364",
"user": "0x289047e767D23561A2035cd3fdc60c94ed1bBA3B",
"tx": "0x61b2f8e5f4efb5049e418ede94fd54dfc1ed8d0f1165ff31517f4f748ef14899"
},
{
"amount": "17.060806698",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tx": "0x9d1ced8d04e8d58af92b1434bcb269487a3f6ddf4305c0739ab0f158f5401ec6"
},
{
"amount": "13.1116203702",
"user": "0x4cBC0C88d8FE503f62823B42f30a4900292C13bE",
@@ -63959,17 +63758,10 @@
"tx": "0x1c2c084e3efafb2ffa09c0b259bd229cce0d6da04dd092d911c7e6136a7ac8a9"
}
],
"withdrawals": [
{
"amount": "141.230663368",
"user": "0xb7df334217A208B5B35a3824D1CDdBCa85BD352B",
"tranche_id": 5,
"tx": "0x41e9fa9b5003c1d6edf1e9423bd6ae7ce6e4e48fcbce0b47ba17968a67da4d48"
}
],
"withdrawals": [],
"total_tokens": "200",
"withdrawn_tokens": "141.230663368",
"remaining_tokens": "58.769336632"
"withdrawn_tokens": "0",
"remaining_tokens": "200"
},
{
"address": "0x287a34EB03418ad55c2e93d0E4FB1e18DCE607ED",
@@ -65982,17 +65774,10 @@
"tx": "0x75de6ca47e0da361181d14aceb5d08e572754861465caa67a12b528886d55307"
}
],
"withdrawals": [
{
"amount": "142.126249364",
"user": "0x289047e767D23561A2035cd3fdc60c94ed1bBA3B",
"tranche_id": 5,
"tx": "0x61b2f8e5f4efb5049e418ede94fd54dfc1ed8d0f1165ff31517f4f748ef14899"
}
],
"withdrawals": [],
"total_tokens": "200",
"withdrawn_tokens": "142.126249364",
"remaining_tokens": "57.873750636"
"withdrawn_tokens": "0",
"remaining_tokens": "200"
},
{
"address": "0x45bAfA40E9e8099cCd9Ed0e3112A11E7a1d37beb",
@@ -67516,12 +67301,6 @@
}
],
"withdrawals": [
{
"amount": "18.399238968",
"user": "0xD3ec605d078326B0a636ca90d496Ebb5Eb457a27",
"tranche_id": 5,
"tx": "0xa82e451753cbced76b064af62868d3170dff7afca557680e657c27ebba5c7beb"
},
{
"amount": "262.76406646",
"user": "0xD3ec605d078326B0a636ca90d496Ebb5Eb457a27",
@@ -67530,8 +67309,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "281.163305428",
"remaining_tokens": "118.836694572"
"withdrawn_tokens": "262.76406646",
"remaining_tokens": "137.23593354"
},
{
"address": "0x7f6aba7563Cb5d31980D440337D3d1A6e3dB58F3",
@@ -72778,12 +72557,6 @@
}
],
"withdrawals": [
{
"amount": "12.107001524",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tranche_id": 5,
"tx": "0xa2e745ec65b1caad05b18aa7c730d0550a263df2208ba10cecee5a30f815447b"
},
{
"amount": "15.80745814",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
@@ -72864,8 +72637,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "282.749759004",
"remaining_tokens": "117.250240996"
"withdrawn_tokens": "270.64275748",
"remaining_tokens": "129.35724252"
},
{
"address": "0x5c90765F50629570738fEe7b7FA82ae118f81Ed1",
@@ -77135,12 +76908,6 @@
}
],
"withdrawals": [
{
"amount": "94.45296804",
"user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392",
"tranche_id": 5,
"tx": "0x10a149356dc25da53994746b06fc8922faa23be9fb781541e91357d7d4f4d36a"
},
{
"amount": "119.5173516",
"user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392",
@@ -77185,8 +76952,8 @@
}
],
"total_tokens": "1200",
"withdrawn_tokens": "850.0164003",
"remaining_tokens": "349.9835997"
"withdrawn_tokens": "755.56343226",
"remaining_tokens": "444.43656774"
},
{
"address": "0x47181cf36aDFb0b5dB7EfDB123f403D890607841",
@@ -77378,12 +77145,6 @@
}
],
"withdrawals": [
{
"amount": "24.023287672",
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
"tranche_id": 5,
"tx": "0xbb25fa75431a6463909c9efde31d110e1aa5c945d0363fcc4bd5c46f869cabac"
},
{
"amount": "37.881582952",
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
@@ -77404,8 +77165,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "283.113077116",
"remaining_tokens": "116.886922884"
"withdrawn_tokens": "259.089789444",
"remaining_tokens": "140.910210556"
},
{
"address": "0x3738bec36216eA2F11B954891C65AAd1Bc852156",
@@ -80229,12 +79990,6 @@
}
],
"withdrawals": [
{
"amount": "17.060806698",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tranche_id": 5,
"tx": "0x9d1ced8d04e8d58af92b1434bcb269487a3f6ddf4305c0739ab0f158f5401ec6"
},
{
"amount": "15.872983256",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
@@ -80261,8 +80016,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "142.641089548",
"remaining_tokens": "57.358910452"
"withdrawn_tokens": "125.58028285",
"remaining_tokens": "74.41971715"
},
{
"address": "0xBef628af8547cE5264835F32487055a7e3dF393D",
@@ -81463,7 +81218,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42",
"total_removed": "66601.1049690697989",
"total_removed": "66301.1049690697989",
"locked_amount": "0",
"deposits": [
{
@@ -85708,16 +85463,6 @@
"user": "0x217254578EFd323CF93b1E0FC900A2f4C97A2e6e",
"tx": "0xbed9954657a0b981080f8b66187ee3ce27ba8b18dc7419f430624cda2d4e435a"
},
{
"amount": "50",
"user": "0xa40284F5359954E1a0c024132940375FD480C640",
"tx": "0x2d1ecfd5ecefe61fa80d8c7d20b9e4afac178554d364c8796eec0d7fdc8f0942"
},
{
"amount": "250",
"user": "0xc0A603F3B555ba1622B80CF54EAFE6fb717f3Cd7",
"tx": "0xcb9db64f3df7fb6f1addc0073e2a8a5e285618b07d955d4435ff674327cec8e5"
},
{
"amount": "30",
"user": "0x4A13d4dC5e06ACdA81C011D55a7DaAc332bC5Dbf",
@@ -95692,17 +95437,10 @@
"tx": "0xadc25e73736cd85e84fee16f2d7ee41f16f7deb83a52ceb82b327974063964f7"
}
],
"withdrawals": [
{
"amount": "50",
"user": "0xa40284F5359954E1a0c024132940375FD480C640",
"tranche_id": 6,
"tx": "0x2d1ecfd5ecefe61fa80d8c7d20b9e4afac178554d364c8796eec0d7fdc8f0942"
}
],
"withdrawals": [],
"total_tokens": "50",
"withdrawn_tokens": "50",
"remaining_tokens": "0"
"withdrawn_tokens": "0",
"remaining_tokens": "50"
},
{
"address": "0x9603B92De110066121E9D7218482A672bd8afbD9",
@@ -100440,17 +100178,10 @@
"tx": "0x3a0390dffa4280c286016566b29c62e4cec36c6504f7ac3d1de80d22b2903008"
}
],
"withdrawals": [
{
"amount": "250",
"user": "0xc0A603F3B555ba1622B80CF54EAFE6fb717f3Cd7",
"tranche_id": 6,
"tx": "0xcb9db64f3df7fb6f1addc0073e2a8a5e285618b07d955d4435ff674327cec8e5"
}
],
"withdrawals": [],
"total_tokens": "250",
"withdrawn_tokens": "250",
"remaining_tokens": "0"
"withdrawn_tokens": "0",
"remaining_tokens": "250"
},
{
"address": "0xE9e7C70a1e2A5F4A612349871aE18eDCEbB253d9",
@@ -1,5 +1,4 @@
/// <reference types="cypress" />
import { associateTokenStartOfTests } from '../../support/governance.functions';
import {
createUpdateNetworkProposalTxBody,
@@ -22,7 +21,6 @@ context(
before('Connect wallets and set approval', function () {
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
@@ -0,0 +1,849 @@
/// <reference types="cypress" />
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const vegaWalletStakedBalances =
'[data-testid="vega-wallet-balance-staked-validators"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWallet = '[data-testid="vega-wallet"]';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const openProposals = '[data-testid="open-proposals"]';
const proposalVoteProgressForPercentage =
'[data-testid="vote-progress-indicator-percentage-for"]';
const proposalVoteProgressAgainstPercentage =
'[data-testid="vote-progress-indicator-percentage-against"]';
const proposalVoteProgressForTokens =
'[data-testid="vote-progress-indicator-tokens-for"]';
const proposalVoteProgressAgainstTokens =
'[data-testid="vote-progress-indicator-tokens-against"]';
const changeVoteButton = '[data-testid="change-vote-button"]';
const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const rawProposalData = '[data-testid="proposal-data"]';
const minVoteButton = '[data-testid="min-vote"]';
const maxVoteButton = '[data-testid="max-vote"]';
const voteButtons = '[data-testid="vote-buttons"]';
const votingDate = '[data-testid="voting-date"]';
const voteTwoMinExtraNote = '[data-testid="voting-2-mins-extra"]';
const voteStatus = '[data-testid="vote-status"]';
const rejectProposalsLink = '[href="/proposals/rejected"]';
const feedbackError = '[data-testid="Error"]';
const noOpenProposals = '[data-testid="no-open-proposals"]';
const noClosedProposals = '[data-testid="no-closed-proposals"]';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
const minCloseDays = 2;
const maxCloseDays = 3;
const requiredParticipation = 0.001;
const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
context(
'Governance flow - with eth and vega wallets connected',
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.visit('/');
cy.get_network_parameters().then((network_parameters) => {
cy.wrap(
network_parameters['spam.protection.proposal.min.tokens'] /
1000000000000000000
).as('minProposerBalance');
cy.wrap(
network_parameters['spam.protection.voting.min.tokens'] /
1000000000000000000
).as('minVoterBalance');
cy.wrap(
network_parameters['governance.proposal.freeform.requiredMajority'] *
100
).as('requiredMajority');
cy.wrap(
network_parameters['governance.proposal.freeform.minClose'].split(
'h'
)[0]
).as('minCloseHours');
cy.wrap(
network_parameters['governance.proposal.freeform.maxClose'].split(
'h'
)[0]
).as('maxCloseHours');
});
cy.vega_wallet_set_specified_approval_amount('1000');
});
describe('Eth wallet - contains VEGA tokens', function () {
before(
'checking network parameters (therefore environment) is fit for test',
function () {
assert.isAtLeast(
parseInt(this.minProposerBalance),
0.00001,
'Asserting that value is at least 0.00001 for network parameter minProposerBalance'
);
assert.isAtLeast(
parseInt(this.minVoterBalance),
0.00001,
'Asserting that value is at least 0.00001 for network parameter minVoterBalance'
);
// workaround for first eth tx hanging
associateTokenStartOfTests();
}
);
beforeEach('visit governance tab', function () {
cy.reload();
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.navigate_to('proposals');
});
it('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003
cy.get(noOpenProposals)
.should('be.visible')
.and('have.text', 'There are no open or yet to enact proposals');
cy.get(noClosedProposals)
.should('be.visible')
.and('have.text', 'There are no enacted or rejected proposals');
});
// 3002-PROP-002
// 3002-PROP-003
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
cy.contains(
`You must have at least ${this.minProposerBalance} VEGA associated to make a proposal`
).should('be.visible');
});
// 3002-PROP-011
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
cy.get(votingDate).should('not.be.empty');
cy.get(voteTwoMinExtraNote).should(
'contain.text',
'we add 2 minutes of extra time'
);
cy.enter_unique_freeform_proposal_body('50', generateProposalTitle());
cy.get(newProposalSubmitButton).should('be.visible').click();
// 3002-PROP-012
// 3002-PROP-016
cy.wait_for_proposal_submitted();
});
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
// 3001-VOTE-005
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
let proposalDays = [
minCloseDays + 1,
maxCloseDays,
minCloseDays + 3,
minCloseDays + 2,
];
for (var index = 0; index < proposalDays.length; index++) {
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days(
proposalDays[index]
).then((closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal submitted', proposalTimeout).should(
'be.visible'
);
cy.get(dialogCloseButton).click();
cy.wait_for_proposal_sync();
}
let arrayOfProposals = [];
cy.navigate_to('proposals');
cy.get(proposalDetailsTitle)
.each((proposalTitleElement) => {
arrayOfProposals.push(proposalTitleElement.text());
})
.then(() => {
cy.get_sort_order_of_supplied_array(arrayOfProposals).should(
'equal',
'descending'
);
});
});
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
cy.ensure_specified_unstaked_tokens_are_associated('2');
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', '2');
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.close_staking_dialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
cy.navigate_to('proposals');
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', generateProposalTitle());
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
});
it('Newly created proposals list - able to filter by proposerID to show it in list', function () {
const proposerId = Cypress.env('vegaWalletPublicKey');
const proposalTitle = generateProposalTitle();
createFreeformProposal(this.minProposerBalance, proposalTitle);
cy.get_proposal_id_from_list(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
cy.get(`#${proposalId}`).should('contain', proposalId);
});
});
it('Newly created proposals list - shows title and portion of summary', function () {
createRawProposal(this.minProposerBalance); // 3001-VOTE-052
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
// 3001-VOTE-008
// 3001-VOTE-034
cy.get(`#${proposalId}`)
// 3001-VOTE-097
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(`#${proposalId}`)
.should(
'contain',
rawProposal.rationale.description.substring(0, 59)
)
.and('be.visible');
});
});
});
});
it('Newly created proposals list - shows open proposals in an open state', function () {
// 3001-VOTE-004
// 3001-VOTE-035
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
cy.get('@proposalIdText').then((proposalId) => {
cy.get_proposal_information_from_table('ID')
.contains(proposalId)
.and('be.visible');
});
cy.get_proposal_information_from_table('State')
.contains('Open')
.and('be.visible');
cy.get_proposal_information_from_table('Type')
.contains('Freeform')
.and('be.visible');
});
});
// 3001-VOTE-071
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateProposalTitle();
createFreeformProposal(this.minProposerBalance, proposalTitle);
cy.get_submitted_proposal_from_proposal_list(proposalTitle)
.as('submittedProposal')
.within(() => {
// 3001-VOTE-039
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
cy.vote_for_proposal('for');
cy.get_proposal_information_from_table('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
cy.get_proposal_information_from_table('Token participation met')
.contains('👍')
.should('be.visible');
cy.navigate_to('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(voteStatus).should('have.text', 'Set to pass')
);
});
});
// 3001-VOTE-055
it('Newly created raw proposal details - shows proposal title and full description', function () {
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
cy.get(`#${proposalId}`).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
});
});
cy.get(proposalDetailsTitle)
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(proposalDetailsDescription)
.should('contain', rawProposal.rationale.description)
.and('be.visible');
});
// 3001-VOTE-052
cy.get('code.language-json')
.should('exist')
.within(() => {
cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
});
});
// 3001-VOTE-043
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateProposalTitle();
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.create_ten_digit_unix_timestamp_for_specified_days('3').then(
(closingDateTimestamp) => {
cy.enter_unique_freeform_proposal_body(
closingVoteHrs,
proposalTitle
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(
() => cy.get(viewProposalButton).click()
);
cy.convert_unix_timestamp_to_governance_data_table_date_format(
closingDateTimestamp
).then((closingDate) => {
cy.get_proposal_information_from_table('Closes on')
.contains(closingDate)
.should('be.visible');
});
}
);
cy.get_governance_proposal_date_format_for_specified_days('0').then(
(proposalDate) => {
cy.get_proposal_information_from_table('Proposed on')
.contains(proposalDate)
.should('be.visible');
}
);
});
it('Newly created proposal details - shows default status set to fail', function () {
// 3001-VOTE-037
// 3001-VOTE-040
// 3001-VOTE-067
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
);
cy.get_proposal_information_from_table('Expected to pass')
.contains('👎')
.should('be.visible');
// 3001-VOTE-062
// 3001-VOTE-040
// 3001-VOTE-070
cy.get_proposal_information_from_table('Token majority met')
.contains('👎')
.should('be.visible');
// 3001-VOTE-068
cy.get_proposal_information_from_table('Token participation met')
.contains('👎')
.should('be.visible');
});
// 3001-VOTE-080 3001-VOTE-090 3001-VOTE-069 3001-VOTE-072 3001-VOTE-073
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-080
cy.get(voteButtons).contains('against').should('be.visible');
cy.get(voteButtons).contains('for').should('be.visible');
cy.vote_for_proposal('for');
cy.get_governance_proposal_date_format_for_specified_days(
'0',
'shortMonth'
).then((votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
});
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
cy.get(proposalVoteProgressForTokens)
.contains('1.00')
.and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(this.minProposerBalance).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
// 3001-VOTE-061
cy.get_proposal_information_from_table('Participation required')
.contains(`${requiredParticipation}%`)
.should('be.visible');
// 3001-VOTE-066
cy.get_proposal_information_from_table('Majority Required') // 3001-VOTE-073
.contains(`${parseFloat(this.requiredMajority).toFixed(2)}%`)
.should('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
// 3001-VOTE-064
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(this.minProposerBalance).toFixed(2))
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('against');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', parseFloat(this.minProposerBalance).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
});
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
)
.as('submittedProposal')
.within(() => cy.get(viewProposalButton).click());
});
cy.vote_for_proposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.get_proposal_information_from_table('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
// 3001-VOTE-065
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
cy.get(proposalVoteProgressForTokens)
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table(
'Total tokens voted percentage'
)
.should('have.text', '0.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', tokensRequiredToAchieveResult)
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get_proposal_information_from_table('Expected to pass')
.contains('👍')
.should('be.visible');
// 3001-VOTE-062
cy.get_proposal_information_from_table('Token majority met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Token participation met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
});
});
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('40', generateProposalTitle());
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
});
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'100000',
generateProposalTitle()
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
});
// 3001-VOTE-006
it('Creating a proposal - proposal rejected - able to access rejected proposals', function () {
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('1000').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
cy.get(rejectProposalsLink).click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible');
cy.get(viewProposalButton).click();
});
});
cy.get_proposal_information_from_table('State')
.contains('Rejected')
.and('be.visible');
cy.get_proposal_information_from_table('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible');
cy.get_proposal_information_from_table('Error details')
.contains('proposal closing time too late')
.and('be.visible');
});
// 0005-ETXN-004
it('Unable to create a proposal - when no tokens are associated', function () {
cy.vega_wallet_teardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as;
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
});
// 3002-PROP-009
it('Unable to create a proposal - when some but not enough tokens are associated', function () {
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance - 0.000001
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
});
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((freeformProposal) => {
freeformProposal.terms.closingTimestamp = closingDateTimestamp;
freeformProposal.unexpected = `i shouldn't be here`;
let proposalPayload = JSON.stringify(freeformProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
cy.get(rawProposalData)
.invoke('val')
.should('contain', "i shouldn't be here");
});
it('Unable to create a freeform proposal - when json terms section contains unexpected field', function () {
// 3001-VOTE-038
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = closingDateTimestamp;
rawProposal.terms.unexpectedField = `i shouldn't be here`;
let proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
});
// 1005-PROP-009
it.skip(
'Unable to vote on a freeform proposal - when some but not enough vega associated',
{ tags: '@smoke' },
function () {
const proposalTitle = generateProposalTitle();
cy.vega_wallet_teardown();
cy.ensure_specified_unstaked_tokens_are_associated(
this.minProposerBalance
);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.staking_page_disassociate_tokens('0.0001');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(
() => cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.get(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
);
}
);
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
createRawProposal(this.minProposerBalance);
cy.get('[data-testid="manage-vega-wallet"]').click();
cy.get('[data-testid="disconnect"]').click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-075
// 3001-VOTE-076
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet')
.click();
cy.getByTestId('connector-jsonRpc').click();
cy.get(vegaWalletNameElement).should('be.visible');
cy.get(connectToVegaWalletButton).should('not.exist');
// 3001-VOTE-100
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'1.00',
txTimeout
);
cy.vote_for_proposal('against');
// 3001-VOTE-079
cy.contains('You voted: Against').should('be.visible');
});
function createRawProposal(proposerBalance) {
if (proposerBalance)
cy.ensure_specified_unstaked_tokens_are_associated(proposerBalance);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
}
function createFreeformProposal(proposerBalance, proposalTitle) {
cy.ensure_specified_unstaked_tokens_are_associated(proposerBalance);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.get(proposalDetailsTitle).invoke('text').as('proposalTitle');
cy.navigate_to('proposals');
}
function generateProposalTitle() {
const randomNum = Math.floor(Math.random() * 1000) + 1;
return randomNum + ': Freeform e2e proposal';
}
// This is a workaround function to begin tests with associating tokens without failing
// Should be removed when eth transaction bug is fixed
function associateTokenStartOfTests() {
cy.highlight(`Associating tokens for first time`);
cy.ethereum_wallet_connect();
cy.connectVegaWallet();
cy.get('[href="/token/associate"]').first().click();
cy.getByTestId('associate-radio-wallet', { timeout: 30000 }).click();
cy.getByTestId('token-amount-input', epochTimeout).type('1');
cy.getByTestId('token-input-submit-button', txTimeout)
.should('be.enabled')
.click();
cy.contains(
`Associating with Vega key. Waiting for ${Cypress.env(
'blockConfirmations'
)} more confirmations..`,
txTimeout
).should('be.visible');
cy.getByTestId('associated-amount', txTimeout).should(
'contain.text',
'1'
);
// Wait is needed to allow time for transaction to complete
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(10000);
cy.vega_wallet_teardown();
cy.clearLocalStorage();
}
});
}
);
@@ -54,11 +54,11 @@ context(
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated('1');
cy.navigate_to('proposals');
});
it('Able to submit valid update network parameter proposal', function () {
cy.ensure_specified_unstaked_tokens_are_associated('1');
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
// 3002-PROP-006
cy.get(newProposalTitle).type('Test update network parameter proposal');
@@ -1,273 +0,0 @@
import { associateTokenStartOfTests } from '../../support/common.functions';
import {
createRawProposal,
generateFreeFormProposalTitle,
governanceProposalType,
} from '../../support/governance.functions';
const proposalVoteProgressForPercentage =
'[data-testid="vote-progress-indicator-percentage-for"]';
const proposalVoteProgressAgainstPercentage =
'[data-testid="vote-progress-indicator-percentage-against"]';
const proposalVoteProgressForTokens =
'[data-testid="vote-progress-indicator-tokens-for"]';
const proposalVoteProgressAgainstTokens =
'[data-testid="vote-progress-indicator-tokens-against"]';
const changeVoteButton = '[data-testid="change-vote-button"]';
const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
describe(
'Governance flow for proposal details',
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
});
beforeEach('visit proposals tab', function () {
cy.reload();
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
});
// 3001-VOTE-055
it('Newly created raw proposal details - shows proposal title and full description', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
cy.get(`#${proposalId}`).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
});
});
cy.get(proposalDetailsTitle)
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(proposalDetailsDescription)
.should('contain', rawProposal.rationale.description)
.and('be.visible');
});
// 3001-VOTE-052
cy.get('code.language-json')
.should('exist')
.within(() => {
cy.get('.hljs-string').eq(0).should('have.text', '"ProposalTerms"');
});
});
// 3001-VOTE-043
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateFreeFormProposalTitle();
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.create_ten_digit_unix_timestamp_for_specified_days('3').then(
(closingDateTimestamp) => {
cy.enter_unique_freeform_proposal_body(closingVoteHrs, proposalTitle);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(
() => cy.get(viewProposalButton).click()
);
cy.convert_unix_timestamp_to_governance_data_table_date_format(
closingDateTimestamp
).then((closingDate) => {
cy.get_proposal_information_from_table('Closes on')
.contains(closingDate)
.should('be.visible');
});
}
);
cy.get_governance_proposal_date_format_for_specified_days('0').then(
(proposalDate) => {
cy.get_proposal_information_from_table('Proposed on')
.contains(proposalDate)
.should('be.visible');
}
);
});
it('Newly created proposal details - shows default status set to fail', function () {
// 3001-VOTE-037
// 3001-VOTE-040
// 3001-VOTE-067
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
);
cy.get_proposal_information_from_table('Expected to pass')
.contains('👎')
.should('be.visible');
// 3001-VOTE-062
// 3001-VOTE-040
// 3001-VOTE-070
cy.get_proposal_information_from_table('Token majority met')
.contains('👎')
.should('be.visible');
// 3001-VOTE-068
cy.get_proposal_information_from_table('Token participation met')
.contains('👎')
.should('be.visible');
});
// 3001-VOTE-080 3001-VOTE-090 3001-VOTE-069 3001-VOTE-072 3001-VOTE-073
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-080
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
cy.getByTestId('vote-buttons').contains('for').should('be.visible');
cy.vote_for_proposal('for');
cy.get_governance_proposal_date_format_for_specified_days(
'0',
'shortMonth'
).then((votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
});
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1.00').and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(1).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
// 3001-VOTE-061
cy.get_proposal_information_from_table('Participation required')
.contains(0.001)
.should('be.visible');
// 3001-VOTE-066
cy.get_proposal_information_from_table('Majority Required') // 3001-VOTE-073
.contains(`${parseFloat(100).toFixed(2)}%`)
.should('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
// 3001-VOTE-064
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(1).toFixed(2))
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('against');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', parseFloat(1).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
});
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
)
.as('submittedProposal')
.within(() => cy.get(viewProposalButton).click());
});
cy.vote_for_proposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.get_proposal_information_from_table('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * 0.001) / 100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
// 3001-VOTE-065
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
cy.get(proposalVoteProgressForTokens)
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table(
'Total tokens voted percentage'
)
.should('have.text', '0.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', tokensRequiredToAchieveResult)
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get_proposal_information_from_table('Expected to pass')
.contains('👍')
.should('be.visible');
// 3001-VOTE-062
cy.get_proposal_information_from_table('Token majority met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Token participation met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
});
});
}
);
@@ -1,347 +0,0 @@
/// <reference types="cypress" />
import {
createRawProposal,
generateFreeFormProposalTitle,
governanceProposalType,
} from '../../support/governance.functions';
import { associateTokenStartOfTests } from '../../support/common.functions';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const vegaWalletStakedBalances =
'[data-testid="vega-wallet-balance-staked-validators"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWallet = '[data-testid="vega-wallet"]';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]';
const minVoteButton = '[data-testid="min-vote"]';
const maxVoteButton = '[data-testid="max-vote"]';
const voteButtons = '[data-testid="vote-buttons"]';
const votingDate = '[data-testid="voting-date"]';
const voteTwoMinExtraNote = '[data-testid="voting-2-mins-extra"]';
const rejectProposalsLink = '[href="/proposals/rejected"]';
const feedbackError = '[data-testid="Error"]';
const noOpenProposals = '[data-testid="no-open-proposals"]';
const noClosedProposals = '[data-testid="no-closed-proposals"]';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
context(
'Governance flow - with eth and vega wallets connected',
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.visit('/');
cy.get_network_parameters().then((network_parameters) => {
cy.wrap(
network_parameters['spam.protection.proposal.min.tokens'] /
1000000000000000000
).as('minProposerBalance');
cy.wrap(
network_parameters['spam.protection.voting.min.tokens'] /
1000000000000000000
).as('minVoterBalance');
cy.wrap(
network_parameters['governance.proposal.freeform.requiredMajority'] *
100
).as('requiredMajority');
});
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
});
beforeEach('visit governance tab', function () {
cy.reload();
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
});
it('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003
cy.get(noOpenProposals)
.should('be.visible')
.and('have.text', 'There are no open or yet to enact proposals');
cy.get(noClosedProposals)
.should('be.visible')
.and('have.text', 'There are no enacted or rejected proposals');
});
// 3002-PROP-002
// 3002-PROP-003
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
cy.contains(
`You must have at least 1 VEGA associated to make a proposal`
).should('be.visible');
});
// 3002-PROP-011
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
cy.get(votingDate).should('not.be.empty');
cy.get(voteTwoMinExtraNote).should(
'contain.text',
'we add 2 minutes of extra time'
);
cy.enter_unique_freeform_proposal_body(
'50',
generateFreeFormProposalTitle()
);
// 3002-PROP-012
// 3002-PROP-016
cy.wait_for_proposal_submitted();
});
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
cy.ensure_specified_unstaked_tokens_are_associated('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', '2');
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.close_staking_dialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
cy.navigate_to('proposals');
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'50',
generateFreeFormProposalTitle()
);
cy.wait_for_proposal_submitted();
});
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'0.1',
generateFreeFormProposalTitle()
);
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
cy.get('input:invalid')
.invoke('prop', 'validationMessage')
.should('equal', 'Value must be greater than or equal to 1.');
});
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'100000',
generateFreeFormProposalTitle()
);
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
cy.get('input:invalid')
.invoke('prop', 'validationMessage')
.should('equal', 'Value must be less than or equal to 8760.');
});
// 3001-VOTE-006
it('Creating a proposal - proposal rejected - able to access rejected proposals', function () {
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('1000').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
cy.get(rejectProposalsLink).click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible');
cy.get(viewProposalButton).click();
});
});
cy.get_proposal_information_from_table('State')
.contains('Rejected')
.and('be.visible');
cy.get_proposal_information_from_table('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible');
cy.get_proposal_information_from_table('Error details')
.contains('proposal closing time too late')
.and('be.visible');
});
// 0005-ETXN-004
it('Unable to create a proposal - when no tokens are associated', function () {
cy.vega_wallet_teardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as;
}
);
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
});
// 3002-PROP-009
it('Unable to create a proposal - when some but not enough tokens are associated', function () {
cy.ensure_specified_unstaked_tokens_are_associated(0.000001);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
}
);
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
});
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((freeformProposal) => {
freeformProposal.terms.closingTimestamp = closingDateTimestamp;
freeformProposal.unexpected = `i shouldn't be here`;
let proposalPayload = JSON.stringify(freeformProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
cy.get(rawProposalData)
.invoke('val')
.should('contain', "i shouldn't be here");
});
it('Unable to create a freeform proposal - when json terms section contains unexpected field', function () {
// 3001-VOTE-038
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = closingDateTimestamp;
rawProposal.terms.unexpectedField = `i shouldn't be here`;
let proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
});
// 1005-PROP-009
it.skip(
'Unable to vote on a freeform proposal - when some but not enough vega associated',
{ tags: '@smoke' },
function () {
const proposalTitle = generateFreeFormProposalTitle();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.wait_for_proposal_submitted();
cy.staking_page_disassociate_tokens('0.0001');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance).should('have.length', 1);
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.get(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
);
}
);
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
createRawProposal();
cy.get('[data-testid="manage-vega-wallet"]').click();
cy.get('[data-testid="disconnect"]').click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-075
// 3001-VOTE-076
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet')
.click();
cy.getByTestId('connector-jsonRpc').click();
cy.get(vegaWalletNameElement).should('be.visible');
cy.get(connectToVegaWalletButton).should('not.exist');
// 3001-VOTE-100
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'1.00',
txTimeout
);
cy.vote_for_proposal('against');
// 3001-VOTE-079
cy.contains('You voted: Against').should('be.visible');
});
}
);
@@ -1,162 +0,0 @@
import {
createFreeformProposal,
createRawProposal,
generateFreeFormProposalTitle,
governanceProposalType,
} from '../../support/governance.functions';
const proposalDetailsTitle = '[data-testid="proposal-title"]';
const openProposals = '[data-testid="open-proposals"]';
const voteStatus = '[data-testid="vote-status"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () {
cy.vega_wallet_set_specified_approval_amount('1000');
cy.visit('/');
});
beforeEach('visit proposals tab', function () {
cy.reload();
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
});
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
const minCloseDays = 2;
const maxCloseDays = 3;
// 3001-VOTE-005
let proposalDays = [
minCloseDays + 1,
maxCloseDays,
minCloseDays + 3,
minCloseDays + 2,
];
for (var index = 0; index < proposalDays.length; index++) {
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days(
proposalDays[index]
).then((closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
});
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
}
let arrayOfProposals = [];
cy.navigate_to('proposals');
cy.get(proposalDetailsTitle)
.each((proposalTitleElement) => {
arrayOfProposals.push(proposalTitleElement.text());
})
.then(() => {
cy.get_sort_order_of_supplied_array(arrayOfProposals).should(
'equal',
'descending'
);
});
});
it('Newly created proposals list - able to filter by proposerID to show it in list', function () {
const proposerId = Cypress.env('vegaWalletPublicKey');
const proposalTitle = generateFreeFormProposalTitle();
createFreeformProposal(proposalTitle);
cy.get_proposal_id_from_list(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
cy.get(`#${proposalId}`).should('contain', proposalId);
});
});
it('Newly created proposals list - shows title and portion of summary', function () {
createRawProposal(this.minProposerBalance); // 3001-VOTE-052
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
// 3001-VOTE-008
// 3001-VOTE-034
cy.get(`#${proposalId}`)
// 3001-VOTE-097
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(`#${proposalId}`)
.should(
'contain',
rawProposal.rationale.description.substring(0, 59)
)
.and('be.visible');
});
});
});
});
it('Newly created proposals list - shows open proposals in an open state', function () {
// 3001-VOTE-004
// 3001-VOTE-035
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
cy.get('@proposalIdText').then((proposalId) => {
cy.get_proposal_information_from_table('ID')
.contains(proposalId)
.and('be.visible');
});
cy.get_proposal_information_from_table('State')
.contains('Open')
.and('be.visible');
cy.get_proposal_information_from_table('Type')
.contains('Freeform')
.and('be.visible');
});
});
// 3001-VOTE-071
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateFreeFormProposalTitle();
const requiredParticipation = 0.001;
createFreeformProposal(proposalTitle);
cy.get_submitted_proposal_from_proposal_list(proposalTitle)
.as('submittedProposal')
.within(() => {
// 3001-VOTE-039
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
cy.vote_for_proposal('for');
cy.get_proposal_information_from_table('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
cy.get_proposal_information_from_table('Token participation met')
.contains('👍')
.should('be.visible');
cy.navigate_to('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(voteStatus).should('have.text', 'Set to pass')
);
});
});
});
@@ -63,39 +63,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
.and('have.text', 'Browse, vote, and propose');
});
});
it('should show open or enacted proposals with proposal summary', function () {
cy.get('body').then(($body) => {
if (!$body.find('[data-testid="proposals-list-item"]').length) {
cy.createMarket();
cy.reload();
cy.wait_for_spinner();
}
});
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
.within(() => {
cy.getByTestId('proposal-title')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-type')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-description')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-status')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('vote-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('view-proposal-btn').should('be.visible');
});
});
it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
@@ -38,6 +38,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
cy.navigate_to('proposals');
cy.go_to_make_new_proposal('Freeform');
cy.enter_unique_freeform_proposal_body('50', 'pub key proposal test');
cy.getByTestId('proposal-submit').should('be.visible').click();
cy.getByTestId('dialog-content').within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
@@ -30,11 +30,11 @@ context('Staking Page - verify elements on page', function () {
describe('with wallets disconnected', { tags: '@smoke' }, function () {
describe('description section', function () {
it('Should have validators tab highlighted', function () {
it('Should have staking tab highlighted', function () {
cy.verify_tab_highlighted('validators');
});
it('Should have validators ON VEGA header visible', function () {
it('Should have STAKING ON VEGA header visible', function () {
cy.verify_page_header('Validators');
});
@@ -338,8 +338,8 @@ context(
.contains(currency.id)
.parent()
.siblings()
.invoke('text')
.should('not.be.empty');
.within(() => cy.contains_exactly('10.00').should('be.visible'));
cy.get(vegaWalletCurrencyTitle)
.contains(currency.id)
.parent()
@@ -1,6 +1,3 @@
const epochTimeout = Cypress.env('epochTimeout');
const txTimeout = Cypress.env('txTimeout');
Cypress.Commands.add(
'convert_token_value_to_number',
{ prevSubject: true },
@@ -52,29 +49,3 @@ Cypress.Commands.add('wait_for_spinner', () => {
cy.get(navigation.pageSpinner, Cypress.env('epochTimeout')).should('exist');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
});
// This is a workaround function to begin tests with associating tokens without failing
// Should be removed when eth transaction bug is fixed
export function associateTokenStartOfTests() {
cy.highlight(`Associating tokens for first time`);
cy.ethereum_wallet_connect();
cy.connectVegaWallet();
cy.get('[href="/token/associate"]').first().click();
cy.getByTestId('associate-radio-wallet', { timeout: 30000 }).click();
cy.getByTestId('token-amount-input', epochTimeout).type('1');
cy.getByTestId('token-input-submit-button', txTimeout)
.should('be.enabled')
.click();
cy.contains(
`Associating with Vega key. Waiting for ${Cypress.env(
'blockConfirmations'
)} more confirmations..`,
txTimeout
).should('be.visible');
cy.getByTestId('associated-amount', txTimeout).should('contain.text', '1');
// Wait is needed to allow time for transaction to complete
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(10000);
cy.vega_wallet_teardown();
cy.clearLocalStorage();
}
@@ -8,7 +8,6 @@ const rawProposalData = '[data-testid="proposal-data"]';
const voteButtons = '[data-testid="vote-buttons"]';
const dialogTitle = '[data-testid="dialog-title"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -62,7 +61,6 @@ Cypress.Commands.add('enter_raw_proposal_body', (timestamp) => {
parseSpecialCharSequences: false,
delay: 2,
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal);
});
});
@@ -75,7 +73,6 @@ Cypress.Commands.add(
'this is a e2e freeform proposal description'
);
cy.get(proposalVoteDeadline).clear().click().type(timestamp);
cy.getByTestId('proposal-submit').should('be.visible').click();
}
);
@@ -188,40 +185,3 @@ Cypress.Commands.add('wait_for_proposal_submitted', () => {
cy.contains('Proposal submitted', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
});
export function createRawProposal(proposerBalance) {
if (proposerBalance)
cy.ensure_specified_unstaked_tokens_are_associated(proposerBalance);
cy.go_to_make_new_proposal('raw proposal');
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
}
export function generateFreeFormProposalTitle() {
const randomNum = Math.floor(Math.random() * 1000) + 1;
return randomNum + ': Freeform e2e proposal';
}
export function createFreeformProposal(proposalTitle) {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.getByTestId('proposal-title').invoke('text').as('proposalTitle');
cy.navigate_to('proposals');
}
export const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
@@ -18,7 +18,7 @@ export const NavDropDown = ({ navbarTheme }: { navbarTheme: NavbarTheme }) => {
<AppNavLink
name={
<NavDropdownMenuTrigger
className="w-auto flex items-center -m-3 p-3 cursor-pointer"
className="w-auto flex items-center"
data-testid="state-trigger"
onClick={() => setOpen(!isOpen)}
>
@@ -1,4 +1,4 @@
import { act } from '@testing-library/react';
import { act } from '@testing-library/react-hooks';
import { usePendingBalancesStore } from './use-pending-balances-manager';
import type { Event } from 'ethers';
@@ -1,6 +1,7 @@
import uniqBy from 'lodash/uniqBy';
import { create } from 'zustand';
import type { Event } from 'ethers';
import uniqBy from 'lodash/uniqBy';
import create from 'zustand';
export type PendingTxsStore = {
pendingBalances: Event[];
@@ -432,7 +432,6 @@
"associatedWithVegaKeys": "Associated with Vega keys",
"thisEpoch": "This Epoch",
"nextEpoch": "Next epoch",
"toSeeYourRewardsConnectYourWallet": "TO SEE YOUR REWARDS, CONNECT YOUR WALLET",
"rewardsIntro": "Earn rewards and infrastructure fees for trading and maintaining the network.",
"rewardsCallout": "Rewards are credited {{duration}} after the epoch ends.",
"rewardsCalloutDetail": "This delay is set by a network parameter",
@@ -458,7 +457,6 @@
"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",
"ofTotalDistributed": "of total distributed",
"checkBackSoon": "Check back soon",
"yourStake": "Your stake",
"reward": "Reward",
@@ -137,24 +137,6 @@ query Proposal($proposalId: ID!) {
}
}
}
priceMonitoringParameters {
triggers {
horizonSecs
probability
auctionExtensionSecs
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
}
}
positionDecimalPlaces
lpPriceRange
linearSlippageFactor
quadraticSlippageFactor
}
... on UpdateMarket {
marketId
File diff suppressed because one or more lines are too long
@@ -1,25 +0,0 @@
import { render } from '@testing-library/react';
import { AppStateProvider } from '../../contexts/app-state/app-state-provider';
import { ConnectToSeeRewards } from './connect-to-see-rewards';
describe('ConnectToSeeRewards', () => {
it('should render button correctly', () => {
const { getByTestId } = render(
<AppStateProvider>
<ConnectToSeeRewards />
</AppStateProvider>
);
expect(getByTestId('connect-to-vega-wallet-btn')).toBeInTheDocument();
});
it('should render the correct text', () => {
const { getByText } = render(
<AppStateProvider>
<ConnectToSeeRewards />
</AppStateProvider>
);
expect(
getByText('TO SEE YOUR REWARDS, CONNECT YOUR WALLET')
).toBeInTheDocument();
});
});
@@ -1,40 +0,0 @@
import classNames from 'classnames';
import { useTranslation } from 'react-i18next';
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button } from '@vegaprotocol/ui-toolkit';
import {
AppStateActionType,
useAppState,
} from '../../contexts/app-state/app-state-context';
import { SubHeading } from '../../components/heading';
export const ConnectToSeeRewards = () => {
const { appDispatch } = useAppState();
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
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('toSeeYourRewardsConnectYourWallet')} />
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
{t('connectVegaWallet')}
</Button>
</div>
);
};
@@ -5,10 +5,9 @@ import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
import { useRewardsQuery } from '../home/__generated__/Rewards';
import { ENV } from '../../../config';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
import { RewardTable } from './reward-table';
export const EpochIndividualRewards = () => {
export const RewardInfo = () => {
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const { delegationsPagination } = ENV;
@@ -31,10 +30,13 @@ export const EpochIndividualRewards = () => {
return removePaginationWrapper(data.party.rewardsConnection.edges);
}, [data]);
const epochIndividualRewardSummaries = useMemo(() => {
if (!data?.party) return [];
return generateEpochIndividualRewardsList(rewards);
}, [data?.party, rewards]);
const delegations = useMemo(() => {
if (!data?.party || !data.party.delegationsConnection?.edges?.length) {
return [];
}
return removePaginationWrapper(data.party.delegationsConnection.edges);
}, [data]);
return (
<AsyncRenderer
@@ -43,18 +45,20 @@ export const EpochIndividualRewards = () => {
data={data}
render={() => (
<div>
<p className="mb-10">
{t('Connected Vega key')}:{' '}
<span className="text-white">{pubKey}</span>
<p>
{t('Connected Vega key')}: {pubKey}
</p>
{epochIndividualRewardSummaries.length ? (
epochIndividualRewardSummaries.map(
(epochIndividualRewardSummary) => (
<EpochIndividualRewardsTable
data={epochIndividualRewardSummary}
{rewards.length ? (
rewards.map((reward, i) => {
if (!reward) return null;
return (
<RewardTable
key={i}
reward={reward}
delegations={delegations || []}
/>
)
)
);
})
) : (
<p>{t('noRewards')}</p>
)}
@@ -0,0 +1,79 @@
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 { DATE_FORMAT_DETAILED } from '../../../lib/date-formats';
import type {
DelegationFieldsFragment,
RewardFieldsFragment,
} from '../home/__generated__/Rewards';
interface RewardTableProps {
reward: RewardFieldsFragment;
delegations: DelegationFieldsFragment[] | [];
}
export const RewardTable = ({ reward, delegations }: RewardTableProps) => {
const { t } = useTranslation();
const {
appState: { decimals },
} = useAppState();
// Get your stake for epoch in which you have rewards
const stakeForEpoch = useMemo(() => {
if (!delegations.length) return '0';
const delegationsForEpoch = delegations
.filter((d) => d.epoch.toString() === reward.epoch.id)
.map((d) => toBigNum(d.amount, decimals));
if (delegationsForEpoch.length) {
return BigNumber.sum.apply(null, [
new BigNumber(0),
...delegationsForEpoch,
]);
}
return new BigNumber(0);
}, [decimals, delegations, reward.epoch.id]);
return (
<div className="mb-24">
<h3 className="text-lg text-white mb-4">
{t('Epoch')} {reward.epoch.id}
</h3>
<KeyValueTable>
<KeyValueTableRow>
{t('rewardType')}
<span>{reward.rewardType}</span>
</KeyValueTableRow>
<KeyValueTableRow>
{t('yourStake')}
<span>{stakeForEpoch.toString()}</span>
</KeyValueTableRow>
<KeyValueTableRow>
{t('reward')}
<span>
{formatNumber(toBigNum(reward.amount, decimals))}{' '}
{reward.asset.symbol}
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{t('shareOfReward')}
<span>
{new BigNumber(reward.percentageOfTotal).dp(2).toString()}%
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{t('received')}
<span>
{format(new Date(reward.receivedAt), DATE_FORMAT_DETAILED)}
</span>
</KeyValueTableRow>
</KeyValueTable>
</div>
);
};
@@ -1,63 +0,0 @@
import { render } from '@testing-library/react';
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
const mockData = {
epoch: '4441',
rewards: [
{
asset: 'tDAI',
totalAmount: '5',
rewardTypes: {
ACCOUNT_TYPE_GLOBAL_REWARD: {
amount: '0',
percentageOfTotal: '0',
},
ACCOUNT_TYPE_FEES_INFRASTRUCTURE: {
amount: '5',
percentageOfTotal: '0.00305237260923',
},
ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: {
amount: '0',
percentageOfTotal: '0',
},
ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: {
amount: '0',
percentageOfTotal: '0',
},
ACCOUNT_TYPE_FEES_LIQUIDITY: {
amount: '0',
percentageOfTotal: '0',
},
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
};
describe('EpochIndividualRewardsTable', () => {
it('should render correctly', () => {
const { getByTestId } = render(
<AppStateProvider>
<EpochIndividualRewardsTable data={mockData} />
</AppStateProvider>
);
expect(getByTestId('epoch-individual-rewards-table')).toBeInTheDocument();
expect(getByTestId('individual-rewards-asset')).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD')).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES')
).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
).toBeInTheDocument();
});
});
@@ -1,111 +0,0 @@
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
} from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { EpochIndividualReward } from './generate-epoch-individual-rewards-list';
import { useTranslation } from 'react-i18next';
interface EpochIndividualRewardsGridProps {
data: EpochIndividualReward;
}
interface RewardItemProps {
value: string;
percentageOfTotal?: string;
dataTestId: string;
last?: boolean;
}
const DisplayReward = ({
reward,
percentageOfTotal,
}: {
reward: string;
percentageOfTotal?: string;
}) => {
const { t } = useTranslation();
const {
appState: { decimals },
} = useAppState();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
}
return (
<Tooltip
description={
<div className="flex flex-col items-start">
<span>{formatNumber(toBigNum(reward, decimals), decimals)}</span>
{percentageOfTotal && (
<span className="text-vega-dark-300">
({percentageOfTotal}% {t('ofTotalDistributed')})
</span>
)}
</div>
}
>
<button>
<div className="flex flex-col items-start">
<span>{formatNumber(toBigNum(reward, decimals))}</span>
{percentageOfTotal && (
<span className="text-vega-dark-300">
({formatNumber(toBigNum(percentageOfTotal, 4)).toString()}%)
</span>
)}
</div>
</button>
</Tooltip>
);
};
const RewardItem = ({
value,
percentageOfTotal,
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 reward={value} percentageOfTotal={percentageOfTotal} />
</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 EpochIndividualRewardsTable = ({
data,
}: EpochIndividualRewardsGridProps) => {
return (
<RewardsTable
dataTestId="epoch-individual-rewards-table"
epoch={Number(data.epoch)}
>
{data.rewards.map(({ asset, rewardTypes, totalAmount }, i) => (
<div className="contents" key={i}>
<div
data-testid="individual-rewards-asset"
className={`${rowGridItemStyles()} p-5`}
>
{asset}
</div>
{Object.entries(rewardTypes).map(
([key, { amount, percentageOfTotal }]) => (
<RewardItem
key={key}
value={amount}
percentageOfTotal={percentageOfTotal}
dataTestId={key}
/>
)
)}
<RewardItem dataTestId="total" value={totalAmount} last={true} />
</div>
))}
</RewardsTable>
);
};
@@ -1,235 +0,0 @@
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
import { AccountType } from '@vegaprotocol/types';
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
describe('generateEpochIndividualRewardsList', () => {
const reward1: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
const reward2: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
const reward3: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '200',
percentageOfTotal: '0.2',
receivedAt: new Date(),
asset: { id: 'gbp', symbol: 'GBP' },
party: { id: 'blah' },
epoch: { id: '2' },
};
const reward4: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '100',
percentageOfTotal: '0.1',
receivedAt: new Date(),
asset: { id: 'usd', symbol: 'USD' },
party: { id: 'blah' },
epoch: { id: '1' },
};
const rewardWrongType: RewardFieldsFragment = {
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '50',
percentageOfTotal: '0.05',
receivedAt: new Date(),
asset: { id: 'eur', symbol: 'EUR' },
party: { id: 'blah' },
epoch: { id: '2' },
};
it('should return an empty array if no rewards are provided', () => {
expect(generateEpochIndividualRewardsList([])).toEqual([]);
});
it('should filter out any rewards of the wrong type', () => {
const result = generateEpochIndividualRewardsList([rewardWrongType]);
expect(result).toEqual([]);
});
it('should return reward in the correct format', () => {
const result = generateEpochIndividualRewardsList([reward1]);
expect(result[0]).toEqual({
epoch: '1',
rewards: [
{
asset: 'USD',
totalAmount: '100',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '100',
percentageOfTotal: '0.1',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
});
});
it('should return an array sorted by epoch descending', () => {
const rewards = [reward1, reward2, reward3, reward4];
const result1 = generateEpochIndividualRewardsList(rewards);
expect(result1[0].epoch).toEqual('2');
expect(result1[1].epoch).toEqual('1');
const reorderedRewards = [reward4, reward3, reward2, reward1];
const result2 = generateEpochIndividualRewardsList(reorderedRewards);
expect(result2[0].epoch).toEqual('2');
expect(result2[1].epoch).toEqual('1');
});
it('correctly calculates the total value of rewards for an asset', () => {
const rewards = [reward1, reward4];
const result = generateEpochIndividualRewardsList(rewards);
expect(result[0].rewards[0].totalAmount).toEqual('200');
});
it('returns data in the expected shape', () => {
// Just sanity checking the whole structure here
const rewards = [reward1, reward2, reward3, reward4];
const result = generateEpochIndividualRewardsList(rewards);
expect(result).toEqual([
{
epoch: '2',
rewards: [
{
asset: 'EUR',
totalAmount: '50',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '50',
percentageOfTotal: '0.05',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
{
asset: 'GBP',
totalAmount: '200',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '200',
percentageOfTotal: '0.2',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
},
{
epoch: '1',
rewards: [
{
asset: 'USD',
totalAmount: '200',
rewardTypes: {
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
amount: '100',
percentageOfTotal: '0.1',
},
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
amount: '100',
percentageOfTotal: '0.1',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
amount: '0',
percentageOfTotal: '0',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
amount: '0',
percentageOfTotal: '0',
},
},
},
],
},
]);
});
});
@@ -1,79 +0,0 @@
import { BigNumber } from '../../../lib/bignumber';
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { RewardFieldsFragment } from '../home/__generated__/Rewards';
import type { AccountType } from '@vegaprotocol/types';
export interface EpochIndividualReward {
epoch: string;
rewards: {
asset: string;
totalAmount: string;
rewardTypes: {
[key in AccountType]?: {
amount: string;
percentageOfTotal: string;
};
};
}[];
}
const accountTypes = Object.keys(RowAccountTypes);
const emptyRowAccountTypes = accountTypes.map((type) => [
type,
{
amount: '0',
percentageOfTotal: '0',
},
]);
export const generateEpochIndividualRewardsList = (
rewards: RewardFieldsFragment[]
) => {
// We take the rewards and aggregate them by epoch and asset.
const epochIndividualRewards = rewards.reduce((map, reward) => {
const epochId = reward.epoch.id;
const assetName = reward.asset.symbol;
const rewardType = reward.rewardType;
const amount = reward.amount;
const percentageOfTotal = reward.percentageOfTotal;
// if the rewardType is not of a type we display in the table, we skip it.
if (!accountTypes.includes(rewardType)) {
return map;
}
if (!map.has(epochId)) {
map.set(epochId, { epoch: epochId, rewards: [] });
}
const epoch = map.get(epochId);
let asset = epoch?.rewards.find((r) => r.asset === assetName);
if (!asset) {
asset = {
asset: assetName,
totalAmount: '0',
rewardTypes: Object.fromEntries(emptyRowAccountTypes),
};
epoch?.rewards.push(asset);
}
asset.rewardTypes[rewardType] = { amount, percentageOfTotal };
// totalAmount is the sum of all rewardTypes amounts
asset.totalAmount = Object.values(asset.rewardTypes).reduce(
(sum, rewardType) => {
return new BigNumber(sum).plus(rewardType.amount).toString();
},
'0'
);
return map;
}, new Map<string, EpochIndividualReward>());
return Array.from(epochIndividualRewards.values()).sort(
(a, b) => Number(b.epoch) - Number(a.epoch)
);
};
@@ -5,7 +5,7 @@ import { generateEpochTotalRewardsList } from './generate-epoch-total-rewards-li
import { NoRewards } from '../no-rewards';
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
export const EpochTotalRewards = () => {
export const EpochRewards = () => {
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
variables: {
epochRewardSummariesPagination: {
@@ -15,7 +15,7 @@ export const EpochTotalRewards = () => {
});
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
const epochTotalRewardSummaries = generateEpochTotalRewardsList(data) || [];
const epochRewardSummaries = generateEpochTotalRewardsList(data) || [];
return (
<AsyncRenderer
@@ -27,12 +27,12 @@ export const EpochTotalRewards = () => {
className="max-w-full overflow-auto"
data-testid="epoch-rewards-total"
>
{epochTotalRewardSummaries.length === 0 ? (
{epochRewardSummaries.length === 0 ? (
<NoRewards />
) : (
<>
{epochTotalRewardSummaries.map((epochTotalSummary, index) => (
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
{epochRewardSummaries.map((aggregatedEpochSummary) => (
<EpochTotalRewardsTable data={aggregatedEpochSummary} />
))}
</>
)}
@@ -1,5 +1,4 @@
import { render } from '@testing-library/react';
import { AppStateProvider } from '../../../contexts/app-state/app-state-provider';
import { EpochTotalRewardsTable } from './epoch-total-rewards-table';
import { AccountType } from '@vegaprotocol/types';
@@ -11,30 +10,10 @@ const mockData = {
'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '295',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
totalAmount: '295',
},
@@ -43,25 +22,15 @@ const mockData = {
describe('EpochTotalRewardsTable', () => {
it('should render correctly', () => {
const { getByTestId } = render(
<AppStateProvider>
<EpochTotalRewardsTable data={mockData} />
</AppStateProvider>
);
const { getByTestId } = render(<EpochTotalRewardsTable data={mockData} />);
expect(getByTestId('epoch-total-rewards-table')).toBeInTheDocument();
expect(getByTestId('asset')).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD')).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES')
).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES')
).toBeInTheDocument();
expect(getByTestId('ACCOUNT_TYPE_FEES_LIQUIDITY')).toBeInTheDocument();
expect(
getByTestId('ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS')
).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,14 +1,19 @@
import { formatNumber, toBigNum } from '@vegaprotocol/react-helpers';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { useAppState } from '../../../contexts/app-state/app-state-context';
import {
rowGridItemStyles,
RewardsTable,
} from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { EpochTotalSummary } from './generate-epoch-total-rewards-list';
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: EpochTotalSummary;
data: AggregatedEpochSummary;
}
interface ColumnHeaderProps {
title: string;
tooltipContent?: string;
className?: string;
}
interface RewardItemProps {
@@ -17,28 +22,61 @@ interface RewardItemProps {
last?: boolean;
}
const DisplayReward = ({ reward }: { reward: string }) => {
const {
appState: { decimals },
} = useAppState();
const displayReward = (reward: string) => {
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">-</span>;
return <span className="text-vega-dark-300">0</span>;
}
return (
<Tooltip description={formatNumber(toBigNum(reward, decimals), decimals)}>
<button>{formatNumber(toBigNum(reward, decimals))}</button>
</Tooltip>
);
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 reward={value} />
</div>
<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>
);
@@ -46,19 +84,129 @@ const RewardItem = ({ value, dataTestId, last }: RewardItemProps) => (
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 (
<RewardsTable dataTestId="epoch-total-rewards-table" epoch={data.epoch}>
{data.assetRewards.map(({ name, rewards, totalAmount }, i) => (
<div className="contents" key={i}>
<div data-testid="asset" className={`${rowGridItemStyles()} p-5`}>
{name}
<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>
{rewards.map(({ rewardType, amount }, i) => (
<RewardItem key={i} dataTestId={rewardType} value={amount} />
))}
<RewardItem dataTestId="total" value={totalAmount} last={true} />
</div>
))}
</RewardsTable>
))}
</div>
</div>
);
};
@@ -61,7 +61,7 @@ describe('generateEpochAssetRewardsList', () => {
expect(result).toEqual([]);
});
it('should return an array of unnamed assets if no asset names are provided (should not happen)', () => {
it('should return an array of unnamed assets if no assets are provided (should not happen)', () => {
const epochData = {
assetsConnection: {
edges: [],
@@ -72,10 +72,18 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '123',
},
},
{
node: {
epoch: 2,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '5',
},
},
],
},
epoch: {
@@ -96,34 +104,30 @@ describe('generateEpochAssetRewardsList', () => {
name: '',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '123',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
totalAmount: '123',
},
],
},
{
epoch: 2,
assetRewards: [
{
assetId: '1',
name: '',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '5',
},
],
totalAmount: '5',
},
],
},
]);
});
@@ -151,7 +155,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '123',
},
},
@@ -163,6 +167,22 @@ describe('generateEpochAssetRewardsList', () => {
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,
@@ -191,31 +211,30 @@ describe('generateEpochAssetRewardsList', () => {
name: 'Asset 1',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
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_REWARD_MAKER_PAID_FEES,
amount: '123',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '17.9873',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
amount: '1',
},
],
totalAmount: '223',
totalAmount: '18.9873',
},
],
},
@@ -226,30 +245,10 @@ describe('generateEpochAssetRewardsList', () => {
assetId: '1',
name: 'Asset 1',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '5',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
totalAmount: '5',
},
@@ -3,8 +3,6 @@ import type {
EpochRewardSummaryFieldsFragment,
} from '../home/__generated__/Rewards';
import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
import { RowAccountTypes } from '../shared-rewards-table-assets/shared-rewards-table-assets';
import type { AccountType } from '@vegaprotocol/types';
interface EpochSummaryWithNamedReward extends EpochRewardSummaryFieldsFragment {
name: string;
@@ -20,16 +18,11 @@ export interface AggregatedEpochRewardSummary {
totalAmount: string;
}
export interface EpochTotalSummary {
export interface AggregatedEpochSummary {
epoch: EpochRewardSummaryFieldsFragment['epoch'];
assetRewards: AggregatedEpochRewardSummary[];
}
const emptyRowAccountTypes = Object.keys(RowAccountTypes).map((type) => ({
rewardType: type as AccountType,
amount: '0',
}));
export const generateEpochTotalRewardsList = (
epochData: EpochAssetsRewardsQuery | undefined
) => {
@@ -65,7 +58,7 @@ export const generateEpochTotalRewardsList = (
}, [] as EpochSummaryWithNamedReward[][]);
// Now aggregate the array of arrays of epoch summaries by asset rewards.
const epochTotalRewards: EpochTotalSummary[] =
const aggregatedEpochSummaries: AggregatedEpochSummary[] =
aggregatedEpochSummariesByEpochNumber.map((epochSummaries) => {
const assetRewards = epochSummaries.reduce((acc, epochSummary) => {
const assetRewardIndex = acc.findIndex(
@@ -79,36 +72,18 @@ export const generateEpochTotalRewardsList = (
assetId: epochSummary.assetId,
name: epochSummary.name,
rewards: [
...emptyRowAccountTypes.map((emptyRowAccountType) => {
if (
emptyRowAccountType.rewardType === epochSummary.rewardType
) {
return {
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
};
} else {
return emptyRowAccountType;
}
}),
{
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
},
],
totalAmount: epochSummary.amount,
});
} else {
acc[assetRewardIndex].rewards = acc[assetRewardIndex].rewards.map(
(reward) => {
if (reward.rewardType === epochSummary.rewardType) {
return {
rewardType: epochSummary.rewardType,
amount: (
Number(reward.amount) + Number(epochSummary.amount)
).toString(),
};
} else {
return reward;
}
}
);
acc[assetRewardIndex].rewards.push({
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
});
acc[assetRewardIndex].totalAmount = (
Number(acc[assetRewardIndex].totalAmount) +
Number(epochSummary.amount)
@@ -124,5 +99,5 @@ export const generateEpochTotalRewardsList = (
};
});
return epochTotalRewards;
return aggregatedEpochSummaries;
};
@@ -1,30 +1,34 @@
// @ts-ignore No types available for duration-js
import Duration from 'duration-js';
import { useMemo, useState } from 'react';
import React, { useMemo, useState } from 'react';
import { formatDistance } from 'date-fns';
import { useTranslation } from 'react-i18next';
import {
Button,
Callout,
Intent,
AsyncRenderer,
Toggle,
ExternalLink,
} from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import {
useNetworkParams,
NetworkParams,
createDocsLinks,
} from '@vegaprotocol/react-helpers';
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 { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-rewards';
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';
import { ConnectToSeeRewards } from '../connect-to-see-rewards';
import { EpochTotalRewards } from '../epoch-total-rewards/epoch-total-rewards';
type RewardsView = 'total' | 'individual';
@@ -35,14 +39,10 @@ export const RewardsPage = () => {
const [toggleRewardsView, setToggleRewardsView] =
useState<RewardsView>('total');
const {
data: epochData,
loading: epochLoading,
error: epochError,
refetch,
} = useEpochQuery();
useRefreshAfterEpoch(epochData?.epoch.timestamps.expiry, refetch);
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { appDispatch } = useAppState();
const {
params,
@@ -50,6 +50,14 @@ export const RewardsPage = () => {
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(() => {
if (!params) {
return 0;
@@ -95,8 +103,8 @@ export const RewardsPage = () => {
</div>
) : null}
{epochData?.epoch &&
epochData.epoch?.id &&
{epochData &&
epochData.epoch.id &&
epochData.epoch.timestamps.start &&
epochData.epoch.timestamps.expiry && (
<section className="mb-16">
@@ -140,13 +148,26 @@ export const RewardsPage = () => {
</section>
{toggleRewardsView === 'total' ? (
<EpochTotalRewards />
<EpochRewards />
) : (
<section>
{pubKey && pubKeys?.length ? (
<EpochIndividualRewards />
<RewardInfo />
) : (
<ConnectToSeeRewards />
<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>
)}
@@ -1,125 +0,0 @@
import classNames from 'classnames';
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import type { ReactNode } from 'react';
import { SubHeading } from '../../../components/heading';
import { AccountType } from '@vegaprotocol/types';
// This is the data structure that matters for defining which Account types
// are displayed in the rewards tables. It sets column titles and tooltips,
// and is used to filter the data that is passed to functions to generate
// the table rows. It's important to preserve the order.
export const RowAccountTypes = {
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
columnTitle: 'rewardsColStakingHeader',
description: 'rewardsColStakingTooltip',
},
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
columnTitle: 'rewardsColInfraHeader',
description: 'rewardsColInfraTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
columnTitle: 'rewardsColPriceTakingHeader',
description: 'rewardsColPriceTakingTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
columnTitle: 'rewardsColPriceMakingHeader',
description: 'rewardsColPriceMakingTooltip',
},
[AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY]: {
columnTitle: 'rewardsColLiquidityProvisionHeader',
description: 'rewardsColLiquidityProvisionTooltip',
},
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
columnTitle: 'rewardsColMarketCreationHeader',
description: 'rewardsColMarketCreationTooltip',
},
};
interface ColumnHeaderProps {
title: string;
tooltipContent?: string;
className?: string;
}
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,
});
export 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 gridStyles = classNames(
'grid grid-cols-[repeat(8,minmax(100px,auto))] max-w-full overflow-auto',
`border-t border-vega-dark-200`,
'text-sm'
);
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 ColumnHeaders = () => {
const { t } = useTranslation();
return (
<div className="contents">
<ColumnHeader
title={t('rewardsColAssetHeader')}
className={headerGridItemStyles()}
/>
{Object.values(RowAccountTypes).map(({ columnTitle, description }) => (
<ColumnHeader
key={columnTitle}
title={t(columnTitle)}
tooltipContent={t(description)}
className={headerGridItemStyles()}
/>
))}
<ColumnHeader
title={t('rewardsColTotalHeader')}
className={headerGridItemStyles(true)}
/>
</div>
);
};
export interface RewardTableProps {
dataTestId: string;
epoch: number;
children: ReactNode;
}
// Rewards table children will be the row items. Make sure they contain
// the same number of columns and map to the data of the ColumnHeaders component.
export const RewardsTable = ({
dataTestId,
epoch,
children,
}: RewardTableProps) => (
<div data-testid={dataTestId} className="mb-12">
<SubHeading title={`EPOCH ${epoch}`} />
<div className={gridStyles}>
<ColumnHeaders />
{children}
</div>
</div>
);
@@ -50,117 +50,101 @@ export const StandbyPendingValidatorsTable = ({
const nodes = useMemo(() => {
if (!data) return [];
return data
.sort((a, b) => {
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
return bVotingPower.minus(aVotingPower).toNumber();
})
.map((node, index) => {
const votingPowerRanking = index + 1;
return data.map(
({
id,
name,
avatarUrl,
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore },
pendingStake,
}) => {
const { rawValidatorScore, performanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakedAmount = getOverstakedAmount(
rawValidatorScore,
stakedTotal,
totalStake
);
let individualStakeNeededForPromotion,
individualStakeNeededForPromotionDescription;
if (stakeNeededForPromotion && performanceScore) {
const stakedTotalBigNum = new BigNumber(stakedTotal);
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
const performanceScoreBigNum = new BigNumber(performanceScore);
const calc = stakeNeededBigNum
.dividedBy(performanceScoreBigNum)
.minus(stakedTotalBigNum);
if (calc.isGreaterThan(0)) {
individualStakeNeededForPromotion = calc.toString();
individualStakeNeededForPromotionDescription = t(
stakeNeededForPromotionDescription,
{
prefix: formatNumber(calc, 2).toString(),
}
);
} else {
individualStakeNeededForPromotion = '0';
individualStakeNeededForPromotionDescription = t(
stakeNeededForPromotionDescription,
{
prefix: formatNumber(0, 2).toString(),
}
);
}
}
return {
...node,
votingPowerRanking,
};
})
.map(
({
id,
name,
avatarUrl,
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore },
pendingStake,
votingPowerRanking,
}) => {
const { rawValidatorScore, performanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakedAmount = getOverstakedAmount(
[ValidatorFields.VALIDATOR]: {
avatarUrl,
name,
},
[ValidatorFields.STAKE]: formatNumber(
toBigNum(stakedTotal, decimals),
2
),
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
individualStakeNeededForPromotion || null,
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
individualStakeNeededForPromotionDescription || t('n/a'),
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
toBigNum(stakedByDelegates, decimals),
2
),
[ValidatorFields.STAKED_BY_OPERATOR]: formatNumber(
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]:
getPerformancePenalty(performanceScore),
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
overstakedAmount,
totalStake
),
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
rawValidatorScore,
performanceScore,
stakedTotal,
totalStake
);
let individualStakeNeededForPromotion,
individualStakeNeededForPromotionDescription;
if (stakeNeededForPromotion && performanceScore) {
const stakedTotalBigNum = new BigNumber(stakedTotal);
const stakeNeededBigNum = new BigNumber(stakeNeededForPromotion);
const performanceScoreBigNum = new BigNumber(performanceScore);
const calc = stakeNeededBigNum
.dividedBy(performanceScoreBigNum)
.minus(stakedTotalBigNum);
if (calc.isGreaterThan(0)) {
individualStakeNeededForPromotion = calc.toString();
individualStakeNeededForPromotionDescription = t(
stakeNeededForPromotionDescription,
{
prefix: formatNumber(calc, 2).toString(),
}
);
} else {
individualStakeNeededForPromotion = '0';
individualStakeNeededForPromotionDescription = t(
stakeNeededForPromotionDescription,
{
prefix: formatNumber(0, 2).toString(),
}
);
}
}
return {
id,
[ValidatorFields.RANKING_INDEX]: votingPowerRanking,
[ValidatorFields.VALIDATOR]: {
avatarUrl,
name,
},
[ValidatorFields.STAKE]: formatNumber(
toBigNum(stakedTotal, decimals),
2
),
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION]:
individualStakeNeededForPromotion || null,
[ValidatorFields.STAKE_NEEDED_FOR_PROMOTION_DESCRIPTION]:
individualStakeNeededForPromotionDescription || t('n/a'),
[ValidatorFields.STAKE_SHARE]: stakedTotalPercentage(stakeScore),
[ValidatorFields.STAKED_BY_DELEGATES]: formatNumber(
toBigNum(stakedByDelegates, decimals),
2
),
[ValidatorFields.STAKED_BY_OPERATOR]: formatNumber(
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]:
getPerformancePenalty(performanceScore),
[ValidatorFields.OVERSTAKED_AMOUNT]: overstakedAmount.toString(),
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
overstakedAmount,
totalStake
),
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
rawValidatorScore,
performanceScore,
stakedTotal,
totalStake
),
[ValidatorFields.PENDING_STAKE]: formatNumber(
toBigNum(pendingStake, decimals),
2
),
};
}
);
),
[ValidatorFields.PENDING_STAKE]: formatNumber(
toBigNum(pendingStake, decimals),
2
),
};
}
);
}, [
data,
decimals,
@@ -174,12 +158,6 @@ export const StandbyPendingValidatorsTable = ({
const StandbyPendingTable = forwardRef<AgGridReact>((_, gridRef) => {
const colDefs = useMemo<ColDef[]>(
() => [
{
field: ValidatorFields.RANKING_INDEX,
headerName: '#',
width: 60,
pinned: 'left',
},
{
field: ValidatorFields.VALIDATOR,
headerName: t(ValidatorFields.VALIDATOR).toString(),
+2 -8
View File
@@ -59,17 +59,11 @@ export const getOverstakedAmount = (
export const getOverstakingPenalty = (
overstakedAmount: BigNumber,
stakedOnNode: string
) => {
// avoid division by zero
if (new BigNumber(stakedOnNode).isZero() || overstakedAmount.isZero()) {
return '0';
}
return formatNumberPercentage(
) =>
formatNumberPercentage(
overstakedAmount.dividedBy(new BigNumber(stakedOnNode)).times(100),
2
);
};
export const getTotalPenalties = (
rawValidatorScore: string | null | undefined,
@@ -25,38 +25,12 @@ const generateProposal = (code: string): ProposalListFieldsFragment => ({
totalWeight: '',
},
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
__typename: 'ProposalTerms',
closingDatetime: '',
enactmentDatetime: undefined,
change: {
__typename: 'NewMarket',
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
__typename: 'InstrumentConfiguration',
code: code,
@@ -68,34 +42,6 @@ const generateProposal = (code: string): ProposalListFieldsFragment => ({
id: 'A',
name: 'A',
symbol: 'A',
decimals: 1,
quantum: '',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
},
},
@@ -3,8 +3,6 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsQuery } from '@vegaprotocol/mock';
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
const dialogCloseBtn = 'dialog-close';
describe('markets table', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.clearLocalStorage().then(() => {
@@ -149,7 +147,7 @@ describe('markets table', { tags: '@smoke' }, () => {
});
cy.visit('#/markets/market-0');
cy.url().should('contain', 'market-0');
cy.getByTestId(dialogCloseBtn).click();
cy.getByTestId('dialog-close').click();
cy.getByTestId('item-value').contains('Opening auction').realHover();
cy.getByTestId('opening-auction-sub-status').should(
'contain.text',
@@ -173,8 +171,7 @@ describe('markets table', { tags: '@smoke' }, () => {
});
function openMarketDropDown() {
cy.getByTestId(dialogCloseBtn).should('be.visible');
cy.getByTestId(dialogCloseBtn).click();
cy.getByTestId('dialog-close').click();
cy.getByTestId('popover-trigger').click();
cy.contains('Loading market data...').should('not.exist');
}
@@ -1,6 +1,6 @@
import { mockConnectWallet } from '@vegaprotocol/cypress';
beforeEach(() => {
before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
@@ -19,11 +19,11 @@ describe('Desktop view', { tags: '@smoke' }, () => {
cy.getByTestId('navbar')
.find(`[data-testid="navbar-links"] a[data-testid=${link}]`)
.then((element) => {
cy.contains('Loading...').should('not.exist');
cy.wrap(element).click();
cy.wrap(element)
.get('span.absolute.md\\:h-1.w-full')
.should('exist');
cy.contains('No market').should('not.exist');
cy.location('hash').should('equal', hashes[index]);
});
});
@@ -69,6 +69,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.contains('No market').should('not.exist');
cy.location('hash').should('equal', '#/markets/market-1');
});
});
+1 -9
View File
@@ -96,15 +96,7 @@ export const MarketPage = () => {
const tradeView = useMemo(() => {
if (w > 960) {
return (
<TradeGrid
market={data}
onSelect={onSelect}
pinnedAsset={
data?.tradableInstrument.instrument.product.settlementAsset
}
/>
);
return <TradeGrid market={data} onSelect={onSelect} />;
}
return (
<TradePanels
@@ -28,7 +28,6 @@ import { NO_MARKET } from './constants';
import { LiquidityContainer } from '../liquidity/liquidity';
import { useNavigate } from 'react-router-dom';
import { Links, Routes } from '../../pages/client-router';
import type { PinnedAsset } from '@vegaprotocol/accounts';
type MarketDependantView =
| typeof CandlesChartContainer
@@ -66,17 +65,14 @@ type TradingView = keyof typeof TradingViews;
interface TradeGridProps {
market: Market | null;
onSelect: (marketId: string) => void;
pinnedAsset?: PinnedAsset;
}
const MainGrid = ({
marketId,
onSelect,
pinnedAsset,
}: {
marketId: string;
onSelect?: (marketId: string) => void;
pinnedAsset?: PinnedAsset;
}) => {
const navigate = useNavigate();
const onMarketClick = (marketId: string) => {
@@ -179,7 +175,7 @@ const MainGrid = ({
</Tab>
<Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer>
<TradingViews.Collateral pinnedAsset={pinnedAsset} />
<TradingViews.Collateral />
</VegaWalletContainer>
</Tab>
</Tabs>
@@ -190,19 +186,11 @@ const MainGrid = ({
};
const MainGridWrapped = memo(MainGrid);
export const TradeGrid = ({
market,
onSelect,
pinnedAsset,
}: TradeGridProps) => {
export const TradeGrid = ({ market, onSelect }: TradeGridProps) => {
return (
<div className="h-full grid grid-rows-[min-content_1fr]">
<TradeMarketHeader market={market} onSelect={onSelect} />
<MainGridWrapped
marketId={market?.id || ''}
onSelect={onSelect}
pinnedAsset={pinnedAsset}
/>
<MainGridWrapped marketId={market?.id || ''} onSelect={onSelect} />
</div>
);
};
@@ -226,14 +214,12 @@ interface TradePanelsProps {
onSelect: (marketId: string) => void;
onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void;
pinnedAsset?: PinnedAsset;
}
export const TradePanels = ({
market,
onSelect,
onClickCollateral,
pinnedAsset,
}: TradePanelsProps) => {
const [view, setView] = useState<TradingView>('Candles');
const renderView = () => {
@@ -242,7 +228,6 @@ export const TradePanels = ({
onSelect: (marketId: string) => void;
onMarketClick?: (marketId: string) => void;
onClickCollateral: () => void;
pinnedAsset?: PinnedAsset;
}>(TradingViews[view]);
if (!Component) {
@@ -256,7 +241,6 @@ export const TradePanels = ({
marketId={market?.id}
onSelect={onSelect}
onClickCollateral={onClickCollateral}
pinnedAsset={pinnedAsset}
/>
);
};
@@ -6,7 +6,7 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
export const DepositsContainer = () => {
const { pubKey, isReadOnly } = useVegaWallet();
const { data, loading, error, reload } = useDataProvider({
const { data, loading, error } = useDataProvider({
dataProvider: depositsProvider,
variables: { partyId: pubKey || '' },
skip: !pubKey,
@@ -27,7 +27,6 @@ export const DepositsContainer = () => {
error={error}
noDataCondition={(data) => !(data && data.length)}
noDataMessage={t('No deposits')}
reload={reload}
/>
</div>
</div>
@@ -10,7 +10,7 @@ import { VegaWalletContainer } from '../../components/vega-wallet-container';
export const WithdrawalsContainer = () => {
const { pubKey, isReadOnly } = useVegaWallet();
const { data, loading, error, reload } = useDataProvider({
const { data, loading, error } = useDataProvider({
dataProvider: withdrawalProvider,
variables: { partyId: pubKey || '' },
skip: !pubKey,
@@ -33,7 +33,6 @@ export const WithdrawalsContainer = () => {
error={error}
noDataCondition={(data) => !(data && data.length)}
noDataMessage={t('No withdrawals')}
reload={reload}
/>
</div>
</div>
@@ -5,15 +5,10 @@ import { useWithdrawalDialog } from '@vegaprotocol/withdraws';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import type { PinnedAsset } from '@vegaprotocol/accounts';
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
import { useDepositDialog } from '@vegaprotocol/deposits';
export const AccountsContainer = ({
pinnedAsset,
}: {
pinnedAsset?: PinnedAsset;
}) => {
export const AccountsContainer = () => {
const { pubKey, isReadOnly } = useVegaWallet();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const openWithdrawalDialog = useWithdrawalDialog((store) => store.open);
@@ -44,7 +39,6 @@ export const AccountsContainer = ({
onClickWithdraw={openWithdrawalDialog}
onClickDeposit={openDepositDialog}
isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset}
/>
</div>
{!isReadOnly && (
@@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { proposalsDataProvider } from '@vegaprotocol/governance';
import { proposalsListDataProvider } from '@vegaprotocol/governance';
import take from 'lodash/take';
import * as Types from '@vegaprotocol/types';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
@@ -19,7 +19,7 @@ export const ProposedMarkets = () => {
};
}, []);
const { data } = useDataProvider({
dataProvider: proposalsDataProvider,
dataProvider: proposalsListDataProvider,
variables,
skipUpdates: true,
});
+3 -11
View File
@@ -4,7 +4,6 @@ import type { AgGridReact } from 'ag-grid-react';
import { useRef, useMemo, memo } from 'react';
import type { AccountFields } from './accounts-data-provider';
import { aggregatedAccountsDataProvider } from './accounts-data-provider';
import type { PinnedAsset } from './accounts-table';
import { AccountTable } from './accounts-table';
interface AccountManagerProps {
@@ -13,7 +12,6 @@ interface AccountManagerProps {
onClickWithdraw?: (assetId?: string) => void;
onClickDeposit?: (assetId?: string) => void;
isReadOnly: boolean;
pinnedAsset?: PinnedAsset;
}
export const AccountManager = ({
@@ -22,15 +20,11 @@ export const AccountManager = ({
onClickDeposit,
partyId,
isReadOnly,
pinnedAsset,
}: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]);
const { data, loading, error, reload } = useDataProvider<
AccountFields[],
never
>({
const { data, loading, error } = useDataProvider<AccountFields[], never>({
dataProvider: aggregatedAccountsDataProvider,
variables,
});
@@ -38,13 +32,12 @@ export const AccountManager = ({
<div className="relative h-full">
<AccountTable
ref={gridRef}
rowData={error ? [] : data}
rowData={data}
onClickAsset={onClickAsset}
onClickDeposit={onClickDeposit}
onClickWithdraw={onClickWithdraw}
isReadOnly={isReadOnly}
noRowsOverlayComponent={() => null}
pinnedAsset={pinnedAsset}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
@@ -52,8 +45,7 @@ export const AccountManager = ({
noDataCondition={(data) => !(data && data.length)}
error={error}
loading={loading}
noDataMessage={pinnedAsset ? ' ' : t('No accounts')}
reload={reload}
noDataMessage={t('No accounts')}
/>
</div>
</div>
+40 -62
View File
@@ -72,74 +72,52 @@ describe('AccountsTable', () => {
cells.forEach((cell, i) => {
expect(cell).toHaveTextContent(expectedValues[i]);
});
const rows = await screen.findAllByRole('row');
expect(rows.length).toBe(6);
});
});
it('should not add first asset as pinned', async () => {
await act(async () => {
render(
<AccountTable
rowData={singleRowData}
onClickAsset={() => null}
isReadOnly={false}
pinnedAsset={{
it('should get correct account data', () => {
const result = getAccountData([singleRow]);
const expected = [
{
asset: {
__typename: 'Asset',
decimals: 5,
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
},
available: '0',
balance: '0',
breakdown: [
{
__typename: 'AccountBalance',
asset: {
__typename: 'Asset',
decimals: 5,
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
name: 'tBTC',
}}
/>
);
});
const rows = await screen.findAllByRole('row');
expect(rows.length).toBe(6);
});
it('should get correct account data', () => {
const result = getAccountData([singleRow]);
const expected = [
{
asset: {
__typename: 'Asset',
decimals: 5,
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
},
available: '0',
balance: '0',
breakdown: [
{
__typename: 'AccountBalance',
asset: {
__typename: 'Asset',
decimals: 5,
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
symbol: 'tBTC',
},
available: '0',
balance: '125600000',
deposited: '125600000',
market: {
__typename: 'Market',
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
name: 'BTCUSD Monthly (30 Jun 2022)',
},
},
available: '0',
balance: '125600000',
deposited: '125600000',
market: {
__typename: 'Market',
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
name: 'BTCUSD Monthly (30 Jun 2022)',
},
},
type: 'ACCOUNT_TYPE_MARGIN',
used: '125600000',
},
],
deposited: '125600000',
type: 'ACCOUNT_TYPE_GENERAL',
used: '125600000',
},
];
expect(result).toEqual(expected);
});
type: 'ACCOUNT_TYPE_MARGIN',
used: '125600000',
},
],
deposited: '125600000',
type: 'ACCOUNT_TYPE_GENERAL',
used: '125600000',
},
];
expect(result).toEqual(expected);
});
+32 -76
View File
@@ -1,4 +1,4 @@
import { forwardRef, useMemo, useState } from 'react';
import { forwardRef, useState } from 'react';
import {
addDecimalsFormatNumber,
isNumeric,
@@ -14,8 +14,6 @@ import type { AgGridReact, AgGridReactProps } from 'ag-grid-react';
import type { VegaValueFormatterParams } from '@vegaprotocol/ui-toolkit';
import BreakdownTable from './breakdown-table';
import type { AccountFields } from './accounts-data-provider';
import type { Asset } from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
export interface GetRowsParams extends Omit<IGetRowsParams, 'successCallback'> {
successCallback(rowsThisBlock: AccountFields[], lastRow?: number): void;
@@ -25,8 +23,6 @@ export interface Datasource extends IDatasource {
getRows(params: GetRowsParams): void;
}
export type PinnedAsset = Pick<Asset, 'symbol' | 'name' | 'id' | 'decimals'>;
export interface AccountTableProps extends AgGridReactProps {
rowData?: AccountFields[] | null;
datasource?: Datasource;
@@ -34,33 +30,12 @@ export interface AccountTableProps extends AgGridReactProps {
onClickWithdraw?: (assetId: string) => void;
onClickDeposit?: (assetId: string) => void;
isReadOnly: boolean;
pinnedAsset?: PinnedAsset;
}
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
({ onClickAsset, onClickWithdraw, onClickDeposit, ...props }, ref) => {
const [openBreakdown, setOpenBreakdown] = useState(false);
const [breakdown, setBreakdown] = useState<AccountFields[] | null>(null);
const pinnedAssetId = props.pinnedAsset?.id;
const pinnedAssetRow = useMemo(() => {
const currentPinnedAssetRow = props.rowData?.find(
(row) => row.asset.id === pinnedAssetId
);
if (!currentPinnedAssetRow) {
if (props.pinnedAsset) {
return {
asset: props.pinnedAsset,
available: '0',
used: '0',
deposited: '0',
balance: '0',
};
}
}
return undefined;
}, [pinnedAssetId, props.pinnedAsset, props.rowData]);
return (
<>
<AgGrid
@@ -76,7 +51,6 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
sortable: true,
}}
{...props}
pinnedTopRowData={pinnedAssetRow ? [pinnedAssetRow] : undefined}
>
<AgGridColumn
headerName={t('Asset')}
@@ -164,55 +138,37 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
cellRenderer={({
data,
}: VegaICellRendererParams<AccountFields>) => {
if (!data) return null;
else {
if (
data.asset.id === pinnedAssetId &&
new BigNumber(data.deposited).isLessThanOrEqualTo(0)
) {
return (
<ButtonLink
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit to trade')}
</ButtonLink>
);
}
return (
<>
<ButtonLink
data-testid="breakdown"
onClick={() => {
setOpenBreakdown(!openBreakdown);
setBreakdown(data.breakdown || null);
}}
>
{t('Breakdown')}
</ButtonLink>
<span className="mx-1" />
<ButtonLink
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit')}
</ButtonLink>
<span className="mx-1" />
<ButtonLink
data-testid="withdraw"
onClick={() =>
onClickWithdraw && onClickWithdraw(data.asset.id)
}
>
{t('Withdraw')}
</ButtonLink>
</>
);
}
return data ? (
<>
<ButtonLink
data-testid="breakdown"
onClick={() => {
setOpenBreakdown(!openBreakdown);
setBreakdown(data.breakdown || null);
}}
>
{t('Breakdown')}
</ButtonLink>
<span className="mx-1" />
<ButtonLink
data-testid="deposit"
onClick={() => {
onClickDeposit && onClickDeposit(data.asset.id);
}}
>
{t('Deposit')}
</ButtonLink>
<span className="mx-1" />
<ButtonLink
data-testid="withdraw"
onClick={() =>
onClickWithdraw && onClickWithdraw(data.asset.id)
}
>
{t('Withdraw')}
</ButtonLink>
</>
) : null;
}}
/>
)}
@@ -25,7 +25,6 @@ export const DealTicketContainer = ({
data: marketData,
error: marketDataError,
loading: marketDataLoading,
reload,
} = useThrottledDataProvider(
{
dataProvider: marketDataProvider,
@@ -40,7 +39,6 @@ export const DealTicketContainer = ({
data={market && marketData}
loading={marketLoading || marketDataLoading}
error={marketError || marketDataError}
reload={reload}
>
{market && marketData ? (
<DealTicket
@@ -1,4 +1,4 @@
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
import { FormGroup, Input, NotificationError } from '@vegaprotocol/ui-toolkit';
import { t, toDecimal, validateAmount } from '@vegaprotocol/react-helpers';
import type { DealTicketAmountProps } from './deal-ticket-amount';
@@ -20,17 +20,17 @@ export const DealTicketLimitAmount = ({
const renderError = () => {
if (sizeError) {
return (
<InputError testId="dealticket-error-message-size-limit">
<NotificationError testId="dealticket-error-message-size-limit">
{sizeError}
</InputError>
</NotificationError>
);
}
if (priceError) {
return (
<InputError testId="dealticket-error-message-price-limit">
<NotificationError testId="dealticket-error-message-price-limit">
{priceError}
</InputError>
</NotificationError>
);
}
@@ -4,7 +4,7 @@ import {
toDecimal,
validateAmount,
} from '@vegaprotocol/react-helpers';
import { Input, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { Input, NotificationError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { isMarketInAuction } from '../../utils';
import type { DealTicketAmountProps } from './deal-ticket-amount';
import { getMarketPrice } from '../../utils/get-price';
@@ -77,12 +77,12 @@ export const DealTicketMarketAmount = ({
</div>
</div>
{sizeError && (
<InputError
<NotificationError
intent="danger"
testId="dealticket-error-message-size-market"
>
{sizeError}
</InputError>
</NotificationError>
)}
</div>
);
@@ -15,7 +15,7 @@ import { normalizeOrderSubmission } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
ExternalLink,
InputError,
NotificationError,
Intent,
Notification,
} from '@vegaprotocol/ui-toolkit';
@@ -308,11 +308,11 @@ const SummaryMessage = memo(
if (isReadOnly) {
return (
<div className="mb-4">
<InputError testId="dealticket-error-message-summary">
<NotificationError testId="dealticket-error-message-summary">
{
'You need to connect your own wallet to start trading on this market'
}
</InputError>
</NotificationError>
</div>
);
}
@@ -353,9 +353,9 @@ const SummaryMessage = memo(
if (errorMessage) {
return (
<div className="mb-4">
<InputError testId="dealticket-error-message-summary">
<NotificationError testId="dealticket-error-message-summary">
{errorMessage}
</InputError>
</NotificationError>
</div>
);
}
@@ -1,4 +1,4 @@
import { FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
import { FormGroup, Input, NotificationError } from '@vegaprotocol/ui-toolkit';
import { formatForInput } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers';
import type { UseFormRegister } from 'react-hook-form';
@@ -35,9 +35,9 @@ export const ExpirySelector = ({
})}
/>
{errorMessage && (
<InputError testId="dealticket-error-message-expiry">
<NotificationError testId="dealticket-error-message-expiry">
{errorMessage}
</InputError>
</NotificationError>
)}
</FormGroup>
);
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import {
FormGroup,
InputError,
NotificationError,
Select,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
@@ -139,9 +139,9 @@ export const TimeInForceSelector = ({
))}
</Select>
{errorMessage && (
<InputError testId="dealticket-error-message-tif">
<NotificationError testId="dealticket-error-message-tif">
{renderError(errorMessage)}
</InputError>
</NotificationError>
)}
</FormGroup>
);
@@ -1,4 +1,8 @@
import { FormGroup, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
import {
FormGroup,
NotificationError,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { DataGrid, t } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { Toggle } from '@vegaprotocol/ui-toolkit';
@@ -78,9 +82,9 @@ export const TypeSelector = ({
onChange={(e) => onSelect(e.target.value as Schema.OrderType)}
/>
{errorMessage && (
<InputError testId="dealticket-error-message-type">
<NotificationError testId="dealticket-error-message-type">
{renderError(errorMessage as MarketModeValidationType)}
</InputError>
</NotificationError>
)}
</FormGroup>
);
+1 -1
View File
@@ -17,7 +17,7 @@ export type DepositEventSubscriptionVariables = Types.Exact<{
}>;
export type DepositEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'Deposit', id: string, status: Types.DepositStatus, amount: string, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number } } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult' } | { __typename?: 'Withdrawal' } }> | null };
export type DepositEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'AccountEvent' } | { __typename?: 'Asset' } | { __typename?: 'AuctionEvent' } | { __typename?: 'Deposit', id: string, status: Types.DepositStatus, amount: string, createdTimestamp: any, creditedTimestamp?: any | null, txHash?: string | null, asset: { __typename?: 'Asset', id: string, symbol: string, decimals: number } } | { __typename?: 'LiquidityProvision' } | { __typename?: 'LossSocialization' } | { __typename?: 'MarginLevels' } | { __typename?: 'Market' } | { __typename?: 'MarketData' } | { __typename?: 'MarketEvent' } | { __typename?: 'MarketTick' } | { __typename?: 'NodeSignature' } | { __typename?: 'OracleSpec' } | { __typename?: 'Order' } | { __typename?: 'Party' } | { __typename?: 'PositionResolution' } | { __typename?: 'Proposal' } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult' } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal' } }> | null };
export const DepositFieldsFragmentDoc = gql`
fragment DepositFields on Deposit {
+1 -2
View File
@@ -19,7 +19,7 @@ export const FillsManager = ({
}: FillsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true);
const { data, error, loading, addNewRows, getRows, reload } = useFillsList({
const { data, error, loading, addNewRows, getRows } = useFillsList({
partyId,
marketId,
gridRef,
@@ -55,7 +55,6 @@ export const FillsManager = ({
data={data}
noDataMessage={t('No fills')}
noDataCondition={(data) => !(data && data.length)}
reload={reload}
/>
</div>
</div>
+2 -2
View File
@@ -78,7 +78,7 @@ export const useFillsList = ({
const variables = useMemo(() => ({ partyId, marketId }), [partyId, marketId]);
const { data, error, loading, load, totalCount, reload } = useDataProvider<
const { data, error, loading, load, totalCount } = useDataProvider<
(TradeEdge | null)[],
Trade[]
>({
@@ -95,5 +95,5 @@ export const useFillsList = ({
load,
newRows
);
return { data, error, loading, addNewRows, getRows, reload };
return { data, error, loading, addNewRows, getRows };
};
@@ -1,139 +1,15 @@
fragment NewMarketFields on NewMarket {
instrument {
name
code
name
futureProduct {
settlementAsset {
id
name
symbol
decimals
quantum
}
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
conditions {
operator
value
}
}
}
}
}
}
}
dataSourceSpecForTradingTermination {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
conditions {
operator
value
}
}
}
}
}
}
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
decimalPlaces
riskParameters {
... on LogNormalRiskModel {
riskAversionParameter
tau
params {
mu
r
sigma
}
}
... on SimpleRiskModel {
params {
factorLong
factorShort
}
}
}
metadata
# priceMonitoringParameters {
# triggers {
# horizonSecs
# probability
# auctionExtensionSecs
# }
# }
# liquidityMonitoringParameters {
# targetStakeParameters {
# timeWindow
# scalingFactor
# }
# triggeringRatio
# auctionExtensionSecs
# }
lpPriceRange
# linearSlippageFactor
# quadraticSlippageFactor
}
fragment UpdateMarketFields on UpdateMarket {
@@ -143,93 +19,8 @@ fragment UpdateMarketFields on UpdateMarket {
code
product {
quoteName
dataSourceSpecForSettlementData {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
conditions {
operator
value
}
}
}
}
}
}
}
dataSourceSpecForTradingTermination {
sourceType {
... on DataSourceDefinitionInternal {
sourceType {
... on DataSourceSpecConfigurationTime {
conditions {
operator
value
}
}
}
}
... on DataSourceDefinitionExternal {
sourceType {
... on DataSourceSpecConfiguration {
signers {
signer {
... on PubKey {
key
}
... on ETHAddress {
address
}
}
}
filters {
key {
name
type
}
conditions {
operator
value
}
}
}
}
}
}
}
dataSourceSpecBinding {
settlementDataProperty
tradingTerminationProperty
}
}
}
metadata
priceMonitoringParameters {
triggers {
horizonSecs
@@ -243,7 +34,6 @@ fragment UpdateMarketFields on UpdateMarket {
scalingFactor
}
triggeringRatio
# auctionExtensionSecs
}
riskParameters {
__typename
@@ -268,23 +58,6 @@ fragment UpdateMarketFields on UpdateMarket {
}
}
fragment NewAssetFields on NewAsset {
name
symbol
decimals
quantum
source {
... on BuiltinAsset {
maxFaucetAmountMint
}
... on ERC20 {
contractAddress
lifetimeLimit
withdrawThreshold
}
}
}
fragment UpdateAssetFields on UpdateAsset {
assetId
quantum
@@ -296,26 +69,11 @@ fragment UpdateAssetFields on UpdateAsset {
}
}
fragment UpdateNetworkParameterFiels on UpdateNetworkParameter {
networkParameter {
key
value
}
}
fragment ProposalListFields on Proposal {
id
rationale {
title
description
}
reference
state
datetime
rejectionReason
party {
id
}
votes {
yes {
totalTokens
@@ -328,32 +86,19 @@ fragment ProposalListFields on Proposal {
totalWeight
}
}
errorDetails
rejectionReason
requiredMajority
requiredParticipation
requiredLpMajority
requiredLpParticipation
terms {
closingDatetime
enactmentDatetime
change {
__typename
... on NewMarket {
...NewMarketFields
}
... on UpdateMarket {
...UpdateMarketFields
}
... on NewAsset {
...NewAssetFields
}
... on UpdateAsset {
...UpdateAssetFields
}
... on UpdateNetworkParameter {
...UpdateNetworkParameterFiels
}
}
}
}
File diff suppressed because one or more lines are too long
@@ -10,7 +10,7 @@ const getData = (responseData: ProposalsListQuery | null) =>
?.filter((edge) => Boolean(edge?.node))
.map((edge) => edge?.node as ProposalListFieldsFragment) || null;
export const proposalsDataProvider = makeDataProvider<
export const proposalsListDataProvider = makeDataProvider<
ProposalsListQuery,
ProposalListFieldsFragment[],
never,
@@ -40,21 +40,6 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
totalWeight: '1',
},
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
__typename: 'ProposalTerms',
closingDatetime: '',
@@ -65,33 +50,7 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
instrument: {
code: '',
product: {
__typename: 'UpdateFutureProduct',
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
},
},
priceMonitoringParameters: {
@@ -133,36 +92,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-15T12:44:34Z',
enactmentDatetime: '2022-11-15T12:44:54Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'ETHUSD',
name: 'ETHUSD',
@@ -171,36 +104,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -231,36 +136,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-15T12:39:41Z',
enactmentDatetime: '2022-11-15T12:39:51Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'ETHUSD',
name: 'ETHUSD',
@@ -269,36 +148,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -329,36 +180,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-14T16:24:24Z',
enactmentDatetime: '2022-11-14T16:24:34Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'LINKUSD',
name: 'LINKUSD',
@@ -367,36 +192,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
name: 'mUSDT-II',
symbol: 'mUSDT-II',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -427,36 +224,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:32:22Z',
enactmentDatetime: '2022-11-11T16:32:32Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'LINKUSD',
name: 'LINKUSD',
@@ -465,36 +236,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
name: 'mUSDT-II',
symbol: 'mUSDT-II',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -525,36 +268,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-14T09:40:57Z',
enactmentDatetime: '2022-11-14T09:41:17Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'ETHUSD',
name: 'ETHUSD',
@@ -563,36 +280,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -623,36 +312,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:32:22Z',
enactmentDatetime: '2022-11-11T16:32:32Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'LINKUSD',
name: 'LINKUSD',
@@ -661,36 +324,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'eb30d55e90e1f9e5c4727d6fa2a5a8cd36ab9ae9738eb8f3faf53e2bee4861ee',
name: 'mUSDT-II',
symbol: 'mUSDT-II',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -721,36 +356,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'ETHDAI.MF21',
name: 'ETHDAI Monthly (Dec 2022)',
@@ -759,36 +368,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -819,36 +400,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'AAPL.MF21',
name: 'Apple Monthly (Dec 2022)',
@@ -857,36 +412,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'c9fe6fc24fce121b2cc72680543a886055abb560043fda394ba5376203b7527d',
name: 'tUSDC TEST',
symbol: 'tUSDC',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -917,36 +444,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'BTCUSD.MF21',
name: 'BTCUSD Monthly (Dec 2022)',
@@ -955,36 +456,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -1015,36 +488,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'TSLA.QM21',
name: 'Tesla Quarterly (Feb 2023)',
@@ -1053,36 +500,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: '177e8f6c25a955bd18475084b99b2b1d37f28f3dec393fab7755a7e69c3d8c3b',
name: 'tEURO TEST',
symbol: 'tEURO',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -1113,36 +532,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'AAVEDAI.MF21',
name: 'AAVEDAI Monthly (Dec 2022)',
@@ -1151,36 +544,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -1211,36 +576,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (Feb 2023)',
@@ -1249,36 +588,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'cee709223217281d7893b650850ae8ee8a18b7539b5658f9b4cc24de95dd18ad',
name: 'tBTC TEST',
symbol: 'tBTC',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -1309,36 +620,10 @@ const proposalListFields: ProposalListFieldsFragment[] = [
},
__typename: 'ProposalVotes',
},
requiredMajority: '',
party: {
__typename: 'Party',
id: '',
},
rationale: {
__typename: 'ProposalRationale',
description: '',
title: '',
},
requiredParticipation: '',
errorDetails: '',
rejectionReason: null,
requiredLpMajority: '',
requiredLpParticipation: '',
terms: {
closingDatetime: '2022-11-11T16:28:25Z',
enactmentDatetime: '2022-11-11T16:30:35Z',
change: {
decimalPlaces: 1,
lpPriceRange: '',
riskParameters: {
__typename: 'SimpleRiskModel',
params: {
__typename: 'SimpleRiskModelParams',
factorLong: 0,
factorShort: 1,
},
},
metadata: [],
instrument: {
code: 'UNIDAI.MF21',
name: 'UNIDAI Monthly (Dec 2022)',
@@ -1347,36 +632,8 @@ const proposalListFields: ProposalListFieldsFragment[] = [
id: 'b340c130096819428a62e5df407fd6abe66e444b89ad64f670beb98621c9c663',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 1,
quantum: '1',
__typename: 'Asset',
},
quoteName: '',
dataSourceSpecBinding: {
__typename: 'DataSourceSpecToFutureBinding',
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
__typename: 'DataSourceDefinition',
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename: 'FutureProduct',
},
__typename: 'InstrumentConfiguration',
@@ -12,7 +12,7 @@ subscription ProposalEvent($partyId: ID!) {
}
}
fragment UpdateNetworkParameterProposal on Proposal {
fragment UpdateNetworkParameterFields on Proposal {
id
state
datetime
@@ -20,15 +20,22 @@ fragment UpdateNetworkParameterProposal on Proposal {
enactmentDatetime
change {
... on UpdateNetworkParameter {
...UpdateNetworkParameterFiels
networkParameter {
key
value
}
}
}
}
}
subscription OnUpdateNetworkParameters {
proposals {
...UpdateNetworkParameterProposal
busEvents(types: [Proposal], batchSize: 0) {
event {
... on Proposal {
...UpdateNetworkParameterFields
}
}
}
}
@@ -1,7 +1,6 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import { UpdateNetworkParameterFielsFragmentDoc } from '../../proposals-data-provider/__generated__/Proposals';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ProposalEventFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null };
@@ -13,12 +12,12 @@ export type ProposalEventSubscriptionVariables = Types.Exact<{
export type ProposalEventSubscription = { __typename?: 'Subscription', proposals: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null } };
export type UpdateNetworkParameterProposalFragment = { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } };
export type UpdateNetworkParameterFieldsFragment = { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } };
export type OnUpdateNetworkParametersSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
export type OnUpdateNetworkParametersSubscription = { __typename?: 'Subscription', proposals: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } };
export type OnUpdateNetworkParametersSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'AccountEvent' } | { __typename?: 'Asset' } | { __typename?: 'AuctionEvent' } | { __typename?: 'Deposit' } | { __typename?: 'LiquidityProvision' } | { __typename?: 'LossSocialization' } | { __typename?: 'MarginLevels' } | { __typename?: 'Market' } | { __typename?: 'MarketData' } | { __typename?: 'MarketEvent' } | { __typename?: 'MarketTick' } | { __typename?: 'NodeSignature' } | { __typename?: 'OracleSpec' } | { __typename?: 'Order' } | { __typename?: 'Party' } | { __typename?: 'PositionResolution' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, datetime: any, terms: { __typename?: 'ProposalTerms', enactmentDatetime?: any | null, change: { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } } } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult' } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal' } }> | null };
export type ProposalOfMarketQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
@@ -36,8 +35,8 @@ export const ProposalEventFieldsFragmentDoc = gql`
errorDetails
}
`;
export const UpdateNetworkParameterProposalFragmentDoc = gql`
fragment UpdateNetworkParameterProposal on Proposal {
export const UpdateNetworkParameterFieldsFragmentDoc = gql`
fragment UpdateNetworkParameterFields on Proposal {
id
state
datetime
@@ -45,12 +44,15 @@ export const UpdateNetworkParameterProposalFragmentDoc = gql`
enactmentDatetime
change {
... on UpdateNetworkParameter {
...UpdateNetworkParameterFiels
networkParameter {
key
value
}
}
}
}
}
${UpdateNetworkParameterFielsFragmentDoc}`;
`;
export const ProposalEventDocument = gql`
subscription ProposalEvent($partyId: ID!) {
proposals(partyId: $partyId) {
@@ -83,11 +85,15 @@ export type ProposalEventSubscriptionHookResult = ReturnType<typeof useProposalE
export type ProposalEventSubscriptionResult = Apollo.SubscriptionResult<ProposalEventSubscription>;
export const OnUpdateNetworkParametersDocument = gql`
subscription OnUpdateNetworkParameters {
proposals {
...UpdateNetworkParameterProposal
busEvents(types: [Proposal], batchSize: 0) {
event {
... on Proposal {
...UpdateNetworkParameterFields
}
}
}
}
${UpdateNetworkParameterProposalFragmentDoc}`;
${UpdateNetworkParameterFieldsFragmentDoc}`;
/**
* __useOnUpdateNetworkParametersSubscription__
@@ -7,18 +7,13 @@ import type { Toast } from '@vegaprotocol/ui-toolkit';
import { ToastHeading } from '@vegaprotocol/ui-toolkit';
import { useToasts } from '@vegaprotocol/ui-toolkit';
import { ExternalLink, Intent } from '@vegaprotocol/ui-toolkit';
import compact from 'lodash/compact';
import { useCallback } from 'react';
import type { UpdateNetworkParameterProposalFragment } from './__generated__/Proposal';
import type { UpdateNetworkParameterFieldsFragment } from './__generated__/Proposal';
import { useOnUpdateNetworkParametersSubscription } from './__generated__/Proposal';
export const PROPOSAL_STATES_TO_TOAST = [
ProposalState.STATE_DECLINED,
ProposalState.STATE_ENACTED,
ProposalState.STATE_OPEN,
ProposalState.STATE_PASSED,
];
const CLOSE_AFTER = 5000;
type Proposal = UpdateNetworkParameterProposalFragment;
type Proposal = UpdateNetworkParameterFieldsFragment;
const UpdateNetworkParameterToastContent = ({
proposal,
@@ -80,16 +75,26 @@ export const useUpdateNetworkParametersToasts = () => {
[remove]
);
return useOnUpdateNetworkParametersSubscription({
onData: ({ data }) => {
// note proposals is poorly named, it is actually a single proposal
const proposal = data.data?.proposals;
if (!proposal) return;
if (proposal.terms.change.__typename !== 'UpdateNetworkParameter') return;
// if one of the following states show a toast
if (PROPOSAL_STATES_TO_TOAST.includes(proposal.state)) {
setToast(fromProposal(proposal));
useOnUpdateNetworkParametersSubscription({
onData: (options) => {
const events = compact(options.data.data?.busEvents);
if (!events || events.length === 0) return;
const validProposals = events
.filter(
(ev) =>
ev.event.__typename === 'Proposal' &&
ev.event.terms.__typename === 'ProposalTerms' &&
ev.event.terms.change.__typename === 'UpdateNetworkParameter' &&
[
ProposalState.STATE_DECLINED,
ProposalState.STATE_ENACTED,
ProposalState.STATE_OPEN,
ProposalState.STATE_PASSED,
].includes(ev.event.state)
)
.map((ev) => ev.event as Proposal);
if (validProposals.length < 5) {
validProposals.forEach((p) => setToast(fromProposal(p)));
}
},
});
@@ -1,19 +1,16 @@
import merge from 'lodash/merge';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import { renderHook } from '@testing-library/react-hooks';
import { ProposalState } from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import {
PROPOSAL_STATES_TO_TOAST,
useUpdateNetworkParametersToasts,
} from './use-update-network-paramaters-toasts';
import { useUpdateNetworkParametersToasts } from './use-update-network-paramaters-toasts';
import type {
UpdateNetworkParameterProposalFragment,
UpdateNetworkParameterFieldsFragment,
OnUpdateNetworkParametersSubscription,
} from './__generated__/Proposal';
import { OnUpdateNetworkParametersDocument } from './__generated__/Proposal';
import { useToasts } from '@vegaprotocol/ui-toolkit';
import { waitFor, renderHook } from '@testing-library/react';
import { waitFor } from '@testing-library/react';
const render = (mocks?: MockedResponse[]) => {
const wrapper = ({ children }: { children: ReactNode }) => (
@@ -26,7 +23,7 @@ const generateUpdateNetworkParametersProposal = (
key: string,
value: string,
state: ProposalState = ProposalState.STATE_OPEN
): UpdateNetworkParameterProposalFragment => ({
): UpdateNetworkParameterFieldsFragment => ({
__typename: 'Proposal',
id: Math.random().toString(),
datetime: Math.random().toString(),
@@ -45,6 +42,56 @@ const generateUpdateNetworkParametersProposal = (
},
});
const mockedWrongEvent: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
__typename: 'Subscription',
busEvents: [
{
__typename: 'BusEvent',
event: {
__typename: 'Asset',
},
},
],
},
},
};
const mockedEmptyEvent: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
__typename: 'Subscription',
busEvents: [],
},
},
};
const mockedEvent: MockedResponse<OnUpdateNetworkParametersSubscription> = {
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
__typename: 'Subscription',
busEvents: [
{
__typename: 'BusEvent',
event: generateUpdateNetworkParametersProposal('abc.def', '123.456'),
},
],
},
},
};
const INITIAL = useToasts.getState();
const clear = () => {
@@ -55,113 +102,23 @@ describe('useUpdateNetworkParametersToasts', () => {
beforeEach(clear);
afterAll(clear);
it.each(PROPOSAL_STATES_TO_TOAST)(
'toasts for %s network param proposals',
async (state) => {
const mockOpenProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
proposals: generateUpdateNetworkParametersProposal(
'abc.def',
'123.456',
state
),
},
},
};
const { result } = render([mockOpenProposal]);
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(useToasts.getState().count).toBe(1);
});
}
);
it('returns toast for update network parameters bus event', async () => {
render([mockedEvent]);
await waitFor(() => {
expect(useToasts.getState().count).toBe(1);
});
});
const IGNORE_STATES = Object.keys(ProposalState).filter((state) => {
return !PROPOSAL_STATES_TO_TOAST.includes(state as ProposalState);
}) as ProposalState[];
it.each(IGNORE_STATES)('does not toast for %s proposals', async (state) => {
const mockFailedProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
proposals: generateUpdateNetworkParametersProposal(
'abc.def',
'123.456',
state
),
},
},
};
const { result } = render([mockFailedProposal]);
expect(result.current.loading).toBe(true);
it('does not return toast for empty event', async () => {
render([mockedEmptyEvent]);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(useToasts.getState().count).toBe(0);
});
});
it('does not return toast for empty propsal', async () => {
const error = console.error;
console.error = () => {
/* no op */
};
const mockEmptyProposal: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
proposals:
undefined as unknown as UpdateNetworkParameterProposalFragment,
},
},
};
const { result } = render([mockEmptyProposal]);
expect(result.current.loading).toBe(true);
it('does not return toast for wrong event', async () => {
render([mockedWrongEvent]);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(useToasts.getState().count).toBe(0);
});
console.error = error;
});
it('does not return toast for wrong proposal type', async () => {
const wrongProposalType = merge(
generateUpdateNetworkParametersProposal('a', 'b'),
{
terms: {
change: {
__typename: 'NewMarket',
},
},
}
);
const mockWrongProposalType: MockedResponse<OnUpdateNetworkParametersSubscription> =
{
request: {
query: OnUpdateNetworkParametersDocument,
},
result: {
data: {
proposals: wrongProposalType,
},
},
};
const { result } = render([mockWrongProposalType]);
expect(result.current.loading).toBe(true);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(useToasts.getState().count).toBe(0);
});
});
@@ -14,18 +14,30 @@ import {
useUpdateProposal,
} from './use-update-proposal';
type Proposal = Pick<ProposalListFieldsFragment, 'terms'> &
Pick<ProposalListFieldsFragment, 'state'> &
Pick<ProposalListFieldsFragment, 'id'>;
const generateUpdateAssetProposal = (
id: string,
quantum = '',
lifetimeLimit = '',
withdrawThreshold = ''
): Proposal => ({
id,
): ProposalListFieldsFragment => ({
reference: '',
state: Schema.ProposalState.STATE_OPEN,
datetime: '',
votes: {
__typename: undefined,
yes: {
__typename: undefined,
totalTokens: '',
totalNumber: '',
totalWeight: '',
},
no: {
__typename: undefined,
totalTokens: '',
totalNumber: '',
totalWeight: '',
},
},
terms: {
__typename: 'ProposalTerms',
closingDatetime: '',
@@ -108,8 +120,25 @@ const generateUpdateMarketProposal = (
riskParametersType:
| 'UpdateMarketLogNormalRiskModel'
| 'UpdateMarketSimpleRiskModel' = 'UpdateMarketLogNormalRiskModel'
): Proposal => ({
): ProposalListFieldsFragment => ({
reference: '',
state: Schema.ProposalState.STATE_OPEN,
datetime: '',
votes: {
__typename: undefined,
yes: {
__typename: undefined,
totalTokens: '',
totalNumber: '',
totalWeight: '',
},
no: {
__typename: undefined,
totalTokens: '',
totalNumber: '',
totalWeight: '',
},
},
terms: {
__typename: 'ProposalTerms',
closingDatetime: '',
@@ -126,28 +155,6 @@ const generateUpdateMarketProposal = (
: undefined,
code,
product: {
dataSourceSpecBinding: {
settlementDataProperty: '',
tradingTerminationProperty: '',
},
dataSourceSpecForSettlementData: {
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
dataSourceSpecForTradingTermination: {
sourceType: {
__typename: 'DataSourceDefinitionInternal',
sourceType: {
__typename: 'DataSourceSpecConfigurationTime',
conditions: [],
},
},
},
__typename:
quoteName.length > 0 ? 'UpdateFutureProduct' : undefined,
quoteName,
@@ -188,7 +195,7 @@ const generateUpdateMarketProposal = (
});
const mockDataProviderData: {
data: Proposal[];
data: ProposalListFieldsFragment[];
error: Error | undefined;
loading: boolean;
} = {
@@ -1,4 +1,4 @@
import { proposalsDataProvider } from '..';
import { proposalsListDataProvider } from '..';
import * as Schema from '@vegaprotocol/types';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { useMemo } from 'react';
@@ -42,7 +42,7 @@ export const useUpdateProposal = ({
);
const { data, loading, error } = useDataProvider({
dataProvider: proposalsDataProvider,
dataProvider: proposalsListDataProvider,
variables,
});
@@ -189,7 +189,7 @@ const fieldGetters = {
};
export const isChangeProposed = (
proposal: Pick<ProposalListFieldsFragment, 'terms'>,
proposal: ProposalListFieldsFragment | undefined,
field: UpdateProposalField
) => {
if (proposal) {
@@ -4,7 +4,7 @@ import {
} from '@vegaprotocol/ui-toolkit';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import * as Types from '@vegaprotocol/types';
import { proposalsDataProvider } from '../proposals-data-provider';
import { proposalsListDataProvider } from '../proposals-data-provider';
import { useCallback, useMemo, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs';
@@ -29,19 +29,14 @@ export const ProposalsList = () => {
proposalType: Types.ProposalType.TYPE_NEW_MARKET,
};
}, []);
const { data, loading, error, reload } = useDataProvider({
dataProvider: proposalsDataProvider,
const { data, loading, error } = useDataProvider({
dataProvider: proposalsListDataProvider,
variables,
});
const filteredData = getNewMarketProposals(data || []);
const { columnDefs, defaultColDef } = useColumnDefs();
return (
<AsyncRenderer
loading={loading}
error={error}
data={filteredData}
reload={reload}
>
<AsyncRenderer loading={loading} error={error} data={filteredData}>
<AgGrid
ref={gridRef}
domLayout="autoHeight"
@@ -14,9 +14,18 @@ import type {
} from '@vegaprotocol/ui-toolkit';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ProposalStateMapping } from '@vegaprotocol/types';
import type { ProposalListFieldsFragment } from '../proposals-data-provider/__generated__/Proposals';
import type {
ProposalListFieldsFragment,
NewMarketFieldsFragment,
} from '../proposals-data-provider/__generated__/Proposals';
import { VoteProgress } from '../voting-progress';
const instrumentGuard = (
change?: ProposalListFieldsFragment['terms']['change']
): change is NewMarketFieldsFragment => {
return change?.__typename === 'NewMarket';
};
export const useColumnDefs = () => {
const { VEGA_TOKEN_URL } = useEnvironment();
const { params } = useNetworkParams([
@@ -44,7 +53,7 @@ export const useColumnDefs = () => {
'terms.change.instrument.code'
>) => {
const { change } = data?.terms || {};
if (change?.__typename === 'NewMarket' && VEGA_TOKEN_URL) {
if (instrumentGuard(change) && VEGA_TOKEN_URL) {
if (data?.id) {
const link = `${VEGA_TOKEN_URL}/proposals/${data.id}`;
return (
@@ -35,7 +35,6 @@ query LedgerEntries(
node {
...LedgerEntry
}
cursor
}
pageInfo {
startCursor
+1 -2
View File
@@ -14,7 +14,7 @@ export type LedgerEntriesQueryVariables = Types.Exact<{
}>;
export type LedgerEntriesQuery = { __typename?: 'Query', ledgerEntries: { __typename?: 'AggregatedLedgerEntriesConnection', edges: Array<{ __typename?: 'AggregatedLedgerEntriesEdge', cursor: string, node: { __typename?: 'AggregatedLedgerEntry', vegaTime: any, quantity: string, assetId?: string | null, transferType?: Types.TransferType | null, toAccountType?: Types.AccountType | null, toAccountMarketId?: string | null, toAccountPartyId?: string | null, toAccountBalance: string, fromAccountType?: Types.AccountType | null, fromAccountMarketId?: string | null, fromAccountPartyId?: string | null, fromAccountBalance: string } } | null>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } };
export type LedgerEntriesQuery = { __typename?: 'Query', ledgerEntries: { __typename?: 'AggregatedLedgerEntriesConnection', edges: Array<{ __typename?: 'AggregatedLedgerEntriesEdge', node: { __typename?: 'AggregatedLedgerEntry', vegaTime: any, quantity: string, assetId?: string | null, transferType?: Types.TransferType | null, toAccountType?: Types.AccountType | null, toAccountMarketId?: string | null, toAccountPartyId?: string | null, toAccountBalance: string, fromAccountType?: Types.AccountType | null, fromAccountMarketId?: string | null, fromAccountPartyId?: string | null, fromAccountBalance: string } } | null>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } };
export const LedgerEntryFragmentDoc = gql`
fragment LedgerEntry on AggregatedLedgerEntry {
@@ -43,7 +43,6 @@ export const LedgerEntriesDocument = gql`
node {
...LedgerEntry
}
cursor
}
pageInfo {
startCursor
@@ -128,11 +128,7 @@ export const ledgerEntriesProvider = makeDerivedDataProvider<
const marketReceiver = markets.find(
(market: Market) => market.id === entry.toAccountMarketId
);
const cursor = edge?.cursor;
return {
node: { ...entry, asset, marketSender, marketReceiver },
cursor,
};
return { node: { ...entry, asset, marketSender, marketReceiver } };
});
}
);
@@ -176,13 +172,14 @@ export const useLedgerEntriesDataProvider = ({
data: (AggregatedLedgerEntriesEdge | null)[] | null;
totalCount?: number;
}) => {
dataRef.current = data;
totalCountRef.current = totalCount;
return updateGridData(dataRef, data, gridRef);
},
[gridRef]
);
const { data, error, loading, load, totalCount, reload } = useDataProvider({
const { data, error, loading, load, totalCount } = useDataProvider({
dataProvider: ledgerEntriesProvider,
update,
insert,
@@ -196,5 +193,5 @@ export const useLedgerEntriesDataProvider = ({
totalCountRef,
load
);
return { loading, error, data, getRows, reload };
return { loading, error, data, getRows };
};
@@ -16,7 +16,6 @@ export const ledgerEntriesQuery = (
edges: ledgerEntries.map((node) => ({
__typename: 'AggregatedLedgerEntriesEdge',
node,
cursor: 'cursor-1',
})),
pageInfo: {
startCursor:
+6 -13
View File
@@ -21,12 +21,11 @@ export const LedgerManager = ({ partyId }: LedgerManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const [filter, setFilter] = useState<Filter | undefined>();
const { data, error, loading, getRows, reload } =
useLedgerEntriesDataProvider({
partyId,
filter,
gridRef,
});
const { data, error, loading, getRows } = useLedgerEntriesDataProvider({
partyId,
filter,
gridRef,
});
const onFilterChanged = useCallback(
(event: FilterChangedEvent) => {
@@ -39,11 +38,7 @@ export const LedgerManager = ({ partyId }: LedgerManagerProps) => {
},
[filter]
);
const getRowId = useCallback(
({ data }: { data: Types.AggregatedLedgerEntry }) =>
`${data.vegaTime}-${data.fromAccountPartyId}-${data.toAccountPartyId}`,
[]
);
return (
<div className="h-full relative">
<LedgerTable
@@ -51,7 +46,6 @@ export const LedgerManager = ({ partyId }: LedgerManagerProps) => {
rowModelType="infinite"
datasource={{ getRows }}
onFilterChanged={onFilterChanged}
getRowId={getRowId}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
@@ -60,7 +54,6 @@ export const LedgerManager = ({ partyId }: LedgerManagerProps) => {
data={data}
noDataMessage={t('No entries')}
noDataCondition={(data) => !(data && data.length)}
reload={reload}
/>
</div>
</div>
@@ -85,7 +85,7 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
[marketId, updateOrderbookData]
);
const { data, error, loading, flush, reload } = useDataProvider({
const { data, error, loading, flush } = useDataProvider({
dataProvider: marketDepthProvider,
update,
variables,
@@ -156,7 +156,6 @@ export const OrderbookManager = ({ marketId }: OrderbookManagerProps) => {
loading={loading || marketDataLoading || marketLoading}
error={error || marketDataError || marketError}
data={data}
reload={reload}
>
<Orderbook
{...orderbookData}
@@ -69,14 +69,14 @@ export const MarketInfoContainer = ({
[marketId, yTimestamp]
);
const { data, loading, error, reload } = useDataProvider({
const { data, loading, error } = useDataProvider({
dataProvider: marketInfoDataProvider,
skipUpdates: true,
variables,
});
return (
<AsyncRenderer data={data} loading={loading} error={error} reload={reload}>
<AsyncRenderer data={data} loading={loading} error={error}>
{data && data.market ? (
<Info market={data.market} onSelect={(id) => onSelect?.(id)} />
) : (
@@ -10,7 +10,7 @@ interface MarketsContainerProps {
}
export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
const { data, error, loading, reload } = useDataProvider({
const { data, error, loading } = useDataProvider({
dataProvider,
skipUpdates: true,
});
@@ -18,8 +18,7 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
return (
<div className="h-full relative">
<MarketListTable
rowData={error ? [] : data}
noRowsOverlayComponent={() => null}
rowData={data}
onRowClicked={(rowEvent: RowClickedEvent) => {
const { data, event } = rowEvent;
// filters out clicks on the symbol column because it should display asset details
@@ -37,7 +36,6 @@ export const MarketsContainer = ({ onSelect }: MarketsContainerProps) => {
error={error}
data={data}
noDataMessage={t('No markets')}
reload={reload}
/>
</div>
</div>
@@ -155,7 +155,6 @@ export const ordersProvider = makeDataProvider({
append,
first: 100,
},
additionalContext: { isEnlargedTimeout: true },
});
export const ordersWithMarketProvider = makeDerivedDataProvider<
@@ -83,15 +83,14 @@ export const OrderListManager = ({
const create = useVegaTransactionStore((state) => state.create);
const hasActiveOrder = useHasActiveOrder(marketId);
const { data, error, loading, addNewRows, getRows, reload } =
useOrderListData({
partyId,
marketId,
sort,
filter,
gridRef,
scrolledToTop,
});
const { data, error, loading, addNewRows, getRows } = useOrderListData({
partyId,
marketId,
sort,
filter,
gridRef,
scrolledToTop,
});
const onBodyScrollEnd = (event: BodyScrollEndEvent) => {
if (event.top === 0) {
@@ -129,7 +128,7 @@ export const OrderListManager = ({
return (
<>
<div className="h-full relative grid grid-rows-[1fr,min-content]">
<div className="relative">
<div className="h-full relative">
<OrderListTable
ref={gridRef}
rowModelType="infinite"
@@ -150,8 +149,6 @@ export const OrderListManager = ({
setEditOrder={setEditOrder}
onMarketClick={onMarketClick}
isReadOnly={isReadOnly}
hasActiveOrder={hasActiveOrder}
blockLoadDebounceMillis={100}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
@@ -160,7 +157,6 @@ export const OrderListManager = ({
data={data}
noDataMessage={t('No orders')}
noDataCondition={(data) => !(data && data.length)}
reload={reload}
/>
</div>
</div>
@@ -119,7 +119,7 @@ export const useOrderListData = ({
[gridRef]
);
const { data, error, loading, load, totalCount, reload } = useDataProvider({
const { data, error, loading, load, totalCount } = useDataProvider({
dataProvider: ordersWithMarketProvider,
update,
insert,
@@ -133,5 +133,5 @@ export const useOrderListData = ({
load,
newRows
);
return { loading, error, data, addNewRows, getRows, reload };
return { loading, error, data, addNewRows, getRows };
};
@@ -33,24 +33,20 @@ export type OrderListTableProps = OrderListProps & {
setEditOrder: (order: Order) => void;
onMarketClick?: (marketId: string) => void;
isReadOnly: boolean;
hasActiveOrder?: boolean;
};
export const OrderListTable = forwardRef<AgGridReact, OrderListTableProps>(
({ cancel, setEditOrder, onMarketClick, hasActiveOrder, ...props }, ref) => {
({ cancel, setEditOrder, onMarketClick, ...props }, ref) => {
return (
<AgGrid
ref={ref}
overlayNoRowsTemplate={t('No orders')}
overlayNoRowsTemplate="No orders"
defaultColDef={{
flex: 1,
resizable: true,
filterParams: { buttons: ['reset'] },
}}
style={{
width: '100%',
height: hasActiveOrder ? 'calc(100% - 46px)' : '100%',
}}
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data.id}
{...props}
>
+2 -7
View File
@@ -18,11 +18,7 @@ export const PositionsManager = ({
isReadOnly,
}: PositionsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data, error, loading, reload } = usePositionsData(
partyId,
gridRef,
true
);
const { data, error, loading } = usePositionsData(partyId, gridRef, true);
const create = useVegaTransactionStore((store) => store.create);
const onClose = ({
marketId,
@@ -55,7 +51,7 @@ export const PositionsManager = ({
return (
<div className="h-full relative">
<PositionsTable
rowData={error ? [] : data}
rowData={data}
ref={gridRef}
onMarketClick={onMarketClick}
onClose={onClose}
@@ -69,7 +65,6 @@ export const PositionsManager = ({
data={data}
noDataMessage={t('No positions')}
noDataCondition={(data) => !(data && data.length)}
reload={reload}
/>
</div>
</div>

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