Feat/Intro to staking page (#736)
* frontend-monorepo-646: Staking intro tweaked and tests amended * frontend-monorepo-646: Sorting grid placements and setting a fixed width for the main content area on larger screens * frontend-monorepo-646: Removing some redundant elements and tweaking some tests * test: fixes ahead of ui changes * test: lint * test: remove unused import * test: renabling tests * test: fixes * test: fixes to catch up with last PR * test: removal of tests no longer required * test: removal of unused import * Update apps/token/src/i18n/translations/dev.json Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com> * frontend-monorepo-646: Updated staking text Co-authored-by: AndyWhiteVega <andy@vegaprotocol.io> Co-authored-by: candida-d <62548908+candida-d@users.noreply.github.com>
This commit is contained in:
parent
3792029bec
commit
936f849cef
@ -25,7 +25,7 @@ const ethWalletContainer = '[data-testid="ethereum-wallet"]';
|
|||||||
const txTimeout = { timeout: 40000 };
|
const txTimeout = { timeout: 40000 };
|
||||||
const epochTimeout = { timeout: 10000 };
|
const epochTimeout = { timeout: 10000 };
|
||||||
|
|
||||||
context('Staking Flow - with eth and vega wallets connected', function () {
|
context('Staking Tab - with eth and vega wallets connected', function () {
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
cy.vega_wallet_import();
|
cy.vega_wallet_import();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
@ -411,7 +411,7 @@ context('Staking Flow - with eth and vega wallets connected', function () {
|
|||||||
.and('be.visible');
|
.and('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Disassociating all tokens - removes all staked tokens', function () {
|
it.skip('Disassociating all tokens max - removes all staked tokens', function () {
|
||||||
cy.staking_page_associate_tokens('3');
|
cy.staking_page_associate_tokens('3');
|
||||||
|
|
||||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
|
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
const guideLink = '[data-testid="staking-guide-link"]';
|
const guideLink = '[data-testid="staking-guide-link"]';
|
||||||
const step1 = '[data-testid="staking-step-1"]';
|
const validators = '[data-testid="node-list-item"]';
|
||||||
const step2 = '[data-testid="staking-step-2"]';
|
|
||||||
const step3 = '[data-testid="staking-step-3"]';
|
|
||||||
const sectionHeader = 'h2';
|
|
||||||
const connectToEthBtn = '[data-testid="connect-to-eth-btn"]';
|
|
||||||
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
|
|
||||||
const link = '[data-testid="link"]';
|
|
||||||
const warning = '[data-testid="callout"]';
|
|
||||||
|
|
||||||
context('Staking Page - verify elements on page', function () {
|
context('Staking Page - verify elements on page', function () {
|
||||||
before('navigate to staking page', function () {
|
before('navigate to staking page', function () {
|
||||||
@ -20,7 +13,7 @@ context('Staking Page - verify elements on page', function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should have STAKING ON VEGA header visible', function () {
|
it('should have STAKING ON VEGA header visible', function () {
|
||||||
cy.verify_page_header('Staking on Vega');
|
cy.verify_page_header('Staking');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have Staking Guide link visible', function () {
|
it('should have Staking Guide link visible', function () {
|
||||||
@ -34,73 +27,9 @@ context('Staking Page - verify elements on page', function () {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('validators section', function () {
|
||||||
describe('step 1 section', function () {
|
it('should be visible', function () {
|
||||||
it('should have header visible', function () {
|
cy.get(validators).should('be.visible');
|
||||||
cy.get(step1).within(() => {
|
|
||||||
cy.get(sectionHeader)
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Step 1. Connect to a Vega Wallet');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have text visible', function () {
|
|
||||||
cy.get(step1).within(() => {
|
|
||||||
cy.get(link)
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Vega Wallet')
|
|
||||||
.and('have.attr', 'href', 'https://vega.xyz/wallet');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have connect to eth button visible', function () {
|
|
||||||
cy.get(step1).within(() => {
|
|
||||||
cy.get(connectToEthBtn)
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Connect Ethereum wallet');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have connect to vega button visible', function () {
|
|
||||||
cy.get(step1).within(() => {
|
|
||||||
cy.get(connectToVegaBtn)
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Connect Vega wallet');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('step 2 section', function () {
|
|
||||||
it('should have header visible', function () {
|
|
||||||
cy.get(step2).within(() => {
|
|
||||||
cy.get(sectionHeader)
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Step 2. Associate tokens with a Vega Wallet');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should have warning visible', function () {
|
|
||||||
cy.get(step2).within(() => {
|
|
||||||
cy.get(warning)
|
|
||||||
.should('be.visible')
|
|
||||||
.and(
|
|
||||||
'have.text',
|
|
||||||
'You need to connect to an Ethereum wallet first'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('step 3 section', function () {
|
|
||||||
it('should have header visible', function () {
|
|
||||||
cy.get(step3).within(() => {
|
|
||||||
cy.get(sectionHeader)
|
|
||||||
.should('be.visible')
|
|
||||||
.and(
|
|
||||||
'have.text',
|
|
||||||
"Step 3. Select the validator you'd like to nominate"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -10,7 +10,7 @@ export const AppBanner = () => {
|
|||||||
if (!bannerMessage) return <div />;
|
if (!bannerMessage) return <div />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="bg-white p-8 text-black" role="alert">
|
<div className="row-start-1 bg-white p-8 text-black" role="alert">
|
||||||
<p>
|
<p>
|
||||||
<span className="inline-block relative top-[1px] text-danger text-ui mr-[5px]">
|
<span className="inline-block relative top-[1px] text-danger text-ui mr-[5px]">
|
||||||
<Error />
|
<Error />
|
||||||
|
@ -9,10 +9,10 @@ export interface TemplateSidebarProps {
|
|||||||
|
|
||||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="border-b border-white lg:grid lg:grid-rows-[auto_minmax(600px,_1fr)] lg:grid-cols-[1fr_450px]">
|
<div className="row-start-2 border-b border-white lg:grid lg:grid-rows-[auto_minmax(600px,_1fr)] lg:grid-cols-[850px_450px]">
|
||||||
<Nav />
|
<Nav />
|
||||||
<main className="p-20">{children}</main>
|
<main className="col-start-1 p-20">{children}</main>
|
||||||
<aside className="hidden lg:block lg:col-start-2 lg:col-end-4 lg:row-start-1 lg: row-end-4 p-20 bg-banner bg-contain border-l border-white">
|
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-20 bg-banner bg-contain border-l border-white">
|
||||||
{sidebar.map((Component, i) => (
|
{sidebar.map((Component, i) => (
|
||||||
<section className="mb-20 last:mb-0" key={i}>
|
<section className="mb-20 last:mb-0" key={i}>
|
||||||
{Component}
|
{Component}
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"pageTitleLiquidity": "Incentivised Liquidity Programme",
|
"pageTitleLiquidity": "Incentivised Liquidity Programme",
|
||||||
"pageTitleRedemptionTranche": "Redeem from Tranche",
|
"pageTitleRedemptionTranche": "Redeem from Tranche",
|
||||||
"pageTitleTranches": "Vesting tranches",
|
"pageTitleTranches": "Vesting tranches",
|
||||||
"pageTitleStaking": "Staking on Vega",
|
"pageTitleStaking": "Staking",
|
||||||
"pageTitle404": "Page not found",
|
"pageTitle404": "Page not found",
|
||||||
"pageTitleNotPermitted": "Can not proceed!",
|
"pageTitleNotPermitted": "Can not proceed!",
|
||||||
"pageTitleDisassociate": "Disassociate $VEGA tokens from a Vega key",
|
"pageTitleDisassociate": "Disassociate $VEGA tokens from a Vega key",
|
||||||
@ -511,10 +511,11 @@
|
|||||||
"unsupportedChainIdError": "You're connected to an unsupported network",
|
"unsupportedChainIdError": "You're connected to an unsupported network",
|
||||||
"userRejectionError": "Please authorise this website to access your Ethereum account",
|
"userRejectionError": "Please authorise this website to access your Ethereum account",
|
||||||
"unknownEthereumConnectionError": "An unknown error occurred. Check the console in your browser's web developer tools for more details",
|
"unknownEthereumConnectionError": "An unknown error occurred. Check the console in your browser's web developer tools for more details",
|
||||||
"stakingDescription1": "Associate VEGA tokens from your Ethereum wallet to a Vega wallet/key to nominate a validator",
|
"stakingDescriptionTitle": "How does staking on Vega work?",
|
||||||
"stakingDescription2": "Validators and their nominators are rewared every 24 hours for securing the network with a cut of trading fees (and rewards from the Vega treasury). Staking rewards are paid into your Vega wallet after an epoch ends.",
|
"stakingDescription1": "1. VEGA is an ERC20 token. Associate it with a Vega wallet using the staking bridge",
|
||||||
"stakingDescription3": "The amount that a validator, and its nominators receives is proportionate to the amount staked on it, however a validator can be penalised during an epoch for a number of reasons. In this case the validator and its nominator will forfeit some or all of its reward from that epoch",
|
"stakingDescription2": "2. Use this site and your Vega wallet to nominate a validator",
|
||||||
"stakingDescription4": "A validator will receive a higher share of the pot than its nominators. This share is the same for all validators and is set by a network parameter.",
|
"stakingDescription3": "3. Earn a share of trading fees and treasury rewards for each full epoch staked",
|
||||||
|
"stakingDescription4": "4. Move your stake if your validator is penalised",
|
||||||
"readMoreStaking": "Read more about staking on Vega",
|
"readMoreStaking": "Read more about staking on Vega",
|
||||||
"networkDown": "This site is not currently connecting to the network please try again later.",
|
"networkDown": "This site is not currently connecting to the network please try again later.",
|
||||||
"ethTransactionModalTitle": "Ethereum Transactions",
|
"ethTransactionModalTitle": "Ethereum Transactions",
|
||||||
|
@ -1,224 +1,50 @@
|
|||||||
import { Button, Callout, Intent } from '@vegaprotocol/ui-toolkit';
|
import { Button, Callout, Intent, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Trans, useTranslation } from 'react-i18next';
|
|
||||||
import { Link as RouteLink } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { BulletHeader } from '../../components/bullet-header';
|
|
||||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
|
||||||
import { Links } from '../../config';
|
import { Links } from '../../config';
|
||||||
import {
|
|
||||||
AppStateActionType,
|
|
||||||
useAppState,
|
|
||||||
} from '../../contexts/app-state/app-state-context';
|
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
|
||||||
import { formatNumber } from '../../lib/format-number';
|
|
||||||
import type { Staking as StakingQueryResult } from './__generated__/Staking';
|
|
||||||
import { ConnectToVega } from './connect-to-vega';
|
|
||||||
import { NodeList } from './node-list';
|
import { NodeList } from './node-list';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import type { Staking as StakingQueryResult } from './__generated__/Staking';
|
||||||
import { truncateMiddle } from '../../lib/truncate-middle';
|
|
||||||
|
|
||||||
const stakingBulletStyles = { marginBottom: '12px', fontSize: '18px' };
|
|
||||||
|
|
||||||
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<section className="mb-24">
|
<section data-testid="staking-description" className="mb-24">
|
||||||
<p>{t('stakingDescription1')}</p>
|
<Callout
|
||||||
<p>{t('stakingDescription2')}</p>
|
intent={Intent.Primary}
|
||||||
<p>{t('stakingDescription3')}</p>
|
iconName="help"
|
||||||
<p>{t('stakingDescription4')}</p>
|
title={t('stakingDescriptionTitle')}
|
||||||
<p>
|
>
|
||||||
|
<ol className="mb-20">
|
||||||
|
<li>{t('stakingDescription1')}</li>
|
||||||
|
<li>{t('stakingDescription2')}</li>
|
||||||
|
<li>{t('stakingDescription3')}</li>
|
||||||
|
<li>{t('stakingDescription4')}</li>
|
||||||
|
</ol>
|
||||||
|
|
||||||
<Link
|
<Link
|
||||||
href={Links.STAKING_GUIDE}
|
href={Links.STAKING_GUIDE}
|
||||||
className="text-white underline"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
data-testid="staking-guide-link"
|
data-testid="staking-guide-link"
|
||||||
>
|
>
|
||||||
{t('readMoreStaking')}
|
<Button variant="secondary">{t('readMoreStaking')}</Button>
|
||||||
</Link>
|
</Link>
|
||||||
</p>
|
</Callout>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section data-testid="staking-step-1">
|
<section>
|
||||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
|
||||||
{t('stakingStep1')}
|
|
||||||
</BulletHeader>
|
|
||||||
<StakingStepConnectWallets />
|
|
||||||
</section>
|
|
||||||
<section data-testid="staking-step-2">
|
|
||||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
|
||||||
{t('stakingStep2')}
|
|
||||||
</BulletHeader>
|
|
||||||
<StakingStepAssociate
|
|
||||||
associated={
|
|
||||||
new BigNumber(
|
|
||||||
data?.party?.stake.currentStakeAvailableFormatted || '0'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
<section data-testid="staking-step-3">
|
|
||||||
<BulletHeader tag="h2" style={stakingBulletStyles}>
|
|
||||||
{t('stakingStep3')}
|
|
||||||
</BulletHeader>
|
|
||||||
<StakingStepSelectNode data={data} />
|
<StakingStepSelectNode data={data} />
|
||||||
</section>
|
</section>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const StakingStepConnectWallets = () => {
|
|
||||||
const { ETHERSCAN_URL } = useEnvironment();
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { account } = useWeb3React();
|
|
||||||
const { keypair } = useVegaWallet();
|
|
||||||
const { appDispatch } = useAppState();
|
|
||||||
|
|
||||||
if (keypair && account) {
|
|
||||||
return (
|
|
||||||
<Callout intent={Intent.Success} iconName="tick" title={'Connected'}>
|
|
||||||
<p>
|
|
||||||
{t('Connected Ethereum address')}
|
|
||||||
<Link
|
|
||||||
title={t('View on Etherscan (opens in a new tab)')}
|
|
||||||
href={`${ETHERSCAN_URL}/tx/${account}`}
|
|
||||||
target="_blank"
|
|
||||||
>
|
|
||||||
{truncateMiddle(account)}
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
<p className="mb-8">
|
|
||||||
{t('stakingVegaWalletConnected', {
|
|
||||||
key: truncateMiddle(keypair.pub),
|
|
||||||
})}
|
|
||||||
</p>
|
|
||||||
</Callout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>
|
|
||||||
<Trans
|
|
||||||
i18nKey="stakingStep1Text"
|
|
||||||
components={{
|
|
||||||
vegaWalletLink: (
|
|
||||||
<Link
|
|
||||||
href={Links.WALLET_PAGE}
|
|
||||||
className="text-white underline"
|
|
||||||
target="_blank"
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</p>
|
|
||||||
{account ? (
|
|
||||||
<div className="mb-24">
|
|
||||||
<Callout
|
|
||||||
iconName="tick"
|
|
||||||
intent={Intent.Success}
|
|
||||||
title={`Ethereum wallet connected: ${account}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<p>
|
|
||||||
<Button
|
|
||||||
onClick={() =>
|
|
||||||
appDispatch({
|
|
||||||
type: AppStateActionType.SET_ETH_WALLET_OVERLAY,
|
|
||||||
isOpen: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
data-testid="connect-to-eth-btn"
|
|
||||||
>
|
|
||||||
{t('connectEthWallet')}
|
|
||||||
</Button>
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
{keypair ? (
|
|
||||||
<div className="mb-24">
|
|
||||||
<Callout
|
|
||||||
iconName="tick"
|
|
||||||
intent={Intent.Success}
|
|
||||||
title={`Vega wallet connected: ${truncateMiddle(keypair.pub)}`}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<ConnectToVega />
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StakingStepAssociate = ({
|
|
||||||
associated,
|
|
||||||
}: {
|
|
||||||
associated: BigNumber;
|
|
||||||
}) => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const { account } = useWeb3React();
|
|
||||||
const { keypair } = useVegaWallet();
|
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
return (
|
|
||||||
<Callout
|
|
||||||
intent={Intent.Danger}
|
|
||||||
iconName="error"
|
|
||||||
title={t('stakingAssociateConnectEth')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else if (!keypair) {
|
|
||||||
return (
|
|
||||||
<Callout
|
|
||||||
intent={Intent.Danger}
|
|
||||||
iconName="error"
|
|
||||||
title={t('stakingAssociateConnectVega')}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (associated.isGreaterThan(0)) {
|
|
||||||
return (
|
|
||||||
<Callout
|
|
||||||
intent={Intent.Success}
|
|
||||||
iconName="tick"
|
|
||||||
title={t('stakingHasAssociated', { tokens: formatNumber(associated) })}
|
|
||||||
>
|
|
||||||
<div className="flex flex-wrap gap-4">
|
|
||||||
<RouteLink to="associate">
|
|
||||||
<Button data-testid="associate-more-tokens-btn">
|
|
||||||
{t('stakingAssociateMoreButton')}
|
|
||||||
</Button>
|
|
||||||
</RouteLink>
|
|
||||||
<RouteLink to="disassociate">
|
|
||||||
<Button data-testid="disassociate-tokens-btn">
|
|
||||||
{t('stakingDisassociateButton')}
|
|
||||||
</Button>
|
|
||||||
</RouteLink>
|
|
||||||
</div>
|
|
||||||
</Callout>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{t('stakingStep2Text')}</p>
|
|
||||||
<RouteLink to="/staking/associate">
|
|
||||||
<Button data-testid="associate-tokens-btn">
|
|
||||||
{t('associateButton')}
|
|
||||||
</Button>
|
|
||||||
</RouteLink>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const StakingStepSelectNode = ({
|
export const StakingStepSelectNode = ({
|
||||||
data,
|
data,
|
||||||
}: {
|
}: {
|
||||||
data?: StakingQueryResult;
|
data?: StakingQueryResult;
|
||||||
}) => {
|
}) => {
|
||||||
return <NodeList epoch={data?.epoch} party={data?.party} />;
|
return (
|
||||||
|
<NodeList data-testid="node-list" epoch={data?.epoch} party={data?.party} />
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user