Compare commits

...
Author SHA1 Message Date
Dariusz Majcherczyk f6ed5016d1 test: added wait for the markets as beforeEach 2023-02-21 17:27:44 +01:00
Dariusz Majcherczyk d4e151d4be test: remove wrong wait 2023-02-21 17:13:07 +01:00
Dariusz Majcherczyk efee4f9e1a test: changed wait 2023-02-21 16:54:10 +01:00
Dariusz Majcherczyk a3257b33b4 test: node version update for live workflow 2023-02-21 15:24:47 +01:00
mattrussell36 96007bd230 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 12:06:41 +00:00
mattrussell36 554d9ecb06 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 06:08:22 +00:00
Matthew Russell a12a5b3a15 chore(trading,governance): remove busEvent for proposals (#2946) 2023-02-20 16:57:25 -08:00
mattrussell36 a79e2f85bc chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-21 00:12:00 +00:00
mattrussell36 a2190fdde0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-20 18:06:33 +00:00
Matthew Russell 849415a78e fix: set size properly for checkbox icon (#2903) 2023-02-20 16:50:27 +00:00
Dexter Edwards d33f5428b6 chore(token): governance missing details (#2938) 2023-02-20 16:18:40 +00:00
m.ray c73b01d549 feat(trading): do not pin row of existing account as it will become duplicated (#2942) 2023-02-20 15:59:01 +00:00
Sam Keen 4286bc37b3 feat(wallet): tighten up the flow for "view as vega user" (#2934) 2023-02-20 15:56:12 +00:00
Art d4ecb88fb4 feat(explorer): new proposals index page (#2925) 2023-02-20 15:53:23 +00:00
Edd 57a8955795 feat(explorer): add feedback link to footer (#2943) 2023-02-20 15:52:12 +00:00
Sam Keen ac53b1f97a feat(governance,ui-toolkit): individual rewards table (#2928) 2023-02-20 14:30:11 +00:00
Art 4b83a10475 chore(explorer): change asset dialog to details page (#2941) 2023-02-20 14:15:18 +00:00
mattrussell36 7e957a2841 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-20 12:09:22 +00:00
Joe Tsang 38c0e70bbc test(governance): proposal flow refactor (#2933) 2023-02-20 09:28:16 +00:00
m.ray 94c40cce7d fix(trading): re-use input errors instead of notification component (#2924) 2023-02-20 07:53:16 +00:00
mattrussell36 0059050440 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-20 06:08:40 +00:00
mattrussell36 f7be890eb9 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-20 00:11:21 +00:00
mattrussell36 59ae971a07 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-19 18:06:29 +00:00
mattrussell36 cfcd5efa2c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-19 12:06:35 +00:00
mattrussell36 fb1e85893c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-19 06:06:22 +00:00
mattrussell36 084972577e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-19 00:11:16 +00:00
mattrussell36 94df5ca048 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 18:06:20 +00:00
mattrussell36 6142a7afbb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 12:08:53 +00:00
mattrussell36 d2e0c57676 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 06:06:37 +00:00
mattrussell36 e48d8d8715 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-02-18 00:11:33 +00:00
86 changed files with 4124 additions and 2010 deletions
+4
View File
@@ -18,6 +18,10 @@ 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:
+3 -12
View File
@@ -4,15 +4,6 @@ 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', () => {
@@ -40,7 +31,7 @@ context('Asset page', { tags: '@regression' }, () => {
});
});
it('should open details dialog when clicked on "View details"', () => {
it('should open details page when clicked on "View details"', () => {
cy.getAssets().then((assets) => {
Object.values(assets).forEach((asset) => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
@@ -49,8 +40,8 @@ context('Asset page', { tags: '@regression' }, () => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
.eq(0)
.click();
cy.getByTestId('dialog-content').should('be.visible');
cy.getByTestId('dialog-close').click();
cy.getByTestId('asset-header').should('have.text', asset.name);
cy.go('back');
});
});
});
@@ -1,22 +1,35 @@
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(<AssetsTable data={null} />);
const res = render(
<MemoryRouter>
<AssetsTable data={null} />
</MemoryRouter>
);
expect(await res.findByText('Loading...')).toBeInTheDocument();
});
it('shows no data message if no assets found', async () => {
const res = render(<AssetsTable data={[]} />);
const res = render(
<MemoryRouter>
<AssetsTable data={[]} />
</MemoryRouter>
);
expect(
await res.findByText('This chain has no assets')
).toBeInTheDocument();
});
it('shows a table/list with all the assets', async () => {
const res = render(<AssetsTable data={assetsList} />);
const res = render(
<MemoryRouter>
<AssetsTable data={assetsList} />
</MemoryRouter>
);
await waitFor(() => {
const rowA1 = res.container.querySelector('[row-id="123"]');
expect(rowA1).toBeInTheDocument();
@@ -1,5 +1,4 @@
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';
@@ -9,15 +8,14 @@ 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 openAssetDetailsDialog = useAssetDetailsDialogStore(
(state) => state.open
);
const navigate = useNavigate();
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
ref.current?.columnApi.setColumnsVisible(
@@ -49,17 +47,23 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
autoHeight: true,
}}
suppressCellFocus={true}
onGridReady={() => {
showColumnsOnDesktop();
onRowClicked={({ data }: RowClickedEvent) => {
navigate(data.id);
}}
>
<AgGridColumn headerName={t('Symbol')} field="symbol" />
<AgGridColumn headerName={t('Name')} field="name" />
<AgGridColumn flex="2" headerName={t('ID')} field="id" />
<AgGridColumn
flex="2"
headerName={t('ID')}
field="id"
hide={window.innerWidth < BREAKPOINT_MD}
/>
<AgGridColumn
colId="type"
headerName={t('Type')}
field="source.__typename"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetTypeMapping[value].value
}
@@ -67,6 +71,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
<AgGridColumn
headerName={t('Status')}
field="status"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetStatusMapping[value].value
}
@@ -83,28 +88,13 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<div className="pb-1">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value, e.target as HTMLElement);
}}
>
{t('View details')}
</ButtonLink>{' '}
<span className="max-md:hidden">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(
value,
e.target as HTMLElement,
true
);
}}
>
{t('View JSON')}
</ButtonLink>
</span>
</div>
<ButtonLink
onClick={(e) => {
navigate(value);
}}
>
{t('View details')}
</ButtonLink>
) : (
''
)
@@ -1,17 +1,24 @@
import { NodeSwitcherDialog, useEnvironment } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/react-helpers';
import { Link } from '@vegaprotocol/ui-toolkit';
import { useState } from 'react';
import { t, useScreenDimensions } from '@vegaprotocol/react-helpers';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo, useState } from 'react';
import { ENV } from '../../config/env';
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">
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
{GIT_COMMIT_HASH && (
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
<p data-testid="git-commit-hash">
{t('Version')}:{' '}
<Link
@@ -25,15 +32,21 @@ export const Footer = () => {
{GIT_COMMIT_HASH}
</Link>
</p>
)}
</div>
</div>
)}
<div className="flex pl-2 content-center">
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
{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,9 +5,12 @@ 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;
};
/**
@@ -15,17 +18,22 @@ 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, ...props }: AssetLinkProps) => {
export const AssetLink = ({ assetId, asDialog, ...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) => {
open(assetId, e.target as HTMLElement);
if (asDialog) {
open(assetId, e.target as HTMLElement);
} else {
navigate(`${Routes.ASSETS}/${asset?.id}`);
}
}}
{...props}
>
@@ -0,0 +1,227 @@
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,4 +33,7 @@ export const ENV = {
parties: truthy.includes(windowOrDefault('NX_EXPLORER_PARTIES')),
validators: truthy.includes(windowOrDefault('NX_EXPLORER_VALIDATORS')),
},
addresses: {
feedback: windowOrDefault('NX_GITHUB_FEEDBACK_URL'),
},
};
@@ -0,0 +1,50 @@
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 Assets = () => {
export const AssetsPage = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
@@ -1 +1,2 @@
export * from './assets';
export * from './assets-page';
export * from './asset-page';
@@ -1,82 +0,0 @@
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
}
}
}
}
}
}
}
@@ -1,122 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type 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,63 +1 @@
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;
export * from './proposals-page';
@@ -0,0 +1,33 @@
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>
);
};
+13 -4
View File
@@ -1,6 +1,6 @@
import { Assets } from './assets';
import { AssetPage, AssetsPage } from './assets';
import BlockPage from './blocks';
import Governance from './governance';
import { Proposals } from './governance';
import Home from './home';
import OraclePage from './oracles';
import Oracles from './oracles/home';
@@ -53,7 +53,16 @@ const assetsRoutes: Route[] = flags.assets
path: Routes.ASSETS,
text: t('Assets'),
name: 'Assets',
element: <Assets />,
children: [
{
index: true,
element: <AssetsPage />,
},
{
path: ':assetId',
element: <AssetPage />,
},
],
},
]
: [];
@@ -75,7 +84,7 @@ const governanceRoutes: Route[] = flags.governance
path: Routes.GOVERNANCE,
name: 'Governance proposals',
text: t('Governance Proposals'),
element: <Governance />,
element: <Proposals />,
},
]
: [];
+268 -55
View File
@@ -2761,7 +2761,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "68916.5304132697229014062",
"locked_amount": "68026.0050809900766642912",
"deposits": [
{
"amount": "86666.297",
@@ -2827,7 +2827,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1418.21231430606425",
"locked_amount": "1366.694488960114",
"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": "2836.415649675",
"locked_amount": "34028.837668815225",
"total_removed": "3009.936598125",
"locked_amount": "33251.80085558624625",
"deposits": [
{
"amount": "7500",
@@ -3244,6 +3244,11 @@
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
"tx": "0x78fd0cf1ca8de93d15eee1424146bcd70b84cc168b4c1ea1ac50020b8d67bcc1"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3279,6 +3284,12 @@
"tranche_id": 34,
"tx": "0x5bfef799be13741d89872cc604df1f35b41069397e681577fc15c039866d8707"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0x6f125819654942fd6c08c0929e7f9e9629eb65c2cfa92acae0f8db9685a43537"
},
{
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -3293,8 +3304,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "561.302466975",
"remaining_tokens": "6938.697533025"
"withdrawn_tokens": "734.823415425",
"remaining_tokens": "6765.176584575"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -3326,7 +3337,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "68853.652750677974505795",
"locked_amount": "67963.9399107138444471",
"deposits": [
{
"amount": "129999.45",
@@ -3392,7 +3403,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "33829.0153855910767",
"locked_amount": "33185.77934424150344",
"deposits": [
{
"amount": "10000",
@@ -3585,7 +3596,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2893.778855910705",
"locked_amount": "2842.40217529173",
"deposits": [
{
"amount": "5000",
@@ -3796,7 +3807,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "10761.2209634480270840046",
"locked_amount": "9888.4955479431840552672",
"deposits": [
{
"amount": "97499.58",
@@ -3829,7 +3840,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "14708.73120973917718086298914",
"locked_amount": "13515.866236501447216654821204",
"deposits": [
{
"amount": "135173.4239508",
@@ -3875,7 +3886,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "4527.0598229766283023892",
"locked_amount": "4159.9193118355352539772",
"deposits": [
{
"amount": "32499.86",
@@ -3908,7 +3919,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1473.514042830201860202",
"locked_amount": "1354.013369100967212235",
"deposits": [
{
"amount": "10833.29",
@@ -3941,7 +3952,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "4720.860935375",
"locked_amount": "5508.324595517016566601",
"locked_amount": "5061.6043871236901291745",
"deposits": [
{
"amount": "6500",
@@ -4092,8 +4103,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "4373.451408",
"locked_amount": "8980.8385128913431",
"total_removed": "4546.97235645",
"locked_amount": "8514.6164249539599",
"deposits": [
{
"amount": "7500",
@@ -4117,6 +4128,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x4a7c7462e3bb6abe2d33dfe5a1bf1e4211e52e5e03239c02554988f7d3635523"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4212,6 +4228,12 @@
"tranche_id": 33,
"tx": "0x4a7c7462e3bb6abe2d33dfe5a1bf1e4211e52e5e03239c02554988f7d3635523"
},
{
"amount": "173.52094845",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0xb40f1c9a538e1998da74acd7fceea8a718e97b2af8e903277e16a4b729628a52"
},
{
"amount": "167.6680479",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -4298,8 +4320,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "4373.451408",
"remaining_tokens": "3126.548592"
"withdrawn_tokens": "4546.97235645",
"remaining_tokens": "2953.02764355"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -4324,7 +4346,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "554053.412289953077578596",
"locked_amount": "534119.9961293638279815094",
"deposits": [
{
"amount": "1852091.69",
@@ -36615,8 +36637,8 @@
"tranche_start": "2022-03-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "592998.0503546212334",
"locked_amount": "875888.518509001519437488391",
"total_removed": "609657.626547646980493",
"locked_amount": "845257.797948570775371065827",
"deposits": [
{
"amount": "1998.95815",
@@ -36785,6 +36807,11 @@
}
],
"withdrawals": [
{
"amount": "16659.576193025747093",
"user": "0xB523235B6c7C74DDB26b10E78bFb2d0Cb63Ae289",
"tx": "0x2c5cfeee95fba21323fdbfde219501aeafe8d7f236d1283b4f8f7c7760e4401c"
},
{
"amount": "144779.049152",
"user": "0x1da69E9C22d77Ef8Ccbf5a1F2d83eDBc5Dcc20fA",
@@ -37884,10 +37911,17 @@
"tx": "0x8cc5159f3d665dd33a2bdac6e990cf1b65dc18b6b5e37a07b37dd54495a8d33c"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "16659.576193025747093",
"user": "0xB523235B6c7C74DDB26b10E78bFb2d0Cb63Ae289",
"tranche_id": 1,
"tx": "0x2c5cfeee95fba21323fdbfde219501aeafe8d7f236d1283b4f8f7c7760e4401c"
}
],
"total_tokens": "21666.5743",
"withdrawn_tokens": "0",
"remaining_tokens": "21666.5743"
"withdrawn_tokens": "16659.576193025747093",
"remaining_tokens": "5006.998106974252907"
},
{
"address": "0x7227e17101E6C70F4dAfC7DDB77BB7D83DdfC1C8",
@@ -37941,8 +37975,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "570447.40018226362274952",
"locked_amount": "8405532.0348557766289364422878397935063531",
"total_removed": "572826.70366554650274952",
"locked_amount": "8296917.466428724395809164572318349628278",
"deposits": [
{
"amount": "16249.93",
@@ -38481,6 +38515,26 @@
"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",
@@ -40189,6 +40243,30 @@
"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",
@@ -41331,8 +41409,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "121278.642239727666375",
"remaining_tokens": "138720.245260272333625"
"withdrawn_tokens": "123657.945723010546375",
"remaining_tokens": "136340.941776989453625"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -43551,8 +43629,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "3926967.723059737571037032",
"locked_amount": "2038488.278459767293130894768769085",
"total_removed": "4396450.802668984389787156",
"locked_amount": "1938216.002398478950673792862823089",
"deposits": [
{
"amount": "129284.449",
@@ -43786,6 +43864,31 @@
"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",
@@ -46682,6 +46785,30 @@
"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",
@@ -49126,8 +49253,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "307569.5302891888704365",
"remaining_tokens": "51553.9392858111295635"
"withdrawn_tokens": "310868.25548215842910375",
"remaining_tokens": "48255.21409284157089625"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -50005,6 +50132,12 @@
}
],
"withdrawals": [
{
"amount": "466184.354416277260082874",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
"tranche_id": 3,
"tx": "0x43c34302d1895983b4f5b28ad186ea31662a045f3c7c63075a1c9db4b059e42d"
},
{
"amount": "32828.670452546475795519",
"user": "0xfc3b2D0b548d3edBb512CeE0Bb79Fb7FaD50AaF3",
@@ -50013,8 +50146,8 @@
}
],
"total_tokens": "575536.79731735",
"withdrawn_tokens": "32828.670452546475795519",
"remaining_tokens": "542708.126864803524204481"
"withdrawn_tokens": "499013.024868823735878393",
"remaining_tokens": "76523.772448526264121607"
},
{
"address": "0xaAeD573103e3f981867C4cd31c6674DE1c8a13c3",
@@ -50465,7 +50598,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2749067.463242913023296295",
"locked_amount": "488515.643953737443539557205122125",
"locked_amount": "448897.461240245945578932424695999",
"deposits": [
{
"amount": "552496.6455",
@@ -52491,8 +52624,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "33803.2676021352685",
"locked_amount": "138789.542549975765711077066869624",
"total_removed": "34093.0379154332685",
"locked_amount": "133935.9297845122141846487163876",
"deposits": [
{
"amount": "3000",
@@ -59186,6 +59319,31 @@
"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",
@@ -65824,10 +65982,17 @@
"tx": "0x75de6ca47e0da361181d14aceb5d08e572754861465caa67a12b528886d55307"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "142.126249364",
"user": "0x289047e767D23561A2035cd3fdc60c94ed1bBA3B",
"tranche_id": 5,
"tx": "0x61b2f8e5f4efb5049e418ede94fd54dfc1ed8d0f1165ff31517f4f748ef14899"
}
],
"total_tokens": "200",
"withdrawn_tokens": "0",
"remaining_tokens": "200"
"withdrawn_tokens": "142.126249364",
"remaining_tokens": "57.873750636"
},
{
"address": "0x45bAfA40E9e8099cCd9Ed0e3112A11E7a1d37beb",
@@ -72613,6 +72778,12 @@
}
],
"withdrawals": [
{
"amount": "12.107001524",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tranche_id": 5,
"tx": "0xa2e745ec65b1caad05b18aa7c730d0550a263df2208ba10cecee5a30f815447b"
},
{
"amount": "15.80745814",
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
@@ -72693,8 +72864,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "270.64275748",
"remaining_tokens": "129.35724252"
"withdrawn_tokens": "282.749759004",
"remaining_tokens": "117.250240996"
},
{
"address": "0x5c90765F50629570738fEe7b7FA82ae118f81Ed1",
@@ -76964,6 +77135,12 @@
}
],
"withdrawals": [
{
"amount": "94.45296804",
"user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392",
"tranche_id": 5,
"tx": "0x10a149356dc25da53994746b06fc8922faa23be9fb781541e91357d7d4f4d36a"
},
{
"amount": "119.5173516",
"user": "0x7345AaA0D0e4C4A46d921fA9323973956F2b7392",
@@ -77008,8 +77185,8 @@
}
],
"total_tokens": "1200",
"withdrawn_tokens": "755.56343226",
"remaining_tokens": "444.43656774"
"withdrawn_tokens": "850.0164003",
"remaining_tokens": "349.9835997"
},
{
"address": "0x47181cf36aDFb0b5dB7EfDB123f403D890607841",
@@ -77201,6 +77378,12 @@
}
],
"withdrawals": [
{
"amount": "24.023287672",
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
"tranche_id": 5,
"tx": "0xbb25fa75431a6463909c9efde31d110e1aa5c945d0363fcc4bd5c46f869cabac"
},
{
"amount": "37.881582952",
"user": "0xcB555C5602cC0b2434A27d277e14Cb5C7bF4Ead1",
@@ -77221,8 +77404,8 @@
}
],
"total_tokens": "400",
"withdrawn_tokens": "259.089789444",
"remaining_tokens": "140.910210556"
"withdrawn_tokens": "283.113077116",
"remaining_tokens": "116.886922884"
},
{
"address": "0x3738bec36216eA2F11B954891C65AAd1Bc852156",
@@ -80046,6 +80229,12 @@
}
],
"withdrawals": [
{
"amount": "17.060806698",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
"tranche_id": 5,
"tx": "0x9d1ced8d04e8d58af92b1434bcb269487a3f6ddf4305c0739ab0f158f5401ec6"
},
{
"amount": "15.872983256",
"user": "0x28FC83947F02f59Cb36b40f97f2D32BBC5D00585",
@@ -80072,8 +80261,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "125.58028285",
"remaining_tokens": "74.41971715"
"withdrawn_tokens": "142.641089548",
"remaining_tokens": "57.358910452"
},
{
"address": "0xBef628af8547cE5264835F32487055a7e3dF393D",
@@ -81274,7 +81463,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42",
"total_removed": "66301.1049690697989",
"total_removed": "66601.1049690697989",
"locked_amount": "0",
"deposits": [
{
@@ -85519,6 +85708,16 @@
"user": "0x217254578EFd323CF93b1E0FC900A2f4C97A2e6e",
"tx": "0xbed9954657a0b981080f8b66187ee3ce27ba8b18dc7419f430624cda2d4e435a"
},
{
"amount": "50",
"user": "0xa40284F5359954E1a0c024132940375FD480C640",
"tx": "0x2d1ecfd5ecefe61fa80d8c7d20b9e4afac178554d364c8796eec0d7fdc8f0942"
},
{
"amount": "250",
"user": "0xc0A603F3B555ba1622B80CF54EAFE6fb717f3Cd7",
"tx": "0xcb9db64f3df7fb6f1addc0073e2a8a5e285618b07d955d4435ff674327cec8e5"
},
{
"amount": "30",
"user": "0x4A13d4dC5e06ACdA81C011D55a7DaAc332bC5Dbf",
@@ -95493,10 +95692,17 @@
"tx": "0xadc25e73736cd85e84fee16f2d7ee41f16f7deb83a52ceb82b327974063964f7"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "50",
"user": "0xa40284F5359954E1a0c024132940375FD480C640",
"tranche_id": 6,
"tx": "0x2d1ecfd5ecefe61fa80d8c7d20b9e4afac178554d364c8796eec0d7fdc8f0942"
}
],
"total_tokens": "50",
"withdrawn_tokens": "0",
"remaining_tokens": "50"
"withdrawn_tokens": "50",
"remaining_tokens": "0"
},
{
"address": "0x9603B92De110066121E9D7218482A672bd8afbD9",
@@ -100234,10 +100440,17 @@
"tx": "0x3a0390dffa4280c286016566b29c62e4cec36c6504f7ac3d1de80d22b2903008"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xc0A603F3B555ba1622B80CF54EAFE6fb717f3Cd7",
"tranche_id": 6,
"tx": "0xcb9db64f3df7fb6f1addc0073e2a8a5e285618b07d955d4435ff674327cec8e5"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xE9e7C70a1e2A5F4A612349871aE18eDCEbB253d9",
@@ -1,849 +0,0 @@
/// <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();
}
});
}
);
@@ -0,0 +1,273 @@
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,4 +1,5 @@
/// <reference types="cypress" />
import { associateTokenStartOfTests } from '../../support/governance.functions';
import {
createUpdateNetworkProposalTxBody,
@@ -21,6 +22,7 @@ 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,347 @@
/// <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');
});
}
);
@@ -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');
@@ -0,0 +1,162 @@
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')
);
});
});
});
@@ -38,7 +38,6 @@ 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);
@@ -1,3 +1,6 @@
const epochTimeout = Cypress.env('epochTimeout');
const txTimeout = Cypress.env('txTimeout');
Cypress.Commands.add(
'convert_token_value_to_number',
{ prevSubject: true },
@@ -49,3 +52,29 @@ 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,6 +8,7 @@ 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 };
@@ -61,6 +62,7 @@ Cypress.Commands.add('enter_raw_proposal_body', (timestamp) => {
parseSpecialCharSequences: false,
delay: 2,
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal);
});
});
@@ -73,6 +75,7 @@ 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();
}
);
@@ -185,3 +188,40 @@ 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',
};
@@ -1,4 +1,4 @@
import { act } from '@testing-library/react-hooks';
import { act } from '@testing-library/react';
import { usePendingBalancesStore } from './use-pending-balances-manager';
import type { Event } from 'ethers';
@@ -1,7 +1,6 @@
import type { Event } from 'ethers';
import uniqBy from 'lodash/uniqBy';
import create from 'zustand';
import { create } from 'zustand';
import type { Event } from 'ethers';
export type PendingTxsStore = {
pendingBalances: Event[];
@@ -432,6 +432,7 @@
"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",
@@ -457,6 +458,7 @@
"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,6 +137,24 @@ 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
@@ -0,0 +1,25 @@
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();
});
});
@@ -0,0 +1,40 @@
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>
);
};
@@ -1,79 +0,0 @@
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>
);
};
@@ -0,0 +1,63 @@
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();
});
});
@@ -0,0 +1,111 @@
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>
);
};
@@ -5,9 +5,10 @@ import { removePaginationWrapper } from '@vegaprotocol/react-helpers';
import { useRewardsQuery } from '../home/__generated__/Rewards';
import { ENV } from '../../../config';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { RewardTable } from './reward-table';
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
export const RewardInfo = () => {
export const EpochIndividualRewards = () => {
const { t } = useTranslation();
const { pubKey } = useVegaWallet();
const { delegationsPagination } = ENV;
@@ -30,13 +31,10 @@ export const RewardInfo = () => {
return removePaginationWrapper(data.party.rewardsConnection.edges);
}, [data]);
const delegations = useMemo(() => {
if (!data?.party || !data.party.delegationsConnection?.edges?.length) {
return [];
}
return removePaginationWrapper(data.party.delegationsConnection.edges);
}, [data]);
const epochIndividualRewardSummaries = useMemo(() => {
if (!data?.party) return [];
return generateEpochIndividualRewardsList(rewards);
}, [data?.party, rewards]);
return (
<AsyncRenderer
@@ -45,20 +43,18 @@ export const RewardInfo = () => {
data={data}
render={() => (
<div>
<p>
{t('Connected Vega key')}: {pubKey}
<p className="mb-10">
{t('Connected Vega key')}:{' '}
<span className="text-white">{pubKey}</span>
</p>
{rewards.length ? (
rewards.map((reward, i) => {
if (!reward) return null;
return (
<RewardTable
key={i}
reward={reward}
delegations={delegations || []}
{epochIndividualRewardSummaries.length ? (
epochIndividualRewardSummaries.map(
(epochIndividualRewardSummary) => (
<EpochIndividualRewardsTable
data={epochIndividualRewardSummary}
/>
);
})
)
)
) : (
<p>{t('noRewards')}</p>
)}
@@ -0,0 +1,235 @@
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',
},
},
},
],
},
]);
});
});
@@ -0,0 +1,79 @@
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)
);
};
@@ -1,4 +1,5 @@
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';
@@ -10,10 +11,30 @@ 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',
},
@@ -22,15 +43,25 @@ const mockData = {
describe('EpochTotalRewardsTable', () => {
it('should render correctly', () => {
const { getByTestId } = render(<EpochTotalRewardsTable data={mockData} />);
const { getByTestId } = render(
<AppStateProvider>
<EpochTotalRewardsTable data={mockData} />
</AppStateProvider>
);
expect(getByTestId('epoch-total-rewards-table')).toBeInTheDocument();
expect(getByTestId('asset')).toBeInTheDocument();
expect(getByTestId('global')).toBeInTheDocument();
expect(getByTestId('infra')).toBeInTheDocument();
expect(getByTestId('taker')).toBeInTheDocument();
expect(getByTestId('maker')).toBeInTheDocument();
expect(getByTestId('liquidity')).toBeInTheDocument();
expect(getByTestId('market-maker')).toBeInTheDocument();
expect(getByTestId('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('total')).toBeInTheDocument();
});
});
@@ -1,19 +1,14 @@
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';
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';
interface EpochTotalRewardsGridProps {
data: AggregatedEpochSummary;
}
interface ColumnHeaderProps {
title: string;
tooltipContent?: string;
className?: string;
data: EpochTotalSummary;
}
interface RewardItemProps {
@@ -22,61 +17,28 @@ interface RewardItemProps {
last?: boolean;
}
const displayReward = (reward: string) => {
const DisplayReward = ({ reward }: { reward: string }) => {
const {
appState: { decimals },
} = useAppState();
if (Number(reward) === 0) {
return <span className="text-vega-dark-300">0</span>;
return <span className="text-vega-dark-300">-</span>;
}
if (reward.split('.')[1] && reward.split('.')[1].length > 4) {
return (
<Tooltip description={formatNumber(reward)}>
<button>{formatNumber(Number(reward).toFixed(4))}</button>
</Tooltip>
);
}
return <span>{formatNumber(reward)}</span>;
return (
<Tooltip description={formatNumber(toBigNum(reward, decimals), decimals)}>
<button>{formatNumber(toBigNum(reward, decimals))}</button>
</Tooltip>
);
};
const gridStyles = classNames(
'grid grid-cols-[repeat(8,minmax(100px,auto))] max-w-full overflow-auto',
`border-t border-vega-dark-200`,
'text-sm'
);
const headerGridItemStyles = (last = false) =>
classNames('border-r border-b border-b-vega-dark-200', 'py-3 px-5', {
'border-r-vega-dark-150': !last,
'border-r-0': last,
});
const rowGridItemStyles = (last = false) =>
classNames('relative', 'border-r border-b border-b-vega-dark-150', {
'border-r-vega-dark-150': !last,
'border-r-0': last,
});
const ColumnHeader = ({
title,
tooltipContent,
className,
}: ColumnHeaderProps) => (
<div className={className}>
<h2 className="mb-1 text-sm text-vega-dark-300">{title}</h2>
{tooltipContent && (
<Tooltip description={tooltipContent}>
<button>
<Icon name={'info-sign'} className="text-vega-dark-200" />
</button>
</Tooltip>
)}
</div>
);
const RewardItem = ({ value, dataTestId, last }: RewardItemProps) => (
<div data-testid={dataTestId} className={rowGridItemStyles(last)}>
<div className="h-full w-5 absolute right-0 top-0 bg-gradient-to-r from-transparent to-black pointer-events-none" />
<div className="overflow-auto p-5">{displayReward(value)}</div>
<div className="overflow-auto p-5">
<DisplayReward reward={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>
);
@@ -84,129 +46,19 @@ 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 (
<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}
/>
<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>
))}
</div>
</div>
{rewards.map(({ rewardType, amount }, i) => (
<RewardItem key={i} dataTestId={rewardType} value={amount} />
))}
<RewardItem dataTestId="total" value={totalAmount} last={true} />
</div>
))}
</RewardsTable>
);
};
@@ -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 EpochRewards = () => {
export const EpochTotalRewards = () => {
const { data, loading, error, refetch } = useEpochAssetsRewardsQuery({
variables: {
epochRewardSummariesPagination: {
@@ -15,7 +15,7 @@ export const EpochRewards = () => {
});
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
const epochRewardSummaries = generateEpochTotalRewardsList(data) || [];
const epochTotalRewardSummaries = generateEpochTotalRewardsList(data) || [];
return (
<AsyncRenderer
@@ -27,12 +27,12 @@ export const EpochRewards = () => {
className="max-w-full overflow-auto"
data-testid="epoch-rewards-total"
>
{epochRewardSummaries.length === 0 ? (
{epochTotalRewardSummaries.length === 0 ? (
<NoRewards />
) : (
<>
{epochRewardSummaries.map((aggregatedEpochSummary) => (
<EpochTotalRewardsTable data={aggregatedEpochSummary} />
{epochTotalRewardSummaries.map((epochTotalSummary, index) => (
<EpochTotalRewardsTable data={epochTotalSummary} key={index} />
))}
</>
)}
@@ -61,7 +61,7 @@ describe('generateEpochAssetRewardsList', () => {
expect(result).toEqual([]);
});
it('should return an array of unnamed assets if no assets are provided (should not happen)', () => {
it('should return an array of unnamed assets if no asset names are provided (should not happen)', () => {
const epochData = {
assetsConnection: {
edges: [],
@@ -72,18 +72,10 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '123',
},
},
{
node: {
epoch: 2,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '5',
},
},
],
},
epoch: {
@@ -104,30 +96,34 @@ describe('generateEpochAssetRewardsList', () => {
name: '',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
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',
},
],
},
]);
});
@@ -155,7 +151,7 @@ describe('generateEpochAssetRewardsList', () => {
node: {
epoch: 1,
assetId: '1',
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
},
@@ -167,22 +163,6 @@ 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,
@@ -211,30 +191,31 @@ describe('generateEpochAssetRewardsList', () => {
name: 'Asset 1',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_INSURANCE,
amount: '123',
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '100',
},
],
totalAmount: '223',
},
{
assetId: '2',
name: 'Asset 2',
rewards: [
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
amount: '17.9873',
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
amount: '123',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_FEES_LIQUIDITY,
amount: '1',
amount: '0',
},
{
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
amount: '0',
},
],
totalAmount: '18.9873',
totalAmount: '223',
},
],
},
@@ -245,10 +226,30 @@ 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,6 +3,8 @@ 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;
@@ -18,11 +20,16 @@ export interface AggregatedEpochRewardSummary {
totalAmount: string;
}
export interface AggregatedEpochSummary {
export interface EpochTotalSummary {
epoch: EpochRewardSummaryFieldsFragment['epoch'];
assetRewards: AggregatedEpochRewardSummary[];
}
const emptyRowAccountTypes = Object.keys(RowAccountTypes).map((type) => ({
rewardType: type as AccountType,
amount: '0',
}));
export const generateEpochTotalRewardsList = (
epochData: EpochAssetsRewardsQuery | undefined
) => {
@@ -58,7 +65,7 @@ export const generateEpochTotalRewardsList = (
}, [] as EpochSummaryWithNamedReward[][]);
// Now aggregate the array of arrays of epoch summaries by asset rewards.
const aggregatedEpochSummaries: AggregatedEpochSummary[] =
const epochTotalRewards: EpochTotalSummary[] =
aggregatedEpochSummariesByEpochNumber.map((epochSummaries) => {
const assetRewards = epochSummaries.reduce((acc, epochSummary) => {
const assetRewardIndex = acc.findIndex(
@@ -72,18 +79,36 @@ export const generateEpochTotalRewardsList = (
assetId: epochSummary.assetId,
name: epochSummary.name,
rewards: [
{
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
},
...emptyRowAccountTypes.map((emptyRowAccountType) => {
if (
emptyRowAccountType.rewardType === epochSummary.rewardType
) {
return {
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
};
} else {
return emptyRowAccountType;
}
}),
],
totalAmount: epochSummary.amount,
});
} else {
acc[assetRewardIndex].rewards.push({
rewardType: epochSummary.rewardType,
amount: epochSummary.amount,
});
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].totalAmount = (
Number(acc[assetRewardIndex].totalAmount) +
Number(epochSummary.amount)
@@ -99,5 +124,5 @@ export const generateEpochTotalRewardsList = (
};
});
return aggregatedEpochSummaries;
return epochTotalRewards;
};
@@ -1,34 +1,30 @@
// @ts-ignore No types available for duration-js
import Duration from 'duration-js';
import React, { useMemo, useState } from 'react';
import { 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, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { useVegaWallet } 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 { RewardInfo } from '../epoch-individual-awards/reward-info';
import { EpochRewards } from '../epoch-total-rewards/epoch-rewards';
import { EpochIndividualRewards } from '../epoch-individual-rewards/epoch-individual-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';
@@ -39,25 +35,21 @@ export const RewardsPage = () => {
const [toggleRewardsView, setToggleRewardsView] =
useState<RewardsView>('total');
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
openVegaWalletDialog: store.openVegaWalletDialog,
}));
const { appDispatch } = useAppState();
const {
params,
loading: paramsLoading,
error: paramsError,
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
const {
data: epochData,
loading: epochLoading,
error: epochError,
refetch,
} = useEpochQuery();
useRefreshAfterEpoch(epochData?.epoch.timestamps.expiry, refetch);
const {
params,
loading: paramsLoading,
error: paramsError,
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
const payoutDuration = useMemo(() => {
if (!params) {
return 0;
@@ -103,8 +95,8 @@ export const RewardsPage = () => {
</div>
) : null}
{epochData &&
epochData.epoch.id &&
{epochData?.epoch &&
epochData.epoch?.id &&
epochData.epoch.timestamps.start &&
epochData.epoch.timestamps.expiry && (
<section className="mb-16">
@@ -148,26 +140,13 @@ export const RewardsPage = () => {
</section>
{toggleRewardsView === 'total' ? (
<EpochRewards />
<EpochTotalRewards />
) : (
<section>
{pubKey && pubKeys?.length ? (
<RewardInfo />
<EpochIndividualRewards />
) : (
<div>
<Button
data-testid="connect-to-vega-wallet-btn"
onClick={() => {
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_OVERLAY,
isOpen: true,
});
openVegaWalletDialog();
}}
>
{t('connectVegaWallet')}
</Button>
</div>
<ConnectToSeeRewards />
)}
</section>
)}
@@ -0,0 +1,125 @@
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>
);
@@ -25,12 +25,38 @@ 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,
@@ -42,6 +68,34 @@ 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: [],
},
},
},
},
},
@@ -1,6 +1,6 @@
import { mockConnectWallet } from '@vegaprotocol/cypress';
before(() => {
beforeEach(() => {
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,7 +69,6 @@ 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,6 +1,6 @@
import { useMemo } from 'react';
import { t, useDataProvider } from '@vegaprotocol/react-helpers';
import { proposalsListDataProvider } from '@vegaprotocol/governance';
import { proposalsDataProvider } 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: proposalsListDataProvider,
dataProvider: proposalsDataProvider,
variables,
skipUpdates: true,
});
@@ -76,7 +76,7 @@ describe('AccountsTable', () => {
expect(rows.length).toBe(6);
});
it('should add first asset as pinned', async () => {
it('should not add first asset as pinned', async () => {
await act(async () => {
render(
<AccountTable
@@ -93,7 +93,7 @@ describe('AccountsTable', () => {
);
});
const rows = await screen.findAllByRole('row');
expect(rows.length).toBe(9);
expect(rows.length).toBe(6);
});
it('should get correct account data', () => {
+1 -1
View File
@@ -58,7 +58,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
};
}
}
return currentPinnedAssetRow;
return undefined;
}, [pinnedAssetId, props.pinnedAsset, props.rowData]);
return (
@@ -1,4 +1,4 @@
import { FormGroup, Input, NotificationError } from '@vegaprotocol/ui-toolkit';
import { FormGroup, Input, InputError } 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 (
<NotificationError testId="dealticket-error-message-size-limit">
<InputError testId="dealticket-error-message-size-limit">
{sizeError}
</NotificationError>
</InputError>
);
}
if (priceError) {
return (
<NotificationError testId="dealticket-error-message-price-limit">
<InputError testId="dealticket-error-message-price-limit">
{priceError}
</NotificationError>
</InputError>
);
}
@@ -4,7 +4,7 @@ import {
toDecimal,
validateAmount,
} from '@vegaprotocol/react-helpers';
import { Input, NotificationError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { Input, InputError, 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 && (
<NotificationError
<InputError
intent="danger"
testId="dealticket-error-message-size-market"
>
{sizeError}
</NotificationError>
</InputError>
)}
</div>
);
@@ -15,7 +15,7 @@ import { normalizeOrderSubmission } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet';
import {
ExternalLink,
NotificationError,
InputError,
Intent,
Notification,
} from '@vegaprotocol/ui-toolkit';
@@ -308,11 +308,11 @@ const SummaryMessage = memo(
if (isReadOnly) {
return (
<div className="mb-4">
<NotificationError testId="dealticket-error-message-summary">
<InputError testId="dealticket-error-message-summary">
{
'You need to connect your own wallet to start trading on this market'
}
</NotificationError>
</InputError>
</div>
);
}
@@ -353,9 +353,9 @@ const SummaryMessage = memo(
if (errorMessage) {
return (
<div className="mb-4">
<NotificationError testId="dealticket-error-message-summary">
<InputError testId="dealticket-error-message-summary">
{errorMessage}
</NotificationError>
</InputError>
</div>
);
}
@@ -1,4 +1,4 @@
import { FormGroup, Input, NotificationError } from '@vegaprotocol/ui-toolkit';
import { FormGroup, Input, InputError } 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 && (
<NotificationError testId="dealticket-error-message-expiry">
<InputError testId="dealticket-error-message-expiry">
{errorMessage}
</NotificationError>
</InputError>
)}
</FormGroup>
);
@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react';
import {
FormGroup,
NotificationError,
InputError,
Select,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
@@ -139,9 +139,9 @@ export const TimeInForceSelector = ({
))}
</Select>
{errorMessage && (
<NotificationError testId="dealticket-error-message-tif">
<InputError testId="dealticket-error-message-tif">
{renderError(errorMessage)}
</NotificationError>
</InputError>
)}
</FormGroup>
);
@@ -1,8 +1,4 @@
import {
FormGroup,
NotificationError,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { FormGroup, InputError, Tooltip } from '@vegaprotocol/ui-toolkit';
import { DataGrid, t } from '@vegaprotocol/react-helpers';
import * as Schema from '@vegaprotocol/types';
import { Toggle } from '@vegaprotocol/ui-toolkit';
@@ -82,9 +78,9 @@ export const TypeSelector = ({
onChange={(e) => onSelect(e.target.value as Schema.OrderType)}
/>
{errorMessage && (
<NotificationError testId="dealticket-error-message-type">
<InputError testId="dealticket-error-message-type">
{renderError(errorMessage as MarketModeValidationType)}
</NotificationError>
</InputError>
)}
</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?: '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 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 const DepositFieldsFragmentDoc = gql`
fragment DepositFields on Deposit {
@@ -1,15 +1,139 @@
fragment NewMarketFields on NewMarket {
instrument {
code
name
code
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 {
@@ -19,8 +143,93 @@ 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
@@ -34,6 +243,7 @@ fragment UpdateMarketFields on UpdateMarket {
scalingFactor
}
triggeringRatio
# auctionExtensionSecs
}
riskParameters {
__typename
@@ -58,6 +268,23 @@ 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
@@ -69,11 +296,26 @@ 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
@@ -86,19 +328,32 @@ 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 proposalsListDataProvider = makeDataProvider<
export const proposalsDataProvider = makeDataProvider<
ProposalsListQuery,
ProposalListFieldsFragment[],
never,
@@ -40,6 +40,21 @@ 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: '',
@@ -50,7 +65,33 @@ 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: {
@@ -92,10 +133,36 @@ 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',
@@ -104,8 +171,36 @@ 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',
@@ -136,10 +231,36 @@ 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',
@@ -148,8 +269,36 @@ 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',
@@ -180,10 +329,36 @@ 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',
@@ -192,8 +367,36 @@ 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',
@@ -224,10 +427,36 @@ 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',
@@ -236,8 +465,36 @@ 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',
@@ -268,10 +525,36 @@ 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',
@@ -280,8 +563,36 @@ 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',
@@ -312,10 +623,36 @@ 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',
@@ -324,8 +661,36 @@ 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',
@@ -356,10 +721,36 @@ 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)',
@@ -368,8 +759,36 @@ 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',
@@ -400,10 +819,36 @@ 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)',
@@ -412,8 +857,36 @@ 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',
@@ -444,10 +917,36 @@ 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)',
@@ -456,8 +955,36 @@ 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',
@@ -488,10 +1015,36 @@ 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)',
@@ -500,8 +1053,36 @@ 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',
@@ -532,10 +1113,36 @@ 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)',
@@ -544,8 +1151,36 @@ 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',
@@ -576,10 +1211,36 @@ 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)',
@@ -588,8 +1249,36 @@ 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',
@@ -620,10 +1309,36 @@ 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)',
@@ -632,8 +1347,36 @@ 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 UpdateNetworkParameterFields on Proposal {
fragment UpdateNetworkParameterProposal on Proposal {
id
state
datetime
@@ -20,22 +20,15 @@ fragment UpdateNetworkParameterFields on Proposal {
enactmentDatetime
change {
... on UpdateNetworkParameter {
networkParameter {
key
value
}
...UpdateNetworkParameterFiels
}
}
}
}
subscription OnUpdateNetworkParameters {
busEvents(types: [Proposal], batchSize: 0) {
event {
... on Proposal {
...UpdateNetworkParameterFields
}
}
proposals {
...UpdateNetworkParameterProposal
}
}
@@ -1,6 +1,7 @@
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 };
@@ -12,12 +13,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 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 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 OnUpdateNetworkParametersSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
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 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 ProposalOfMarketQueryVariables = Types.Exact<{
marketId: Types.Scalars['ID'];
@@ -35,8 +36,8 @@ export const ProposalEventFieldsFragmentDoc = gql`
errorDetails
}
`;
export const UpdateNetworkParameterFieldsFragmentDoc = gql`
fragment UpdateNetworkParameterFields on Proposal {
export const UpdateNetworkParameterProposalFragmentDoc = gql`
fragment UpdateNetworkParameterProposal on Proposal {
id
state
datetime
@@ -44,15 +45,12 @@ export const UpdateNetworkParameterFieldsFragmentDoc = gql`
enactmentDatetime
change {
... on UpdateNetworkParameter {
networkParameter {
key
value
}
...UpdateNetworkParameterFiels
}
}
}
}
`;
${UpdateNetworkParameterFielsFragmentDoc}`;
export const ProposalEventDocument = gql`
subscription ProposalEvent($partyId: ID!) {
proposals(partyId: $partyId) {
@@ -85,15 +83,11 @@ export type ProposalEventSubscriptionHookResult = ReturnType<typeof useProposalE
export type ProposalEventSubscriptionResult = Apollo.SubscriptionResult<ProposalEventSubscription>;
export const OnUpdateNetworkParametersDocument = gql`
subscription OnUpdateNetworkParameters {
busEvents(types: [Proposal], batchSize: 0) {
event {
... on Proposal {
...UpdateNetworkParameterFields
}
}
proposals {
...UpdateNetworkParameterProposal
}
}
${UpdateNetworkParameterFieldsFragmentDoc}`;
${UpdateNetworkParameterProposalFragmentDoc}`;
/**
* __useOnUpdateNetworkParametersSubscription__
@@ -7,13 +7,18 @@ 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 { UpdateNetworkParameterFieldsFragment } from './__generated__/Proposal';
import type { UpdateNetworkParameterProposalFragment } 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 = UpdateNetworkParameterFieldsFragment;
type Proposal = UpdateNetworkParameterProposalFragment;
const UpdateNetworkParameterToastContent = ({
proposal,
@@ -75,26 +80,16 @@ export const useUpdateNetworkParametersToasts = () => {
[remove]
);
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)));
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));
}
},
});
@@ -1,16 +1,19 @@
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 { useUpdateNetworkParametersToasts } from './use-update-network-paramaters-toasts';
import {
PROPOSAL_STATES_TO_TOAST,
useUpdateNetworkParametersToasts,
} from './use-update-network-paramaters-toasts';
import type {
UpdateNetworkParameterFieldsFragment,
UpdateNetworkParameterProposalFragment,
OnUpdateNetworkParametersSubscription,
} from './__generated__/Proposal';
import { OnUpdateNetworkParametersDocument } from './__generated__/Proposal';
import { useToasts } from '@vegaprotocol/ui-toolkit';
import { waitFor } from '@testing-library/react';
import { waitFor, renderHook } from '@testing-library/react';
const render = (mocks?: MockedResponse[]) => {
const wrapper = ({ children }: { children: ReactNode }) => (
@@ -23,7 +26,7 @@ const generateUpdateNetworkParametersProposal = (
key: string,
value: string,
state: ProposalState = ProposalState.STATE_OPEN
): UpdateNetworkParameterFieldsFragment => ({
): UpdateNetworkParameterProposalFragment => ({
__typename: 'Proposal',
id: Math.random().toString(),
datetime: Math.random().toString(),
@@ -42,56 +45,6 @@ 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 = () => {
@@ -102,23 +55,113 @@ describe('useUpdateNetworkParametersToasts', () => {
beforeEach(clear);
afterAll(clear);
it('returns toast for update network parameters bus event', async () => {
render([mockedEvent]);
await waitFor(() => {
expect(useToasts.getState().count).toBe(1);
});
});
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('does not return toast for empty event', async () => {
render([mockedEmptyEvent]);
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);
await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(useToasts.getState().count).toBe(0);
});
});
it('does not return toast for wrong event', async () => {
render([mockedWrongEvent]);
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);
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,30 +14,18 @@ import {
useUpdateProposal,
} from './use-update-proposal';
type Proposal = Pick<ProposalListFieldsFragment, 'terms'> &
Pick<ProposalListFieldsFragment, 'state'> &
Pick<ProposalListFieldsFragment, 'id'>;
const generateUpdateAssetProposal = (
id: string,
quantum = '',
lifetimeLimit = '',
withdrawThreshold = ''
): ProposalListFieldsFragment => ({
reference: '',
): Proposal => ({
id,
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: '',
@@ -120,25 +108,8 @@ const generateUpdateMarketProposal = (
riskParametersType:
| 'UpdateMarketLogNormalRiskModel'
| 'UpdateMarketSimpleRiskModel' = 'UpdateMarketLogNormalRiskModel'
): ProposalListFieldsFragment => ({
reference: '',
): Proposal => ({
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: '',
@@ -155,6 +126,28 @@ 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,
@@ -195,7 +188,7 @@ const generateUpdateMarketProposal = (
});
const mockDataProviderData: {
data: ProposalListFieldsFragment[];
data: Proposal[];
error: Error | undefined;
loading: boolean;
} = {
@@ -1,4 +1,4 @@
import { proposalsListDataProvider } from '..';
import { proposalsDataProvider } 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: proposalsListDataProvider,
dataProvider: proposalsDataProvider,
variables,
});
@@ -189,7 +189,7 @@ const fieldGetters = {
};
export const isChangeProposed = (
proposal: ProposalListFieldsFragment | undefined,
proposal: Pick<ProposalListFieldsFragment, 'terms'>,
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 { proposalsListDataProvider } from '../proposals-data-provider';
import { proposalsDataProvider } from '../proposals-data-provider';
import { useCallback, useMemo, useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useColumnDefs } from './use-column-defs';
@@ -30,7 +30,7 @@ export const ProposalsList = () => {
};
}, []);
const { data, loading, error, reload } = useDataProvider({
dataProvider: proposalsListDataProvider,
dataProvider: proposalsDataProvider,
variables,
});
const filteredData = getNewMarketProposals(data || []);
@@ -14,18 +14,9 @@ import type {
} from '@vegaprotocol/ui-toolkit';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ProposalStateMapping } from '@vegaprotocol/types';
import type {
ProposalListFieldsFragment,
NewMarketFieldsFragment,
} from '../proposals-data-provider/__generated__/Proposals';
import type { ProposalListFieldsFragment } 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([
@@ -53,7 +44,7 @@ export const useColumnDefs = () => {
'terms.change.instrument.code'
>) => {
const { change } = data?.terms || {};
if (instrumentGuard(change) && VEGA_TOKEN_URL) {
if (change?.__typename === 'NewMarket' && VEGA_TOKEN_URL) {
if (data?.id) {
const link = `${VEGA_TOKEN_URL}/proposals/${data.id}`;
return (
+67 -53
View File
@@ -344,58 +344,12 @@ export type BusEvent = {
/** Event types */
export enum BusEventType {
/** An account has been updated */
Account = 'Account',
/** An asset has been created or update */
Asset = 'Asset',
/** A market has either entered or exited auction */
Auction = 'Auction',
/** Collateral has deposited in to this Vega network via the bridge */
Deposit = 'Deposit',
/** A liquidity commitment change occurred */
LiquidityProvision = 'LiquidityProvision',
/** A position has been closed without sufficient insurance pool balance to cover it */
LossSocialization = 'LossSocialization',
/** Margin levels have changed for a position */
MarginLevels = 'MarginLevels',
/** Constant for market events - mainly used for logging */
Market = 'Market',
/** A new market has been created */
MarketCreated = 'MarketCreated',
/** Market data has been updated */
MarketData = 'MarketData',
/** A market has progressed by one tick */
MarketTick = 'MarketTick',
/** A market has been updated */
MarketUpdated = 'MarketUpdated',
/** Validator node signatures for an event */
NodeSignature = 'NodeSignature',
/** An oracle spec has been registered */
OracleSpec = 'OracleSpec',
/** An order has been created or updated */
Order = 'Order',
/** A party has been updated */
Party = 'Party',
/** A position resolution event has occurred */
PositionResolution = 'PositionResolution',
/** A governance proposal has been created or updated */
Proposal = 'Proposal',
/** A risk factor adjustment was made */
RiskFactor = 'RiskFactor',
/** A distressed position has been settled */
SettleDistressed = 'SettleDistressed',
/** A position has been settled */
SettlePosition = 'SettlePosition',
/** Vega Time has changed */
TimeUpdate = 'TimeUpdate',
/** A trade has been created */
Trade = 'Trade',
/** The results from processing at transaction */
TransactionResult = 'TransactionResult',
/** A balance has been transferred between accounts */
TransferResponses = 'TransferResponses',
/** A vote has been placed on a governance proposal */
Vote = 'Vote',
/** Collateral has been withdrawn from this Vega network via the bridge */
Withdrawal = 'Withdrawal'
}
@@ -1014,7 +968,7 @@ export type EthereumKeyRotationsConnection = {
};
/** Union type for wrapped events in stream PROPOSAL is mapped to governance data, something to keep in mind */
export type Event = AccountEvent | Asset | AuctionEvent | Deposit | LiquidityProvision | LossSocialization | MarginLevels | Market | MarketData | MarketEvent | MarketTick | NodeSignature | OracleSpec | Order | Party | PositionResolution | Proposal | RiskFactor | SettleDistressed | SettlePosition | TimeUpdate | Trade | TransactionResult | TransferResponses | Vote | Withdrawal;
export type Event = Deposit | TimeUpdate | TransactionResult | Withdrawal;
export type ExternalData = {
__typename?: 'ExternalData';
@@ -1100,8 +1054,6 @@ export type FutureProduct = {
/** A segment of data node history */
export type HistorySegment = {
__typename?: 'HistorySegment';
/** Chain ID of the history segment */
chainID: Scalars['String'];
/** From block height of the history segment */
fromHeight: Scalars['Int'];
/** ID of the history segment */
@@ -1225,6 +1177,8 @@ export type LedgerEntryFilter = {
/** Configuration of a market liquidity monitoring parameters */
export type LiquidityMonitoringParameters = {
__typename?: 'LiquidityMonitoringParameters';
/** Specifies by how many seconds an auction should be extended if leaving the auction were to trigger a liquidity auction */
auctionExtensionSecs: Scalars['Int'];
/** Specifies parameters related to target stake calculation */
targetStakeParameters: TargetStakeParameters;
/** Specifies the triggering ratio for entering liquidity auction */
@@ -1489,6 +1443,8 @@ export type Market = {
fees: Fees;
/** Market ID */
id: Scalars['ID'];
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
linearSlippageFactor: Scalars['String'];
/** Liquidity monitoring parameters for the market */
liquidityMonitoringParameters: LiquidityMonitoringParameters;
/** The list of the liquidity provision commitments for this market */
@@ -1515,6 +1471,8 @@ export type Market = {
priceMonitoringSettings: PriceMonitoringSettings;
/** The proposal that initiated this market */
proposal?: Maybe<Proposal>;
/** Quadratic slippage factor is used to cap the slippage component of maintainence margin - it is applied to the square of the slippage volume */
quadraticSlippageFactor: Scalars['String'];
/** Risk factors for the market */
riskFactors?: Maybe<RiskFactor>;
/** Current state of the market */
@@ -1870,10 +1828,20 @@ export type NewMarket = {
decimalPlaces: Scalars['Int'];
/** New market instrument configuration */
instrument: InstrumentConfiguration;
/** Linear slippage factor is used to cap the slippage component of maintainence margin - it is applied to the slippage volume */
linearSlippageFactor: Scalars['String'];
/** Liquidity monitoring parameters */
liquidityMonitoringParameters: LiquidityMonitoringParameters;
/** Liquidity Provision order price range */
lpPriceRange: Scalars['String'];
/** Metadata for this instrument, tags */
metadata?: Maybe<Array<Scalars['String']>>;
/** Decimal places for order sizes, sets what size the smallest order / position on the market can be */
positionDecimalPlaces: Scalars['Int'];
/** Price monitoring parameters */
priceMonitoringParameters: PriceMonitoringParameters;
/** Quadratic slippage factor is used to cap the slippage component of maintainence margin - it is applied to the square of the slippage volume */
quadraticSlippageFactor: Scalars['String'];
/** New market risk configuration */
riskParameters: RiskModel;
};
@@ -2519,7 +2487,10 @@ export type Party = {
marginsConnection?: Maybe<MarginConnection>;
/** Orders relating to a party */
ordersConnection?: Maybe<OrderConnection>;
/** Trading positions relating to a party */
/**
* Trading positions relating to a party
* @deprecated Use root positions query instead of sub-query
*/
positionsConnection?: Maybe<PositionConnection>;
/** All governance proposals in the Vega network */
proposalsConnection?: Maybe<ProposalsConnection>;
@@ -2610,7 +2581,9 @@ export type PartyrewardSummariesArgs = {
/** Represents a party on Vega, could be an ethereum wallet address in the future */
export type PartyrewardsConnectionArgs = {
assetId?: InputMaybe<Scalars['ID']>;
fromEpoch?: InputMaybe<Scalars['Int']>;
pagination?: InputMaybe<Pagination>;
toEpoch?: InputMaybe<Scalars['Int']>;
};
@@ -2707,6 +2680,8 @@ export type Position = {
__typename?: 'Position';
/** Average entry price for this position */
averageEntryPrice: Scalars['String'];
/** The total amount of profit and loss that was not transferred due to loss socialisation */
lossSocializationAmount: Scalars['String'];
/** Margins of the party for the given position */
marginsConnection?: Maybe<MarginConnection>;
/** Market relating to this position */
@@ -2715,6 +2690,8 @@ export type Position = {
openVolume: Scalars['String'];
/** The party holding this position */
party: Party;
/** Enum set if the position was closed out or orders were removed because party was distressed */
positionStatus: PositionStatus;
/** Realised Profit and Loss (int64) */
realisedPNL: Scalars['String'];
/** Unrealised Profit and Loss (int64) */
@@ -2764,6 +2741,16 @@ export type PositionResolution = {
marketId: Scalars['ID'];
};
/** Position status can change if a position is distressed */
export enum PositionStatus {
/** The position was distressed, and had to be closed out entirely - orders were removed from the book, and the open volume was closed out by the network */
POSITION_STATUS_CLOSED_OUT = 'POSITION_STATUS_CLOSED_OUT',
/** The position was distressed, but removing open orders from the book brought the margin level back to a point where the open position could be maintained */
POSITION_STATUS_ORDERS_CLOSED = 'POSITION_STATUS_ORDERS_CLOSED',
/** The position is either healthy, or if closed out, was closed out normally */
POSITION_STATUS_UNSPECIFIED = 'POSITION_STATUS_UNSPECIFIED'
}
/**
* An individual party at any point in time is considered net long or net short. This refers to their Open Volume,
* calculated using FIFO. This volume is signed as either negative for LONG positions and positive for SHORT positions. A
@@ -2788,6 +2775,12 @@ export type PositionUpdate = {
updatedAt?: Maybe<Scalars['Timestamp']>;
};
/** Filter to apply to the positions connection query */
export type PositionsFilter = {
marketIds?: InputMaybe<Array<Scalars['ID']>>;
partyIds?: InputMaybe<Array<Scalars['ID']>>;
};
/** Represents a price on either the buy or sell side and all the orders at that price */
export type PriceLevel = {
__typename?: 'PriceLevel';
@@ -2826,7 +2819,7 @@ export type PriceMonitoringSettings = {
parameters?: Maybe<PriceMonitoringParameters>;
};
/** PriceMonitoringParameters holds together price projection horizon τ, probability level p, and auction extension duration */
/** PriceMonitoringTrigger holds together price projection horizon τ, probability level p, and auction extension duration */
export type PriceMonitoringTrigger = {
__typename?: 'PriceMonitoringTrigger';
/**
@@ -3281,6 +3274,8 @@ export type Query = {
partiesConnection?: Maybe<PartyConnection>;
/** An entity that is trading on the Vega network */
party?: Maybe<Party>;
/** Fetch all positions */
positions?: Maybe<PositionConnection>;
/** A governance proposal located by either its ID or reference. If both are set, ID is used. */
proposal?: Maybe<Proposal>;
/** All governance proposals in the Vega network */
@@ -3348,9 +3343,8 @@ export type QueryepochArgs = {
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QueryepochRewardSummariesArgs = {
fromEpoch?: InputMaybe<Scalars['Int']>;
filter?: InputMaybe<RewardSummaryFilter>;
pagination?: InputMaybe<Pagination>;
toEpoch?: InputMaybe<Scalars['Int']>;
};
@@ -3453,6 +3447,7 @@ export type QuerymarketArgs = {
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerymarketsConnectionArgs = {
id?: InputMaybe<Scalars['ID']>;
includeSettled?: InputMaybe<Scalars['Boolean']>;
pagination?: InputMaybe<Pagination>;
};
@@ -3546,6 +3541,13 @@ export type QuerypartyArgs = {
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QuerypositionsArgs = {
filter?: InputMaybe<PositionsFilter>;
pagination?: InputMaybe<Pagination>;
};
/** Queries allow a caller to read data and filter data via GraphQL. */
export type QueryproposalArgs = {
id?: InputMaybe<Scalars['ID']>;
@@ -3698,6 +3700,14 @@ export type RewardSummaryEdge = {
node: RewardSummary;
};
/** Filter for historical reward summary queries */
export type RewardSummaryFilter = {
assetIds?: InputMaybe<Array<Scalars['ID']>>;
fromEpoch?: InputMaybe<Scalars['Int']>;
marketIds?: InputMaybe<Array<Scalars['ID']>>;
toEpoch?: InputMaybe<Scalars['Int']>;
};
/** Connection type for retrieving cursor-based paginated rewards information */
export type RewardsConnection = {
__typename?: 'RewardsConnection';
@@ -3894,6 +3904,10 @@ export type Statistics = {
chainVersion: Scalars['String'];
/** RFC3339Nano current time (real) */
currentTime: Scalars['Timestamp'];
/** Total number of events on the last block */
eventCount: Scalars['String'];
/** The number of events per second on the last block */
eventsPerSecond: Scalars['String'];
/** RFC3339Nano genesis time of the chain */
genesisTime: Scalars['Timestamp'];
/** Number of orders per seconds */
@@ -48,7 +48,8 @@ export const Checkbox = ({
) : (
<Icon
name="tick"
className="relative w-[10px] h-[10px] text-white dark:text-black"
size={3}
className="relative text-white dark:text-black"
/>
)}
</CheckboxPrimitive.CheckboxIndicator>
@@ -1,52 +1,18 @@
import classNames from 'classnames';
import type { HTMLAttributes } from 'react';
import { Intent } from '../../utils/intent';
import { Notification } from '../notification';
interface InputErrorProps extends HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
intent?: 'danger' | 'warning';
forInput?: string;
}
interface NotificationErrorProps extends HTMLAttributes<HTMLDivElement> {
children?: React.ReactNode;
intent?: Intent | 'danger' | 'warning';
forInput?: string;
testId?: string;
}
const getIntent = (intent: Intent | 'danger' | 'warning') => {
switch (intent) {
case 'danger':
return Intent.Danger;
case 'warning':
return Intent.Warning;
default:
return intent;
}
};
export const NotificationError = ({
intent = Intent.Danger,
children,
forInput,
testId,
}: NotificationErrorProps) => {
return (
<Notification
intent={getIntent(intent)}
testId={testId || 'input-error-text'}
message={<div className="role">{children}</div>}
aria-describedby={forInput}
/>
);
};
export const InputError = ({
intent = 'danger',
children,
forInput,
testId,
...props
}: InputErrorProps) => {
const effectiveClassName = classNames(
@@ -63,7 +29,7 @@ export const InputError = ({
);
return (
<div
data-testid="input-error-text"
data-testid={testId || 'input-error-text'}
aria-describedby={forInput}
className={effectiveClassName}
{...props}
@@ -46,6 +46,7 @@ export const ExternalLink = ({ children, className, ...props }: LinkProps) => (
className={classNames(className, 'inline-flex items-baseline')}
target="_blank"
data-testid="external-link"
rel="noreferrer nofollow noopener"
{...props}
>
{typeof children === 'string' ? (
@@ -35,7 +35,7 @@ export const Toggle = ({
);
const radioClasses = classnames('sr-only', 'peer');
const buttonClasses = classnames(
'relative inline-block w-full text-center',
'relative inline-flex w-full h-full text-center items-center justify-center',
'peer-checked:rounded-full',
{
'peer-checked:bg-neutral-400 dark:peer-checked:bg-white dark:peer-checked:text-black':
@@ -76,7 +76,9 @@ export const Toggle = ({
}
className={radioClasses}
/>
<span className={buttonClasses}>{label}</span>
<span className={buttonClasses}>
<span>{label}</span>
</span>
</label>
);
})}
+3 -3
View File
@@ -10,7 +10,7 @@ export type TransactionEventSubscriptionVariables = Types.Exact<{
}>;
export type TransactionEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', type: Types.BusEventType, 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' } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult', partyId: string, hash: string, status: boolean, error?: string | null } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal' } }> | null };
export type TransactionEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', type: Types.BusEventType, event: { __typename?: 'Deposit' } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult', partyId: string, hash: string, status: boolean, error?: string | null } | { __typename?: 'Withdrawal' } }> | null };
export type WithdrawalBusEventFieldsFragment = { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null };
@@ -19,7 +19,7 @@ export type WithdrawalBusEventSubscriptionVariables = Types.Exact<{
}>;
export type WithdrawalBusEventSubscription = { __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' } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult' } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export type WithdrawalBusEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'Deposit' } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export type OrderTxUpdateFieldsFragment = { __typename?: 'OrderUpdate', type?: Types.OrderType | null, id: string, status: Types.OrderStatus, rejectionReason?: Types.OrderRejectionReason | null, createdAt: any, size: string, price: string, timeInForce: Types.OrderTimeInForce, expiresAt?: any | null, side: Types.Side, marketId: string };
@@ -37,7 +37,7 @@ export type DepositBusEventSubscriptionVariables = Types.Exact<{
}>;
export type DepositBusEventSubscription = { __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 type DepositBusEventSubscription = { __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 const TransactionEventFieldsFragmentDoc = gql`
fragment TransactionEventFields on TransactionResult {
@@ -7,7 +7,7 @@ export const ConnectDialogTitle = ({ children }: { children: ReactNode }) => {
return (
<h1
data-testid="wallet-dialog-title"
className="text-2xl uppercase mb-6 text-center"
className="text-2xl uppercase mb-6 text-center font-alpha"
>
{children}
</h1>
@@ -229,6 +229,7 @@ const ConnectorList = ({
</li>
)}
<li className="mb-4 last:mb-0">
<div className="my-4 text-center text-vega-dark-400">{t('OR')}</div>
<ConnectionOption
type="view"
text={t('View as vega user')}
@@ -264,6 +265,13 @@ const SelectedForm = ({
return (
<>
<ConnectDialogContent>
<button
onClick={reset}
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
data-testid="back-button"
>
<Icon name={'chevron-left'} ariaLabel="back" size={4} />
</button>
<ConnectDialogTitle>{t('Connect')}</ConnectDialogTitle>
<div className="mb-2">
<RestConnectorForm connector={connector} onConnect={onConnect} />
@@ -315,7 +323,11 @@ const SelectedForm = ({
return (
<>
<ConnectDialogContent>
<ViewConnectorForm connector={connector} onConnect={onConnect} />
<ViewConnectorForm
connector={connector}
onConnect={onConnect}
reset={reset}
/>
</ConnectDialogContent>
<ConnectDialogFooter />
</>
@@ -1,5 +1,11 @@
import { t } from '@vegaprotocol/react-helpers';
import { Button, FormGroup, Input, InputError } from '@vegaprotocol/ui-toolkit';
import {
Button,
FormGroup,
Icon,
Input,
InputError,
} from '@vegaprotocol/ui-toolkit';
import { useForm } from 'react-hook-form';
import type { ViewConnector } from '../connectors';
import { useVegaWallet } from '../use-vega-wallet';
@@ -11,11 +17,13 @@ interface FormFields {
interface RestConnectorFormProps {
connector: ViewConnector;
onConnect: (connector: ViewConnector) => void;
reset: () => void;
}
export function ViewConnectorForm({
connector,
onConnect,
reset,
}: RestConnectorFormProps) {
const { connect } = useVegaWallet();
const {
@@ -41,24 +49,46 @@ export function ViewConnectorForm({
}
return (
<form onSubmit={handleSubmit(onSubmit)} data-testid="view-connector-form">
<FormGroup label={t('Vega Pubkey')} labelFor="address">
<Input
{...register('address', {
required: t('Required'),
validate: validatePubkey,
})}
id="address"
data-testid="address"
type="text"
/>
{errors.address?.message && (
<InputError intent="danger">{errors.address.message}</InputError>
)}
</FormGroup>
<Button data-testid="connect" variant="primary" type="submit" fill={true}>
{t('Connect')}
</Button>
</form>
<>
<button
onClick={reset}
className="absolute p-2 top-0 left-0 md:top-2 md:left-2"
data-testid="back-button"
>
<Icon name={'chevron-left'} ariaLabel="back" size={4} />
</button>
<form onSubmit={handleSubmit(onSubmit)} data-testid="view-connector-form">
<h1 className="text-2xl uppercase mb-6 text-center font-alpha">
{t('VIEW AS VEGA USER')}
</h1>
<p className="mb-4">
{t(
'Browse from the perspective of another Vega user in read-only mode.'
)}
</p>
<FormGroup label={t('Vega Pubkey')} labelFor="address">
<Input
{...register('address', {
required: t('Required'),
validate: validatePubkey,
})}
id="address"
data-testid="address"
type="text"
/>
{errors.address?.message && (
<InputError intent="danger">{errors.address.message}</InputError>
)}
</FormGroup>
<Button
data-testid="connect"
variant="primary"
type="submit"
fill={true}
>
{t('Browse network')}
</Button>
</form>
</>
);
}
@@ -1,9 +1,8 @@
import { renderHook, act } from '@testing-library/react-hooks';
import { renderHook, waitFor } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { useVegaTransactionUpdater } from './use-vega-transaction-updater';
import waitForNextTick from 'flush-promises';
import {
OrderTxUpdateDocument,
TransactionEventDocument,
@@ -112,14 +111,13 @@ const mockedTransactionResultBusEvent: MockedResponse<TransactionEventSubscripti
data: {
busEvents: [
{
type: BusEventType.Order,
type: BusEventType.TransactionResult,
event: transactionResultBusEvent,
},
],
},
},
};
const withdrawalBusEvent: WithdrawalBusEventFieldsFragment = {
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
status: WithdrawalStatus.STATUS_OPEN,
@@ -167,20 +165,16 @@ const mockedWithdrawalBusEvent: MockedResponse<WithdrawalBusEventSubscription> =
describe('useVegaTransactionManager', () => {
it('updates order on OrderTxUpdate', async () => {
mockTransactionStoreState.mockReturnValue(defaultState);
const { waitForNextUpdate } = render([mockedOrderUpdate]);
await act(async () => {
waitForNextUpdate();
await waitForNextTick();
render([mockedOrderUpdate]);
await waitFor(() => {
expect(updateOrder).toHaveBeenCalledWith(orderUpdate);
});
});
it('updates transaction on TransactionResultBusEvents', async () => {
mockTransactionStoreState.mockReturnValue(defaultState);
const { waitForNextUpdate } = render([mockedTransactionResultBusEvent]);
await act(async () => {
waitForNextUpdate();
await waitForNextTick();
render([mockedTransactionResultBusEvent]);
await waitFor(() => {
expect(updateTransactionResult).toHaveBeenCalledWith(
transactionResultBusEvent
);
@@ -193,10 +187,8 @@ describe('useVegaTransactionManager', () => {
mockWaitForWithdrawalApproval.mockResolvedValueOnce(
erc20WithdrawalApproval
);
const { waitForNextUpdate } = render([mockedWithdrawalBusEvent]);
await act(async () => {
waitForNextUpdate();
await waitForNextTick();
render([mockedWithdrawalBusEvent]);
await waitFor(() => {
expect(updateWithdrawal).toHaveBeenCalledWith(
withdrawalBusEvent,
erc20WithdrawalApproval
@@ -38,9 +38,9 @@ export const useVegaTransactionUpdater = () => {
result.data?.busEvents?.forEach((event) => {
if (event.event.__typename === 'Withdrawal') {
const withdrawal = event.event;
waitForWithdrawalApproval(withdrawal.id, client).then((approval) =>
updateWithdrawal(withdrawal, approval)
);
waitForWithdrawalApproval(withdrawal.id, client).then((approval) => {
updateWithdrawal(withdrawal, approval);
});
}
}),
});
@@ -1,9 +1,8 @@
import { renderHook } from '@testing-library/react-hooks';
import { renderHook, waitFor } from '@testing-library/react';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { ReactNode } from 'react';
import { useEthTransactionUpdater } from './use-ethereum-transaction-updater';
import waitForNextTick from 'flush-promises';
import {
DepositBusEventDocument,
VegaWalletContext,
@@ -77,9 +76,9 @@ const mockedDepositBusEvent: MockedResponse<DepositBusEventSubscription> = {
describe('useEthTransactionUpdater', () => {
it('updates deposit on DepositBusEvents', async () => {
mockTransactionStoreState.mockReturnValue(defaultState);
const { waitForNextUpdate } = render([mockedDepositBusEvent]);
waitForNextUpdate();
await waitForNextTick();
expect(updateDeposit).toHaveBeenCalledWith(depositBusEvent);
render([mockedDepositBusEvent]);
await waitFor(() => {
expect(updateDeposit).toHaveBeenCalledWith(depositBusEvent);
});
});
});
+1 -1
View File
@@ -19,7 +19,7 @@ export type WithdrawalEventSubscriptionVariables = Types.Exact<{
}>;
export type WithdrawalEventSubscription = { __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' } | { __typename?: 'RiskFactor' } | { __typename?: 'SettleDistressed' } | { __typename?: 'SettlePosition' } | { __typename?: 'TimeUpdate' } | { __typename?: 'Trade' } | { __typename?: 'TransactionResult' } | { __typename?: 'TransferResponses' } | { __typename?: 'Vote' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export type WithdrawalEventSubscription = { __typename?: 'Subscription', busEvents?: Array<{ __typename?: 'BusEvent', event: { __typename?: 'Deposit' } | { __typename?: 'TimeUpdate' } | { __typename?: 'TransactionResult' } | { __typename?: 'Withdrawal', id: string, status: Types.WithdrawalStatus, amount: string, createdTimestamp: any, withdrawnTimestamp?: any | null, txHash?: string | null, pendingOnForeignChain: boolean, asset: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, status: Types.AssetStatus, source: { __typename?: 'BuiltinAsset' } | { __typename?: 'ERC20', contractAddress: string } }, details?: { __typename?: 'Erc20WithdrawalDetails', receiverAddress: string } | null } }> | null };
export const PendingWithdrawalFragmentDoc = gql`
fragment PendingWithdrawal on Withdrawal {
-1
View File
@@ -128,7 +128,6 @@
"@svgr/webpack": "^5.4.0",
"@testing-library/jest-dom": "^5.16.2",
"@testing-library/react": "13.3.0",
"@testing-library/react-hooks": "^8.0.1",
"@testing-library/user-event": "^14.4.1",
"@types/classnames": "^2.3.1",
"@types/faker": "^5.5.8",
-15
View File
@@ -6364,14 +6364,6 @@
lodash "^4.17.15"
redent "^3.0.0"
"@testing-library/react-hooks@^8.0.1":
version "8.0.1"
resolved "https://registry.yarnpkg.com/@testing-library/react-hooks/-/react-hooks-8.0.1.tgz#0924bbd5b55e0c0c0502d1754657ada66947ca12"
integrity sha512-Aqhl2IVmLt8IovEVarNDFuJDVWVvhnr9/GCU6UUnrYXwgDFF9h2L2o2P9KBni1AST5sT6riAyoukFLyjQUgD/g==
dependencies:
"@babel/runtime" "^7.12.5"
react-error-boundary "^3.1.0"
"@testing-library/react@13.3.0":
version "13.3.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.3.0.tgz#bf298bfbc5589326bbcc8052b211f3bb097a97c5"
@@ -19130,13 +19122,6 @@ react-element-to-jsx-string@^14.3.4:
is-plain-object "5.0.0"
react-is "17.0.2"
react-error-boundary@^3.1.0:
version "3.1.4"
resolved "https://registry.yarnpkg.com/react-error-boundary/-/react-error-boundary-3.1.4.tgz#255db92b23197108757a888b01e5b729919abde0"
integrity sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==
dependencies:
"@babel/runtime" "^7.12.5"
react-hook-form@^7.27.0:
version "7.37.0"
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.37.0.tgz#4d1738f092d3d8a3ade34ee892d97350b1032b19"