- Success:
+ Success
{isNumber(code) ? (
diff --git a/apps/explorer/src/app/components/vote-icon/vote-icon.spec.tsx b/apps/explorer/src/app/components/vote-icon/vote-icon.spec.tsx
new file mode 100644
index 000000000..b26323803
--- /dev/null
+++ b/apps/explorer/src/app/components/vote-icon/vote-icon.spec.tsx
@@ -0,0 +1,37 @@
+import { render } from '@testing-library/react';
+import { VoteIcon } from './vote-icon';
+
+describe('Vote TX icon', () => {
+ it('should use the text For by default for yes votes', () => {
+ const yes = render(
);
+ expect(yes.getByTestId('label')).toHaveTextContent('For');
+ });
+
+ it('should use the yesText for yes votes if specified', () => {
+ const yes = render(
);
+ expect(yes.getByTestId('label')).toHaveTextContent('Test');
+ });
+
+ it('should display the tick icon for yes votes', () => {
+ const no = render(
);
+ expect(no.getByRole('img')).toHaveAttribute(
+ 'aria-label',
+ 'tick-circle icon'
+ );
+ });
+
+ it('should use the text Against by default for no votes', () => {
+ const no = render(
);
+ expect(no.getByTestId('label')).toHaveTextContent('Against');
+ });
+
+ it('should use the noText for no votes if specified', () => {
+ const no = render(
);
+ expect(no.getByTestId('label')).toHaveTextContent('Test');
+ });
+
+ it('should display the delete icon for no votes', () => {
+ const no = render(
);
+ expect(no.getByRole('img')).toHaveAttribute('aria-label', 'delete icon');
+ });
+});
diff --git a/apps/explorer/src/app/components/vote-icon/vote-icon.tsx b/apps/explorer/src/app/components/vote-icon/vote-icon.tsx
new file mode 100644
index 000000000..f7b29e6d7
--- /dev/null
+++ b/apps/explorer/src/app/components/vote-icon/vote-icon.tsx
@@ -0,0 +1,40 @@
+import { Icon } from '@vegaprotocol/ui-toolkit';
+import type { IconName } from '@vegaprotocol/ui-toolkit';
+
+export interface VoteIconProps {
+ // True is a yes vote, false is undefined or no vorte
+ vote: boolean;
+ // Defaults to 'For', but can be any text
+ yesText?: string;
+ // Defaults to 'Against', but can be any text
+ noText?: string;
+}
+
+/**
+ * Displays a lozenge with an icon representing the way a user voted for a proposal.
+ * The yes and no text can be overridden
+ *
+ * @returns
+ */
+export function VoteIcon({
+ vote,
+ yesText = 'For',
+ noText = 'Against',
+}: VoteIconProps) {
+ const label = vote ? yesText : noText;
+ const bg = vote ? 'bg-vega-green-550' : 'bg-vega-pink-550';
+ const icon: IconName = vote ? 'tick-circle' : 'delete';
+ const fill = vote ? 'vega-green-300' : 'vega-pink-300';
+ const text = vote ? 'vega-green-200' : 'vega-pink-200';
+
+ return (
+
+
+
+ {label}
+
+
+ );
+}
diff --git a/apps/explorer/src/styles.css b/apps/explorer/src/styles.css
index 594a3d81d..831d10a77 100644
--- a/apps/explorer/src/styles.css
+++ b/apps/explorer/src/styles.css
@@ -60,3 +60,7 @@
--ag-row-hover-color: theme(colors.neutral[800]);
--ag-font-size: 12px;
}
+
+.voteicon svg {
+ vertical-align: baseline;
+}
diff --git a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts
index f7683bb31..abcdbb8c1 100644
--- a/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts
+++ b/apps/governance-e2e/src/integration/flow/staking-flow.cy.ts
@@ -7,6 +7,7 @@ import {
waitForSpinner,
navigateTo,
navigation,
+ turnTelemetryOff,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -56,6 +57,7 @@ context(
// 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
+ cy.validatorsSelfDelegate();
ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
@@ -67,6 +69,7 @@ context(
'teardown wallet & drill into a specific validator',
function () {
cy.clearLocalStorage();
+ turnTelemetryOff();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
@@ -252,10 +255,10 @@ context(
waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text',
- '100%'
+ '50.02%'
);
navigateTo(navigation.validators);
- validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
+ validateValidatorListTotalStakeAndShare('0', '3,002.00', '50.02%');
}
);
diff --git a/apps/governance-e2e/src/integration/view/validators.cy.ts b/apps/governance-e2e/src/integration/view/validators.cy.ts
index 15186eb37..2c3ffce97 100644
--- a/apps/governance-e2e/src/integration/view/validators.cy.ts
+++ b/apps/governance-e2e/src/integration/view/validators.cy.ts
@@ -29,7 +29,10 @@ const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
-const stakeNumberRegex = /^\d*\.?\d*$/;
+const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
+
+// If running locally, validators need to have self-stake to be displayed
+// Run cy.validatorsSelfDelegate() in before hook
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', function () {
@@ -84,13 +87,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(stakedByOperatorToolTip)
.invoke('text')
- .should('contain', 'Staked by operator: 0.00');
+ .should('contain', 'Staked by operator: 3,000.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
- .should('contain', 'Total stake: 0.00');
+ .should('contain', 'Total stake: 3,000.00');
});
it('Should be able to see validator normalised voting power', function () {
@@ -106,10 +109,10 @@ context('Validators Page - verify elements on page', function () {
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
- .should('contain', 'Unnormalised voting power: 0.00%');
+ .should('contain', 'Unnormalised voting power: 20.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
- .should('contain', 'Normalised voting power: 0.10%');
+ .should('contain', 'Normalised voting power: 50.00%');
});
// 2002-SINC-018
@@ -126,13 +129,13 @@ context('Validators Page - verify elements on page', function () {
cy.get(performancePenaltyToolTip)
.invoke('text')
- .should('contain', 'Performance penalty: 100.00%');
+ .should('contain', 'Performance penalty: 0.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
- .should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
+ .should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
- .should('contain', 'Total penalties: 0.00%');
+ .should('contain', 'Total penalties: 60.00%');
});
it('Should be able to see validator pending stake', function () {
diff --git a/apps/governance/src/app.tsx b/apps/governance/src/app.tsx
index 0e44162cc..911b5ab40 100644
--- a/apps/governance/src/app.tsx
+++ b/apps/governance/src/app.tsx
@@ -2,7 +2,6 @@ import './i18n';
import React, { useEffect } from 'react';
import * as Sentry from '@sentry/react';
-import { Integrations } from '@sentry/tracing';
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
import { AppLoader } from './app-loader';
import { NetworkInfo } from '@vegaprotocol/network-info';
@@ -184,6 +183,10 @@ const ScrollToTop = () => {
return null;
};
+const removeQueryParams = (url: string) => {
+ return url.split('?')[0];
+};
+
const AppContainer = () => {
const { config, loading, error } = useEthereumConfig();
const {
@@ -204,22 +207,46 @@ const AppContainer = () => {
if (ENV.dsn && telemetryOn) {
Sentry.init({
dsn: ENV.dsn,
- integrations: [new Integrations.BrowserTracing()],
tracesSampleRate: 0.1,
enabled: true,
environment: VEGA_ENV,
release: GIT_COMMIT_HASH,
beforeSend(event) {
- if (event.request?.url?.includes('/claim?')) {
- return {
- ...event,
- request: {
- ...event.request,
- url: event.request?.url.split('?')[0],
- },
- };
- }
- return event;
+ const requestUrl = event.request?.url;
+ const transaction = event.transaction;
+
+ const updatedRequest =
+ requestUrl && requestUrl.includes('/test?')
+ ? { ...event.request, url: removeQueryParams(requestUrl) }
+ : event.request;
+
+ const updatedTransaction =
+ transaction && transaction.includes('/test?')
+ ? removeQueryParams(transaction)
+ : transaction;
+
+ const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
+ if (
+ breadcrumb.type === 'navigation' &&
+ breadcrumb.data?.to?.includes('/test?')
+ ) {
+ return {
+ ...breadcrumb,
+ data: {
+ ...breadcrumb.data,
+ to: removeQueryParams(breadcrumb.data.to),
+ },
+ };
+ }
+ return breadcrumb;
+ });
+
+ return {
+ ...event,
+ request: updatedRequest,
+ transaction: updatedTransaction,
+ breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
+ };
},
});
Sentry.setTag('branch', GIT_BRANCH);
diff --git a/apps/governance/src/i18n/translations/dev.json b/apps/governance/src/i18n/translations/dev.json
index d714cc82b..e8776b4af 100644
--- a/apps/governance/src/i18n/translations/dev.json
+++ b/apps/governance/src/i18n/translations/dev.json
@@ -202,6 +202,7 @@
"tokenVotes": "Token votes",
"liquidityVotes": "Liquidity votes",
"castYourVote": "Cast your vote",
+ "yourVote": "Your vote",
"for": "For",
"against": "Against",
"majorityRequired": "Majority Required",
@@ -787,9 +788,9 @@
"homeVegaTokenButtonText": "Manage tokens",
"downloadProposalJson": "Download proposal as JSON",
"networkUpgrade": "Network Upgrade",
- "PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved",
- "PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Pending",
- "PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Rejected",
+ "PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED": "Approved by validators",
+ "PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING": "Waiting for validator votes",
+ "PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED": "Declined by validators",
"PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED": "Unspecified",
"vegaRelease{release}": "Vega Release {{release}}",
"upgradeBlockHeight": "Upgrade block height",
diff --git a/apps/governance/src/lib/tranches/tranches-store.tsx b/apps/governance/src/lib/tranches/tranches-store.tsx
index 6b48ed913..6336e8719 100644
--- a/apps/governance/src/lib/tranches/tranches-store.tsx
+++ b/apps/governance/src/lib/tranches/tranches-store.tsx
@@ -50,7 +50,22 @@ export const useTranches = create
()((set) => ({
?.map((t) => {
const tranche_progress =
t.duration !== 0 ? (now - t.cliff_start) / t.duration : 0;
- const lockedDecimal = tranche_progress < 0 ? 1 : 1 - tranche_progress;
+ let lockedDecimal;
+ if (t.duration !== 0) {
+ if (tranche_progress < 0) {
+ lockedDecimal = 1;
+ } else {
+ lockedDecimal = 1 - tranche_progress;
+ }
+ } else {
+ if (now < t.cliff_start) {
+ lockedDecimal = 1;
+ } else {
+ lockedDecimal = 0;
+ }
+ }
+ const clampedLockedDecimal = Math.max(0, Math.min(1, lockedDecimal));
+
return {
tranche_id: t.tranche_id,
tranche_start: secondsToDate(t.cliff_start),
@@ -60,7 +75,7 @@ export const useTranches = create()((set) => ({
toBigNum(t.current_balance, decimals)
),
locked_amount: toBigNum(t.initial_balance, decimals).times(
- lockedDecimal
+ clampedLockedDecimal
),
users: t.users,
};
diff --git a/apps/governance/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx b/apps/governance/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx
index 6a37b4e72..2016f0633 100644
--- a/apps/governance/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx
+++ b/apps/governance/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx
@@ -24,7 +24,6 @@ const openProposalClosesNextMonth = generateProposal({
},
terms: {
closingDatetime: nextMonth.toString(),
- enactmentDatetime: nextMonth.toString(),
},
});
@@ -36,7 +35,6 @@ const openProposalClosesNextWeek = generateProposal({
},
terms: {
closingDatetime: nextWeek.toString(),
- enactmentDatetime: nextWeek.toString(),
},
});
@@ -45,7 +43,6 @@ const enactedProposalClosedLastWeek = generateProposal({
state: ProposalState.STATE_ENACTED,
terms: {
closingDatetime: lastWeek.toString(),
- enactmentDatetime: lastWeek.toString(),
},
});
@@ -54,7 +51,6 @@ const failedProposalClosedLastMonth = generateProposal({
state: ProposalState.STATE_FAILED,
terms: {
closingDatetime: lastMonth.toString(),
- enactmentDatetime: lastMonth.toString(),
},
});
diff --git a/apps/governance/src/routes/proposals/components/protocol-upgrade-proposal-detail-info/protocol-upgrade-proposal-detail-info.spec.tsx b/apps/governance/src/routes/proposals/components/protocol-upgrade-proposal-detail-info/protocol-upgrade-proposal-detail-info.spec.tsx
index 2ec6b2f65..2e3a4d614 100644
--- a/apps/governance/src/routes/proposals/components/protocol-upgrade-proposal-detail-info/protocol-upgrade-proposal-detail-info.spec.tsx
+++ b/apps/governance/src/routes/proposals/components/protocol-upgrade-proposal-detail-info/protocol-upgrade-proposal-detail-info.spec.tsx
@@ -32,7 +32,9 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
it('should render the state', () => {
const { getByTestId } = renderComponent();
- expect(getByTestId('protocol-upgrade-state')).toHaveTextContent('Pending');
+ expect(getByTestId('protocol-upgrade-state')).toHaveTextContent(
+ 'Waiting for validator votes'
+ );
});
it('should render the vega release tag', () => {
diff --git a/apps/governance/src/routes/proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item.spec.tsx b/apps/governance/src/routes/proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item.spec.tsx
index 4d0bdce7f..47a035b29 100644
--- a/apps/governance/src/routes/proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item.spec.tsx
+++ b/apps/governance/src/routes/proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item.spec.tsx
@@ -26,28 +26,34 @@ describe('ProtocolUpgradeProposalsListItem', () => {
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
icon: 'protocol-upgrade-proposal-status-icon-rejected',
+ text: 'Declined by validators',
},
{
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
icon: 'protocol-upgrade-proposal-status-icon-pending',
+ text: 'Waiting for validator votes',
},
{
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
icon: 'protocol-upgrade-proposal-status-icon-approved',
+ text: 'Approved by validators',
},
{
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
+ text: 'Unspecified',
},
];
- statuses.forEach(({ status, icon }) => {
+ statuses.forEach(({ status, icon, text }) => {
renderComponent({ ...proposal, status });
const statusIcon = screen.getByTestId(icon);
+ const textContent = screen.getByText(text);
expect(statusIcon).toBeInTheDocument();
+ expect(textContent).toBeInTheDocument();
});
});
diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx
index d4aa90279..7f1ae36b6 100644
--- a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx
+++ b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.spec.tsx
@@ -111,7 +111,9 @@ describe('Vote buttons', () => {
);
expect(
- screen.getByText('You need some VEGA tokens to participate in governance')
+ screen.getByText(
+ 'You need some VEGA tokens to participate in governance.'
+ )
).toBeTruthy();
});
diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx
index 3101758f6..7b2c16203 100644
--- a/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx
+++ b/apps/governance/src/routes/proposals/components/vote-details/vote-buttons.tsx
@@ -2,7 +2,12 @@ import { format } from 'date-fns';
import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
-import { AsyncRenderer, Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
+import {
+ AsyncRenderer,
+ Button,
+ ButtonLink,
+ ExternalLink,
+} from '@vegaprotocol/ui-toolkit';
import { addDecimal, toBigNum } from '@vegaprotocol/utils';
import { ProposalState, VoteValue } from '@vegaprotocol/types';
import {
@@ -161,7 +166,12 @@ export const VoteButtons = ({
{changeVote || (voteState === VoteState.NotCast && proposalVotable) ? (
<>
{currentStakeAvailable.isLessThanOrEqualTo(0) && (
- {t('noGovernanceTokens')}
+ <>
+ {t('noGovernanceTokens')}.
+
+ {t('findOutMoreAboutHowToVote')}
+
+ >
)}
diff --git a/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx b/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx
index 19746d9c2..a819118a4 100644
--- a/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx
+++ b/apps/governance/src/routes/proposals/components/vote-details/vote-details.tsx
@@ -1,6 +1,6 @@
import { useTranslation } from 'react-i18next';
import { formatDistanceToNow } from 'date-fns';
-import { RoundedWrapper, Icon } from '@vegaprotocol/ui-toolkit';
+import { RoundedWrapper, Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { ProposalState } from '@vegaprotocol/types';
import { useVoteSubmit, VoteProgress } from '@vegaprotocol/proposals';
@@ -202,7 +202,12 @@ export const VoteDetails = ({
)}
-
+ {proposal?.state === ProposalState.STATE_OPEN ? (
+
+ ) : (
+
+ )}
+
{pubKey ? (
proposal && (
{t('connectAVegaWalletToVote')}
+
+ {t('findOutMoreAboutHowToVote')}
+
diff --git a/apps/governance/src/routes/proposals/proposals/proposals-container.tsx b/apps/governance/src/routes/proposals/proposals/proposals-container.tsx
index 7d63a91f2..29b289d27 100644
--- a/apps/governance/src/routes/proposals/proposals/proposals-container.tsx
+++ b/apps/governance/src/routes/proposals/proposals/proposals-container.tsx
@@ -20,8 +20,11 @@ import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
- [(p) => new Date(p?.terms?.closingDatetime).getTime(), (p) => p.id],
- ['desc', 'desc']
+ [
+ (p) => new Date(p?.terms?.closingDatetime).getTime(),
+ (p) => new Date(p?.datetime).getTime(),
+ ],
+ ['asc', 'asc']
);
const orderByUpgradeBlockHeight = (
diff --git a/apps/governance/src/routes/redemption/tranche-item.tsx b/apps/governance/src/routes/redemption/tranche-item.tsx
index fd9056815..9b2677a7a 100644
--- a/apps/governance/src/routes/redemption/tranche-item.tsx
+++ b/apps/governance/src/routes/redemption/tranche-item.tsx
@@ -54,22 +54,16 @@ export const TrancheItem = ({
{formatNumber(total, 2)}