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:
Sam Keen 2022-07-18 14:12:36 +01:00 committed by GitHub
parent 3792029bec
commit 936f849cef
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 39 additions and 283 deletions

View File

@ -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(

View File

@ -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"
);
});
}); });
}); });
}); });

View File

@ -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 />

View File

@ -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}

View File

@ -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",

View File

@ -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')}&nbsp;
<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} />
);
}; };