Compare commits

..
Author SHA1 Message Date
sam-keen 0530019633 feat(3835) avoid showing untitled proposals 2023-05-19 14:52:41 +01:00
177 changed files with 150675 additions and 4087 deletions
+16 -17
View File
@@ -98,37 +98,36 @@ jobs:
# See affected apps
- name: See affected apps
run: |
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
echo ">>>> debug"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo "Affected: ${affected}"
echo "Branch slug: ${branch_slug}"
echo ">>>> eof debug"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
projects_e2e=""
preview_governance="not deployed"
preview_trading="not deployed"
preview_explorer="not deployed"
if [[ $affected == *"governance"* ]]; then
projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if [[ $affected == *"trading"* ]]; then
projects_e2e+='"trading-e2e" '
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if [[ $affected == *"explorer"* ]]; then
projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
if [[ -z "$projects_e2e" ]]; then
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
else
if [[ $affected == *"governance"* ]]; then
projects_e2e+='"governance-e2e" '
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
fi
if [[ $affected == *"trading"* ]]; then
projects_e2e+='"trading-e2e" '
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
fi
if [[ $affected == *"explorer"* ]]; then
projects_e2e+='"explorer-e2e" '
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
fi
fi
projects_e2e=${projects_e2e%?}
projects_e2e=[${projects_e2e// /,}]
+32
View File
@@ -0,0 +1,32 @@
name: Generate tranches
on:
schedule:
- cron: '0 */6 * * *'
jobs:
master:
name: Generate Queries
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
with:
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
- name: Install root dependencies
run: yarn install
- name: Generate queries
run: node ./scripts/get-tranches.js
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: 'chore: update tranches'
commit_options: '--no-verify --signoff'
skip_fetch: true
skip_checkout: true
+3 -3
View File
@@ -3,15 +3,15 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_TOKEN_URL=https://stagnet1.governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_GOVERNANCE_URL=https://governance.stagnet1.vega.rocks
NX_VEGA_GOVERNANCE_URL=https://stagnet1.governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.jsoo
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
-2
View File
@@ -1,7 +1,6 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_SENTRY_DSN=https://b3a56b03eda842faad731f3ea9dfd1bc@o286262.ingest.sentry.io/6242427
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
@@ -10,4 +9,3 @@ NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
NX_VEGA_CONSOLE_URL=https://vega.trading
-1
View File
@@ -10,4 +10,3 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://governance.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
@@ -7,9 +7,8 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
NX_VEGA_GOVERNANCE_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks/
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz/
@@ -4,11 +4,9 @@ import {
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { Link, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { ENV } from '../../config/env';
import { Routes } from '../../routes/route-names';
import { Link as RouteLink } from 'react-router-dom';
export const Footer = () => {
const { VEGA_URL, GIT_COMMIT_HASH, GIT_ORIGIN_URL } = useEnvironment();
@@ -23,7 +21,7 @@ export const Footer = () => {
);
return (
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
<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-vega-light-200 dark:border-vega-dark-200">
<div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
@@ -58,16 +56,11 @@ export const Footer = () => {
</div>
) : null}
</div>
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
Disclaimer
</RouteLink>
</div>
</footer>
);
};
export const NodeUrl = ({ url }: { url: string }) => {
const NodeUrl = ({ url }: { url: string }) => {
// get base url from api url, api sub domain
const urlObj = new URL(url);
const nodeUrl = urlObj.origin.replace(/^[^.]+\./g, '');
@@ -84,17 +84,15 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
<AgGridColumn
colId="asset"
headerName={t('Settlement asset')}
field="tradableInstrument.instrument.product.settlementAsset.symbol"
field="tradableInstrument.instrument.product.settlementAsset"
hide={window.innerWidth <= BREAKPOINT_MD}
cellRenderer={({
data,
value,
}: VegaICellRendererParams<
MarketFieldsFragment,
'tradableInstrument.instrument.product.settlementAsset.symbol'
>) => {
const value =
data?.tradableInstrument.instrument.product.settlementAsset;
return value ? (
'tradableInstrument.instrument.product.settlementAsset'
>) =>
value ? (
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value.id, e.target as HTMLElement);
@@ -104,8 +102,8 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
</ButtonLink>
) : (
''
);
}}
)
}
/>
<AgGridColumn
flex={2}
@@ -1,13 +1,12 @@
import { render } from '@testing-library/react';
import { OracleDetailsType, isInternalSourceType } from './oracle-details-type';
import type { SourceType } from './oracle';
import { PropertyKeyType } from '@vegaprotocol/types';
import { OracleDetailsType } from './oracle-details-type';
import type { SourceTypeName } from './oracle-details-type';
function renderComponent(type: SourceType) {
return <OracleDetailsType sourceType={type} />;
function renderComponent(type: SourceTypeName) {
return <OracleDetailsType type={type} />;
}
function renderWrappedComponent(type: SourceType) {
function renderWrappedComponent(type: SourceTypeName) {
return (
<table>
<tbody>{renderComponent(type)}</tbody>
@@ -15,53 +14,19 @@ function renderWrappedComponent(type: SourceType) {
);
}
function mock(name: string): SourceType {
return {
sourceType: {
filters: [
{
__typename: 'Filter',
key: {
name,
type: PropertyKeyType.TYPE_STRING,
},
},
],
},
};
}
describe('Oracle type view', () => {
it('Renders nothing when type is null', () => {
const res = render(renderComponent(null as unknown as SourceType));
const res = render(renderComponent(null as unknown as SourceTypeName));
expect(res.container).toBeEmptyDOMElement();
});
it('Renders Internal time for internal sources - timestamp', () => {
const s = mock('vegaprotocol.builtin.timestamp');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders Internal time for internal sources - potential future types', () => {
const s = mock('vegaprotocol.builtin.boolean');
expect(isInternalSourceType(s)).toEqual(true);
const res = render(renderWrappedComponent(s));
expect(res.getByText('Internal data')).toBeInTheDocument();
});
it('Renders External data otherwise - prices.external.whatever', () => {
const s = mock('prices.external.whatever');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
expect(res.getByText('External data')).toBeInTheDocument();
it('Renders Internal time for internal sources', () => {
const res = render(renderWrappedComponent('DataSourceDefinitionInternal'));
expect(res.getByText('Internal time')).toBeInTheDocument();
});
it('Renders External data otherwise', () => {
const s = mock('prices.external.vegaprotocol.builtin.');
expect(isInternalSourceType(s)).toEqual(false);
const res = render(renderWrappedComponent(s));
const res = render(renderWrappedComponent('DataSourceDefinitionExternal'));
expect(res.getByText('External data')).toBeInTheDocument();
});
});
@@ -1,51 +1,27 @@
import { TableRow, TableCell, TableHeader } from '../../../components/table';
import type { SourceType } from './oracle';
/**
* Basic function to determine if a source is internal or external.
*
* This should be distinguishable using __typename, but the type is incorrectly
* reported at the moment, so instead we check the filters.
*
* @param s SourceType
* @returns boolean True if the source is internal
*/
export function isInternalSourceType(s: SourceType) {
if ('filters' in s.sourceType) {
const filters = s.sourceType.filters;
if (filters) {
return (
filters?.filter((f) => {
return f.key.name?.indexOf('vegaprotocol.builtin.') === 0;
}).length > 0
);
}
}
return false;
}
export type SourceTypeName = SourceType['__typename'] | undefined;
interface OracleDetailsTypeProps {
sourceType: SourceType;
type: SourceTypeName;
}
/**
* Renders a a single table row for the Oracle Details view that shows
* if the oracle is using the internal time oracle or external data
*/
export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
if (!sourceType) {
export function OracleDetailsType({ type }: OracleDetailsTypeProps) {
if (!type) {
return null;
}
const isInternal = isInternalSourceType(sourceType);
return (
<TableRow modifier="bordered">
<TableHeader scope="row">Type</TableHeader>
<TableCell modifier="bordered">
{isInternal ? 'Internal data' : 'External data'}
{type === 'DataSourceDefinitionInternal'
? 'Internal time'
: 'External data'}
</TableCell>
</TableRow>
);
@@ -53,7 +53,7 @@ export const OracleDetails = ({
<OracleLink id={id} />
</TableCell>
</TableRow>
<OracleDetailsType sourceType={sourceType} />
<OracleDetailsType type={sourceType.__typename} />
<OracleSigners sourceType={sourceType} />
<OracleMarkets id={id} />
<TableRow modifier="bordered">
@@ -1,40 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../components/route-title';
export const Disclaimer = () => {
return (
<section>
<div className="px-40 max-sm:px-0 max-md:px-10 mb-4 max-w-5xl">
<RouteTitle data-testid="disclaimer-header">
{t('Disclaimer')}
</RouteTitle>
<p className="mt-3">
The Vega Block Explorer is an application that allows users to, among
other things, browse through blocks, view wallet addresses, network
hashrate, transaction data and other key information on the Vega
blockchain. It is free, public and open source software. Software
upgrades may contain bugs or security vulnerabilities that might
result in loss of functionality.
</p>
<p className="mt-3">
The Vega Block Explorer uses data from nodes on the Vega Blockchain.
The developers of the Vega Block Explorer do not operate or run the
Vega Blockchain or any other blockchain.
</p>
<p className="mt-3 font-semibold">
The Vega Block Explorer is provided as is. The developers of the
Vega Block Explorer make no representations or warranties of any kind,
whether express or implied, statutory or otherwise regarding the Vega
Block Explorer. They disclaim all warranties of merchantability,
quality, fitness for purpose. They disclaim all warranties that the
Vega Block Explorer is free of harmful components or errors.
</p>
<p className="mt-3 font-bold">
No developer of the Vega Block Explorer accepts any responsibility
for, or liability to users in connection with their use of the Vega
Block Explorer.
</p>
</div>
</section>
);
};
@@ -10,5 +10,4 @@ export const Routes = {
MARKETS: 'markets',
ORACLES: 'oracles',
NETWORK_PARAMETERS: 'network-parameters',
DISCLAIMER: 'disclaimer',
};
@@ -29,7 +29,6 @@ import { AssetLink, MarketLink } from '../components/links';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
import { Disclaimer } from './pages/disclaimer';
export type Navigable = {
path: string;
@@ -336,17 +335,6 @@ export const routerConfig: Route[] = [
},
],
},
{
path: Routes.DISCLAIMER,
element: <Disclaimer />,
handle: {
name: t('Disclaimer'),
text: t('Disclaimer'),
breadcrumb: () => (
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
),
},
},
...partiesRoutes,
...assetsRoutes,
...genesisRoutes,
@@ -1,113 +0,0 @@
{
"rationale": {
"title": "New Market Proposal E2E submission",
"description": "E2E new market proposal"
},
"terms": {
"newMarket": {
"changes": {
"decimalPlaces": "5",
"positionDecimalPlaces": "5",
"linearSlippageFactor": "0.001",
"quadraticSlippageFactor": "0",
"lpPriceRange": "10",
"instrument": {
"name": "Token test market",
"code": "TEST.24h",
"future": {
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
"quoteName": "fBTC",
"dataSourceSpecForSettlementData": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "prices.ETH.value",
"type": "TYPE_INTEGER",
"numberDecimalPlaces": "0"
},
"conditions": [
{
"operator": "OPERATOR_GREATER_THAN",
"value": "0"
}
]
}
]
}
}
},
"dataSourceSpecForTradingTermination": {
"external": {
"oracle": {
"signers": [
{
"pubKey": {
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
}
}
],
"filters": [
{
"key": {
"name": "trading.terminated.ETH5",
"type": "TYPE_BOOLEAN"
},
"conditions": [
{
"operator": "OPERATOR_EQUALS",
"value": "true"
}
]
}
]
}
}
},
"dataSourceSpecBinding": {
"settlementDataProperty": "prices.ETH.value",
"tradingTerminationProperty": "trading.terminated.ETH5"
}
}
},
"metadata": ["sector:energy", "sector:tech", "source:docs.vega.xyz"],
"priceMonitoringParameters": {
"triggers": [
{
"horizon": "43200",
"probability": "0.9999999",
"auctionExtension": "600"
}
]
},
"liquidityMonitoringParameters": {
"targetStakeParameters": {
"timeWindow": "3600",
"scalingFactor": 10
},
"triggeringRatio": "0.7",
"auctionExtension": "1"
},
"logNormal": {
"tau": 0.0001140771161,
"riskAversionParameter": 0.01,
"params": {
"mu": 0,
"r": 0.016,
"sigma": 0.5
}
}
}
},
"closingTimestamp": 0,
"enactmentTimestamp": 0
}
}
@@ -7,12 +7,17 @@ import {
import {
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getDateFormatForSpecifiedDays,
getProposalFromTitle,
getProposalIdFromList,
getProposalInformationFromTable,
submitUniqueRawProposal,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../../../governance-e2e/src/support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
@@ -33,8 +38,6 @@ 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"]';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-terms-toggle';
describe(
'Governance flow for proposal details',
@@ -59,25 +62,23 @@ describe(
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () {
const proposalDescription =
'I propose that everyone evaluate the following IPFS document and vote Yes if they agree. bafybeigwwctpv37xdcwacqxvekr6e4kaemqsrv34em6glkbiceo3fcy4si';
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
cy.get(openProposals).within(() => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
getProposalIdFromList(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.text',
rawProposal.rationale.title
);
cy.get(proposalDetailsTitle)
.should('contain', rawProposal.rationale.title)
.and('be.visible');
cy.get(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
.should('contain', rawProposal.rationale.description)
.and('be.visible');
});
cy.getByTestId(proposalTermsToggle).click();
// 3001-VOTE-052
cy.get('code.language-json')
.should('exist')
@@ -88,36 +89,33 @@ describe(
// 3001-VOTE-043
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateFreeFormProposalTitle();
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
// const currentDate = new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
// const proposedDate = new Date(currentDate.getTime() + 60000)
submitUniqueRawProposal({
proposalTitle: proposalTitle,
closingTimestamp: proposalTimeStamp,
});
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(closingVoteHrs, proposalTitle);
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.wrap(
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
).then((closingDate) => {
getProposalInformationFromTable('Closes on').should(
'have.text',
closingDate
);
getProposalInformationFromTable('Closes on')
.contains(closingDate)
.should('be.visible');
});
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on').should(
'have.text',
proposalDate
);
getProposalInformationFromTable('Proposed on')
.contains(proposalDate)
.should('be.visible');
});
});
@@ -127,14 +125,13 @@ describe(
// 3001-VOTE-067
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
);
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Expected to pass')
.contains('👎')
.should('be.visible');
@@ -154,9 +151,9 @@ describe(
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-080
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
@@ -182,7 +179,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
@@ -224,15 +220,14 @@ describe(
vegaWalletSetSpecifiedApprovalAmount('1000');
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
voteForProposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total Supply')
.invoke('text')
.then((totalSupply) => {
@@ -240,15 +235,14 @@ describe(
(Number(totalSupply.replace(/,/g, '')) * 0.001) /
100
).toFixed(2);
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated(
tokensRequiredToAchieveResult
);
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
@@ -265,7 +259,6 @@ describe(
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total tokens voted percentage')
.should('have.text', '0.00%')
.and('be.visible');
@@ -58,12 +58,14 @@ context(
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.get(proposalStatus).should('have.text', 'Enacted');
cy.get(proposalStatus).should('have.text', 'Enacted ');
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.get(proposalStatus).should('have.text', 'Enacted');
getProposalInformationFromTable('State')
.contains('Enacted')
.and('be.visible');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
@@ -85,10 +87,16 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Passed');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Passed', proposalTimeout)
.and('be.visible');
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
@@ -113,9 +121,13 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
voteForProposal('for');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
});
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
@@ -132,8 +144,12 @@ context(
.last()
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalStatus).should('have.text', 'Open');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Declined');
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Declined', proposalTimeout)
.and('be.visible');
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
.and('be.visible');
@@ -5,11 +5,10 @@ import {
enterRawProposalBody,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
submitUniqueRawProposal,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
@@ -82,6 +81,7 @@ context(
});
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.associateTokensToVegaWallet('1');
});
beforeEach('visit governance tab', function () {
@@ -95,8 +95,7 @@ context(
navigateTo(navigation.proposals);
});
// Test can only pass if run before other proposal tests.
it.skip('Should be able to see that no proposals exist', function () {
it('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003
cy.get(noOpenProposals)
.should('be.visible')
@@ -108,7 +107,7 @@ context(
// 3002-PROP-002
// 3002-PROP-003
it('Proposal form - shows how many vega tokens are required to make a proposal', function () {
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.contains(
@@ -116,9 +115,8 @@ context(
).should('be.visible');
});
// Skipping as currently unable to propose using forms other than raw
// 3002-PROP-011
it.skip('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
@@ -142,13 +140,16 @@ context(
closeStakingDialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
createRawProposal();
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
waitForProposalSubmitted();
});
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
@@ -157,7 +158,7 @@ context(
.should('equal', 'Value must be greater than or equal to 1.');
});
it.skip('Creating a proposal - proposal rejected - when closing time later than system default', function () {
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(
'100000',
@@ -184,13 +185,17 @@ context(
navigateTo(navigation.proposals);
cy.get(rejectProposalsLink).click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible');
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-status').should('have.text', 'Rejected');
getProposalInformationFromTable('State')
.contains('Rejected')
.and('be.visible');
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible');
@@ -285,19 +290,18 @@ context(
const proposalTitle = generateFreeFormProposalTitle();
ensureSpecifiedUnstakedTokensAreAssociated('1');
submitUniqueRawProposal({ proposalTitle: proposalTitle });
ethereumWalletConnect();
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', proposalTitle);
waitForProposalSubmitted();
stakingPageDisassociateTokens('0.0001');
cy.get(vegaWallet)
.first()
.within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
@@ -315,9 +319,9 @@ context(
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
cy.get('[data-testid="disconnect"]').click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.get(viewProposalButton).click()
);
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-075
// 3001-VOTE-076
@@ -8,7 +8,6 @@ import {
import {
getProposalInformationFromTable,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
} from '../../support/governance.functions';
@@ -57,8 +56,18 @@ const fUSDCId =
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
UPDATE_ASSET: 'Update asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
// 3001-VOTE-007
context.skip(
context(
'Governance flow - form validations for different governance proposals',
{ tags: '@slow' },
function () {
@@ -6,15 +6,16 @@ import {
waitForSpinner,
} from '../../support/common.functions';
import {
createFreeformProposal,
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
generateFreeFormProposalTitle,
getProposalFromTitle,
getProposalIdFromList,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
submitUniqueRawProposal,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
@@ -23,14 +24,10 @@ import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/stakin
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalListItem = 'proposals-list-item';
const openProposals = '[data-testid="open-proposals"]';
const voteStatus = 'vote-status';
const proposalType = 'proposal-type';
const proposalStatus = 'proposal-status';
const voteStatus = '[data-testid="vote-status"]';
const proposalClosingDate = '[data-testid="vote-details"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const voteBreakDownToggle = 'vote-breakdown-toggle';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () {
@@ -63,12 +60,12 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
navigateTo(navigation.proposals);
cy.get(openProposals).within(() => {
cy.get(proposalClosingDate).first().should('contain.text', 'year');
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate)
.first()
.last()
.invoke('text')
.should('match', /days|minutes/);
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate).last().should('contain.text', 'year');
});
});
@@ -76,27 +73,36 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const proposerId = Cypress.env('vegaWalletPublicKey');
const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle });
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
// cy.get(`#${proposalId}`).should('contain', proposalId);
cy.contains(proposalTitle).should('be.visible');
cy.get('[data-testid="filter-input"]').type('123');
cy.getByTestId(proposalListItem).should('not.exist');
createFreeformProposal(proposalTitle);
getProposalIdFromList(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 () {
const proposalPath = '/proposals/new-market-raw.json';
const enactmentTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
submitUniqueRawProposal({
proposalBody: proposalPath,
enactmentTimestamp: enactmentTimestamp,
}); // 3001-VOTE-052
// 3001-VOTE-008
// 3001-VOTE-034
// 3001-VOTE-097
cy.contains('New Market Proposal E2E submission');
cy.contains('Code: TEST.24h. fBTC settled future.').should('be.visible');
createRawProposal(this.minProposerBalance); // 3001-VOTE-052
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalIdFromList(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 () {
@@ -104,11 +110,23 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
// 3001-VOTE-035
createRawProposal(this.minProposerBalance);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.get(viewProposalButton).should('be.visible');
cy.getByTestId(proposalType).should('have.text', 'Freeform');
cy.getByTestId(proposalStatus).should('have.text', 'Open');
getSubmittedProposalFromProposalList(rawProposal.rationale.title).within(
() => {
cy.get(viewProposalButton).should('be.visible').click();
}
);
cy.get('@proposalIdText').then((proposalId) => {
getProposalInformationFromTable('ID')
.contains(String(proposalId))
.and('be.visible');
});
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
getProposalInformationFromTable('Type')
.contains('Freeform')
.and('be.visible');
});
});
@@ -116,22 +134,18 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle });
getProposalFromTitle(proposalTitle).within(() => {
createFreeformProposal(proposalTitle);
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
// 3001-VOTE-039
cy.getByTestId(voteStatus).should(
'have.text',
'Participation not reached'
);
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
cy.get(voteStatus).should('have.text', 'Set to pass');
cy.get(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
@@ -96,7 +96,7 @@ context(
navigateTo(navigation.validators);
// 2002-SINC-007
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to view validators staked by me', function () {
@@ -146,7 +146,7 @@ context(
verifyThisEpochValue(2.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
@@ -166,11 +166,10 @@ context(
verifyThisEpochValue(6.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,006.00', '50.05%');
validateValidatorListTotalStakeAndShare('0', '6.00', '100.00%');
});
it('Able to stake against multiple validators', function () {
vegaWalletTeardown();
stakingPageAssociateTokens('5');
verifyUnstakedBalance(5.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -198,10 +197,14 @@ context(
.eq(1)
.within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.should('have.text', '3,002.00')
.should('have.text', '2.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '50.01%')
.should('have.text', '66.67%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible');
});
cy.get(`[row-id="${1}"]`)
@@ -209,10 +212,14 @@ context(
.within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '3,001.00')
.should('have.text', '1.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '49.99%')
.should('have.text', '33.33%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
});
});
@@ -275,7 +282,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
cy.getByTestId(userStakeBtn).should('not.exist');
cy.getByTestId(userStake).should('not.exist');
@@ -347,7 +354,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating all vesting contract tokens max - removes all staked tokens', function () {
@@ -375,7 +382,7 @@ context(
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,000.00', '50.00%');
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
@@ -397,7 +404,7 @@ context(
});
verifyStakedBalance(2.0);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
@@ -111,10 +111,10 @@ context(
// 1004-ASSO-028
// 1004-ASSO-029
// 1004-ASSO-031
vegaWalletTeardown();
stakingPageAssociateTokens('2');
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('6,002.00');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get('button').contains('Select a validator to nominate').click();
stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
@@ -125,18 +125,17 @@ context(
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
cy.get(
'[data-testid="eth-wallet-associated-balances"]:visible',
txTimeout
).should('have.length', 2);
verifyEthWalletTotalAssociatedBalance('6,000.00');
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
'not.exist'
);
verifyEthWalletTotalAssociatedBalance('0.00');
});
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
//1004-ASSO-011
stakingPageAssociateTokens('1001', { approve: true });
verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('7,001.00');
verifyEthWalletTotalAssociatedBalance('1,001.00');
cy.get(vegaWallet)
.last()
.within(() => {
@@ -47,8 +47,11 @@ context(
function () {
before('visit withdrawals and connect vega wallet', function () {
cy.visit('/');
ethereumWalletConnect();
depositAsset(usdcEthAddress, '1000', 5);
// When running tests locally, will fail if run without restarting capsule
cy.updateCapsuleMultiSig().then(() => {
ethereumWalletConnect();
depositAsset(usdcEthAddress, '1000', 5);
});
});
beforeEach('Navigate to withdrawal page', function () {
@@ -9,7 +9,6 @@ import {
import {
enterUniqueFreeFormProposalBody,
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
@@ -51,7 +50,7 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
goToMakeNewProposal('Freeform');
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content')
.first()
@@ -1,12 +1,8 @@
import { format } from 'date-fns';
import {
closeDialog,
navigateTo,
navigation,
waitForSpinner,
} from './common.functions';
import { closeDialog, navigateTo, navigation } from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
const newProposalButton = '[data-testid="new-proposal-link"]';
const proposalInformationTableRows = '[data-testid="key-value-table-row"]';
const proposalListItem = '[data-testid="proposals-list-item"]';
const newProposalTitle = '[data-testid="proposal-title"]';
@@ -50,53 +46,6 @@ export function enterRawProposalBody(timestamp: number) {
});
}
export function submitUniqueRawProposal(proposalFields: {
proposalBody?: string;
proposalTitle?: string;
proposalDescription?: string;
closingTimestamp?: number;
enactmentTimestamp?: number;
submit?: boolean;
}) {
goToMakeNewProposal(governanceProposalType.RAW);
let proposalBodyPath = '/proposals/raw.json';
if (proposalFields.proposalBody) {
proposalBodyPath = proposalFields.proposalBody;
}
cy.fixture(proposalBodyPath).then((rawProposal) => {
if (proposalFields.proposalTitle) {
rawProposal.rationale.title = proposalFields.proposalTitle;
cy.wrap(proposalFields.proposalTitle).as('proposalTitle');
}
if (proposalFields.proposalDescription) {
rawProposal.rationale.description = proposalFields.proposalDescription;
}
if (proposalFields.closingTimestamp) {
rawProposal.terms.closingTimestamp = proposalFields.closingTimestamp;
} else {
const minTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(2);
rawProposal.terms.closingTimestamp = minTimeStamp;
}
if (proposalFields.enactmentTimestamp) {
rawProposal.terms.enactmentTimestamp = proposalFields.enactmentTimestamp;
}
const proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
if (proposalFields.submit !== false) {
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal).as('rawProposal');
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
}
});
}
export function enterUniqueFreeFormProposalBody(
timestamp: string,
proposalTitle: string
@@ -109,10 +58,6 @@ export function enterUniqueFreeFormProposalBody(
cy.getByTestId('proposal-submit').should('be.visible').click();
}
export function getProposalFromTitle(proposalTitle: string) {
return cy.contains(proposalTitle).parentsUntil(proposalListItem).last();
}
export function getSubmittedProposalFromProposalList(proposalTitle: string) {
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
@@ -175,17 +120,13 @@ export function waitForProposalSync() {
});
}
export function goToMakeNewProposal(proposalType: governanceProposalType) {
cy.visit('/proposals/propose');
waitForSpinner();
export function goToMakeNewProposal(proposalType: string) {
navigateTo(navigation.proposals);
cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/proposals/propose');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
if (proposalType == governanceProposalType.RAW) {
cy.get('[href="/proposals/propose/raw"]').click();
} else {
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
}
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
}
export function waitForProposalSubmitted() {
@@ -222,12 +163,11 @@ export function createFreeformProposal(proposalTitle: string) {
navigateTo(navigation.proposals);
}
export enum governanceProposalType {
NETWORK_PARAMETER = 'Network parameter',
NEW_MARKET = 'New market',
UPDATE_MARKET = 'Update market',
NEW_ASSET = 'New asset',
UPDATE_ASSET = 'Update asset',
FREEFORM = 'Freeform',
RAW = 'raw proposal',
}
export const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
@@ -85,132 +85,6 @@ export function createFreeFormProposalTxBody(): ProposalSubmissionBody {
};
}
export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
const MIN_CLOSE_SEC = 5;
const MIN_ENACT_SEC = 7;
const closingDate = addSeconds(new Date(), MIN_CLOSE_SEC);
const enactmentDate = addSeconds(closingDate, MIN_ENACT_SEC);
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
return {
proposalSubmission: {
rationale: {
title: 'New Market Proposal E2E submission',
description: 'E2E new market proposal',
},
terms: {
newMarket: {
changes: {
decimalPlaces: '5',
positionDecimalPlaces: '5',
linearSlippageFactor: '0.001',
quadraticSlippageFactor: '0',
lpPriceRange: '10',
instrument: {
name: 'Token test market',
code: 'TEST.24h',
future: {
settlementAsset:
'73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
quoteName: 'fBTC',
dataSourceSpecForSettlementData: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'prices.ETH.value',
type: 'TYPE_INTEGER' as const,
numberDecimalPlaces: '0',
},
conditions: [
{
operator: 'OPERATOR_GREATER_THAN' as const,
value: '0',
},
],
},
],
},
},
},
dataSourceSpecForTradingTermination: {
external: {
oracle: {
signers: [
{
pubKey: {
key: '70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680',
},
},
],
filters: [
{
key: {
name: 'trading.terminated.ETH5',
type: 'TYPE_BOOLEAN' as const,
},
conditions: [
{
operator: 'OPERATOR_EQUALS' as const,
value: 'true',
},
],
},
],
},
},
},
dataSourceSpecBinding: {
settlementDataProperty: 'prices.ETH.value',
tradingTerminationProperty: 'trading.terminated.ETH5',
},
},
},
metadata: ['sector:energy', 'sector:tech', 'source:docs.vega.xyz'],
priceMonitoringParameters: {
triggers: [
{
horizon: '43200',
probability: '0.9999999',
auctionExtension: '600',
},
],
},
liquidityMonitoringParameters: {
targetStakeParameters: {
timeWindow: '3600',
scalingFactor: 10,
},
triggeringRatio: '0.7',
auctionExtension: '1',
},
logNormal: {
tau: 0.0001140771161,
riskAversionParameter: 0.01,
params: {
mu: 0,
r: 0.016,
sigma: 0.5,
},
},
},
},
closingTimestamp,
enactmentTimestamp,
},
},
};
}
export function mockNetworkUpgradeProposal() {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'Nodes', nodeData);
@@ -67,6 +67,7 @@ export async function faucetAsset(assetEthAddress: string) {
export async function vegaWalletTeardown() {
cy.get(associatedAmountInWallet)
.should('be.visible')
.invoke('text')
.then((associatedAmount) => {
cy.get('body').then(($body) => {
@@ -81,11 +82,9 @@ export async function vegaWalletTeardown() {
cy.get(vegaWalletContainer).within(() => {
cy.get(associatedAmountInWallet, {
timeout: transactionTimeout,
})
.should('have.length', 1, { timeout: transactionTimeout })
.contains('0.00', {
timeout: transactionTimeout,
});
}).contains('0.00', {
timeout: transactionTimeout,
});
});
});
}
+2 -3
View File
@@ -5,11 +5,10 @@ NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_FAIRGROUND=false
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
-1
View File
@@ -12,4 +12,3 @@ NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
+1 -2
View File
@@ -3,10 +3,9 @@ NX_VEGA_ENV=MAINNET
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/6684b914570bbfa533ba9324
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
+2 -2
View File
@@ -1,9 +1,9 @@
# App configuration variables
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_VEGA_ENV=STAGNET1
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://governance.stagnet1.vega.rocks","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","STAGNET1":"https://stagnet1.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz"}'
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
+1 -2
View File
@@ -12,5 +12,4 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
@@ -9,5 +9,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
@@ -1,40 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import {
ExternalLink,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import Routes from '../../routes/routes';
export const RiskMessage = () => {
return (
<>
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
<ul className="list-[square] ml-4">
<li>
{t(
'You may encounter bugs, loss of functionality or loss of assets using the App.'
)}
</li>
<li>
{t(
'No party accepts any liability for any losses whatsoever related to its use.'
)}
</li>
</ul>
</div>
<p className="mb-8">
{t(
'By using the Vega Governance App, you acknowledge that you have read and understood the'
)}{' '}
<ExternalLink href={Routes.DISCLAIMER} className="underline">
<span className="flex items-center gap-1">
<span>{t('Vega Governance Disclaimer')}</span>
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} />
</span>
</ExternalLink>
.
</p>
</>
);
};
@@ -4,11 +4,9 @@ import {
useAppState,
} from '../../contexts/app-state/app-state-context';
import { Connectors } from '../../lib/vega-connectors';
import { RiskMessage } from './risk-message';
export const VegaWalletDialogs = () => {
const { appState, appDispatch } = useAppState();
return (
<>
<VegaConnectDialog
@@ -19,7 +17,6 @@ export const VegaWalletDialogs = () => {
isOpen: open,
})
}
riskMessage={<RiskMessage />}
/>
<VegaManageDialog
@@ -815,11 +815,5 @@
"NoThanks": "No thanks",
"ShareData": "Share data",
"ContinueSharingData": "Continue sharing data",
"NodeUnsuitable": "Node: {{url}} is unsuitable",
"Disclaimer": "Disclaimer",
"disclaimer1": "The Vega Governance App allows the Vega network to arrive at on-chain decisions, where tokenholders can create proposals that other tokenholders can vote to approve or reject. Vega supports on-chain proposals for creating markets and assets, and changing network parameters, markets and assets. Vega also supports freeform proposals for community suggestions that will not be enacted on-chain.",
"disclaimer2": "The Vega Governance App is free, public and open source software. Software upgrades may contain bugs or security vulnerabilities that might result in loss of functionality.",
"disclaimer3": "The Vega Governance App uses data obtained from nodes on the Vega Blockchain. The developers of the Vega Governance App do not operate or run the Vega Blockchain or any other blockchain.",
"disclaimer4": "The Vega Governance App is provided “as is”. The developers of the Vega Governance App make no representations or warranties of any kind, whether express or implied, statutory or otherwise regarding the Vega Governance App. They disclaim all warranties of merchantability, quality, fitness for purpose. They disclaim all warranties that the Vega Governance App is free of harmful components or errors.",
"disclaimer5": "No developer of the Vega Governance App accepts any responsibility for, or liability to users in connection with their use of the Vega Governance App."
"NodeUnsuitable": "Node: {{url}} is unsuitable"
}
@@ -1,21 +0,0 @@
import type { RouteChildProps } from '..';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { Heading } from '../../components/heading';
import { useTranslation } from 'react-i18next';
const Disclaimer = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
const { t } = useTranslation();
return (
<>
<Heading title={t('Disclaimer')} />
<p className="mb-6 mt-10">{t('disclaimer1')}</p>
<p className="mb-6">{t('disclaimer2')}</p>
<p className="mb-6">{t('disclaimer3')}</p>
<p className="mb-8">{t('disclaimer4')}</p>
<p className="mb-8">{t('disclaimer5')}</p>
</>
);
};
export default Disclaimer;
+6 -18
View File
@@ -27,10 +27,6 @@ import type { ProposalFieldsFragment } from '../proposals/proposals/__generated_
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
import {
orderByDate,
orderByUpgradeBlockHeight,
} from '../proposals/components/proposals-list/proposals-list';
const nodesToShow = 6;
@@ -204,20 +200,17 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
const proposals = useMemo(
() =>
proposalsData
? getNotRejectedProposals(proposalsData.proposalsConnection)
? getNotRejectedProposals<ProposalFieldsFragment>(
proposalsData.proposalsConnection
)
: [],
[proposalsData]
);
const sortedProposals = useMemo(
() => orderByDate(proposals).reverse(),
[proposals]
);
const protocolUpgradeProposals = useMemo(
() =>
protocolUpgradesData
? getNotRejectedProtocolUpgradeProposals(
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
protocolUpgradesData.protocolUpgradeProposals
).filter(
(p) =>
@@ -228,20 +221,15 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
[protocolUpgradesData]
);
const sortedProtocolUpgradeProposals = useMemo(
() => orderByUpgradeBlockHeight(protocolUpgradeProposals),
[protocolUpgradeProposals]
);
const totalProposalsDesired = 4;
const protocolUpgradeProposalsToShow = sortedProtocolUpgradeProposals.slice(
const protocolUpgradeProposalsToShow = protocolUpgradeProposals.slice(
0,
totalProposalsDesired
);
const proposalsToShow =
protocolUpgradeProposalsToShow.length === totalProposalsDesired
? []
: sortedProposals.slice(
: proposals.slice(
0,
totalProposalsDesired - protocolUpgradeProposalsToShow.length
);
@@ -101,12 +101,9 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
<ProposalDescription description={proposal.rationale.description} />
</div>
{proposal.terms.change.__typename !== 'NewMarket' &&
proposal.terms.change.__typename !== 'UpdateMarket' && (
<div className="mb-4">
<ProposalTerms data={proposal.terms} />
</div>
)}
<div className="mb-4">
<ProposalTerms data={proposal.terms} />
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
@@ -132,10 +132,10 @@ describe('Proposals list', () => {
const closedProposalsItems = closedProposals.getAllByTestId(
'proposals-list-item'
);
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal2');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal1');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal3');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal4');
expect(openProposalsItems[0]).toHaveAttribute('id', 'proposal1');
expect(openProposalsItems[1]).toHaveAttribute('id', 'proposal2');
expect(closedProposalsItems[0]).toHaveAttribute('id', 'proposal4');
expect(closedProposalsItems[1]).toHaveAttribute('id', 'proposal3');
});
it('Displays info on no proposals', () => {
@@ -1,6 +1,5 @@
import orderBy from 'lodash/orderBy';
import { isFuture } from 'date-fns';
import { useState, useMemo } from 'react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Heading, SubHeading } from '../../../../components/heading';
import { ProposalsListItem } from '../proposals-list-item';
@@ -31,25 +30,6 @@ interface SortedProtocolUpgradeProposalsProps {
closed: ProtocolUpgradeProposalFieldsFragment[];
}
export const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
);
export const orderByUpgradeBlockHeight = (
arr: ProtocolUpgradeProposalFieldsFragment[]
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
['desc', 'desc']
);
export const ProposalsList = ({
proposals,
protocolUpgradeProposals,
@@ -57,57 +37,35 @@ export const ProposalsList = ({
}: ProposalsListProps) => {
const { t } = useTranslation();
const [filterString, setFilterString] = useState('');
const sortedProposals: SortedProposalsProps = useMemo(() => {
const initialSorting = proposals.reduce(
(acc: SortedProposalsProps, proposal) => {
if (isFuture(new Date(proposal?.terms.closingDatetime))) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
const sortedProposals = proposals.reduce(
(acc: SortedProposalsProps, proposal) => {
if (isFuture(new Date(proposal?.terms.closingDatetime))) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
);
return {
open:
initialSorting.open.length > 0
? orderByDate(initialSorting.open as ProposalFieldsFragment[])
: [],
closed:
initialSorting.closed.length > 0
? orderByDate(
initialSorting.closed as ProposalFieldsFragment[]
).reverse()
: [],
};
}, [proposals]);
return acc;
},
{
open: [],
closed: [],
}
);
const sortedProtocolUpgradeProposals: SortedProtocolUpgradeProposalsProps =
useMemo(() => {
const initialSorting = protocolUpgradeProposals.reduce(
(acc: SortedProtocolUpgradeProposalsProps, proposal) => {
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
}
);
return {
open: orderByUpgradeBlockHeight(initialSorting.open),
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
};
}, [protocolUpgradeProposals, lastBlockHeight]);
const sortedProtocolUpgradeProposals = protocolUpgradeProposals.reduce(
(acc: SortedProtocolUpgradeProposalsProps, proposal) => {
if (Number(proposal?.upgradeBlockHeight) > Number(lastBlockHeight)) {
acc.open.push(proposal);
} else {
acc.closed.push(proposal);
}
return acc;
},
{
open: [],
closed: [],
}
);
const filterPredicate = (
p: ProposalFieldsFragment | ProposalQuery['proposal']
@@ -11,8 +11,9 @@ import { ENV } from '../../../config';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
const {
state: { data: restData },
state: { loading: restLoading, error: restError, data: restData },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
console.log(restLoading, restError, restData);
const { data, loading, error, refetch } = useProposalQuery({
fetchPolicy: 'network-only',
errorPolicy: 'ignore',
@@ -1,4 +1,3 @@
import flow from 'lodash/flow';
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
@@ -7,15 +6,36 @@ import { SplashLoader } from '../../../components/splash-loader';
import { ProposalsList } from '../components/proposals-list';
import { useProposalsQuery } from './__generated__/Proposals';
import { getNodes } from '@vegaprotocol/utils';
import flow from 'lodash/flow';
import {
ProposalState,
ProtocolUpgradeProposalStatus,
} from '@vegaprotocol/types';
import type { NodeConnection, NodeEdge } from '@vegaprotocol/utils';
import type { ProposalFieldsFragment } from './__generated__/Proposals';
import orderBy from 'lodash/orderBy';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => new Date(p?.datetime).getTime(),
],
['asc', 'asc']
);
const orderByUpgradeBlockHeight = (
arr: ProtocolUpgradeProposalFieldsFragment[]
) =>
orderBy(
arr,
[(p) => p?.upgradeBlockHeight, (p) => p.vegaReleaseTag],
['desc', 'desc']
);
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
data?: NodeConnection<NodeEdge<T>> | null
): T[] {
@@ -24,6 +44,7 @@ export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
getNodes<ProposalFieldsFragment>(data, (p) =>
p ? p.state !== ProposalState.STATE_REJECTED : false
),
orderByDate,
])(data);
}
@@ -38,6 +59,7 @@ export function getNotRejectedProtocolUpgradeProposals<
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED
: false
),
orderByUpgradeBlockHeight,
])(data);
}
@@ -60,14 +82,17 @@ export const ProposalsContainer = () => {
});
const proposals = useMemo(
() => getNotRejectedProposals(data?.proposalsConnection),
() =>
getNotRejectedProposals<ProposalFieldsFragment>(
data?.proposalsConnection
),
[data]
);
const protocolUpgradeProposals = useMemo(
() =>
protocolUpgradesData
? getNotRejectedProtocolUpgradeProposals(
? getNotRejectedProtocolUpgradeProposals<ProtocolUpgradeProposalFieldsFragment>(
protocolUpgradesData.protocolUpgradeProposals
)
: [],
@@ -5,9 +5,6 @@ import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrad
import { getNormalisedVotingPower } from '../../staking/shared';
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
import { useVegaRelease } from '@vegaprotocol/environment';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
export interface ProtocolUpgradeProposalProps {
proposal: ProtocolUpgradeProposalFieldsFragment;
@@ -45,9 +42,6 @@ export const ProtocolUpgradeProposal = ({
lastBlockHeight,
consensusValidators,
}: ProtocolUpgradeProposalProps) => {
const { t } = useTranslation();
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
const consensusApprovals = useMemo(
() => getConsensusApprovals(consensusValidators || [], proposal),
[consensusValidators, proposal]
@@ -84,14 +78,6 @@ export const ProtocolUpgradeProposal = ({
totalConsensusValidators={consensusValidators.length}
/>
)}
{releaseInfo && releaseInfo.htmlUrl && (
<div className="mb-10">
<ExternalLink href={releaseInfo.htmlUrl}>
{t('Explore release on GitHub')}
</ExternalLink>
</div>
)}
</section>
);
};
@@ -16,10 +16,11 @@ const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.closingDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered
(p) => new Date(p?.terms?.enactmentDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered.
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => p.id,
],
['desc', 'desc']
['desc', 'desc', 'desc']
);
export function getRejectedProposals<T extends ProposalFieldsFragment>(
@@ -207,13 +207,6 @@ const LazyWithdrawals = React.lazy(
)
);
const LazyDisclaimer = React.lazy(
() =>
import(
/* webpackChunkName: "route-disclaimer", webpackPrefetch: true */ './disclaimer'
)
);
const redirects = [
{
path: Routes.VALIDATORS,
@@ -356,10 +349,6 @@ const routerConfig = [
path: Routes.CONTRACTS,
element: <LazyContracts name="Contracts" />,
},
{
path: Routes.DISCLAIMER,
element: <LazyDisclaimer name="Disclaimer" />,
},
{
path: '*',
// Not lazy as loaded when a user first hits the site
-1
View File
@@ -15,7 +15,6 @@ const Routes = {
SUPPLY: '/token/tranches',
ASSOCIATE: '/token/associate',
DISASSOCIATE: '/token/disassociate',
DISCLAIMER: '/disclaimer',
};
export default Routes;
@@ -5,22 +5,12 @@ query PreviousEpoch($epochId: ID) {
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
}
@@ -8,7 +8,7 @@ export type PreviousEpochQueryVariables = Types.Exact<{
}>;
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, stakedTotal: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string, multisigScore: string, validatorScore: string, normalisedScore: string, validatorStatus: Types.ValidatorStatus } | null, rankingScore: { __typename?: 'RankingScore', status: Types.ValidatorStatus, previousStatus: Types.ValidatorStatus, rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string } } } | null> | null } | null } };
export type PreviousEpochQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, validatorsConnection?: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, rewardScore?: { __typename?: 'RewardScore', rawValidatorScore: string, performanceScore: string } | null, rankingScore: { __typename?: 'RankingScore', stakeScore: string } } } | null> | null } | null } };
export const PreviousEpochDocument = gql`
@@ -19,22 +19,12 @@ export const PreviousEpochDocument = gql`
edges {
node {
id
stakedTotal
rewardScore {
rawValidatorScore
performanceScore
multisigScore
validatorScore
normalisedScore
validatorStatus
}
rankingScore {
status
previousStatus
rankingScore
stakeScore
performanceScore
votingPower
}
}
}
@@ -79,72 +79,36 @@ const MOCK_PREVIOUS_EPOCH: PreviousEpochQuery = {
{
node: {
id: 'ccc022b7e63a4d0a6d3a193c3940c88574060e58a184964c994998d86835a1b4',
stakedTotal: '14182454495731682635157',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.9998677767864936',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.2499583402766206',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '966438c6bffac737cfb08173ffcb3f393c4692b099ad80cb45a82e2dc0a8cf99',
stakedTotal: '9618711883996159534058',
rewardScore: {
rawValidatorScore: '0.3',
performanceScore: '1',
multisigScore: '',
validatorScore: '0.31067',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '12c81b738e8051152e1afe44376ec37bca9216466e6d44cdd772194bad0ada81',
stakedTotal: '4041343338923442976709',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.999629748500531',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.2312',
performanceScore: '0.9998677767864936',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
@@ -7,12 +7,12 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
calculateOverallPenalty,
calculateOverstakedPenalty,
calculatesPerformancePenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
} from '../../shared';
import {
@@ -32,7 +32,6 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import { VALIDATOR_LOGO_MAP } from './logo-map';
@@ -137,10 +136,6 @@ export const ConsensusValidatorsTable = ({
[totalStake]
);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
const nodes = useMemo(() => {
if (!data) return [];
let canonisedNodes = data
@@ -165,7 +160,7 @@ export const ConsensusValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore, votingPower, performanceScore },
rankingScore: { stakeScore, votingPower },
pendingStake,
stakedTotalRanking,
stakedByUser,
@@ -177,8 +172,11 @@ export const ConsensusValidatorsTable = ({
: avatarUrl
? avatarUrl
: null;
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
return {
id,
@@ -201,19 +199,21 @@ export const ConsensusValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
),
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
),
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -328,12 +328,12 @@ export const ConsensusValidatorsTable = ({
...remaining,
];
}, [
allNodesInPreviousEpoch,
data,
decimals,
hideTopThird,
previousEpochData,
thirdOfTotalStake,
totalStake,
validatorsView,
]);
@@ -5,14 +5,15 @@ import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { BigNumber } from '../../../../lib/bignumber';
import {
calculatesPerformancePenalty,
calculateOverallPenalty,
calculateOverstakedPenalty,
getFormattedPerformanceScore,
getLastEpochScoreAndPerformance,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
} from '../../shared';
import {
defaultColDef,
StakeNeededForPromotionRenderer,
stakedTotalPercentage,
ValidatorFields,
ValidatorRenderer,
@@ -27,7 +28,6 @@ import type { ValidatorsTableProps } from './shared';
import {
formatNumber,
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
@@ -52,10 +52,6 @@ export const StandbyPendingValidatorsTable = ({
const gridRef = useRef<AgGridReact | null>(null);
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
let nodes = useMemo(() => {
if (!data) return [];
@@ -81,15 +77,18 @@ export const StandbyPendingValidatorsTable = ({
stakedByDelegates,
stakedByOperator,
stakedTotal,
rankingScore: { stakeScore, performanceScore },
rankingScore: { stakeScore },
pendingStake,
stakedTotalRanking,
stakedByUser,
pendingUserStake,
userStakeShare,
}) => {
const { performanceScore: previousEpochPerformanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const {
rawValidatorScore: previousEpochValidatorScore,
performanceScore: previousEpochPerformanceScore,
stakeScore: previousEpochStakeScore,
} = getLastEpochScoreAndPerformance(previousEpochData, id);
let individualStakeNeededForPromotion,
individualStakeNeededForPromotionDescription;
@@ -145,19 +144,21 @@ export const StandbyPendingValidatorsTable = ({
toBigNum(stakedByOperator, decimals),
2
),
[ValidatorFields.PERFORMANCE_SCORE]:
getFormattedPerformanceScore(performanceScore).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: formatNumberPercentage(
calculatesPerformancePenalty(performanceScore),
2
[ValidatorFields.PERFORMANCE_SCORE]: getFormattedPerformanceScore(
previousEpochPerformanceScore
).toString(),
[ValidatorFields.PERFORMANCE_PENALTY]: getPerformancePenalty(
previousEpochPerformanceScore
),
[ValidatorFields.OVERSTAKING_PENALTY]: formatNumberPercentage(
calculateOverstakedPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.OVERSTAKING_PENALTY]: getOverstakingPenalty(
previousEpochValidatorScore,
previousEpochStakeScore
),
[ValidatorFields.TOTAL_PENALTIES]: formatNumberPercentage(
calculateOverallPenalty(id, allNodesInPreviousEpoch),
2
[ValidatorFields.TOTAL_PENALTIES]: getTotalPenalties(
previousEpochValidatorScore,
previousEpochPerformanceScore,
stakedTotal,
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
@@ -171,13 +172,13 @@ export const StandbyPendingValidatorsTable = ({
}
);
}, [
allNodesInPreviousEpoch,
data,
decimals,
previousEpochData,
stakeNeededForPromotion,
stakeNeededForPromotionDescription,
t,
totalStake,
]);
if (validatorsView === 'myStake') {
@@ -225,21 +226,21 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: StakeShareRenderer,
width: 100,
},
// {
// field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
// headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
// headerTooltip: t(stakeNeededForPromotionDescription, {
// prefix: t('The'),
// }),
// cellRenderer: StakeNeededForPromotionRenderer,
// width: 210,
// },
{
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
headerTooltip: t(stakeNeededForPromotionDescription, {
prefix: t('The'),
}),
cellRenderer: StakeNeededForPromotionRenderer,
width: 210,
},
{
field: ValidatorFields.TOTAL_PENALTIES,
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
headerTooltip: t('TotalPenaltiesDescription').toString(),
cellRenderer: TotalPenaltiesRenderer,
width: 120 + 210,
width: 120,
},
],
[]
@@ -1,15 +1,11 @@
import { useMemo } from 'react';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import {
useEnvironment,
DocsLinks,
ExternalLinks,
} from '@vegaprotocol/environment';
import {
formatNumberPercentage,
removePaginationWrapper,
toBigNum,
} from '@vegaprotocol/utils';
import { toBigNum } from '@vegaprotocol/utils';
import * as Schema from '@vegaprotocol/types';
import {
Link as UTLink,
@@ -28,11 +24,11 @@ import { SubHeading } from '../../../components/heading';
import {
getLastEpochScoreAndPerformance,
getNormalisedVotingPower,
getOverstakingPenalty,
getPerformancePenalty,
getTotalPenalties,
getUnnormalisedVotingPower,
getStakePercentage,
calculatesPerformancePenalty,
calculateOverstakedPenalty,
calculateOverallPenalty,
} from '../shared';
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
@@ -82,27 +78,17 @@ export const ValidatorTable = ({
const stakedOnNode = toBigNum(node.stakedTotal, decimals);
const { rawValidatorScore } = getLastEpochScoreAndPerformance(
previousEpochData,
node.id
);
const { rawValidatorScore, performanceScore, stakeScore } =
getLastEpochScoreAndPerformance(previousEpochData, node.id);
const stakePercentage = getStakePercentage(total, stakedOnNode);
const penalties = useMemo(() => {
const allNodesInPreviousEpoch = removePaginationWrapper(
previousEpochData?.epoch.validatorsConnection?.edges
);
return {
// current epoch
performance: calculatesPerformancePenalty(
node.rankingScore.performanceScore
),
// previous epoch
overstaked: calculateOverstakedPenalty(node.id, allNodesInPreviousEpoch),
overall: calculateOverallPenalty(node.id, allNodesInPreviousEpoch),
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const totalPenaltiesAmount = getTotalPenalties(
rawValidatorScore,
performanceScore,
stakedOnNode.toString(),
total.toString()
);
return (
<>
@@ -256,7 +242,7 @@ export const ValidatorTable = ({
<Tooltip description={t('OverstakedPenaltyDescription')}>
<span data-testid="overstaking-penalty">
{formatNumberPercentage(penalties.overstaked, 2)}
{getOverstakingPenalty(rawValidatorScore, stakeScore)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -265,7 +251,7 @@ export const ValidatorTable = ({
<Tooltip description={t('PerformancePenaltyDescription')}>
<span data-testid="performance-penalty">
{formatNumberPercentage(penalties.performance, 2)}
{getPerformancePenalty(performanceScore)}
</span>
</Tooltip>
</KeyValueTableRow>
@@ -274,7 +260,7 @@ export const ValidatorTable = ({
<strong>{t('TOTAL PENALTIES')}</strong>
</span>
<span data-testid="total-penalties">
<strong>{formatNumberPercentage(penalties.overall, 2)}</strong>
<strong>{totalPenaltiesAmount}</strong>
</span>
</KeyValueTableRow>
</KeyValueTable>
@@ -9,7 +9,6 @@ import {
getTotalPenalties,
getStakePercentage,
} from './shared';
import * as Schema from '@vegaprotocol/types';
describe('getLastEpochScoreAndPerformance', () => {
const mockPreviousEpochData = {
@@ -20,48 +19,24 @@ describe('getLastEpochScoreAndPerformance', () => {
{
node: {
id: '0x123',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.25',
performanceScore: '0.75',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.75',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
{
node: {
id: '0x234',
stakedTotal: '',
rewardScore: {
rawValidatorScore: '0.35',
performanceScore: '0.85',
multisigScore: '',
validatorScore: '',
normalisedScore: '',
validatorStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
},
rankingScore: {
stakeScore: '0.25',
performanceScore: '0.85',
status: Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
previousStatus:
Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT,
rankingScore: '',
votingPower: '',
},
},
},
+1 -89
View File
@@ -4,94 +4,6 @@ import {
} from '@vegaprotocol/utils';
import type { PreviousEpochQuery } from './__generated__/PreviousEpoch';
import { BigNumber } from '../../lib/bignumber';
import type { LastArrayElement } from 'type-fest';
type Node = NonNullable<
LastArrayElement<
NonNullable<
NonNullable<PreviousEpochQuery['epoch']['validatorsConnection']>['edges']
>
>
>['node'];
/**
* Calculates theoretical stake score for a given node
* @param nodeId Id of a node for which a score is calculated
* @param nodes A collection of all nodes
* @returns Theoretical stake score for given node based on the staked total
* of all node of the same type (status)
*/
const calculateTheoreticalStakeScore = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
if (!node) {
return new BigNumber(0);
}
const all = nodes
.filter((n) => n.rankingScore.status === node.rankingScore.status)
.map((n) => new BigNumber(n.stakedTotal));
const sumOfSameType = all.reduce((acc, a) => acc.plus(a), new BigNumber(0));
if (sumOfSameType.isZero()) {
return new BigNumber(0);
}
return new BigNumber(node.stakedTotal).dividedBy(sumOfSameType);
};
/**
* Calculates overall penalty for a given node
* @param nodeId Id of a node for which a penalty is calculated
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
* @returns %
*/
export const calculateOverallPenalty = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
if (!node || tts.isZero()) {
return new BigNumber(0);
}
const penalty = new BigNumber(1)
.minus(new BigNumber(node.rewardScore?.validatorScore || 0).dividedBy(tts))
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
/**
* Calculates over-staked penalty for a given node
* @param nodeId Id of a node for which a penalty is calculated
* @param nodes A collection of all nodes - needed to calculate theoretical stake score
* @returns %
*/
export const calculateOverstakedPenalty = (nodeId: string, nodes: Node[]) => {
const node = nodes.find((n) => n.id === nodeId);
const tts = calculateTheoreticalStakeScore(nodeId, nodes);
if (!node || tts.isZero()) {
return new BigNumber(0);
}
const penalty = new BigNumber(1)
.minus(
new BigNumber(node.rewardScore?.rawValidatorScore || 0).dividedBy(tts)
)
.times(100);
console.log(
nodeId,
new BigNumber(node.rewardScore?.rawValidatorScore || 0).toString(),
tts.toString(),
new BigNumber(node.rewardScore?.rawValidatorScore || 0)
.dividedBy(tts)
.toString()
);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
/**
* Calculates performance penalty based on the given performance score.
* @returns %
*/
export const calculatesPerformancePenalty = (performanceScore: string) => {
const penalty = new BigNumber(1)
.minus(new BigNumber(performanceScore))
.times(100);
return penalty.isLessThan(0) ? new BigNumber(0) : penalty;
};
export const getLastEpochScoreAndPerformance = (
previousEpochData: PreviousEpochQuery | undefined,
@@ -103,7 +15,7 @@ export const getLastEpochScoreAndPerformance = (
return {
rawValidatorScore: validator?.rewardScore?.rawValidatorScore,
performanceScore: validator?.rankingScore?.performanceScore,
performanceScore: validator?.rewardScore?.performanceScore,
stakeScore: validator?.rankingScore?.stakeScore,
};
};
+1 -2
View File
@@ -23,6 +23,5 @@ NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
NX_VEGA_WALLET_URL=http://localhost:1789
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
@@ -2,7 +2,7 @@
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_ENV=DEVNET
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
@@ -2,8 +2,7 @@
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_CONSOLE_URL=https://vega.trading
@@ -4,6 +4,5 @@ NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
NX_VEGA_ENV=STAGNET1
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
@@ -2,8 +2,7 @@
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_VEGA_ENV=TESTNET
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
@@ -1,5 +1,5 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.tom
NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql
NX_VEGA_NETWORKS={'{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=VALIDATOR_TESTNET
File diff suppressed because it is too large Load Diff
+1 -3
View File
@@ -6,8 +6,7 @@ NX_VEGA_CONFIG_URL=''
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_URL=http://localhost:3008/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
@@ -20,7 +19,6 @@ CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
+1 -3
View File
@@ -5,8 +5,7 @@ NX_VEGA_CONFIG_URL=''
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_URL=http://localhost:3008/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
@@ -18,7 +17,6 @@ CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
@@ -1,10 +1,5 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import type { DataSourceDefinition } from '@vegaprotocol/types';
import {
MarketState,
MarketStateMapping,
PropertyKeyType,
} from '@vegaprotocol/types';
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
import { addDays, subDays } from 'date-fns';
import {
chainIdQuery,
@@ -25,25 +20,6 @@ import {
} from '@vegaprotocol/utils';
describe('Closed markets', { tags: '@smoke' }, () => {
const settlementDataProperty = 'settlement-data-property';
const settlementDataPropertyKey = {
__typename: 'PropertyKey' as const,
name: settlementDataProperty,
type: PropertyKeyType.TYPE_INTEGER,
numberDecimalPlaces: 2,
};
const settlementDataSourceData: DataSourceDefinition = {
sourceType: {
sourceType: {
filters: [
{
__typename: 'Filter',
key: settlementDataPropertyKey,
},
],
},
},
};
const rowSelector =
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row';
@@ -61,15 +37,11 @@ describe('Closed markets', { tags: '@smoke' }, () => {
tradableInstrument: {
instrument: {
product: {
dataSourceSpecBinding: {
settlementDataProperty,
},
dataSourceSpecForTradingTermination: {
id: 'market-1-trading-termination-oracle-id',
},
dataSourceSpecForSettlementData: {
id: 'market-1-settlement-data-oracle-id',
data: settlementDataSourceData,
},
settlementAsset,
},
@@ -91,15 +63,6 @@ describe('Closed markets', { tags: '@smoke' }, () => {
`settlement-expiry-date:${addDays(new Date(), 4).toISOString()}`,
],
},
product: {
dataSourceSpecBinding: {
settlementDataProperty,
},
dataSourceSpecForSettlementData: {
id: 'market-1-settlement-data-oracle-id',
data: settlementDataSourceData,
},
},
},
},
});
@@ -118,15 +81,6 @@ describe('Closed markets', { tags: '@smoke' }, () => {
`settlement-expiry-date:${subDays(new Date(), 2).toISOString()}`,
],
},
product: {
dataSourceSpecBinding: {
settlementDataProperty,
},
dataSourceSpecForSettlementData: {
id: 'market-1-settlement-data-oracle-id',
data: settlementDataSourceData,
},
},
},
},
});
@@ -347,7 +301,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
addDecimalsFormatNumber(
// @ts-ignore cannot deep un-partial
specDataConnection.externalData.data.data[0].value,
settlementDataPropertyKey.numberDecimalPlaces
settledMarket.decimalPlaces
)
);
@@ -1,9 +1,11 @@
import { closeWelcomeDialog } from '../support/helpers';
const dialogContent = 'dialog-content';
const nodeHealth = 'node-health';
describe.skip('home', { tags: '@regression' }, () => {
describe('home', { tags: '@regression' }, () => {
before(() => {
cy.clearAllLocalStorage();
cy.clearLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
@@ -11,6 +13,7 @@ describe.skip('home', { tags: '@regression' }, () => {
describe('footer', () => {
it('shows current block height', () => {
closeWelcomeDialog();
// 0006-NETW-004
// 0006-NETW-005
// 0006-NETW-008
@@ -19,7 +22,7 @@ describe.skip('home', { tags: '@regression' }, () => {
cy.intercept('POST', 'http://localhost:3008/graphql', (req) => {
req.on('response', (res) => {
res.setDelay(3001);
res.setDelay(3000);
});
});
@@ -50,6 +53,7 @@ describe.skip('home', { tags: '@regression' }, () => {
// 0006-NETW-014
// 0006-NETW-015
// 0006-NETW-016
cy.getByTestId(nodeHealth).click();
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
cy.getByTestId(dialogContent).should(
@@ -74,6 +78,7 @@ describe.skip('home', { tags: '@regression' }, () => {
// 0006-NETW-018
// 0006-NETW-019
// 0006-NETW-020
cy.getByTestId(nodeHealth).click();
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('node-url-custom').click();
+97 -3
View File
@@ -1,7 +1,9 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { marketsDataQuery } from '@vegaprotocol/mock';
import * as Schema from '@vegaprotocol/types';
const selectMarketOverlay = 'select-market-list';
const dialogContent = 'dialog-content';
const generateProposal = (code: string): ProposalListFieldsFragment => ({
@@ -108,12 +110,77 @@ describe('home', { tags: '@regression' }, () => {
cy.get('main[data-testid^="/markets/"]');
// Overlay should be shown
cy.getByTestId(selectMarketOverlay).should('exist');
cy.contains('Select a market to get started').should('be.visible');
// I expect the market overlay table to contain at least 3 rows (one header row)
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.then((row) => {
expect(row.length >= 3).to.be.true;
});
// each market shown in overlay table contains content under the last price and change fields
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.each(($element, index) => {
if (index > 0) {
// skip header row
cy.root().within(() => {
cy.getByTestId('price').should('not.be.empty');
});
}
});
cy.getByTestId('welcome-notice-proposed-markets')
.children('div.pt-1.flex.justify-between')
.should('have.length', 3)
.each((item) => {
cy.wrap(item).getByTestId('external-link').should('exist');
});
cy.getByTestId('dialog-close').click();
cy.getByTestId(selectMarketOverlay).should('not.exist');
// the choose market overlay is no longer showing
cy.contains('Select a market to get started').should('not.exist');
cy.contains('Loading...').should('not.exist');
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
});
});
describe('market table should be properly rendered', () => {
it('redirects to a default market with the landing dialog open', () => {
const override = {
marketsConnection: {
edges: [
{
node: {
data: {
markPrice: '46126900581221212121212121212121212121212121212',
},
},
},
],
},
};
// @ts-ignore partial deep check failing
const data = marketsDataQuery(override);
cy.mockGQL((req) => {
aliasGQLQuery(req, 'MarketsData', data);
});
cy.visit('/');
cy.wait('@Markets');
cy.getByTestId(selectMarketOverlay)
.get('table')
.invoke('outerWidth')
.then((value) => {
expect(value).to.be.closeTo(554, 10);
});
});
});
describe('no markets found', () => {
beforeEach(() => {
cy.mockGQL((req) => {
@@ -139,13 +206,40 @@ describe('home', { tags: '@regression' }, () => {
cy.wait('@Markets');
cy.wait('@MarketsData');
});
it('redirects to market/all and displays welcome notice', () => {
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
it('redirects to a the empty market page and displays welcome notice', () => {
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets`);
cy.getByTestId('welcome-notice-title').should(
'contain.text',
'Welcome to Console'
);
cy.getByTestId('welcome-notice-proposed-markets').should(
'contain.text',
'AAAZZZ'
);
});
});
describe('no proposal found', () => {
it('there is a link to propose market', () => {
cy.mockGQL((req) => {
aliasGQLQuery(req, 'ProposalsList', {
proposalsConnection: {
__typename: 'ProposalsConnection',
edges: null,
},
});
});
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.then((row) => {
expect(row.length >= 3).to.be.true;
});
cy.getByTestId('external-link')
.contains('Propose a market')
.should('exist');
});
});
@@ -1,3 +1,4 @@
const selectMarketOverlay = 'select-market-list';
const marketInfoBtn = 'Info';
const marketInfoSubtitle = 'accordion-title';
const marketSummaryBlock = 'header-summary';
@@ -13,6 +14,45 @@ const itemHeader = 'item-header';
const itemValue = 'item-value';
const marketListContent = 'popover-content';
describe(
'Console - market list - live env',
{ tags: '@live', testIsolation: true },
() => {
beforeEach(() => {
cy.visit('/');
});
it('shows the market list page', () => {
cy.get('main', { timeout: 20000 });
// Overlay should be shown
cy.getByTestId(selectMarketOverlay).should('exist');
cy.contains('Select a market to get started').should('be.visible');
// I expect the market overlay table to contain at least one row
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.should('have.length.greaterThan', 1);
// each market shown in overlay table contains content under the last price and change fields
cy.getByTestId(selectMarketOverlay)
.get('table tr')
.getByTestId('price')
.should('not.be.empty');
});
it('redirects to a default market', () => {
cy.getByTestId('dialog-close').click();
cy.getByTestId(selectMarketOverlay).should('not.exist');
// the choose market overlay is no longer showing
cy.contains('Select a market to get started').should('not.exist');
cy.contains('Loading...').should('not.exist');
cy.getByTestId('popover-trigger').should('not.be.empty');
});
}
);
describe(
'Console - market info - live env',
{ tags: '@live', testIsolation: true },
@@ -38,28 +38,28 @@ describe('markets selector', { tags: '@smoke' }, () => {
// TODO: load data from mocks in. Using alias and wrap intermittently fails
const data = [
{
code: 'SOLUSD',
markPrice: '84.41XYZalpha',
code: 'AAPL.MF21',
name: 'Apple Monthly (30 Jun 2022)',
markPrice: '46,126.90058',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'ETHBTC.QM21',
markPrice: '46,126.90058tBTC',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'BTCUSD.MF21',
markPrice: '46,126.90058tDAI',
name: 'ACTIVE MARKET',
markPrice: '46,126.90058',
change: '+200.00%',
vol: '324h vol',
},
{
code: 'AAPL.MF21',
markPrice: '46,126.90058tUSDC',
code: 'ETHBTC.QM21',
name: 'ETHBTC Quarterly (30 Jun 2022)',
markPrice: '46,126.90058',
change: '+200.00%',
},
{
code: 'SOLUSD',
name: 'SUSPENDED MARKET',
markPrice: '84.41',
change: '+200.00%',
vol: '324h vol',
},
];
cy.getByTestId(list)
@@ -68,13 +68,12 @@ describe('markets selector', { tags: '@smoke' }, () => {
const market = data[i];
// 6001-MARK-021
expect(item.find('h3').text()).equals(market.code);
expect(
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
).contains(market.vol);
// 6001-MARK-022
expect(item.find('h4').text()).equals(market.name);
// 6001-MARK-024
expect(
item.find('[data-testid="market-selector-data-row"]').eq(1).text()
).contains(market.markPrice);
expect(item.find('[data-testid="market-item-price"]').text()).equals(
market.markPrice
);
// 6001-MARK-023
expect(item.find('[data-testid="market-item-change"]').text()).equals(
market.change
@@ -97,8 +96,8 @@ describe('markets selector', { tags: '@smoke' }, () => {
// 6001-MARK-29
cy.getByTestId(searchInput).clear().type('btc');
cy.getByTestId(list).find('a').should('have.length', 2);
cy.getByTestId(list).find('a').eq(1).contains('BTCUSD.MF21');
cy.getByTestId(list).find('a').eq(0).contains('ETHBTC.QM21');
cy.getByTestId(list).find('a').eq(0).contains('BTCUSD.MF21');
cy.getByTestId(list).find('a').eq(1).contains('ETHBTC.QM21');
cy.getByTestId(searchInput).clear();
cy.getByTestId(list).find('a').should('have.length', 4);
+17 -5
View File
@@ -3,6 +3,8 @@ import { aliasGQLQuery, checkSorting } from '@vegaprotocol/cypress';
import { marketsQuery } from '@vegaprotocol/mock';
import { getDateTimeFormat } from '@vegaprotocol/utils';
const dialogCloseBtn = 'dialog-close';
describe('markets table', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.clearLocalStorage().then(() => {
@@ -12,18 +14,20 @@ describe('markets table', { tags: '@smoke' }, () => {
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
);
cy.mockSubscription();
cy.visit('/#/markets/all');
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
});
});
it('renders markets correctly', () => {
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.get('[data-testid^="market-link-"]').should('not.be.empty');
cy.getByTestId('price').invoke('text').should('not.be.empty');
cy.getByTestId('settlement-asset').should('not.be.empty');
cy.getByTestId('price-change-percentage').should('not.be.empty');
cy.getByTestId('price-change').should('not.be.empty');
cy.getByTestId('sparkline-svg').should('be.visible');
});
it('able to open and sort full market list - market page', () => {
@@ -33,6 +37,9 @@ describe('markets table', { tags: '@smoke' }, () => {
'ETHBTC.QM21',
'SOLUSD',
];
cy.getByTestId('view-market-list-link')
.should('have.attr', 'href', '#/markets/all')
.click();
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
cy.contains('AAPL.MF21').should('be.visible');
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
@@ -44,6 +51,10 @@ describe('markets table', { tags: '@smoke' }, () => {
});
it('proposed markets tab should be rendered properly', () => {
cy.getByTestId('view-market-list-link')
.should('have.attr', 'href', '#/markets/all')
.click();
cy.get('[data-testid="All markets"]').should(
'have.attr',
'data-state',
@@ -76,8 +87,8 @@ describe('markets table', { tags: '@smoke' }, () => {
`${Cypress.env('VEGA_TOKEN_URL')}/proposals/propose/new-market`
);
});
it('proposed markets tab should be sorted properly', () => {
cy.getByTestId('view-market-list-link').click();
cy.get('[data-testid="Proposed markets"]').click();
const marketColDefault = [
'ETHUSD',
@@ -156,7 +167,7 @@ describe('markets table', { tags: '@smoke' }, () => {
checkSorting('state', stateColDefault, stateColAsc, stateColDesc);
});
it.skip('opening auction subsets should be properly displayed', () => {
it('opening auction subsets should be properly displayed', () => {
cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION
@@ -189,6 +200,7 @@ describe('markets table', { tags: '@smoke' }, () => {
});
cy.visit('#/markets/market-0');
cy.url().should('contain', 'market-0');
cy.getByTestId(dialogCloseBtn).click();
cy.getByTestId('item-value').contains('Opening auction').realHover();
cy.getByTestId('opening-auction-sub-status').should(
'contain.text',
+4 -13
View File
@@ -1,13 +1,16 @@
import { mockConnectWallet } from '@vegaprotocol/cypress';
describe('Navbar', { tags: '@smoke' }, () => {
beforeEach(() => {
before(() => {
cy.clearAllLocalStorage();
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
// close welcome dialog
cy.getByTestId('dialog-close').click();
});
const pages = [
@@ -44,18 +47,6 @@ describe('Navbar', { tags: '@smoke' }, () => {
});
});
});
it('Disclaimer should be presented after choosing from menu', () => {
cy.get('nav')
.find('ul li:contains(Resources)')
.contains('Resources')
.click();
cy.getByTestId('Disclaimer').eq(0).click();
cy.location('hash').should('equal', '#/disclaimer');
cy.get('p').contains(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets.'
);
});
});
describe('mobile view', () => {
@@ -1,9 +1,12 @@
import { closeWelcomeDialog } from '../support/helpers';
describe('Settings page', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.clearLocalStorage().then(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
closeWelcomeDialog();
cy.get('[aria-label="cog icon"]').click();
});
});
@@ -482,7 +482,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
testOrderCancellation(order);
});
});
it('must be able to cancel all orders on all markets', () => {
it('must be able to cancel all orders on a market', () => {
// 7003-MORD-009
// 7003-MORD-010
// 7003-MORD-011
@@ -496,7 +496,9 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
.should('have.text', 'Cancel all')
.then(($btn) => {
cy.wrap($btn).click({ force: true });
const order: OrderCancellation = {};
const order: OrderCancellation = {
marketId: 'market-0',
};
testOrderCancellation(order);
});
});
+5
View File
@@ -9,3 +9,8 @@ export const selectAsset = (assetIndex: number) => {
// eslint-disable-next-line
cy.wait(100);
};
export const closeWelcomeDialog = () => {
cy.getByTestId('select-market-list').should('exist');
cy.getByTestId('dialog-close').click();
};
+1 -1
View File
@@ -85,7 +85,7 @@ const mockTradingPage = (
trigger?: Schema.AuctionTrigger
) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'NodeCheck', statisticsQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
aliasGQLQuery(
req,
+3 -3
View File
@@ -5,11 +5,11 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"MAINNET\":\"https://vega.trading\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
+2 -2
View File
@@ -4,8 +4,8 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_CONFIG_URL=''
NX_VEGA_ENV=CUSTOM
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_URL=http://localhost:3008/graphql
NX_VEGA_WALLET_URL=http://localhost:1789
+1 -2
View File
@@ -2,11 +2,10 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_VEGA_ENV=DEVNET
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
+2 -6
View File
@@ -1,17 +1,13 @@
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_ENV=MAINNET
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
NX_VEGA_NETWORKS={\"MAINNET\":\"https://vega.trading\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://vega.trading
# TAG name of the current app version - TODO: bump to the latest upon release
NX_APP_VERSION=v0.20.8-core-0.71.4
+3 -4
View File
@@ -2,12 +2,11 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_TOKEN_URL=https://governance.stagnet1.vega.rocks
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://stagnet1.governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
+1 -3
View File
@@ -2,15 +2,13 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
NX_VEGA_ENV=TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\"}
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
@@ -2,15 +2,13 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
NX_VEGA_ENV=VALIDATOR_TESTNET
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks
NX_VEGA_NETWORKS={\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_TOKEN_URL=https://governance.validators-testnet.vega.rocks
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz
NX_VEGA_NETWORKS={\"STAGNET1\":\"https://stagnet1.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_VEGA_TOKEN_URL=https://validator-testnet.governance.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
@@ -1,48 +0,0 @@
import { t } from '@vegaprotocol/i18n';
export const Disclaimer = () => {
return (
<div className="py-16 px-8 flex w-full justify-center">
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
{t('Disclaimer')}
</h1>
<p className="mb-6 mt-10">
{t(
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets. The Vega Protocol is an implementation layer (layer one) protocol made of free, public, open-source or source-available software. Use of the Vega Protocol involves various risks, including but not limited to, losses while digital assets are supplied to the Vega Protocol and losses due to the fluctuation of prices of assets.'
)}
</p>
<p className="mb-6">
{t(
'Before using the Vega Protocol, review the relevant documentation at docs.vega.xyz to make sure that you understand how it works. Conduct your own due diligence and consult your financial advisor before making any investment decisions.'
)}
</p>
<p className="mb-6">
{t(
'As described in the Vega Protocol core license, the Vega Protocol is provided “as is”, at your own risk, and without warranties of any kind. Although Gobalsky Labs Limited developed much of the initial code for the Vega Protocol, it does not provide or control the Vega Protocol, which is run by third parties deploying it on a bespoke blockchain. Upgrades and modifications to the Vega Protocol are managed in a community-driven way by holders of the VEGA governance token.'
)}
</p>
<p className="mb-8">
{t(
'No developer or entity involved in creating the Vega Protocol will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the Vega Protocol, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or legal costs, or loss of profits, cryptoassets, tokens or anything else of value.'
)}
</p>
<p className="mb-8">
{t(
'This website is hosted on a decentralised network, the Interplanetary File System (“IPFS”). The IPFS decentralised web is made up of all the computers (nodes) connected to it. Data is therefore stored on many different computers.'
)}
</p>
<p className="mb-8">
{t(
"The information provided on this website does not constitute investment advice, financial advice, trading advice, or any other sort of advice and you should not treat any of the website's content as such. No party recommends that any cryptoasset should be bought, sold, or held by you via this website. No party ensures the accuracy of information listed on this website or holds any responsibility for any missing or wrong information. You understand that you are using any and all information available here at your own risk."
)}
</p>
<p className="mb-8">
{t(
'Additionally, just as you can access email protocols such as SMTP through multiple email clients, you can potentially access the Vega Protocol through many web or mobile interfaces. You are responsible for doing your own diligence on those interfaces to understand the associated risks and any fees.'
)}
</p>
</div>
</div>
);
};
@@ -1,3 +0,0 @@
import { Disclaimer } from './disclaimer';
export default Disclaimer;
+2 -2
View File
@@ -9,7 +9,7 @@ import { useGlobalStore } from '../../stores';
export const Home = () => {
const navigate = useNavigate();
// The default market selected in the platform behind the overlay
// should be the oldest market that is currently trading in continuous mode(i.e. not in auction).
// should be the oldest market that is currently trading in us mode(i.e. not in auction).
const { data, error, loading } = useDataProvider({
dataProvider: marketsWithDataProvider,
variables: undefined,
@@ -30,7 +30,7 @@ export const Home = () => {
replace: true,
});
} else {
navigate(Links[Routes.MARKETS]());
navigate(Links[Routes.MARKET]());
}
}
}, [marketId, data, navigate, update]);
@@ -22,19 +22,7 @@ describe('MarketSelectorItem', () => {
id: 'market-0',
decimalPlaces: 2,
// @ts-ignore fragment doesn't contain candles
candles: [
{ close: '5', volume: '50' },
{ close: '10', volume: '50' },
],
tradableInstrument: {
instrument: {
product: {
settlementAsset: {
symbol: 'SYM',
},
},
},
},
candles: [{ close: '5' }, { close: '10' }],
});
const marketData: MarketDataUpdateFieldsFragment = {
__typename: 'ObservableMarketData',
@@ -95,9 +83,6 @@ describe('MarketSelectorItem', () => {
};
it('renders market information', async () => {
const symbol =
market.tradableInstrument.instrument.product.settlementAsset.symbol;
renderJsx();
const link = screen.getByRole('link');
@@ -106,8 +91,7 @@ describe('MarketSelectorItem', () => {
expect(link).toHaveClass('ring-1');
expect(screen.getByTitle('24h vol')).toHaveTextContent('100');
expect(screen.getByTitle(symbol)).toHaveTextContent('-');
expect(screen.getByTestId('market-item-price')).toHaveTextContent('-');
// candles are loaded immediately
expect(screen.getByTestId('market-item-change')).toHaveTextContent(
@@ -115,7 +99,7 @@ describe('MarketSelectorItem', () => {
);
await waitFor(() => {
expect(screen.getByTitle(symbol)).toHaveTextContent(
expect(screen.getByTestId('market-item-price')).toHaveTextContent(
addDecimalsFormatNumber(marketData.markPrice, market.decimalPlaces)
);
});
@@ -7,14 +7,8 @@ import {
priceChangePercentage,
} from '@vegaprotocol/utils';
import type { MarketMaybeWithDataAndCandles } from '@vegaprotocol/markets';
import { calcCandleVolume } from '@vegaprotocol/markets';
import { useMarketDataUpdateSubscription } from '@vegaprotocol/markets';
import { Sparkline } from '@vegaprotocol/ui-toolkit';
import {
MarketTradingMode,
MarketTradingModeMapping,
} from '@vegaprotocol/types';
import { t } from '@vegaprotocol/i18n';
export const MarketSelectorItem = ({
market,
@@ -44,6 +38,13 @@ export const MarketSelectorItem = ({
onSelect && onSelect(market.id);
}}
>
<h3>{market.tradableInstrument.instrument.code}</h3>
<h4
title={market.tradableInstrument.instrument.name}
className="text-sm text-vega-light-300 dark:text-vega-dark-300 text-ellipsis whitespace-nowrap overflow-hidden"
>
{market.tradableInstrument.instrument.name}
</h4>
<MarketData market={market} />
</Link>
</div>
@@ -67,84 +68,30 @@ const MarketData = ({ market }: { market: MarketMaybeWithDataAndCandles }) => {
? addDecimalsFormatNumber(market.data.markPrice, market.decimalPlaces)
: '-';
const marketTradingMode = marketData
? marketData.marketTradingMode
: market.tradingMode;
const mode = [
MarketTradingMode.TRADING_MODE_BATCH_AUCTION,
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
].includes(marketTradingMode)
? MarketTradingModeMapping[marketTradingMode]
: '';
const instrument = market.tradableInstrument.instrument;
const vol = market.candles ? calcCandleVolume(market.candles) : '0';
const volume =
vol && vol !== '0'
? addDecimalsFormatNumber(vol, market.positionDecimalPlaces)
: '0.00';
return (
<>
<div className="flex items-end gap-1 mb-1">
<h3
className={classNames(
'overflow-hidden text-ellipsis whitespace-nowrap',
{
'w-1/2': mode, // make space for showing the trading mode
}
)}
<div className="flex flex-nowrap justify-between items-center mt-1">
<div className="w-1/2">
<div
className="text-ellipsis whitespace-nowrap overflow-hidden"
data-testid="market-item-price"
>
{market.tradableInstrument.instrument.code}
</h3>
{mode && (
<p className="w-1/2 text-xs text-right text-vega-orange-500 dark:text-vega-orange-550">
{mode}
</p>
)}
</div>
<DataRow value={volume} label={t('24h vol')} />
<DataRow
value={price}
label={instrument.product.settlementAsset.symbol}
/>
<div className="relative">
{price}
</div>
{market.candles && (
<PriceChange candles={market.candles.map((c) => c.close)} />
)}
<div
// absolute so height is not larger than price change value
className="absolute right-0 bottom-0 w-[120px]"
>
{market.candles && (
<Sparkline
width={120}
height={20}
data={market.candles.filter(Boolean).map((c) => Number(c.close))}
/>
)}
</div>
</div>
</>
);
};
const DataRow = ({ value, label }: { value: string; label: string }) => {
return (
<div
className="text-ellipsis whitespace-nowrap overflow-hidden leading-tight"
data-testid="market-selector-data-row"
>
<span title={label} className="text-sm mr-1">
{value}
</span>
<span className="text-xs text-vega-light-300 dark:text-vega-light-300">
{label}
</span>
<div className="w-1/2 max-w-[120px]">
{market.candles ? (
<Sparkline
width={120}
height={20}
data={market.candles.filter(Boolean).map((c) => Number(c.close))}
/>
) : (
'-'
)}
</div>
</div>
);
};
@@ -2,8 +2,8 @@ import classNames from 'classnames';
// Make sure these match the available __typename properties on product
export const Product = {
Future: 'Future',
Spot: 'Spot',
Future: 'Future',
Perpetual: 'Perpetual',
} as const;
@@ -12,8 +12,8 @@ export type ProductType = keyof typeof Product;
const ProductTypeMapping: {
[key in ProductType]: string;
} = {
[Product.Future]: 'Futures',
[Product.Spot]: 'Spot',
[Product.Future]: 'Futures',
[Product.Perpetual]: 'Perpetuals',
};
+33 -38
View File
@@ -247,7 +247,7 @@ const MainGrid = memo(
const navigate = useNavigate();
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
id: 'middle-1',
id: 'middle',
});
return (
@@ -259,7 +259,27 @@ const MainGrid = memo(
onChange={handleOnMiddleLayoutChange}
>
<ResizableGridPanel
preferredSize={sizesMiddle[0] || 330}
priority={LayoutPriority.High}
minSize={200}
preferredSize={sizesMiddle[0] || '50%'}
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-left">
<Tab id="chart" name={t('Chart')}>
<TradingViews.candles.component marketId={marketId} />
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.depth.component marketId={marketId} />
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<TradingViews.liquidity.component marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizesMiddle[1] || 330}
minSize={300}
>
<TradeGridChild>
@@ -277,25 +297,7 @@ const MainGrid = memo(
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.High}
minSize={200}
preferredSize={sizesMiddle[1] || '50%'}
>
<TradeGridChild>
<Tabs storageKey="console-trade-grid-main-left">
<Tab id="chart" name={t('Chart')}>
<TradingViews.candles.component marketId={marketId} />
</Tab>
<Tab id="depth" name={t('Depth')}>
<TradingViews.depth.component marketId={marketId} />
</Tab>
<Tab id="liquidity" name={t('Liquidity')}>
<TradingViews.liquidity.component marketId={marketId} />
</Tab>
</Tabs>
</TradeGridChild>
</ResizableGridPanel>
<ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizesMiddle[2] || 430}
minSize={200}
>
@@ -339,31 +341,24 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
return (
<div className={wrapperClasses}>
<div className="border-b border-r border-default">
<div className="h-full flex gap-2 justify-between items-end px-4 pt-1 pb-3">
<div className="flex gap-2 justify-between items-center px-4 py-2">
<HeaderTitle
primaryContent={market?.tradableInstrument.instrument.code}
secondaryContent={market?.tradableInstrument.instrument.name}
/>
<button
onClick={() => setSidebarOpen((x) => !x)}
className="flex flex-col items-center text-xs w-12"
className="p-2"
data-testid="sidebar-toggle"
>
{sidebarOpen ? (
<>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Close')}
</span>
</>
) : (
<>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} />
<span className="text-vega-light-300 dark:text-vega-dark-300">
{t('Markets')}
</span>
</>
)}
<span
className={classNames('block', {
'rotate-90 translate-x-1': !sidebarOpen,
'-rotate-90 -translate-x-1': sidebarOpen,
})}
>
<VegaIcon name={VegaIconNames.CHEVRON_UP} />
</span>
</button>
</div>
</div>
@@ -315,47 +315,39 @@ describe('useMarketSelectorList', () => {
]);
});
it('sorts by state and volume by default', () => {
it('sorts albphabetically', () => {
const markets = [
createMarketFragment({
id: 'market-0',
state: MarketState.STATE_PENDING,
// @ts-ignore candles not on fragment
candles: [
{
volume: '200',
tradableInstrument: {
instrument: {
code: 'd',
},
],
},
}),
createMarketFragment({
id: 'market-1',
state: MarketState.STATE_ACTIVE,
// @ts-ignore candles not on fragment
candles: [
{
volume: '200',
tradableInstrument: {
instrument: {
code: 'b',
},
],
},
}),
createMarketFragment({
id: 'market-2',
state: MarketState.STATE_ACTIVE,
// @ts-ignore candles not on fragment
candles: [
{
volume: '100',
tradableInstrument: {
instrument: {
code: 'a',
},
],
},
}),
createMarketFragment({
state: MarketState.STATE_PENDING,
id: 'market-3',
// @ts-ignore candles not on fragment
candles: [
{
volume: '100',
tradableInstrument: {
instrument: {
code: 'c',
},
],
},
}),
];
@@ -372,10 +364,10 @@ describe('useMarketSelectorList', () => {
assets: [],
});
expect(result.current.markets).toEqual([
markets[1],
markets[2],
markets[0],
markets[1],
markets[3],
markets[0],
]);
});
@@ -1,18 +1,11 @@
import { useMemo } from 'react';
import orderBy from 'lodash/orderBy';
import { MarketState } from '@vegaprotocol/types';
import { calcCandleVolume, useMarketList } from '@vegaprotocol/markets';
import { useMarketList } from '@vegaprotocol/markets';
import { priceChangePercentage } from '@vegaprotocol/utils';
import type { Filter } from './market-selector';
import { Sort } from './sort-dropdown';
// Used for sort order and filter
const MARKET_TEMPLATE = [
MarketState.STATE_ACTIVE,
MarketState.STATE_SUSPENDED,
MarketState.STATE_PENDING,
];
export const useMarketSelectorList = ({
product,
assets,
@@ -53,19 +46,7 @@ export const useMarketSelectorList = ({
});
if (sort === Sort.None) {
// Sort by market state primarilly and AtoZ secondarilly
return orderBy(
markets,
[
(m) => MARKET_TEMPLATE.indexOf(m.state),
(m) => {
if (!m.candles?.length) return 0;
const vol = calcCandleVolume(m.candles);
return Number(vol || 0);
},
],
['asc', 'desc']
);
return orderBy(markets, ['tradableInstrument.instrument.code'], ['asc']);
}
if (sort === Sort.Gained || sort === Sort.Lost) {
@@ -97,5 +78,9 @@ export const useMarketSelectorList = ({
};
export const isMarketActive = (state: MarketState) => {
return MARKET_TEMPLATE.includes(state);
return [
MarketState.STATE_ACTIVE,
MarketState.STATE_SUSPENDED,
MarketState.STATE_PENDING,
].includes(state);
};
@@ -1,6 +1,6 @@
import { act, render, screen, within } from '@testing-library/react';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
import { MarketStateMapping } from '@vegaprotocol/types';
import { PositionStatus } from '@vegaprotocol/types';
import { MarketState } from '@vegaprotocol/types';
import { subDays } from 'date-fns';
@@ -55,23 +55,6 @@ describe('Closed', () => {
product: {
dataSourceSpecForSettlementData: {
id: settlementDataId,
data: {
sourceType: {
sourceType: {
filters: [
{
__typename: 'Filter',
key: {
__typename: 'PropertyKey',
name: settlementDataProperty,
type: PropertyKeyType.TYPE_INTEGER,
numberDecimalPlaces: 5,
},
},
],
},
},
},
},
dataSourceSpecBinding: {
settlementDataProperty,
+14 -30
View File
@@ -13,10 +13,7 @@ import {
getMarketExpiryDate,
} from '@vegaprotocol/utils';
import { usePositionsQuery } from '@vegaprotocol/positions';
import type {
DataSourceFilterFragment,
MarketMaybeWithData,
} from '@vegaprotocol/markets';
import type { MarketMaybeWithData } from '@vegaprotocol/markets';
import {
MarketTableActions,
closedMarketsWithDataProvider,
@@ -45,7 +42,6 @@ interface Row {
markPrice: string | undefined;
settlementDataOracleId: string;
settlementDataSpecBinding: string;
setlementDataSourceFilter: DataSourceFilterFragment | undefined;
tradingTerminationOracleId: string;
settlementAsset: SettlementAsset;
realisedPNL: string | undefined;
@@ -78,40 +74,28 @@ export const Closed = () => {
}
);
const instrument = market.tradableInstrument.instrument;
const spec =
instrument.product.dataSourceSpecForSettlementData.data.sourceType
.__typename === 'DataSourceDefinitionExternal'
? instrument.product.dataSourceSpecForSettlementData.data.sourceType
.sourceType
: undefined;
const filters = spec?.filters || [];
const settlementDataSpecBinding =
instrument.product.dataSourceSpecBinding.settlementDataProperty;
const filter = filters?.find((filter) => {
return filter.key.name === settlementDataSpecBinding;
});
const row: Row = {
id: market.id,
code: instrument.code,
name: instrument.name,
code: market.tradableInstrument.instrument.code,
name: market.tradableInstrument.instrument.name,
decimalPlaces: market.decimalPlaces,
state: market.state,
metadata: instrument.metadata.tags ?? [],
metadata: market.tradableInstrument.instrument.metadata.tags ?? [],
closeTimestamp: market.marketTimestamps.close,
bestBidPrice: market.data?.bestBidPrice,
bestOfferPrice: market.data?.bestOfferPrice,
markPrice: market.data?.markPrice,
settlementDataOracleId:
instrument.product.dataSourceSpecForSettlementData.id,
settlementDataSpecBinding,
setlementDataSourceFilter: filter,
market.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.id,
settlementDataSpecBinding:
market.tradableInstrument.instrument.product.dataSourceSpecBinding
.settlementDataProperty,
tradingTerminationOracleId:
instrument.product.dataSourceSpecForTradingTermination.id,
settlementAsset: instrument.product.settlementAsset,
market.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.id,
settlementAsset:
market.tradableInstrument.instrument.product.settlementAsset,
realisedPNL: position?.node.realisedPNL,
};
@@ -254,8 +238,8 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
}: VegaICellRendererParams<Row, 'settlementDataOracleId'>) => (
<SettlementPriceCell
oracleSpecId={value}
decimalPlaces={data?.decimalPlaces ?? 0}
settlementDataSpecBinding={data?.settlementDataSpecBinding}
filter={data?.setlementDataSourceFilter}
/>
),
},
@@ -12,7 +12,7 @@ export const Proposed = () => {
const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL);
return (
<>
<div className="h-[400px]">
<div className="min-h-[200px]">
<ProposalsList />
</div>
<ExternalLink className="py-4 px-[11px] text-sm" href={externalLink}>
@@ -1,17 +1,13 @@
import { render, screen } from '@testing-library/react';
import type { Property } from '@vegaprotocol/types';
import { PropertyKeyType } from '@vegaprotocol/types';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/markets';
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/markets';
import type { SettlementPriceCellProps } from './settlement-price-cell';
import { SettlementPriceCell } from './settlement-price-cell';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
describe('SettlementPriceCell', () => {
const settlementDataSpecBinding = 'settlement-data-spec-binding';
const createMock = (
id: string,
property: Property
@@ -44,19 +40,11 @@ describe('SettlementPriceCell', () => {
},
};
};
const createProps = (
filterKey = {
name: settlementDataSpecBinding,
type: PropertyKeyType.TYPE_INTEGER,
numberDecimalPlaces: 2,
}
): SettlementPriceCellProps => {
const createProps = (): SettlementPriceCellProps => {
return {
oracleSpecId: 'oracle-spec-id',
filter: {
key: filterKey,
},
settlementDataSpecBinding,
decimalPlaces: 2,
settlementDataSpecBinding: 'settlement-data-spec-binding',
};
};
it('renders fetches and renders the settlment data value', async () => {
@@ -77,19 +65,14 @@ describe('SettlementPriceCell', () => {
expect(screen.getByText('-')).toBeInTheDocument();
const link = await screen.findByRole('link');
expect(link).toHaveTextContent(
addDecimalsFormatNumber(
property.value,
props.filter?.key.numberDecimalPlaces || 0
)
);
expect(link).toHaveTextContent('12.34');
expect(link).toHaveAttribute(
'href',
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
);
});
it('renders "Unknown" if no spec value is found', async () => {
it('renders "-" if no spec value is found', async () => {
const props = createProps();
const property = {
__typename: 'Property' as const,
@@ -107,7 +90,7 @@ describe('SettlementPriceCell', () => {
expect(screen.getByText('-')).toBeInTheDocument();
const link = await screen.findByRole('link');
expect(link).toHaveTextContent('Unknown');
expect(link).toHaveTextContent('-');
expect(link).toHaveAttribute(
'href',
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
@@ -127,33 +110,4 @@ describe('SettlementPriceCell', () => {
expect(screen.getByText('-')).toBeInTheDocument();
expect(screen.queryByRole('link')).not.toBeInTheDocument();
});
it('does not format value if property key type is not integer', async () => {
const props = createProps({
name: settlementDataSpecBinding,
type: PropertyKeyType.TYPE_TIMESTAMP,
numberDecimalPlaces: 2,
});
const property = {
__typename: 'Property' as const,
name: props.settlementDataSpecBinding as string,
value: '1234',
};
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const mock = createMock(props.oracleSpecId!, property);
render(
<MockedProvider mocks={[mock]}>
<SettlementPriceCell {...props} />
</MockedProvider>
);
expect(screen.getByText('-')).toBeInTheDocument();
const link = await screen.findByRole('link');
expect(link).toHaveTextContent(property.value);
expect(link).toHaveAttribute(
'href',
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
);
});
});
@@ -1,21 +1,19 @@
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
import { useOracleSpecBindingData } from '@vegaprotocol/markets';
import { PropertyKeyType } from '@vegaprotocol/types';
import { Link } from '@vegaprotocol/ui-toolkit';
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
export interface SettlementPriceCellProps {
oracleSpecId: string | undefined;
decimalPlaces: number;
settlementDataSpecBinding: string | undefined;
filter: DataSourceFilterFragment | undefined;
}
export const SettlementPriceCell = ({
oracleSpecId,
decimalPlaces,
settlementDataSpecBinding,
filter,
}: SettlementPriceCellProps) => {
const linkCreator = useLinks(DApp.Explorer);
const { property, loading } = useOracleSpecBindingData(
@@ -27,32 +25,15 @@ export const SettlementPriceCell = ({
return <span>-</span>;
}
const renderText = () => {
if (!property || !filter) {
return t('Unknown');
}
if (
filter.key.type === PropertyKeyType.TYPE_INTEGER &&
filter.key.numberDecimalPlaces !== null &&
filter.key.numberDecimalPlaces !== undefined
) {
return addDecimalsFormatNumber(
property.value,
filter.key.numberDecimalPlaces
);
}
return property.value;
};
return (
<Link
href={linkCreator(EXPLORER_ORACLE.replace(':id', oracleSpecId))}
className="underline font-mono"
target="_blank"
>
{renderText()}
{property
? addDecimalsFormatNumber(property.value, decimalPlaces)
: t('-')}
</Link>
);
};
-1
View File
@@ -1,2 +1 @@
export * from './announcement-banner';
export * from './upgrade-banner';
@@ -1,79 +0,0 @@
import { useMemo, useState } from 'react';
import {
ReleasesFeed,
useEnvironment,
useReleases,
Networks,
} from '@vegaprotocol/environment';
import { t } from '@vegaprotocol/i18n';
import {
CopyWithTooltip,
ExternalLink,
Intent,
NotificationBanner,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
const CANONICAL_URL = 'https://vega.trading';
type UpgradeBannerProps = {
showVersionChange: boolean;
};
export const UpgradeBanner = ({ showVersionChange }: UpgradeBannerProps) => {
const [visible, setVisible] = useState(true);
const { data } = useReleases(ReleasesFeed.FrontEnd);
const { APP_VERSION, VEGA_ENV } = useEnvironment();
/**
* Filtering out the release candidates.
*/
const latest = useMemo(() => {
const valid =
VEGA_ENV === Networks.MAINNET
? data?.filter((r) => !/-rc$/i.test(r.tagName))
: data;
return valid && valid.length > 0 ? valid[0] : undefined;
}, [VEGA_ENV, data]);
if (!APP_VERSION) return null;
if (!visible || !latest || latest.tagName === APP_VERSION) return null;
const versionChange = (
<span>
<span className="line-through text-vega-light-300 dark:text-vega-dark-300">
{APP_VERSION}
</span>{' '}
<VegaIcon size={14} name={VegaIconNames.ARROW_RIGHT} />{' '}
<span className="text-vega-orange-500 dark:text-vega-yellow-500">
<ExternalLink href={latest.htmlUrl}>{latest.tagName}</ExternalLink>
</span>
</span>
);
return (
<NotificationBanner
intent={Intent.Warning}
onClose={() => {
setVisible(false);
}}
>
<div className="uppercase ">
{t('Upgrade to the latest version of Console')}{' '}
{showVersionChange && versionChange}
</div>
<div data-testid="bookmark-message">
{t('Bookmark')}{' '}
<ExternalLink href={CANONICAL_URL}>{t('vega.trading')}</ExternalLink>
<CopyWithTooltip text={CANONICAL_URL}>
<button title={t('Copy %s', CANONICAL_URL)}>
<span className="sr-only">{t('Copy %s', CANONICAL_URL)}</span>
<VegaIcon size={14} name={VegaIconNames.COPY} />
</button>
</CopyWithTooltip>{' '}
{'to always see the latest version.'}
</div>
</NotificationBanner>
);
};
-1
View File
@@ -1,5 +1,4 @@
import { t } from '@vegaprotocol/i18n';
export const THROTTLE_UPDATE_TIME = 500;
export const RISK_ACCEPTED_KEY = 'vega_risk_accepted';
export const MAINNET_WELCOME_HEADER = t(
+2 -2
View File
@@ -73,10 +73,10 @@ export const HeaderTitle = ({
}) => {
return (
<div className="text-left" data-testid="header-title">
<div className="text-sm md:text-md lg:text-lg whitespace-nowrap !leading-[1]">
<div className="text-sm md:text-md lg:text-lg whitespace-nowrap leading-4">
{primaryContent}
</div>
<div className="text-xs whitespace-nowrap text-vega-light-300 dark:text-vega-dark-300">
<div className="text-xs whitespace-nowrap text-neutral-500 dark:text-neutral-400">
{secondaryContent}
</div>
</div>

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