Merge branch 'master' into task/token-flow-tests

This commit is contained in:
AndyWhiteVega 2022-06-30 14:47:28 +01:00
commit 2e15e4dc2e
223 changed files with 5649 additions and 946 deletions

View File

@ -16,7 +16,7 @@ The trading interface built based on a component toolkit. It will provide a way
### [Token](./apps/token)
The utlity dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
The utility dApp for interacting with the Vega token and using its' utility. This includes; delegation, nomination, governance and redemption of tokens.
### [Explorer](./apps/explorer)
@ -51,7 +51,7 @@ A utility library for connecting to the Ethereum network and interacting with Ve
### [React Helpers](./libs/react-helpers)
Generic react helpers that can be used across multilpe applications, along with other utilities.
Generic react helpers that can be used across multiple applications, along with other utilities.
# 💻 Develop

View File

@ -0,0 +1,26 @@
const { defineConfig } = require('cypress');
const setupNodeEvents = require('./src/plugins/index.js');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
setupNodeEvents,
baseUrl: 'http://localhost:3000',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: '**/*.feature',
excludeSpecPattern: '**/*.js',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
chromeWebSecurity: false,
},
env: {
environment: 'CUSTOM',
tsConfig: 'tsconfig.json',
TAGS: 'not @todo and not @ignore and not @manual',
},
});

View File

@ -1,21 +0,0 @@
{
"baseUrl": "http://localhost:3000",
"projectId": "et4snf",
"fileServerFolder": ".",
"fixturesFolder": false,
"pluginsFile": "./src/plugins/index.js",
"testFiles": "*.{ts,feature,features}",
"ignoreTestFiles": "**/*.js",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"video": true,
"videosFolder": "../../dist/cypress/apps/explorer-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/explorer-e2e/screenshots",
"chromeWebSecurity": false,
"env": {
"environment": "CUSTOM",
"tsConfig": "tsconfig.json",
"TAGS": "not @todo and not @ignore and not @manual"
}
}

View File

@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/explorer-e2e/cypress.json",
"cypressConfig": "apps/explorer-e2e/cypress.config.js",
"devServerTarget": "explorer:serve"
},
"configurations": {

View File

@ -1,3 +1,5 @@
@ignore
# tendermint times out getting txs on testnet atm
Feature: Transactions Page
Scenario: Navigate to transactions page
@ -5,14 +7,12 @@ Feature: Transactions Page
When I navigate to the transactions page
Then transactions page is correctly displayed
@ignore
Scenario: Navigate to transaction details page
Given I am on the homepage
When I navigate to the transactions page
And I click on the top transaction
Then transaction details are displayed
@ignore
Scenario: Navigate to transactions page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the transactions page

View File

@ -15,11 +15,9 @@ export default class TransactionsPage extends BasePage {
txType = 'tx-type';
validateTransactionsPagedisplayed() {
cy.getByTestId(this.transactionsList).should('have.length.above', 1);
cy.getByTestId(this.blockHeight).first().should('not.be.empty');
cy.getByTestId(this.numberOfTransactions).first().should('not.be.empty');
cy.getByTestId(this.validatorLink).first().should('not.be.empty');
cy.getByTestId(this.blockTime).first().should('not.be.empty');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000); // Wait for transactions to load if there are any
cy.getByTestId(this.transactionRow).should('have.length.above', 1);
}
validateRefreshBtn() {

View File

@ -28,9 +28,10 @@ export const Nav = ({ menuOpen }: NavProps) => {
className={({ isActive }) =>
classnames(
'block mb-8 px-8',
'text-h5 hover:bg-vega-yellow hover:text-black',
'text-h5 hover:bg-vega-pink dark:hover:bg-vega-yellow hover:text-white dark:hover:text-black',
{
'bg-vega-yellow text-black': isActive,
'bg-vega-pink text-white dark:bg-vega-yellow dark:text-black':
isActive,
}
)
}

View File

@ -1,3 +1,5 @@
export { TxList } from './tx-list';
export { BlockTxsData } from './block-txs-data';
export { TxOrderType } from './tx-order-type';
export { TxsInfiniteList } from './txs-infinite-list';
export { TxsInfiniteListItem } from './txs-infinite-list-item';

View File

@ -0,0 +1,92 @@
import { TxsInfiniteListItem } from './txs-infinite-list-item';
import { render, screen, fireEvent, act } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
describe('Txs infinite list item', () => {
it('should display "missing vital data" if "Type" data missing', () => {
render(
<TxsInfiniteListItem
// @ts-ignore testing deliberate failure
Type={undefined}
Command={'test'}
Sig={'test'}
PubKey={'test'}
Nonce={1}
TxHash={'test'}
/>
);
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
});
it('should display "missing vital data" if "Command" data missing', () => {
render(
<TxsInfiniteListItem
Type={'test'}
// @ts-ignore testing deliberate failure
Command={undefined}
Sig={'test'}
PubKey={'test'}
Nonce={1}
TxHash={'test'}
/>
);
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
});
it('should display "missing vital data" if "Pubkey" data missing', () => {
render(
<TxsInfiniteListItem
Type={'test'}
Command={'test'}
Sig={'test'}
// @ts-ignore testing deliberate failure
PubKey={undefined}
Nonce={1}
TxHash={'test'}
/>
);
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
});
it('should display "missing vital data" if "TxHash" data missing', () => {
render(
<TxsInfiniteListItem
Type={'test'}
Command={'test'}
Sig={'test'}
PubKey={'test'}
Nonce={1}
// @ts-ignore testing deliberate failure
TxHash={undefined}
/>
);
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
});
it('renders data correctly', () => {
const testCommandData = JSON.stringify({
test: 'test of command data',
});
render(
<MemoryRouter>
<TxsInfiniteListItem
Type={'testType'}
Command={testCommandData}
Sig={'testSig'}
PubKey={'testPubKey'}
Nonce={1}
TxHash={'testTxHash'}
/>
</MemoryRouter>
);
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
expect(screen.getByTestId('type')).toHaveTextContent('testType');
const button = screen.getByTestId('command-details');
act(() => {
fireEvent.click(button);
});
expect(screen.getByText('"test of command data"')).toBeInTheDocument();
});
});

View File

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import {
Dialog,
Icon,
Intent,
SyntaxHighlighter,
} from '@vegaprotocol/ui-toolkit';
import { TruncatedLink } from '../truncate/truncated-link';
import { Routes } from '../../routes/route-names';
import { TxOrderType } from './tx-order-type';
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response';
const TRUNCATE_LENGTH = 14;
export const TxsInfiniteListItem = ({
TxHash,
PubKey,
Type,
Command,
}: ChainExplorerTxResponse) => {
const [open, setOpen] = useState(false);
if (!TxHash || !PubKey || !Type || !Command) {
return <div>Missing vital data</div>;
}
return (
<div
data-testid="transaction-row"
className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full border-t border-black-60 dark:border-white-25 py-8 txs-infinite-list-item"
>
<div className="whitespace-nowrap overflow-scroll" data-testid="tx-hash">
<TruncatedLink
to={`/${Routes.TX}/${TxHash}`}
text={TxHash}
startChars={TRUNCATE_LENGTH}
endChars={TRUNCATE_LENGTH}
/>
</div>
<div className="whitespace-nowrap overflow-scroll" data-testid="pub-key">
<TruncatedLink
to={`/${Routes.PARTIES}/${PubKey}`}
text={PubKey}
startChars={TRUNCATE_LENGTH}
endChars={TRUNCATE_LENGTH}
/>
</div>
<div
className="flex justify-between whitespace-nowrap overflow-scroll"
data-testid="type"
>
<TxOrderType orderType={Type} />
<button
title="More details"
onClick={() => setOpen(true)}
data-testid="command-details"
>
<Icon name="search-template" />
<Dialog
open={open}
onChange={(isOpen) => setOpen(isOpen)}
intent={Intent.None}
>
<SyntaxHighlighter data={JSON.parse(Command)} />
</Dialog>
</button>
</div>
</div>
);
};

View File

@ -0,0 +1,134 @@
import { TxsInfiniteList } from './txs-infinite-list';
import { render, screen, fireEvent, act } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
const generateTxs = (number: number) => {
return Array.from(Array(number)).map((_) => ({
Type: 'ChainEvent',
Command:
'{"txId":"0xc8941ac4ea989988cb8f72e8fdab2e2009376fd17619491439d36b519d27bc93","nonce":"1494","stakingEvent":{"index":"263","block":"14805346","stakeDeposited":{"ethereumAddress":"0x2e5fe63e5d49c26998cf4bfa9b64de1cf9ae7ef2","vegaPublicKey":"657c2a8a5867c43c831e24820b7544e2fdcc1cf610cfe0ece940fe78137400fd","amount":"38471116086510047870875","blockTime":"1652968806"}}}',
Sig: 'fe7624ab742c492cf1e667e79de4777992aca8e093c8707e1f22685c3125c6082cd21b85cd966a61ad4ca0cca2f8bed3082565caa5915bc3b2f78c1ae35cac0b',
PubKey:
'0x7d69327393cdfaaae50e5e215feca65273eafabfb38f32b8124e66298af346d5',
Nonce: 18296387398179254000,
TxHash:
'0x9C753FA6325F7A40D9C4FA5C25E24476C54613E12B1FA2DD841E3BB00D088B77',
}));
};
describe('Txs infinite list', () => {
it('should display a "no items" message when no items provided', () => {
render(
<TxsInfiniteList
txs={undefined}
areTxsLoading={false}
hasMoreTxs={false}
loadMoreTxs={() => null}
error={undefined}
/>
);
expect(screen.getByText('No items')).toBeInTheDocument();
});
it('error is displayed at item level', () => {
const txs = generateTxs(1);
render(
<TxsInfiniteList
txs={txs}
areTxsLoading={false}
hasMoreTxs={false}
loadMoreTxs={() => null}
error={Error('test error!')}
/>
);
expect(screen.getByText('Error: test error!')).toBeInTheDocument();
});
it('item renders data of n length into list of n length', () => {
// Provided the number of items doesn't exceed the 30 it initially
// desires, all txs will initially render
const txs = generateTxs(10);
render(
<MemoryRouter>
<TxsInfiniteList
txs={txs}
areTxsLoading={false}
hasMoreTxs={false}
loadMoreTxs={() => null}
error={undefined}
/>
</MemoryRouter>
);
expect(
screen
.getByTestId('infinite-scroll-wrapper')
.querySelectorAll('.txs-infinite-list-item')
).toHaveLength(10);
});
it('tries to load more items when required to initially fill the list', () => {
// For example, if initially rendering 15, the bottom of the list is
// in view of the viewport, and the callback should be executed
const txs = generateTxs(15);
const callback = jest.fn();
render(
<MemoryRouter>
<TxsInfiniteList
txs={txs}
areTxsLoading={false}
hasMoreTxs={true}
loadMoreTxs={callback}
error={undefined}
/>
</MemoryRouter>
);
expect(callback.mock.calls.length).toEqual(1);
});
it('does not try to load more items if there are no more', () => {
const txs = generateTxs(3);
const callback = jest.fn();
render(
<MemoryRouter>
<TxsInfiniteList
txs={txs}
areTxsLoading={false}
hasMoreTxs={false}
loadMoreTxs={callback}
error={undefined}
/>
</MemoryRouter>
);
expect(callback.mock.calls.length).toEqual(0);
});
it('loads more items is called when scrolled', () => {
const txs = generateTxs(20);
const callback = jest.fn();
render(
<MemoryRouter>
<TxsInfiniteList
txs={txs}
areTxsLoading={false}
hasMoreTxs={true}
loadMoreTxs={callback}
error={undefined}
/>
</MemoryRouter>
);
act(() => {
fireEvent.scroll(screen.getByTestId('infinite-scroll-wrapper'), {
target: { scrollY: 600 },
});
});
expect(callback.mock.calls.length).toEqual(1);
});
});

View File

@ -0,0 +1,109 @@
import React from 'react';
import { FixedSizeList as List } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';
import { t } from '@vegaprotocol/react-helpers';
import { TxsInfiniteListItem } from './txs-infinite-list-item';
import type { ChainExplorerTxResponse } from '../../routes/types/chain-explorer-response';
interface TxsInfiniteListProps {
hasMoreTxs: boolean;
areTxsLoading: boolean | undefined;
txs: ChainExplorerTxResponse[] | undefined;
loadMoreTxs: () => void;
error: Error | undefined;
className?: string;
}
interface ItemProps {
index: ChainExplorerTxResponse;
style: React.CSSProperties;
isLoading: boolean;
error: Error | undefined;
}
// eslint-disable-next-line @typescript-eslint/no-empty-function
const NOOP = () => {};
const Item = ({ index, style, isLoading, error }: ItemProps) => {
let content;
if (error) {
content = t(`${error}`);
} else if (isLoading) {
content = t('Loading...');
} else {
const { TxHash, PubKey, Type, Command, Sig, Nonce } = index;
content = (
<TxsInfiniteListItem
Type={Type}
Command={Command}
Sig={Sig}
PubKey={PubKey}
Nonce={Nonce}
TxHash={TxHash}
/>
);
}
return <div style={style}>{content}</div>;
};
export const TxsInfiniteList = ({
hasMoreTxs,
areTxsLoading,
txs,
loadMoreTxs,
error,
className,
}: TxsInfiniteListProps) => {
if (!txs) {
return <div>No items</div>;
}
// If there are more items to be loaded then add an extra row to hold a loading indicator.
const itemCount = hasMoreTxs ? txs.length + 1 : txs.length;
// Pass an empty callback to InfiniteLoader in case it asks us to load more than once.
// eslint-disable-next-line @typescript-eslint/no-empty-function
const loadMoreItems = areTxsLoading ? NOOP : loadMoreTxs;
// Every row is loaded except for our loading indicator row.
const isItemLoaded = (index: number) => !hasMoreTxs || index < txs.length;
return (
<div className={className} data-testid="transactions-list">
<div className="grid grid-cols-[repeat(2,_1fr)_240px] gap-12 w-full mb-8">
<div className="text-h5 font-bold">Txn hash</div>
<div className="text-h5 font-bold">Party</div>
<div className="text-h5 font-bold pl-2">Type</div>
</div>
<div data-testid="infinite-scroll-wrapper">
<InfiniteLoader
isItemLoaded={isItemLoaded}
itemCount={itemCount}
loadMoreItems={loadMoreItems}
>
{({ onItemsRendered, ref }) => (
<List
className="List"
height={595}
itemCount={itemCount}
itemSize={41}
onItemsRendered={onItemsRendered}
ref={ref}
width={'100%'}
>
{({ index, style }) => (
<Item
index={txs[index]}
style={style}
isLoading={!isItemLoaded(index)}
error={error}
/>
)}
</List>
)}
</InfiniteLoader>
</div>
</div>
);
};

View File

@ -12,7 +12,7 @@ import Genesis from './genesis';
import { Block } from './blocks/id';
import { Blocks } from './blocks/home';
import { Tx } from './txs/id';
import { Txs as TxHome } from './txs/home';
import { TxsHome } from './txs/home';
import { PendingTxs } from './pending';
import flags from '../config/flags';
import { t } from '@vegaprotocol/react-helpers';
@ -129,7 +129,7 @@ const routerConfig = [
},
{
index: true,
element: <TxHome />,
element: <TxsHome />,
},
],
},

View File

@ -1,32 +1,122 @@
import type { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
import { DATA_SOURCES } from '../../../config';
import { useCallback, useState, useMemo } from 'react';
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../../components/route-title';
import { BlocksRefetch } from '../../../components/blocks';
import { RenderFetched } from '../../../components/render-fetched';
import { BlockTxsData } from '../../../components/txs';
import { JumpToBlock } from '../../../components/jump-to-block';
import { t, useFetch } from '@vegaprotocol/react-helpers';
import { TxsInfiniteList } from '../../../components/txs';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { ChainExplorerTxResponse } from '../../types/chain-explorer-response';
import type { TendermintBlockchainResponse } from '../../blocks/tendermint-blockchain-response';
const Txs = () => {
const {
state: { data, error, loading },
refetch,
} = useFetch<TendermintBlockchainResponse>(
`${DATA_SOURCES.tendermintUrl}/blockchain`
interface TxsProps {
latestBlockHeight: string;
}
interface TxsStateProps {
txsData: ChainExplorerTxResponse[];
hasMoreTxs: boolean;
nextPage: number;
}
const Txs = ({ latestBlockHeight }: TxsProps) => {
const [{ txsData, hasMoreTxs, nextPage }, setTxsState] =
useState<TxsStateProps>({
txsData: [],
hasMoreTxs: true,
nextPage: 1,
});
const reusedBodyParams = useMemo(
() => ({
node_url: DATA_SOURCES.tendermintUrl,
transaction_height: parseInt(latestBlockHeight),
page_size: 30,
}),
[latestBlockHeight]
);
const {
state: { error, loading },
refetch,
} = useFetch(
DATA_SOURCES.chainExplorerUrl,
{
method: 'POST',
body: JSON.stringify(reusedBodyParams),
},
false
);
const loadTxs = useCallback(async () => {
const data = await refetch(
undefined,
JSON.stringify({
...reusedBodyParams,
page_number: nextPage,
})
);
if (data) {
setTxsState((prev) => ({
...prev,
nextPage: prev.nextPage + 1,
hasMoreTxs: true,
txsData: [...prev.txsData, ...(data as ChainExplorerTxResponse[])],
}));
}
}, [nextPage, refetch, reusedBodyParams]);
return (
<section>
<RouteTitle>{t('Transactions')}</RouteTitle>
<RenderFetched error={error} loading={loading}>
<>
<BlocksRefetch refetch={refetch} />
<BlockTxsData data={data} />
</>
</RenderFetched>
<BlocksRefetch
refetch={() =>
refetch(
undefined,
JSON.stringify({
...reusedBodyParams,
page_number: 1,
})
)
}
/>
<TxsInfiniteList
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
txs={txsData}
loadMoreTxs={loadTxs}
error={error}
className="mb-28"
/>
<JumpToBlock />
</section>
);
};
export { Txs };
const Wrapper = () => {
const {
state: { data, error, loading },
} = useFetch<TendermintBlockchainResponse>(
`${DATA_SOURCES.tendermintUrl}/blockchain`
);
return (
<AsyncRenderer
loading={!!loading}
loadingMessage={t('Getting latest block height...')}
error={error}
data={data}
noDataMessage={t('Could not get latest block height')}
render={(data) => (
<Txs
latestBlockHeight={
data?.result?.block_metas?.[0]?.header?.height || ''
}
/>
)}
/>
);
};
export { Wrapper as TxsHome };

View File

@ -0,0 +1,18 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
baseUrl: 'http://localhost:4200',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: './src/integration/*.ts',
excludeSpecPattern: '**/*.js',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
chromeWebSecurity: false,
},
});

View File

@ -1,16 +0,0 @@
{
"baseUrl": "http://localhost:4200",
"fileServerFolder": ".",
"testFiles": "*.{ts,feature,features}",
"ignoreTestFiles": "**/*.js",
"fixturesFolder": "./src/fixtures",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"pluginsFile": false,
"video": true,
"videosFolder": "../../dist/cypress/apps/simple-trading-app-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/simple-trading-app-e2e/screenshots",
"chromeWebSecurity": false,
"projectId": "et4snf"
}

View File

@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/simple-trading-app-e2e/cypress.json",
"cypressConfig": "apps/simple-trading-app-e2e/cypress.config.js",
"devServerTarget": "simple-trading-app:serve"
},
"configurations": {

View File

@ -60,7 +60,7 @@ describe('market list', () => {
cy.intercept('POST', '/query').as('Filters');
cy.visit('/markets');
cy.wait('@Filters').then((filters) => {
if (filters?.response?.body.data.markets.length) {
if (filters?.response?.body?.data?.markets?.length) {
const asset =
filters.response.body.data.markets[0].tradableInstrument.instrument
.product.settlementAsset.symbol;

View File

@ -47,6 +47,10 @@ export interface SimpleMarkets_markets_tradableInstrument_instrument_product_set
export interface SimpleMarkets_markets_tradableInstrument_instrument_product {
__typename: "Future";
/**
* String representing the quote (e.g. BTCUSD -> USD is quote)
*/
quoteName: string;
/**
* The name of the asset (string)
*/

View File

@ -1,19 +1,5 @@
import { Intent } from '@vegaprotocol/ui-toolkit';
import { MarketState } from '@vegaprotocol/types';
import { t } from '@vegaprotocol/react-helpers';
export const MARKET_STATUS: Record<MarketState | '', Intent> = {
[MarketState.Active]: Intent.Success,
[MarketState.Cancelled]: Intent.Primary,
[MarketState.Closed]: Intent.None,
[MarketState.Pending]: Intent.Warning,
[MarketState.Proposed]: Intent.Warning,
[MarketState.Rejected]: Intent.Danger,
[MarketState.Settled]: Intent.Primary,
[MarketState.Suspended]: Intent.Warning,
[MarketState.TradingTerminated]: Intent.Danger,
'': Intent.Primary,
};
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
export const STATES_FILTER = [
{ value: 'all', text: t('All') },
@ -27,3 +13,115 @@ export const STATES_FILTER = [
{ value: 'Suspended', text: t('Suspended') },
{ value: 'TradingTerminated', text: t('TradingTerminated') },
];
export const agGridLightVariables = `
.ag-theme-balham {
--ag-row-border-color: ${theme.colors.transparent};
--ag-row-hover-color: ${theme.colors.transparent};
--ag-font-size: 15px;
}
.ag-theme-balham .ag-row-hover {
--ag-row-border-color: ${theme.colors.black[100]};
}
.ag-theme-balham [col-id="status"] .ag-header-cell-label,
.ag-theme-balham [col-id="asset"] .ag-header-cell-label,
.ag-theme-balham [col-id="change"] .ag-header-cell-label{
justify-content: center;
}
.ag-theme-balham .ag-header-row .ag-header-cell:first-child{
padding-left: 0;
}
.ag-theme-balham .ag-ltr .ag-header-cell::after, .ag-theme-balham .ag-ltr .ag-header-group-cell::after {
right: 0;
}
.ag-theme-balham .ag-header-cell::after{
width: 0;
}
.ag-theme-balham .ag-header{
border-bottom-width: 0;
}
.ag-theme-balham .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
outline: none;
border-width: 0;
}
.ag-theme-balham .ag-header-label-icon .ag-icon{
position: relative;
}
.ag-theme-balham .ag-icon::before{
font-size: 10px;
line-height: 12px;
position: absolute;
transform: rotate(45deg);
top: -6px;
right: -14px;
content: "◾";
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham .ag-icon-desc::before{
background: -webkit-linear-gradient(135deg, #000 0%, #000 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.54) 52%, rgba(0,0,0,0.54) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham .ag-icon-asc::before{
background: -webkit-linear-gradient(135deg, rgba(0,0,0,0.54) 0%, rgba(0,0,0,0.54) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #000 52%, #000 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
`;
export const agGridDarkVariables = `
.ag-theme-balham-dark {
--ag-row-border-color: ${theme.colors.transparent};
--ag-row-hover-color: ${theme.colors.transparent};
--ag-font-size: 15px;
}
.ag-theme-balham-dark .ag-row-hover {
--ag-row-border-color: ${theme.colors.white[100]};
}
.ag-theme-balham-dark [col-id="status"] .ag-header-cell-label,
.ag-theme-balham-dark [col-id="asset"] .ag-header-cell-label,
.ag-theme-balham-dark [col-id="change"] .ag-header-cell-label{
justify-content: center;
}
.ag-theme-balham-dark .ag-header-row .ag-header-cell:first-child{
padding-left: 0;
}
.ag-theme-balham-dark .ag-header-cell::after{
width: 0;
}
.ag-theme-balham-dark .ag-header{
border-bottom-width: 0;
}
.ag-theme-balham-dark .ag-has-focus .ag-row.ag-row-focus .ag-cell-focus {
outline: none;
border-width: 0;
}
.ag-theme-balham-dark .ag-header-label-icon .ag-icon{
position: relative;
}
.ag-theme-balham-dark .ag-icon::before{
font-size: 10px;
line-height: 12px;
position: absolute;
transform: rotate(45deg);
top: -6px;
right: -14px;
content: "◾";
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-position: center;
}
.ag-theme-balham-dark .ag-icon-desc::before{
background: -webkit-linear-gradient(135deg, #fff 0%, #fff 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, rgba(245, 245, 245, 0.64) 52%, rgba(245, 245, 245, 0.64) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.ag-theme-balham-dark .ag-icon-asc::before{
background: -webkit-linear-gradient(135deg, rgba(245, 245, 245, 0.64) 0%, rgba(245, 245, 245, 0.64) 40%, rgba(0,0,0,0) 40%, rgba(0,0,0,0) 52%, #fff 52%, #fff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
`;

View File

@ -36,6 +36,7 @@ export const MARKETS_QUERY = gql`
product {
__typename
... on Future {
quoteName
settlementAsset {
symbol
}

View File

@ -1,6 +1,12 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { render, screen, waitFor } from '@testing-library/react';
import {
render,
screen,
waitFor,
cleanup,
getAllByRole,
} from '@testing-library/react';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { BrowserRouter } from 'react-router-dom';
@ -37,6 +43,7 @@ describe('SimpleMarketList', () => {
afterEach(() => {
jest.clearAllMocks();
cleanup();
});
it('should be properly renderer as empty', async () => {
@ -130,8 +137,12 @@ describe('SimpleMarketList', () => {
await new Promise((resolve) => setTimeout(resolve, 0));
});
await waitFor(() => {
expect(screen.getByTestId('simple-market-list')).toBeInTheDocument();
expect(
document.querySelector('.ag-center-cols-container')
).toBeInTheDocument();
});
expect(screen.getByTestId('simple-market-list').children).toHaveLength(2);
const container = document.querySelector('.ag-center-cols-container');
expect(getAllByRole(container as HTMLDivElement, 'row')).toHaveLength(2);
});
});

View File

@ -1,17 +1,29 @@
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
import React, {
useCallback,
useContext,
useEffect,
useMemo,
useRef,
} from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { subDays } from 'date-fns';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { useDataProvider } from '@vegaprotocol/react-helpers';
import { t } from '@vegaprotocol/react-helpers';
import { AsyncRenderer, Lozenge, Splash } from '@vegaprotocol/ui-toolkit';
import { Button } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { ThemeContext } from '@vegaprotocol/react-helpers';
import type { MarketState } from '@vegaprotocol/types';
import SimpleMarketPercentChange from './simple-market-percent-change';
import SimpleMarketExpires from './simple-market-expires';
import DataProvider from './data-provider';
import { MARKET_STATUS } from './constants';
import SimpleMarketToolbar from './simple-market-toolbar';
import useMarketsFilterData from '../../hooks/use-markets-filter-data';
import useColumnDefinitions from '../../hooks/use-column-definitions';
import DataProvider from './data-provider';
import * as constants from './constants';
import SimpleMarketToolbar from './simple-market-toolbar';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
export type SimpleMarketsType = SimpleMarkets_markets & {
percentChange?: number | '-';
};
export type RouterParams = Partial<{
product: string;
@ -22,8 +34,9 @@ export type RouterParams = Partial<{
const SimpleMarketList = () => {
const navigate = useNavigate();
const params = useParams<RouterParams>();
const theme = useContext(ThemeContext);
const statusesRef = useRef<Record<string, MarketState | ''>>({});
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(
() => ({
CandleSince: subDays(Date.now(), 1).toJSON(),
@ -40,7 +53,14 @@ const SimpleMarketList = () => {
update,
variables
);
const localData = useMarketsFilterData(data || [], params);
const localData: Array<SimpleMarketsType> = useMarketsFilterData(
data || [],
params
);
const handleOnGridReady = useCallback(() => {
gridRef.current?.api.sizeColumnsToFit();
}, [gridRef]);
useEffect(() => {
const statuses: Record<string, MarketState | ''> = {};
@ -50,6 +70,11 @@ const SimpleMarketList = () => {
statusesRef.current = statuses;
}, [data, statusesRef]);
useEffect(() => {
window.addEventListener('resize', handleOnGridReady);
return () => window.removeEventListener('resize', handleOnGridReady);
}, [handleOnGridReady]);
const onClick = useCallback(
(marketId) => {
navigate(`/trading/${marketId}`);
@ -57,62 +82,34 @@ const SimpleMarketList = () => {
[navigate]
);
const { columnDefs, defaultColDef } = useColumnDefinitions({ onClick });
const getRowId = useCallback(({ data }) => data.id, []);
return (
<>
<div className="h-full grid grid-rows-[min-content,1fr]">
<SimpleMarketToolbar />
<AsyncRenderer loading={loading} error={error} data={localData}>
{localData && localData.length > 0 ? (
<ul
className="list-none relative pt-8 pb-8"
data-testid="simple-market-list"
>
{localData?.map((market) => (
<li
className="w-full relative flex justify-start items-center no-underline box-border text-left py-8 mb-10"
key={market.id}
>
<div className="w-full grid sm:grid-cols-2">
<div className="w-full grid sm:auto-rows-auto">
<div className="font-extrabold py-2">{market.name}</div>
<SimpleMarketExpires
tags={market.tradableInstrument.instrument.metadata.tags}
/>
<div className="py-2">{`${t('settled in')} ${
market.tradableInstrument.instrument.product
.settlementAsset.symbol
}`}</div>
</div>
<div className="w-full grid sm:grid-rows-2">
<div>
<SimpleMarketPercentChange
candles={market.candles}
marketId={market.id}
/>
</div>
<div>
<Lozenge
variant={MARKET_STATUS[market.data?.market.state || '']}
>
{market.data?.market.state}
</Lozenge>
</div>
</div>
</div>
<div className="absolute right-16 top-1/2 -translate-y-1/2">
<Button
onClick={() => onClick(market.id)}
variant="inline-link"
prependIconName="chevron-right"
/>
</div>
</li>
))}
</ul>
) : (
<Splash>{t('No data to display')}</Splash>
)}
<AgGrid
className="mb-32 min-h-[300px]"
defaultColDef={defaultColDef}
columnDefs={columnDefs}
rowData={localData}
rowHeight={60}
customThemeParams={
theme === 'dark'
? constants.agGridDarkVariables
: constants.agGridLightVariables
}
onGridReady={handleOnGridReady}
ref={gridRef}
overlayNoRowsTemplate={t('No data to display')}
suppressContextMenu
getRowId={getRowId}
suppressMovableColumns
/>
</AsyncRenderer>
</>
</div>
);
};

View File

@ -7,11 +7,16 @@ import type { SimpleMarkets_markets_candles } from './__generated__/SimpleMarket
describe('SimpleMarketPercentChange should parse proper change', () => {
let candles: (SimpleMarkets_markets_candles | null)[] | null;
const setValue = () => undefined;
it('empty array', () => {
candles = [];
render(
<MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
<SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider>
);
expect(screen.getByText('-')).toBeInTheDocument();
@ -20,7 +25,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
candles = null;
render(
<MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
<SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider>
);
expect(screen.getByText('-')).toBeInTheDocument();
@ -33,7 +42,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
];
render(
<MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
<SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider>
);
expect(screen.getByText('100.000%')).toBeInTheDocument();
@ -49,7 +62,11 @@ describe('SimpleMarketPercentChange should parse proper change', () => {
];
render(
<MockedProvider>
<SimpleMarketPercentChange candles={candles} marketId={'1'} />
<SimpleMarketPercentChange
candles={candles}
marketId={'1'}
setValue={setValue}
/>
</MockedProvider>
);
expect(screen.getByText('-50.000%')).toBeInTheDocument();

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { InView } from 'react-intersection-observer';
import { useSubscription } from '@apollo/client';
import { themelite as theme } from '@vegaprotocol/tailwindcss-config';
@ -12,6 +12,7 @@ import { CANDLE_SUB } from './data-provider';
interface Props {
candles: (SimpleMarkets_markets_candles | null)[] | null;
marketId: string;
setValue: (arg: unknown) => void;
}
const getChange = (
@ -51,20 +52,32 @@ const SimpleMarketPercentChangeWrapper = (props: Props) => {
return (
// @ts-ignore falsy wrong type?
<InView onChange={setInView}>
<InView
onChange={setInView}
className="flex h-full items-center justify-center"
>
{inView && <SimpleMarketPercentChange {...props} />}
</InView>
);
};
const SimpleMarketPercentChange = ({ candles, marketId }: Props) => {
const SimpleMarketPercentChange = ({ candles, marketId, setValue }: Props) => {
const { data: { candles: { close = undefined } = {} } = {} } =
useSubscription<CandleLive, CandleLiveVariables>(CANDLE_SUB, {
variables: { marketId },
});
const change = getChange(candles, close);
const color = getColor(change);
return <p style={{ color }}>{change}</p>;
useEffect(() => {
const value = parseFloat(change);
setValue(isNaN(value) ? '-' : value);
}, [setValue, change]);
return (
<div className="flex text-center" style={{ color }}>
{change}
</div>
);
};
export default SimpleMarketPercentChangeWrapper;

View File

@ -0,0 +1,26 @@
import React from 'react';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import SimpleMarketExpires from './simple-market-expires';
interface Props {
data: SimpleMarkets_markets;
}
const MarketNameRenderer = ({ data }: Props) => {
return (
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-8 gap-y-0 grid-cols-[min-content,1fr,1fr]">
<div className="w-60 row-span-2 bg-pink rounded-full w-44 h-44 bg-gradient-to-br from-white-60 to--white-80 opacity-30" />
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-market self-end">
{data.name}{' '}
<SimpleMarketExpires
tags={data.tradableInstrument.instrument.metadata.tags}
/>
</div>
<div className="col-span-2 ui-small text-deemphasise dark:text-midGrey self-start leading-3">
{data.tradableInstrument.instrument.product.quoteName}
</div>
</div>
);
};
export default MarketNameRenderer;

View File

@ -67,7 +67,7 @@ const SimpleMarketToolbar = () => {
);
return (
<div className="w-max mb-16 font-alpha">
<div className="w-max mb-32 font-alpha">
<ul
ref={slideContRef}
className="grid grid-flow-col auto-cols-min gap-8 relative pb-4 mb-16"
@ -162,7 +162,7 @@ const SimpleMarketToolbar = () => {
</div>
{activeNumber > 0 && (
<ul
className="grid grid-flow-col auto-cols-min md:gap-16 sm:gap-12 pb-4 md:ml-16"
className="grid grid-flow-col auto-cols-min md:gap-16 gap-12 pb-4 md:ml-16"
data-testid="market-assets-menu"
aria-label={t('Asset on the market')}
>

View File

@ -0,0 +1,125 @@
import React, { useMemo } from 'react';
import { t } from '@vegaprotocol/react-helpers';
import type { SimpleMarkets_markets } from '../components/simple-market-list/__generated__/SimpleMarkets';
import MarketNameRenderer from '../components/simple-market-list/simple-market-renderer';
import SimpleMarketPercentChange from '../components/simple-market-list/simple-market-percent-change';
import { Button } from '@vegaprotocol/ui-toolkit';
import type { ValueSetterParams } from 'ag-grid-community';
import type { SimpleMarketsType } from '../components/simple-market-list/simple-market-list';
interface Props {
onClick: (marketId: string) => void;
}
const useColumnDefinitions = ({ onClick }: Props) => {
const columnDefs = useMemo(() => {
return [
{
colId: 'market',
headerName: t('Markets'),
headerClass: 'uppercase',
minWidth: 300,
field: 'name',
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
<MarketNameRenderer data={data} />
),
},
{
colId: 'asset',
headerName: t('Settlement asset'),
headerClass: 'uppercase',
minWidth: 100,
cellClass: 'uppercase flex h-full items-center',
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
<div className="flex h-full items-center justify-center">
{data.tradableInstrument.instrument.product.settlementAsset.symbol}
</div>
),
},
{
colId: 'change',
headerName: t('24h change'),
headerClass: 'uppercase',
field: 'percentChange',
minWidth: 100,
valueSetter: (params: ValueSetterParams): boolean => {
const { oldValue, newValue, api, data } = params;
if (oldValue !== newValue) {
const newdata = { percentChange: newValue, ...data };
api.applyTransaction({ update: [newdata] });
return true;
}
return false;
},
cellRenderer: ({
data,
setValue,
}: {
data: SimpleMarketsType;
setValue: (arg: unknown) => void;
}) => (
<SimpleMarketPercentChange
candles={data.candles}
marketId={data.id}
setValue={setValue}
/>
),
comparator: (valueA: number | '-', valueB: number | '-') => {
if (valueA === valueB) return 0;
if (valueA === '-') {
return -1;
}
if (valueB === '-') {
return 1;
}
return valueA > valueB ? 1 : -1;
},
},
{
colId: 'status',
headerName: t('Status'),
field: 'data.market.state',
headerClass: 'uppercase',
minWidth: 100,
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
<div className="uppercase flex h-full items-center justify-center">
<div className="border text-center px-8">
{data.data?.market.state}
</div>
</div>
),
},
{
colId: 'trade',
headerName: '',
headerClass: 'uppercase',
sortable: false,
minWidth: 100,
cellRenderer: ({ data }: { data: SimpleMarkets_markets }) => (
<div className="h-full flex h-full items-center justify-end">
<Button
onClick={() => onClick(data.id)}
variant="inline-link"
appendIconName="arrow-top-right"
className="uppercase no-underline hover:no-underline"
>
{t('Trade')}
</Button>
</div>
),
},
];
}, [onClick]);
const defaultColDef = useMemo(() => {
return {
sortable: true,
unSortIcon: true,
};
}, []);
return { columnDefs, defaultColDef };
};
export default useColumnDefinitions;

View File

@ -38,7 +38,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "124661.013711767544930765",
"locked_amount": "123949.3351449367653555",
"deposits": [
{
"amount": "129999.45",
@ -488,7 +488,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "65503.04337545080622283",
"locked_amount": "64804.952997247079204604",
"deposits": [
{
"amount": "97499.58",
@ -521,7 +521,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "0",
"locked_amount": "89531.35166557213976323285608",
"locked_amount": "88577.18263884362270361639432",
"deposits": [
{
"amount": "135173.4239508",
@ -554,7 +554,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "27555.99917099787937361",
"locked_amount": "27262.324604263582721104",
"deposits": [
{
"amount": "32499.86",
@ -587,7 +587,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "8969.210333073256934278",
"locked_amount": "8873.622111351784071168",
"deposits": [
{
"amount": "10833.29",
@ -675,7 +675,7 @@
"tranche_end": "2022-11-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "0",
"locked_amount": "15468.3027626811615",
"locked_amount": "15101.45352128623275",
"deposits": [
{
"amount": "15000",
@ -761,7 +761,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "0",
"locked_amount": "1804379.69836047439128314",
"locked_amount": "1788435.020023831121162896",
"deposits": [
{
"amount": "1852091.69",
@ -1777,7 +1777,7 @@
"tranche_end": "2022-09-30T00:00:00.000Z",
"total_added": "60916.66666633337",
"total_removed": "18705.279504739679372649",
"locked_amount": "14760.0056484630890178248654459722",
"locked_amount": "14291.414097045104884895635487211",
"deposits": [
{
"amount": "2833.333333",
@ -5226,9 +5226,9 @@
"tranche_id": 11,
"tranche_start": "2021-09-03T00:00:00.000Z",
"tranche_end": "2022-09-03T00:00:00.000Z",
"total_added": "19455.000000000000000003",
"total_added": "19457.000000000000000003",
"total_removed": "5056.88782409978",
"locked_amount": "3597.64752092846370375055476445966514475",
"locked_amount": "3438.09619897894364166053010683028919314",
"deposits": [
{
"amount": "75",
@ -5260,6 +5260,11 @@
"user": "0x3a380f7CFdEeb723228cA57d2795EA215094000d",
"tx": "0x72adba1aa2d918148f1a95eb6a447da22fa06217cadafa94d56f12fd22a389be"
},
{
"amount": "2",
"user": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
"tx": "0x09792a45790c762f046f34979cbbe7687661ce7643da9ba6bc9c128710191b46"
},
{
"amount": "30",
"user": "0xb4eE687f019A8e48F7087f4b4f8653208B8cc48f",
@ -8669,6 +8674,21 @@
"withdrawn_tokens": "940.30940496195",
"remaining_tokens": "649.69059503805"
},
{
"address": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
"deposits": [
{
"amount": "2",
"user": "0xB2BddeEb907797F9be51E3f9f75824f83cD71B67",
"tranche_id": 11,
"tx": "0x09792a45790c762f046f34979cbbe7687661ce7643da9ba6bc9c128710191b46"
}
],
"withdrawals": [],
"total_tokens": "2",
"withdrawn_tokens": "0",
"remaining_tokens": "2"
},
{
"address": "0xb4eE687f019A8e48F7087f4b4f8653208B8cc48f",
"deposits": [
@ -14123,7 +14143,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "74162.9780761646031",
"locked_amount": "2797204.7028220379638062054",
"locked_amount": "2772703.2835660003368440463",
"deposits": [
{
"amount": "1998.95815",
@ -14835,8 +14855,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15788853.065470999700000001",
"total_removed": "7926.7627792759659",
"locked_amount": "15140482.7365636033722263648641426217383077",
"total_removed": "8977.81072959055965",
"locked_amount": "15054047.07609322321427388182456345806299",
"deposits": [
{
"amount": "16249.93",
@ -15360,6 +15380,26 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{
"amount": "180.278846094717375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xca332ec8365253d3b17445c182d287e56783ab4552f3581f3a43dc6334fbfc7b"
},
{
"amount": "173.18268014001675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x793dabbba22d3e8e88f7600162ee6456d6d1b58e49ce07209d3f97eaa485161f"
},
{
"amount": "368.40753967648125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x959378e42a224a7cff0dde794e80a0f07cbbe6b61e9158092a037efaa726aa28"
},
{
"amount": "329.178884403378375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x57a5f040ac80c34cc5c90c2cf939c21fa2ef872042998703d69995c71184ab44"
},
{
"amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15482,6 +15522,30 @@
"tranche_id": 2,
"tx": "0xa79f7f3e6436a1f473f3beab9e0a5c8bc4f52b38ac7aedb8610a1a9a9c4a786c"
},
{
"amount": "180.278846094717375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0xca332ec8365253d3b17445c182d287e56783ab4552f3581f3a43dc6334fbfc7b"
},
{
"amount": "173.18268014001675",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x793dabbba22d3e8e88f7600162ee6456d6d1b58e49ce07209d3f97eaa485161f"
},
{
"amount": "368.40753967648125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x959378e42a224a7cff0dde794e80a0f07cbbe6b61e9158092a037efaa726aa28"
},
{
"amount": "329.178884403378375",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x57a5f040ac80c34cc5c90c2cf939c21fa2ef872042998703d69995c71184ab44"
},
{
"amount": "2446.31552516990115",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@ -15526,8 +15590,8 @@
}
],
"total_tokens": "194999.1675",
"withdrawn_tokens": "7926.7627792759659",
"remaining_tokens": "187072.4047207240341"
"withdrawn_tokens": "8977.81072959055965",
"remaining_tokens": "186021.35677040944035"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@ -16931,8 +16995,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "2113641.840890679071831632",
"locked_amount": "8328080.66510881884167481291924447",
"total_removed": "2158746.556523576443330677",
"locked_amount": "8247873.1795980633455589418403988",
"deposits": [
{
"amount": "129284.449",
@ -17181,6 +17245,41 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{
"amount": "331.7071722886032605",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xc279c1a171709ff76a9875fea2e8f98ceb5797714ead511ba95a12f61412fc13"
},
{
"amount": "321.15600624174480075",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x7289f6af545a72235c26ecd98b8a0c14fbb5e62af98ded20862cfad2ef59d3bc"
},
{
"amount": "679.7663781595895195",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x4cef72f21dd9cf51cb6fbc965bc0b514b9c2ea4e2d9ca62f19c5a5defc6eef60"
},
{
"amount": "614.746506577953841",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x4ef5e959da18a286e010bc26fdc2bdda73f3e612334b232cb3e4af4c8c3d018d"
},
{
"amount": "17701.463096745440206695",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
"tx": "0x8ff111c8ad92e1fa74add7edaadb2a7fd781d6d50e43c17d8042b32054c2a210"
},
{
"amount": "20364.3503859717532968",
"user": "0x47FbE34Fd93416c63d35544dEE6c6f442Db8513d",
"tx": "0x0327fb136e8b6a56b8ed65d6f5e7c729455a2b3d3684fc22c92cedcce5c00fc5"
},
{
"amount": "5091.5260869122865738",
"user": "0xcc2cf726A84e71301f9079FC177BA946aa98A694",
"tx": "0x1f9f8184e28b9dabb8878b08ea6e50c93b7f18a724b1070a62c0223f12256339"
},
{
"amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -18799,6 +18898,30 @@
"tranche_id": 3,
"tx": "0xc62ffa6bf5029422f44d9406972fc074b498e02f667a86ae9faba138b6cfd758"
},
{
"amount": "331.7071722886032605",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xc279c1a171709ff76a9875fea2e8f98ceb5797714ead511ba95a12f61412fc13"
},
{
"amount": "321.15600624174480075",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x7289f6af545a72235c26ecd98b8a0c14fbb5e62af98ded20862cfad2ef59d3bc"
},
{
"amount": "679.7663781595895195",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x4cef72f21dd9cf51cb6fbc965bc0b514b9c2ea4e2d9ca62f19c5a5defc6eef60"
},
{
"amount": "614.746506577953841",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x4ef5e959da18a286e010bc26fdc2bdda73f3e612334b232cb3e4af4c8c3d018d"
},
{
"amount": "652.48254356494551875",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@ -20037,8 +20160,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "154092.9757912160630365",
"remaining_tokens": "205030.4937837839369635"
"withdrawn_tokens": "156040.35185448395445825",
"remaining_tokens": "203083.11772051604554175"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@ -20185,6 +20308,12 @@
}
],
"withdrawals": [
{
"amount": "5091.5260869122865738",
"user": "0xcc2cf726A84e71301f9079FC177BA946aa98A694",
"tranche_id": 3,
"tx": "0x1f9f8184e28b9dabb8878b08ea6e50c93b7f18a724b1070a62c0223f12256339"
},
{
"amount": "6310.1901227904940866",
"user": "0xcc2cf726A84e71301f9079FC177BA946aa98A694",
@ -20211,8 +20340,8 @@
}
],
"total_tokens": "66299.71746",
"withdrawn_tokens": "23745.761900811119424",
"remaining_tokens": "42553.955559188880576"
"withdrawn_tokens": "28837.2879877234059978",
"remaining_tokens": "37462.4294722765940022"
},
{
"address": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@ -20529,6 +20658,12 @@
}
],
"withdrawals": [
{
"amount": "20364.3503859717532968",
"user": "0x47FbE34Fd93416c63d35544dEE6c6f442Db8513d",
"tranche_id": 3,
"tx": "0x0327fb136e8b6a56b8ed65d6f5e7c729455a2b3d3684fc22c92cedcce5c00fc5"
},
{
"amount": "25242.3008271937471296",
"user": "0x47FbE34Fd93416c63d35544dEE6c6f442Db8513d",
@ -20555,8 +20690,8 @@
}
],
"total_tokens": "265198.86984",
"withdrawn_tokens": "94983.1600369573350624",
"remaining_tokens": "170215.7098030426649376"
"withdrawn_tokens": "115347.5104229290883592",
"remaining_tokens": "149851.3594170709116408"
},
{
"address": "0x35022e6c85B50F4904C7BC7e692A9F3bbEE8D2CE",
@ -20726,6 +20861,12 @@
}
],
"withdrawals": [
{
"amount": "17701.463096745440206695",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
"tranche_id": 3,
"tx": "0x8ff111c8ad92e1fa74add7edaadb2a7fd781d6d50e43c17d8042b32054c2a210"
},
{
"amount": "45513.77740810577941926",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
@ -20746,8 +20887,8 @@
}
],
"total_tokens": "230391.5181735",
"withdrawn_tokens": "82506.25192192818881679",
"remaining_tokens": "147885.26625157181118321"
"withdrawn_tokens": "100207.715018673629023485",
"remaining_tokens": "130183.803154826370976515"
},
{
"address": "0xC6b2864fbCD2A8dEC8DEfD1D421bC1E450C370AD",
@ -21034,8 +21175,8 @@
"tranche_start": "2021-10-05T00:00:00.000Z",
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "1390546.591547348229906227",
"locked_amount": "2973571.63505682836710488797665665",
"total_removed": "1417003.438054406291892727",
"locked_amount": "2941881.17244069509858904234270872",
"deposits": [
{
"amount": "552496.6455",
@ -21184,6 +21325,21 @@
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tx": "0xa6b9993b9288eac739756499b2997155ce8b1bc6e862c1a6569c8e04463bb31f"
},
{
"amount": "3635.564694620199747",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tx": "0x89b7e9f6200ba171c5f1115f9fd110284ef4ffaba58cf174b686bbe4905fe18a"
},
{
"amount": "10109.350358551032104",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
"tx": "0xc695996be3df837d7040aec8c5532965ac6554d5615575ee46fc87d604e2ebc0"
},
{
"amount": "12711.9314538868301355",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
"tx": "0x68b69c1a3ebd9666638809539b4d8ced5b1ab8fe19c720dfffdc548b378513d3"
},
{
"amount": "13341.31568777778021",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
@ -21552,6 +21708,12 @@
}
],
"withdrawals": [
{
"amount": "12711.9314538868301355",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
"tranche_id": 4,
"tx": "0x68b69c1a3ebd9666638809539b4d8ced5b1ab8fe19c720dfffdc548b378513d3"
},
{
"amount": "32683.576612762644585",
"user": "0x91715128a71c9C734CDC20E5EdEEeA02E72e428E",
@ -21572,8 +21734,8 @@
}
],
"total_tokens": "165749.29365",
"withdrawn_tokens": "68641.624849504055385",
"remaining_tokens": "97107.668800495944615"
"withdrawn_tokens": "81353.5563033908855205",
"remaining_tokens": "84395.7373466091144795"
},
{
"address": "0x72e7BB93E73b2885a22CA29c34759361399a5C0e",
@ -21666,6 +21828,12 @@
}
],
"withdrawals": [
{
"amount": "10109.350358551032104",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
"tranche_id": 4,
"tx": "0xc695996be3df837d7040aec8c5532965ac6554d5615575ee46fc87d604e2ebc0"
},
{
"amount": "7013.412182841867109",
"user": "0xBc934494675a6ceB639B9EfEe5b9C0f017D35a75",
@ -21722,8 +21890,8 @@
}
],
"total_tokens": "110499.5291",
"withdrawn_tokens": "43654.936134887817154",
"remaining_tokens": "66844.592965112182846"
"withdrawn_tokens": "53764.286493438849258",
"remaining_tokens": "56735.242606561150742"
},
{
"address": "0xdbC5d439F373EB646345e1c67D1d46231ACE7dD3",
@ -22018,6 +22186,12 @@
"tranche_id": 4,
"tx": "0xa6b9993b9288eac739756499b2997155ce8b1bc6e862c1a6569c8e04463bb31f"
},
{
"amount": "3635.564694620199747",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
"tranche_id": 4,
"tx": "0x89b7e9f6200ba171c5f1115f9fd110284ef4ffaba58cf174b686bbe4905fe18a"
},
{
"amount": "13341.31568777778021",
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
@ -22080,8 +22254,8 @@
}
],
"total_tokens": "331498.5873",
"withdrawn_tokens": "157304.28913691490831",
"remaining_tokens": "174194.29816308509169"
"withdrawn_tokens": "160939.853831535108057",
"remaining_tokens": "170558.733468464891943"
},
{
"address": "0x16da609341ed67750A8BCC5AAa2005471006Cd77",
@ -22157,8 +22331,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "34.173053016",
"locked_amount": "443233.07466590775804275636428212",
"total_removed": "75.634316334",
"locked_amount": "439350.6847287052296870462810756",
"deposits": [
{
"amount": "3000",
@ -28786,6 +28960,11 @@
"amount": "23.010020292",
"user": "0xD27929d68ac0E5fd5C919A5eb5968C1D06D3Fb83",
"tx": "0x8daf320262d0384cf5f9c290cc33721587beabd5e93026b3e9b76dc3fcd6659c"
},
{
"amount": "41.461263318",
"user": "0x6EfF23b65688CaE71Af19128A7674b7Dd53f7f19",
"tx": "0x16a1ae4e6602247b005985fff7824f896a344caa9b6be09f4eacd3df840fa36c"
}
],
"users": [
@ -43700,10 +43879,17 @@
"tx": "0xd23813c30e93f3867eaa257b7aef7052a050b1ee1c1a90102a3f40c5d989fe82"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "41.461263318",
"user": "0x6EfF23b65688CaE71Af19128A7674b7Dd53f7f19",
"tranche_id": 5,
"tx": "0x16a1ae4e6602247b005985fff7824f896a344caa9b6be09f4eacd3df840fa36c"
}
],
"total_tokens": "600",
"withdrawn_tokens": "0",
"remaining_tokens": "600"
"withdrawn_tokens": "41.461263318",
"remaining_tokens": "558.538736682"
},
{
"address": "0xa37d20cd446898508Ab961c6Be8d3fe1c15413fA",
@ -47836,7 +48022,7 @@
"tranche_start": "2021-12-05T00:00:00.000Z",
"tranche_end": "2022-06-05T00:00:00.000Z",
"total_added": "171288.42",
"total_removed": "32094.1716569431377",
"total_removed": "32535.6624708206377",
"locked_amount": "0",
"deposits": [
{
@ -52136,6 +52322,26 @@
"user": "0xDD5730a33719083470e641cF0e4154Dd04D5738d",
"tx": "0x49bd6332008e65069aad8012f76f15f3dae19f664237b02f9152946297db812d"
},
{
"amount": "101.100697625",
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
"tx": "0x204f08eb22345e352efcbfd3921f813fdf1097ea3fc33b36387c3c1cc5678936"
},
{
"amount": "250",
"user": "0xbCc28D073f7a96c0568D36FdB21A98105ee60Ca3",
"tx": "0xf1201851e334cac5ab0920f89874163c5074c608b8d810b5eb89fb62454ddc19"
},
{
"amount": "63.3869111225",
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
"tx": "0x901a76735076a5058146ce6fc79628d78d71e8d2b3161459c2daf64ae043f1d5"
},
{
"amount": "27.00320513",
"user": "0x2fCBC94236FeF3e5173a7ec27a25925cd2304540",
"tx": "0xf44b015cc0a7f3c369d48e8c9b71985ad5280c709fd7d541c4d3194dac79482d"
},
{
"amount": "183.6335597275",
"user": "0x690Fc36d52eD3f198F0eBDea1557333a1766f786",
@ -53517,6 +53723,12 @@
}
],
"withdrawals": [
{
"amount": "101.100697625",
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
"tranche_id": 6,
"tx": "0x204f08eb22345e352efcbfd3921f813fdf1097ea3fc33b36387c3c1cc5678936"
},
{
"amount": "148.899302375",
"user": "0x6886CA9FfE30C5BB86590c71799Bf5550EDF845b",
@ -53525,8 +53737,8 @@
}
],
"total_tokens": "250",
"withdrawn_tokens": "148.899302375",
"remaining_tokens": "101.100697625"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xDFdC0c291ee8499777272ab3A7B786518c895966",
@ -56458,6 +56670,12 @@
}
],
"withdrawals": [
{
"amount": "63.3869111225",
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
"tranche_id": 6,
"tx": "0x901a76735076a5058146ce6fc79628d78d71e8d2b3161459c2daf64ae043f1d5"
},
{
"amount": "186.6130888775",
"user": "0x002dA530f8691f012Cecf0ba859Dd5dFa7d9871D",
@ -56466,8 +56684,8 @@
}
],
"total_tokens": "250",
"withdrawn_tokens": "186.6130888775",
"remaining_tokens": "63.3869111225"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0xDE321bC0E688C3cfda2D07f4BE1B7D4fB019cc91",
@ -64583,6 +64801,12 @@
}
],
"withdrawals": [
{
"amount": "27.00320513",
"user": "0x2fCBC94236FeF3e5173a7ec27a25925cd2304540",
"tranche_id": 6,
"tx": "0xf44b015cc0a7f3c369d48e8c9b71985ad5280c709fd7d541c4d3194dac79482d"
},
{
"amount": "129.597196785",
"user": "0x2fCBC94236FeF3e5173a7ec27a25925cd2304540",
@ -64603,8 +64827,8 @@
}
],
"total_tokens": "500",
"withdrawn_tokens": "472.99679487",
"remaining_tokens": "27.00320513"
"withdrawn_tokens": "500",
"remaining_tokens": "0"
},
{
"address": "0x45564345F125F78d3C3894A2C5226C673F8eB0Ae",
@ -65762,10 +65986,17 @@
"tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5"
}
],
"withdrawals": [],
"withdrawals": [
{
"amount": "250",
"user": "0xbCc28D073f7a96c0568D36FdB21A98105ee60Ca3",
"tranche_id": 6,
"tx": "0xf1201851e334cac5ab0920f89874163c5074c608b8d810b5eb89fb62454ddc19"
}
],
"total_tokens": "250",
"withdrawn_tokens": "0",
"remaining_tokens": "250"
"withdrawn_tokens": "250",
"remaining_tokens": "0"
},
{
"address": "0x8c951C54F9cd08Cf81F770248e835D7A4F491a46",

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "41.663429096905125",
"locked_amount": "40.84151128868595",
"deposits": [
{
"amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100",
"total_removed": "673.04388635",
"locked_amount": "321.05967465753423",
"locked_amount": "312.01857876712325",
"deposits": [
{
"amount": "1000",

View File

@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "294.7911558219177930002918724315068493",
"locked_amount": "286.4897859589040750002836532534246575",
"deposits": [
{
"amount": "1000",

View File

@ -0,0 +1,18 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
baseUrl: 'http://localhost:3010',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: './src/integration/*.ts',
excludeSpecPattern: '**/*.js',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
chromeWebSecurity: false,
},
});

View File

@ -1,14 +0,0 @@
{
"baseUrl": "http://localhost:3010",
"projectId": "et4snf",
"fileServerFolder": ".",
"fixturesFolder": "./src/fixtures",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"pluginsFile": false,
"video": true,
"videosFolder": "../../dist/cypress/apps/stats-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/stats-e2e/screenshots",
"chromeWebSecurity": false
}

View File

@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/stats-e2e/cypress.json",
"cypressConfig": "apps/stats-e2e/cypress.config.js",
"devServerTarget": "stats:serve"
},
"configurations": {

View File

@ -0,0 +1,17 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
baseUrl: 'http://localhost:4210',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
chromeWebSecurity: false,
},
});

View File

@ -1,20 +0,0 @@
{
"baseUrl": "http://localhost:4210",
"projectId": "et4snf",
"fileServerFolder": ".",
"fixturesFolder": "./src/fixtures",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"pluginsFile": false,
"video": true,
"videosFolder": "../../dist/cypress/apps/token-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/token-e2e/screenshots",
"chromeWebSecurity": false,
"env": {
"ETH_WALLET_MNEMONIC": "ugly gallery notice network true range brave clarify flat logic someone chunk",
"VEGA_WALLET_NAME" : "capsule_wallet",
"VEGA_WALLET_LOCATION" : "~/.vegacapsule/testnet/wallet",
"VEGA_WALLET_PASSPHRASE" : "123"
}
}

View File

@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/token-e2e/cypress.json",
"cypressConfig": "apps/token-e2e/cypress.config.js",
"devServerTarget": "token:serve"
},
"configurations": {

View File

@ -0,0 +1,12 @@
const fairgroundSet = Cypress.env('FAIRGROUND');
describe('token', () => {
beforeEach(() => cy.visit('/'));
it('should always have a header title based on environment', () => {
cy.get('[data-testid="header-title"]', { timeout: 8000 }).should(
'have.text',
`${fairgroundSet ? 'Fairground token' : 'VEGA TOKEN'}`
);
});
});

View File

@ -1,6 +1,7 @@
import navigation from '../../locators/navigation.locators';
import home from '../../locators/home.locators';
import vegaToken from '../../fixtures/vegaToken.json';
import navigation from '../locators/navigation.locators';
import home from '../locators/home.locators';
import vegaToken from '../fixtures/vegaToken.json';
context('Home Page - verify elements on page', function () {
before('visit token home page', function () {

View File

@ -0,0 +1,34 @@
import navigation from '../locators/navigation.locators';
import vesting from '../locators/vesting.locators';
context('Vesting Page - verify elements on page', function () {
before('navigate to vesting page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.vesting).click();
});
});
describe('with wallets disconnected', function () {
it('should have vesting tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.vesting).should('have.attr', 'aria-current');
});
});
it('should have VESTING header visible', function () {
cy.get(vesting.header).should('be.visible').and('have.text', 'Vesting');
});
it('should have connect Eth wallet info', function () {
cy.get(vesting.connectPrompt).should('be.visible');
});
it('should have connect Eth wallet button', function () {
cy.get(vesting.connectButton)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});
});
});

View File

@ -0,0 +1,5 @@
export default {
header: 'header h1',
connectPrompt: '[data-testid="eth-connect-prompt"]',
connectButton: '[data-testid="connect-to-eth-btn"]',
};

View File

@ -7,7 +7,7 @@ import { ENV } from '../../config/env';
export const AppFooter = () => {
return (
<footer className="p-12 text-ui">
<p className="mb-8">Version: {ENV.commit || 'development'}</p>
<p>Version: {ENV.commit || 'development'}</p>
<p>
<Trans
i18nKey="footerLinksText"

View File

@ -9,7 +9,10 @@ interface BulletHeaderProps {
export const BulletHeader = ({ tag, children, style }: BulletHeaderProps) => {
return React.createElement(
tag,
{ className: 'mt-24 pt-8 pb-20 uppercase text-white', style },
{
className: 'mt-24 py-8 border-t border-white uppercase text-white',
style,
},
<>
<span className="inline-block w-[12px] h-[12px] mr-12 bg-white" />
{children}

View File

@ -9,9 +9,7 @@ export const ConnectedVegaKey = ({ pubKey }: { pubKey: string | null }) => {
<strong data-testid="connected-vega-key-label">
{pubKey ? t('Connected Vega key') : <ConnectToVega />}
</strong>
<p className="mb-12" data-testid="connected-vega-key">
{pubKey}
</p>
<p data-testid="connected-vega-key">{pubKey}</p>
</section>
);
};

View File

@ -74,19 +74,24 @@ const AssociatedAmounts = ({
light={false}
/>
{vestingAssociationByVegaKey.length ? (
<>
<hr style={{ borderStyle: 'dashed', color: Colors.text }} />
<WalletCardRow label="Associated with Vega keys" bold={true} />
<div>
<hr style={{ borderStyle: 'dashed' }} />
<WalletCardRow
label="Associated with Vega keys"
bold={true}
dark={true}
/>
{vestingAssociationByVegaKey.map(([key, amount]) => {
return (
<WalletCardRow
key={key}
label={removeLeadingAddressSymbol(key)}
value={amount}
dark={true}
/>
);
})}
</>
</div>
) : null}
</>
);

View File

@ -7,9 +7,7 @@ export const Heading = ({ title }: HeadingProps) => {
return (
<header className="my-0 mx-auto">
<h1 className="font-alpha calt text-h3 text-white uppercase mb-4">
{title}
</h1>
<h1 className="font-alpha calt">{title}</h1>
</header>
);
};

View File

@ -1 +1 @@
export { Heading } from './heading';
export * from './heading';

View File

@ -14,7 +14,7 @@ const ProgressContents = ({
children: React.ReactNode;
}) => (
<div
className={classnames('flex justify-between py-2 font-mono', {
className={classnames('flex justify-between py-2 font-mono mb-2', {
'gap-0 px-0 text-black': light,
'gap-y-0 gap-x-4': !light,
})}
@ -93,7 +93,7 @@ export const LockedProgress = ({
}, [total, unlocked]);
return (
<>
<div className="mb-8">
<div
className={classnames('flex border', {
'border-black': light,
@ -122,6 +122,6 @@ export const LockedProgress = ({
<span>{formatNumber(locked, decimals)}</span>
<span>{formatNumber(unlocked, decimals)}</span>
</ProgressContents>
</>
</div>
);
};

View File

@ -5,7 +5,8 @@ import debounce from 'lodash/debounce';
import React from 'react';
import * as Dialog from '@radix-ui/react-dialog';
import { useTranslation } from 'react-i18next';
import { NavLink } from 'react-router-dom';
import { Link, NavLink } from 'react-router-dom';
import vegaWhite from '../../images/vega_white.png';
import { Flags } from '../../config';
import {
@ -16,6 +17,69 @@ import { Routes } from '../../routes/router-config';
import { EthWallet } from '../eth-wallet';
import { VegaWallet } from '../vega-wallet';
const Fish = () => (
<svg
xmlns="http://www.w3.org/2000/svg"
width="33"
height="20"
viewBox="0 0 200 116"
fill="none"
data-testid="fairground-icon"
>
<g clipPath="url(#clip0)">
<path
d="M70.5918 32.8569L70.5918 22.7932L60.5254 22.7932L60.5254 32.8569L70.5918 32.8569Z"
fill="black"
/>
<path
d="M80.6641 83.2006L80.6641 73.1377L70.5977 73.1377L70.5977 83.2006L80.6641 83.2006Z"
fill="black"
/>
<path
d="M70.5918 93.2409L70.5918 83.1772L60.5254 83.1772L60.5254 93.2409L70.5918 93.2409Z"
fill="black"
/>
<path
d="M100.797 93.2636L100.797 73.1377L90.7305 73.1377L90.7305 93.2636L100.797 93.2636Z"
fill="black"
/>
<path
d="M90.7285 103.33L90.7285 93.2671L80.662 93.2671L80.662 103.33L90.7285 103.33Z"
fill="black"
/>
<path
d="M90.7285 22.8026L90.7285 12.74L80.662 12.74L80.662 22.8026L90.7285 22.8026Z"
fill="black"
/>
<path
d="M110.869 12.6108L110.869 2.54785L100.803 2.54785L100.803 12.6108L110.869 12.6108Z"
fill="black"
/>
<path
d="M120.934 103.326L120.934 73.1377L110.867 73.1377L110.867 103.326L120.934 103.326Z"
fill="black"
/>
<path
d="M110.869 113.384L110.869 103.321L100.803 103.321L100.803 113.384L110.869 113.384Z"
fill="black"
/>
<path
d="M161.328 52.9855L161.328 42.9226L151.262 42.9226L151.262 52.9855L161.328 52.9855Z"
fill="black"
/>
<path
d="M20.133 83.187L30.3354 83.187L30.3354 73.124L40.4017 73.124L40.4017 63.0613L50.4681 63.0613L50.4681 73.124L60.5345 73.124L60.5345 63.0613L70.6008 63.0613L80.6672 63.0613L131.135 63.0613L131.135 113.376L161.334 113.376L161.334 103.313L171.4 103.313L171.4 93.25L181.467 93.25L181.467 83.187L191.533 83.187L191.533 63.0613L181.467 63.0613L181.467 73.1241L171.4 73.1241L171.4 83.187L161.334 83.187L161.334 73.1241L171.4 73.1241L171.4 63.0613L161.334 63.0613L151.268 63.0613L141.201 63.0613L141.201 52.9983L141.201 32.8726L161.334 32.8726L171.4 32.8726L171.4 63.0613L181.467 63.0613L181.467 52.9983L191.533 52.9983L191.533 32.8726L181.467 32.8726L181.467 22.8096L171.4 22.8096L171.4 12.7467L161.334 12.7467L161.334 2.54785L141.201 2.54785L131.135 2.54785L131.135 52.9983L120.933 52.9983L120.933 12.7467L110.866 12.7467L110.866 52.9983L100.8 52.9983L100.8 22.8096L90.7336 22.8096L90.7336 52.9983L80.6672 52.9983L80.6672 32.8726L70.6008 32.8726L70.6008 52.9983L60.5345 52.9983L60.5345 42.9354L50.4681 42.9354L50.4681 52.9983L40.4017 52.9983L40.4017 42.9354L30.3354 42.9354L30.3354 32.8726L20.133 32.8726L20.133 22.8096L0.000308081 22.8096L0.000307201 42.9354L10.0666 42.9354L10.0666 52.9983L20.133 52.9983L20.133 63.0613L10.0666 63.0613L10.0666 73.124L0.000305881 73.124L0.000305002 93.25L20.133 93.25L20.133 83.187Z"
fill="black"
/>
</g>
<defs>
<clipPath id="clip0">
<rect width="200" height="116" fill="none" />
</clipPath>
</defs>
</svg>
);
export const Nav = () => {
const [windowWidth, setWindowWidth] = React.useState(window.innerWidth);
const isDesktop = windowWidth > 959;
@ -35,7 +99,7 @@ export const Nav = () => {
return (
<div
className={`p-16 ${
className={`px-16 py-8 ${
inverted
? 'bg-clouds bg-no-repeat bg-cover bg-vega-yellow'
: 'border-b-white border-b-1'
@ -46,7 +110,7 @@ export const Nav = () => {
{!isDesktop && <NavHeader fairground={inverted} />}
<div className="flex gap-12 lg:flex-auto">
{isDesktop ? (
<NavLinks isDesktop={isDesktop} />
<NavLinks isDesktop={isDesktop} isInverted={inverted} />
) : (
<NavDrawer inverted={inverted} />
)}
@ -60,15 +124,24 @@ const NavHeader = ({ fairground }: { fairground: boolean }) => {
const { t } = useTranslation();
return (
<div className="h-[30px] inline-flex items-center lg:h-40 uppercase">
<h1
data-testid="header-title"
className={`text-h3 font-alpha uppercase calt mb-2 ${
fairground ? 'text-black' : 'text-white'
}`}
>
{fairground ? t('fairgroundTitle') : t('title')}
</h1>
<div className="flex items-center gap-8">
<Link to="/">
{fairground ? (
<Fish />
) : (
<img alt="Vega" src={vegaWhite} height={30} width={30} />
)}
</Link>
<div className="h-[30px] inline-flex items-center lg:h-40 uppercase">
<h1
data-testid="header-title"
className={`text-h4 sm:text-h3 font-alpha uppercase calt mb-2 ${
fairground ? 'text-black' : 'text-white'
}`}
>
{fairground ? t('fairgroundTitle') : t('title')}
</h1>
</div>
</div>
);
};
@ -135,7 +208,13 @@ const NavDrawer = ({ inverted }: { inverted: boolean }) => {
);
};
const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
const NavLinks = ({
isDesktop,
isInverted,
}: {
isDesktop: boolean;
isInverted?: boolean;
}) => {
const { appDispatch } = useAppState();
const { t } = useTranslation();
const linkProps = {
@ -163,18 +242,21 @@ const NavLinks = ({ isDesktop }: { isDesktop: boolean }) => {
{...linkProps}
to={route}
key={route}
className={({ isActive }) => {
const linkClasses = classNames(
'no-underline hover:no-underline',
className={({ isActive }) =>
classNames(
'no-underline hover:no-underline focus-visible:outline-none focus-visible:border-none focus-visible:shadow-inset-white',
{
'bg-vega-yellow text-black hover:text-black': isActive,
'bg-black text-white': !isActive,
'bg-vega-yellow text-black': !isInverted && isActive,
'bg-transparent text-white hover:text-vega-yellow':
!isInverted && !isActive,
'bg-black text-white': isInverted && isActive,
'bg-transparent text-black hover:text-white':
isInverted && !isActive,
'py-2 px-12': isDesktop,
'border-t border-white p-20': !isDesktop,
}
);
return linkClasses;
}}
)
}
>
{text}
</NavLink>

View File

@ -23,12 +23,8 @@ export const TransactionComplete = ({
intent={Intent.Success}
title={heading || t('Complete')}
>
{body && (
<p className="mb-8" data-testid="transaction-complete-body">
{body}
</p>
)}
<p className="mb-8">
{body && <p data-testid="transaction-complete-body">{body}</p>}
<p>
<Link
title={t('View transaction on Etherscan')}
target="_blank"

View File

@ -20,11 +20,9 @@ export const TransactionError = ({
return (
<Callout iconName="error" intent={Intent.Danger}>
<p className="mb-8">
{error ? error.message : t('Something went wrong')}
</p>
<p>{error ? error.message : t('Something went wrong')}</p>
{hash ? (
<p className="mb-8">
<p>
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${hash}`}

View File

@ -38,12 +38,8 @@ export const TransactionPending = ({
}, [heading, remainingConfirmations, t]);
return (
<Callout icon={<Loader size="small" />} title={title}>
{body && (
<p className="mb-8" data-testid="transaction-pending-body">
{body}
</p>
)}
<p className="mb-8">
{body && <p data-testid="transaction-pending-body">{body}</p>}
<p>
<Link
title={t('View transaction on Etherscan')}
target="_blank"

View File

@ -5,18 +5,18 @@ import { Links } from '../../config';
export const DownloadWalletPrompt = () => {
const { t } = useTranslation();
return (
<div className="mt-8">
<h3>{t('getWallet')}</h3>
<p>
<>
<h3 className="mt-12 mb-4">{t('getWallet')}</h3>
<p className="mb-4">
<Link className="text-deemphasise" href={Links.WALLET_GUIDE}>
{t('readGuide')}
</Link>
</p>
<p>
<p className="mb-4">
<Link className="text-deemphasise" href={Links.WALLET_RELEASES}>
{t('downloadWallet')}
</Link>
</p>
</div>
</>
);
};

View File

@ -40,14 +40,16 @@ export const VegaWallet = () => {
<section className="vega-wallet" data-testid="vega-wallet">
<WalletCard dark={true}>
<WalletCardHeader dark={true}>
<div>
<h1 className="text-h3 uppercase">{t('vegaWallet')}</h1>
<span className="text-h6">{keypair && `(${keypair.name})`}</span>
</div>
<h1 className="col-start-1 m-0">{t('vegaWallet')}</h1>
{keypair && (
<span className="font-mono px-8">
{truncateMiddle(keypair.pub)}
</span>
<>
<div className="sm:row-start-2 sm:col-start-1 sm:col-span-2 text-h6 mb-12">
{keypair.name}
</div>
<span className="sm:col-start-2 place-self-end font-mono pb-2 px-4">
{truncateMiddle(keypair.pub)}
</span>
</>
)}
</WalletCardHeader>
<WalletCardContent>{child}</WalletCardContent>
@ -128,6 +130,7 @@ const VegaWalletConnected = ({ vegaKeys }: VegaWalletConnectedProps) => {
<WalletCardActions>
<Button
variant="inline-link"
className="mt-4"
onClick={() =>
appDispatch({
type: AppStateActionType.SET_VEGA_WALLET_MANAGE_OVERLAY,

View File

@ -29,10 +29,14 @@ interface WalletCardProps {
}
export const WalletCard = ({ dark, children }: WalletCardProps) => {
const className = classNames('text-ui border border-white', 'p-8', {
'bg-black text-white': dark,
'bg-white text-black': !dark,
});
const className = classNames(
'text-ui border border-white',
'pt-4 pl-8 pr-12 pb-12',
{
'bg-black text-white': dark,
'bg-white text-black': !dark,
}
);
return <div className={className}>{children}</div>;
};
@ -43,7 +47,9 @@ interface WalletCardHeaderProps {
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
return (
<div className="flex gap-4 justify-between items-center">{children}</div>
<div className="grid grid-cols-1 sm:grid-cols-[auto_1fr] gap-4">
{children}
</div>
);
};
@ -145,23 +151,23 @@ export const WalletCardAsset = ({
}`}
/>
<div>
<div className="flex font-medium">
<div className="flex font-medium align-center">
<h1
className={`text-h5 px-8 uppercase ${
className={`text-h5 mb-0 px-8 uppercase leading-none ${
dark ? 'text-white' : 'text-black'
}`}
>
{name}
</h1>
<h2
className={`text-h5 uppercase ${
className={`text-h5 mb-0 uppercase leading-none ${
dark ? 'text-white-60' : 'text-black-60'
}`}
>
{subheading || symbol}
</h2>
</div>
<div className="px-8 text-h5 basis-full">
<div className="px-8 text-h5 basis-full font-mono">
<span>{integers}.</span>
<span className={dark ? 'text-white-60' : 'text-black-60'}>
{decimalsPlaces}

View File

@ -79,7 +79,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
return (
<Splash>
<div className="flex flex-col items-center gap-12">
<p className="mb-12">This app only works on chain ID: {appChainId}</p>
<p>This app only works on chain ID: {appChainId}</p>
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
</div>
</Splash>

View File

@ -10,7 +10,7 @@ export const ProposalTermsJson = ({
const { t } = useTranslation();
return (
<section>
<h2 className="text-h4 text-white mb-8">{t('proposalTerms')}</h2>
<h2>{t('proposalTerms')}</h2>
<SyntaxHighlighter data={terms} />
</section>
);

View File

@ -23,10 +23,10 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
return (
<>
<Heading title={t('pageTitleGovernance')} />
<p className="mb-8">{t('proposedChangesToVegaNetwork')}</p>
<p className="mb-8">{t('vegaTokenHoldersCanVote')}</p>
<p className="mb-8">{t('requiredMajorityDescription')}</p>
<h2 className="text-h4 text-white">{t('proposals')}</h2>
<p>{t('proposedChangesToVegaNetwork')}</p>
<p>{t('vegaTokenHoldersCanVote')}</p>
<p>{t('requiredMajorityDescription')}</p>
<h2>{t('proposals')}</h2>
<ul>
{proposals.map((proposal) => (
<ProposalListItem proposal={proposal} />

View File

@ -45,7 +45,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
return (
<section>
<h3 className="text-h4 text-white mb-8">{t('votes')}</h3>
<p className="mb-8">
<p>
<span>
<CurrentProposalStatus proposal={proposal} />
</span>
@ -90,7 +90,7 @@ export const VoteDetails = ({ proposal }: VoteDetailsProps) => {
</tr>
</tbody>
</table>
<p className="mb-8">
<p>
{t('participation')}
{': '}
{participationMet ? (

View File

@ -42,13 +42,13 @@ const Home = ({ name }: RouteChildProps) => {
/>
</HomeSection>
<HomeSection>
<h2 className="text-h4 text-white">{t('Token Vesting')}</h2>
<p className="mb-8">
<h2>{t('Token Vesting')}</h2>
<p>
{t(
'The vesting contract holds VEGA tokens until they have become unlocked.'
)}
</p>
<p className="mb-8">
<p>
<Trans
i18nKey="Tokens are held in different <trancheLink>Tranches</trancheLink>. Each tranche has its own schedule for how the tokens are unlocked."
components={{
@ -62,7 +62,7 @@ const Home = ({ name }: RouteChildProps) => {
}}
/>
</p>
<p className="mb-8">
<p>
{t(
'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.'
)}
@ -74,13 +74,13 @@ const Home = ({ name }: RouteChildProps) => {
</Link>
</HomeSection>
<HomeSection>
<h2 className="text-h4 text-white">{t('Use your Vega tokens')}</h2>
<p className="mb-8">
<h2 className="uppercase">{t('Use your Vega tokens')}</h2>
<p>
{t(
'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.'
)}
</p>
<p className="mb-8">
<p>
{t(
'This can happen both while held in the vesting contract as well as when redeemed.'
)}
@ -109,8 +109,8 @@ const Home = ({ name }: RouteChildProps) => {
<div className="flex gap-40">
<div className="flex-1">
<HomeSection>
<h2 className="text-h4 text-white">{t('Staking')}</h2>
<p className="mb-8">
<h2>{t('Staking')}</h2>
<p>
{t(
'VEGA token holders can nominate a validator node and receive staking rewards.'
)}
@ -129,8 +129,8 @@ const Home = ({ name }: RouteChildProps) => {
</div>
<div className="flex-1">
<HomeSection>
<h2 className="text-h4 text-white">{t('Governance')}</h2>
<p className="mb-8">
<h2>{t('Governance')}</h2>
<p>
{t(
'VEGA token holders can vote on proposed changes to the network and create proposals.'
)}
@ -155,5 +155,5 @@ const Home = ({ name }: RouteChildProps) => {
export default Home;
export const HomeSection = ({ children }: { children: React.ReactNode }) => {
return <section className="mb-20">{children}</section>;
return <section className="mb-28">{children}</section>;
};

View File

@ -37,7 +37,7 @@ export const TokenDetailsCirculating = ({
}) => {
const totalCirculating = sumCirculatingTokens(tranches);
return (
<span data-testid="circulating-supply">
<span data-testid="circulating-supply" className="font-mono">
{formatNumber(totalCirculating, 2)}
</span>
);

View File

@ -42,13 +42,13 @@ export const TokenDetails = ({
}
return (
<KeyValueTable className={'token-details'}>
<KeyValueTable className={'token-details text-white'}>
<KeyValueTableRow>
{t('Token address').toUpperCase()}
<Link
data-testid="token-address"
title={t('View on Etherscan (opens in a new tab)')}
className="font-mono"
className="font-mono text-white text-right"
href={`${ETHERSCAN_URL}/address/${token.address}`}
>
{token.address}
@ -59,7 +59,7 @@ export const TokenDetails = ({
<Link
data-testid="token-contract"
title={t('View on Etherscan (opens in a new tab)')}
className="font-mono"
className="font-mono text-white text-right"
href={`${ETHERSCAN_URL}/address/${config.token_vesting_contract.address}`}
>
{config.token_vesting_contract.address}
@ -67,7 +67,9 @@ export const TokenDetails = ({
</KeyValueTableRow>
<KeyValueTableRow>
{t('Total supply').toUpperCase()}
<span data-testid="total-supply">{formatNumber(totalSupply, 2)}</span>
<span className="font-mono" data-testid="total-supply">
{formatNumber(totalSupply, 2)}
</span>
</KeyValueTableRow>
<KeyValueTableRow>
{t('Circulating supply').toUpperCase()}
@ -75,7 +77,9 @@ export const TokenDetails = ({
</KeyValueTableRow>
<KeyValueTableRow>
{t('Staked on Vega validator').toUpperCase()}
<span data-testid="staked">{formatNumber(totalStaked, 2)}</span>
<span data-testid="staked" className="font-mono">
{formatNumber(totalStaked, 2)}
</span>
</KeyValueTableRow>
</KeyValueTable>
);

View File

@ -55,16 +55,18 @@ export const RedemptionInformation = () => {
if (!filteredTranches.length) {
return (
<section data-testid="redemption-page">
<p className="mb-12" data-testid="redemption-no-balance">
<Trans
i18nKey="noVestingTokens"
components={{
tranchesLink: (
<Link className="underline text-white" to={Routes.TRANCHES} />
),
}}
/>
</p>
<div className="mb-20">
<p data-testid="redemption-no-balance">
<Trans
i18nKey="noVestingTokens"
components={{
tranchesLink: (
<Link className="underline text-white" to={Routes.TRANCHES} />
),
}}
/>
</p>
</div>
<AddLockedTokenAddress />
</section>
);
@ -92,9 +94,7 @@ export const RedemptionInformation = () => {
vested={totalVestedBalance}
/>
</div>
{filteredTranches.length ? (
<h2 className="text-h4 text-white mb-12">{t('Tranche breakdown')}</h2>
) : null}
{filteredTranches.length ? <h2>{t('Tranche breakdown')}</h2> : null}
{zeroTranche && (
<Tranche0Table
trancheId={0}
@ -133,7 +133,7 @@ export const RedemptionInformation = () => {
iconName="hand-up"
intent={Intent.Warning}
>
<p className="mb-12">{t('Find out more about Staking.')}</p>
<p>{t('Find out more about Staking.')}</p>
<Link to="/staking" className="underline text-white">
{t('Stake VEGA tokens')}
</Link>

View File

@ -68,7 +68,7 @@ const RedemptionRouter = () => {
if (!account) {
return (
<EthConnectPrompt>
<p data-testid="eth-connect-prompt" className="mb-8">
<p data-testid="eth-connect-prompt">
{t(
"Use the Ethereum wallet you want to send your tokens to. You'll also need enough Ethereum to pay gas."
)}

View File

@ -73,7 +73,7 @@ export const RedeemFromTranche = () => {
!trancheBalance
) {
return (
<section data-testid="redemption-page">
<section data-testid="redemption-page" className="mb-28">
<p data-testid="redemption-no-balance">
<Trans
i18nKey="noVestingTokens"
@ -99,7 +99,7 @@ export const RedeemFromTranche = () => {
}
completeBody={
<>
<p className="mb-8">
<p>
{t(
'You have redeemed {{redeemedAmount}} VEGA tokens from this tranche. They are now free to transfer from your Ethereum wallet.',
{
@ -107,7 +107,7 @@ export const RedeemFromTranche = () => {
}
)}
</p>
<p className="mb-8">
<p>
{t(
'The VEGA token address is {{address}}, make sure you add this to your wallet to see your tokens',
{

View File

@ -109,8 +109,8 @@ export const RewardsIndex = () => {
return (
<section className="rewards">
<Heading title={t('pageTitleRewards')} />
<p className="mb-8">{t('rewardsPara1')}</p>
<p className="mb-8">{t('rewardsPara2')}</p>
<p>{t('rewardsPara1')}</p>
<p>{t('rewardsPara2')}</p>
{payoutDuration ? (
<div className="my-24">
<Callout

View File

@ -64,7 +64,7 @@ export const RewardInfo = ({
return (
<div className="mt-24">
<p className="mb-8">
<p>
{t('Connected Vega key')}: {currVegaKey.pub}
</p>
{vegaTokenRewards.length ? (

View File

@ -13,7 +13,7 @@ export const AssociateInfo = ({ pubKey }: { pubKey: string | null }) => {
{t('What Vega key is going to control your stake?')}
</h2>
<ConnectedVegaKey pubKey={pubKey} />
<h2 className="text-h4 text-white" data-testid="associate-amount-header">
<h2 data-testid="associate-amount-header">
{t('How much would you like to associate?')}
</h2>
</>

View File

@ -62,13 +62,13 @@ export const AssociateTransaction = ({
if (derivedTxState === TxState.Pending) {
return (
<Callout icon={<Loader size="small" />} title={title}>
<p data-testid="transaction-pending-body" className="mb-8">
<p data-testid="transaction-pending-body">
{t('Associating {{amount}} VEGA tokens with Vega key {{vegaKey}}', {
amount,
vegaKey: truncateMiddle(vegaKey),
})}
</p>
<p className="mb-8">
<p>
<Link
title={t('View transaction on Etherscan')}
href={`${ETHERSCAN_URL}/tx/${state.txData.hash}`}
@ -77,7 +77,7 @@ export const AssociateTransaction = ({
{t('View on Etherscan (opens in a new tab)')}
</Link>
</p>
<p data-testid="transaction-pending-footer" className="mb-8">
<p data-testid="transaction-pending-footer">
{t('pendingAssociationText', {
confirmations: requiredConfirmations,
})}

View File

@ -60,24 +60,20 @@ export const DisassociatePage = ({
return (
<section className="disassociate-page" data-testid="disassociate-page">
<p className="mb-12">
<p>
{t(
'Use this form to disassociate VEGA tokens with a Vega key. This returns them to either the Ethereum wallet that used the Staking bridge or the vesting contract.'
)}
</p>
<p className="mb-12">
<p>
<span className="text-vega-red">{t('Warning')}:</span>{' '}
{t(
'Any Tokens that have been nominated to a node will sacrifice any Rewards they are due for the current epoch. If you do not wish to sacrifices fees you should remove stake from a node at the end of an epoch before disassocation.'
)}
</p>
<h2 className="text-h4 text-white mb-8">
{t('What Vega wallet are you removing Tokens from?')}
</h2>
<h2>{t('What Vega wallet are you removing Tokens from?')}</h2>
<ConnectedVegaKey pubKey={vegaKey.pub} />
<h2 className="text-h4 text-white mb-8">
{t('What tokens would you like to return?')}
</h2>
<h2>{t('What tokens would you like to return?')}</h2>
<StakingMethodRadio
setSelectedStakingMethod={setSelectedStakingMethod}
selectedStakingMethod={selectedStakingMethod}

View File

@ -223,7 +223,7 @@ export const StakingForm = ({
return (
<>
<h2 className="text-h4 mb-8">{t('Manage your stake')}</h2>
<h2>{t('Manage your stake')}</h2>
<FormGroup>
<RadioGroup
onChange={(value) => {
@ -254,9 +254,7 @@ export const StakingForm = ({
<>
{action === Actions.Add ? (
<>
<h2 className="text-h4 mb-8">
{t('How much to Add in next epoch?')}
</h2>
<h2>{t('How much to Add in next epoch?')}</h2>
<p>
{t('minimumNomination', {
minTokens: minTokensWithDecimals,

View File

@ -96,10 +96,7 @@ export const StakingNode = ({ vegaKey, data }: StakingNodeProps) => {
return (
<>
<h2
data-test-id="validator-node-title"
className="text-h4 break-word mb-8"
>
<h2 data-test-id="validator-node-title" className="text-h4 break-word">
{nodeInfo.name
? t('validatorTitle', { nodeName: nodeInfo.name })
: t('validatorTitle', { nodeName: t('validatorTitleFallback') })}

View File

@ -19,17 +19,19 @@ import { NodeList } from './node-list';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { truncateMiddle } from '../../lib/truncate-middle';
const stakingBulletStyles = { marginBottom: '12px', fontSize: '18px' };
export const Staking = ({ data }: { data?: StakingQueryResult }) => {
const { t } = useTranslation();
return (
<>
<section className="mb-24">
<p className="mb-12">{t('stakingDescription1')}</p>
<p className="mb-12">{t('stakingDescription2')}</p>
<p className="mb-12">{t('stakingDescription3')}</p>
<p className="mb-12">{t('stakingDescription4')}</p>
<p className="mb-12">
<p>{t('stakingDescription1')}</p>
<p>{t('stakingDescription2')}</p>
<p>{t('stakingDescription3')}</p>
<p>{t('stakingDescription4')}</p>
<p>
<Link
href={Links.STAKING_GUIDE}
className="text-white underline"
@ -41,11 +43,15 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
</section>
<section>
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
<BulletHeader tag="h2" style={stakingBulletStyles}>
{t('stakingStep1')}
</BulletHeader>
<StakingStepConnectWallets />
</section>
<section>
<BulletHeader tag="h2">{t('stakingStep1')}</BulletHeader>
<BulletHeader tag="h2" style={stakingBulletStyles}>
{t('stakingStep2')}
</BulletHeader>
<StakingStepAssociate
associated={
new BigNumber(
@ -55,7 +61,9 @@ export const Staking = ({ data }: { data?: StakingQueryResult }) => {
/>
</section>
<section>
<BulletHeader tag="h2">{t('stakingStep3')}</BulletHeader>
<BulletHeader tag="h2" style={stakingBulletStyles}>
{t('stakingStep3')}
</BulletHeader>
<StakingStepSelectNode data={data} />
</section>
</>
@ -82,7 +90,7 @@ export const StakingStepConnectWallets = () => {
{truncateMiddle(account)}
</Link>
</p>
<p>
<p className="mb-8">
{t('stakingVegaWalletConnected', {
key: truncateMiddle(keypair.pub),
})}
@ -93,7 +101,7 @@ export const StakingStepConnectWallets = () => {
return (
<>
<p className="mb-8">
<p>
<Trans
i18nKey="stakingStep1Text"
components={{
@ -116,7 +124,7 @@ export const StakingStepConnectWallets = () => {
/>
</div>
) : (
<p className="mb-8">
<p>
<Button
onClick={() =>
appDispatch({

View File

@ -17,7 +17,7 @@ export const YourStake = ({
return (
<div data-testid="your-stake">
<h2 className="text-h4 mb-8">{t('Your stake')}</h2>
<h2>{t('Your stake')}</h2>
<KeyValueTable>
<KeyValueTableRow>
{t('Your Stake On Node (This Epoch)')}

View File

@ -67,7 +67,7 @@ export const Tranche = () => {
{formatNumber(tranche.total_removed)}
</span>
</div>
<h2 className="text-h4 text-white mb-8">{t('Holders')}</h2>
<h2>{t('Holders')}</h2>
{tranche.users.length ? (
<ul role="list">
{tranche.users.map((user, i) => {

View File

@ -29,10 +29,10 @@ export const Tranches = () => {
return (
<section>
<h2 className="text-h4">{t('chartTitle')}</h2>
<p className="mb-12">{t('chartAbove')}</p>
<h2>{t('chartTitle')}</h2>
<p>{t('chartAbove')}</p>
<VestingChart />
<p className="mb-12">{t('chartBelow')}</p>
<p>{t('chartBelow')}</p>
{tranches?.length ? (
<ul role="list">
{(showAll ? tranches : filteredTranches).map((tranche) => {

View File

@ -23,7 +23,7 @@ const Withdraw = () => {
return (
<>
<Heading title={t('withdrawPageHeading')} />
<p className="mb-8">{t('withdrawPageText')}</p>
<p>{t('withdrawPageText')}</p>
<div className="mb-24">
<VegaWalletContainer>
{(currVegaKey) => <WithdrawContainer currVegaKey={currVegaKey} />}
@ -141,7 +141,7 @@ export const WithdrawContainer = ({ currVegaKey }: WithdrawContainerProps) => {
title={t('pendingWithdrawalsCalloutTitle')}
intent={Intent.Warning}
>
<p className="mb-8">{t('pendingWithdrawalsCalloutText')}</p>
<p>{t('pendingWithdrawalsCalloutText')}</p>
<p>
<Link to={Routes.WITHDRAWALS} className="underline text-white">
{t('pendingWithdrawalsCalloutButton')}

View File

@ -74,7 +74,7 @@ const WithdrawPendingContainer = () => {
return (
<>
<h2>{t('withdrawalsPreparedWarningHeading')}</h2>
<p className="mb-8">{t('withdrawalsText')}</p>
<p>{t('withdrawalsText')}</p>
<p className="mb-28">{t('withdrawalsPreparedWarningText')}</p>
<ul role="list">
{withdrawals.map((w) => (

View File

@ -1,3 +1,15 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
h1 {
@apply text-h3 text-white uppercase mb-12;
}
h2 {
@apply text-h4 text-white mb-8;
}
p {
@apply mb-12;
}
}

View File

@ -1,3 +0,0 @@
{
"stepDefinitions": "src/support/step_definitions"
}

View File

@ -0,0 +1,48 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
component: {
baseUrl: 'http://localhost:4200',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
chromeWebSecurity: false,
projectId: 'et4snf',
defaultCommandTimeout: 10000,
},
e2e: {
baseUrl: 'http://localhost:4200',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
chromeWebSecurity: false,
projectId: 'et4snf',
defaultCommandTimeout: 10000,
},
env: {
TRADING_TEST_VEGA_WALLET_NAME: 'UI_Trading_Test',
ETHEREUM_PROVIDER_URL:
'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
VEGA_PUBLIC_KEY:
'47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278',
VEGA_PUBLIC_KEY2:
'1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4',
TRUNCATED_VEGA_PUBLIC_KEY: '47836c…c7d278',
TRUNCATED_VEGA_PUBLIC_KEY2: '1a18cd…0cf2e4',
ETHEREUM_WALLET_ADDRESS: '0x265Cc6d39a1B53d0d92068443009eE7410807158',
ETHERSCAN_URL: 'https://ropsten.etherscan.io',
tsConfig: 'tsconfig.json',
TAGS: 'not @todo and not @ignore and not @manual',
TRADING_TEST_VEGA_WALLET_PASSPHRASE: '123',
ETH_WALLET_MNEMONIC:
'ugly gallery notice network true range brave clarify flat logic someone chunk',
},
});

View File

@ -1,31 +0,0 @@
{
"baseUrl": "http://localhost:4200",
"fileServerFolder": ".",
"fixturesFolder": false,
"pluginsFile": "./src/plugins/index.js",
"testFiles": "*.{ts,feature,features}",
"ignoreTestFiles": "**/*.js",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"video": true,
"videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
"chromeWebSecurity": false,
"projectId": "et4snf",
"defaultCommandTimeout": 10000,
"env": {
"TRADING_TEST_VEGA_WALLET_NAME": "UI_Trading_Test",
"ETHEREUM_PROVIDER_URL": "https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8",
"VEGA_PUBLIC_KEY": "47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278",
"VEGA_PUBLIC_KEY2": "1a18cdcaaa4f44a57b35a4e9b77e0701c17a476f2b407620f8c17371740cf2e4",
"TRUNCATED_VEGA_PUBLIC_KEY": "47836c…c7d278",
"TRUNCATED_VEGA_PUBLIC_KEY2": "1a18cd…0cf2e4",
"ETHEREUM_WALLET_ADDRESS": "0x265Cc6d39a1B53d0d92068443009eE7410807158",
"ETHERSCAN_URL": "https://ropsten.etherscan.io",
"tsConfig": "tsconfig.json",
"TAGS": "not @todo and not @ignore and not @manual",
"TRADING_TEST_VEGA_WALLET_PASSPHRASE": "123",
"ETH_WALLET_MNEMONIC": "ugly gallery notice network true range brave clarify flat logic someone chunk"
}
}

View File

@ -6,7 +6,7 @@
"e2e": {
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/trading-e2e/cypress.json",
"cypressConfig": "apps/trading-e2e/cypress.config.js",
"devServerTarget": "trading:serve"
},
"configurations": {

View File

@ -10,6 +10,7 @@ describe('vega wallet', () => {
beforeEach(() => {
// Using portfolio page as it requires vega wallet connection
cy.visit('/portfolio');
cy.get('main[data-testid="portfolio"]').should('exist');
});
it('can connect', () => {
@ -64,8 +65,10 @@ describe('vega wallet', () => {
describe('ethereum wallet', () => {
beforeEach(() => {
cy.mockWeb3Provider();
// Using portfolio is it requires Ethereum wallet connection
// Using portfolio withdrawals tab is it requires Ethereum wallet connection
cy.visit('/portfolio');
cy.get('main[data-testid="portfolio"]').should('exist');
cy.getByTestId('Withdrawals').click();
});
it('can connect', () => {
@ -73,6 +76,6 @@ describe('ethereum wallet', () => {
cy.getByTestId('web3-connector-list').should('exist');
cy.getByTestId('web3-connector-MetaMask').click();
cy.getByTestId('web3-connector-list').should('not.exist');
cy.getByTestId('portfolio-grid').should('exist');
cy.getByTestId('tab-withdrawals').should('not.be.empty');
});
});

View File

@ -71,6 +71,8 @@ describe('markets table', () => {
cy.wait('@Market');
cy.contains('ACTIVE MARKET');
cy.url().should('include', '/markets/market-0');
verifyMarketSummaryDisplayed('Active');
});
it('can select a suspended market', () => {
@ -88,5 +90,28 @@ describe('markets table', () => {
cy.wait('@Market');
cy.contains('SUSPENDED MARKET');
cy.url().should('include', '/markets/market-1');
verifyMarketSummaryDisplayed('Suspended');
});
function verifyMarketSummaryDisplayed(expectedMarketState: string) {
const marketSummaryBlock = 'market-summary';
const percentageValue = 'price-change-percentage';
const priceChangeValue = 'price-change';
const tradingVolume = 'trading-volume';
const tradingMode = 'trading-mode';
const marketState = 'market-state';
cy.getByTestId(marketSummaryBlock).within(() => {
cy.contains('Change (24h)');
cy.getByTestId(percentageValue).should('not.be.empty');
cy.getByTestId(priceChangeValue).should('not.be.empty');
cy.contains('Volume');
cy.getByTestId(tradingVolume).should('not.be.empty');
cy.contains('Trading mode');
cy.getByTestId(tradingMode).should('not.be.empty');
cy.contains('State');
cy.getByTestId(marketState).should('have.text', expectedMarketState);
});
}
});

View File

@ -0,0 +1,120 @@
import { aliasQuery } from '@vegaprotocol/cypress';
import { generateFill, generateFills } from '../support/mocks/generate-fills';
import { Side } from '@vegaprotocol/types';
import { connectVegaWallet } from '../support/vega-wallet';
describe('fills', () => {
before(() => {
const fills = [
generateFill({
buyer: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
}),
generateFill({
id: '1',
seller: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
aggressor: Side.Sell,
buyerFee: {
infrastructureFee: '5000',
},
market: {
name: 'Apples Daily v3',
positionDecimalPlaces: 2,
},
}),
generateFill({
id: '2',
seller: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
aggressor: Side.Buy,
}),
generateFill({
id: '3',
aggressor: Side.Sell,
market: {
name: 'ETHBTC Quarterly (30 Jun 2022)',
},
buyer: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
}),
];
const result = generateFills({
party: {
tradesPaged: {
edges: fills.map((f, i) => {
return {
__typename: 'TradeEdge',
node: f,
cursor: i.toString(),
};
}),
},
},
});
cy.mockGQL((req) => {
aliasQuery(req, 'Fills', result);
});
cy.visit('/portfolio');
cy.get('main[data-testid="portfolio"]').should('exist');
});
it('renders fills', () => {
cy.getByTestId('Fills').click();
cy.getByTestId('tab-fills').contains('Please connect Vega wallet');
connectVegaWallet();
cy.getByTestId('tab-fills').should('be.visible');
cy.getByTestId('tab-fills')
.get('[role="gridcell"][col-id="market.name"]')
.each(($marketSymbol) => {
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
});
cy.getByTestId('tab-fills')
.get('[role="gridcell"][col-id="size"]')
.each(($amount) => {
cy.wrap($amount).invoke('text').should('not.be.empty');
});
cy.getByTestId('tab-positions')
.get('[role="gridcell"][col-id="price"]')
.each(($prices) => {
cy.wrap($prices).invoke('text').should('not.be.empty');
});
cy.getByTestId('tab-positions')
.get('[role="gridcell"][col-id="price_1"]')
.each(($total) => {
cy.wrap($total).invoke('text').should('not.be.empty');
});
cy.getByTestId('tab-positions')
.get('[role="gridcell"][col-id="aggressor"]')
.each(($role) => {
cy.wrap($role)
.invoke('text')
.then((text) => {
const roles = ['Maker', 'Taker'];
expect(roles.indexOf(text.trim())).to.be.greaterThan(-1);
});
});
cy.getByTestId('tab-positions')
.get(
'[role="gridcell"][col-id="market.tradableInstrument.instrument.product"]'
)
.each(($fees) => {
cy.wrap($fees).invoke('text').should('not.be.empty');
});
const dateTimeRegex =
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
cy.get('[col-id="createdAt"]').each(($tradeDateTime, index) => {
if (index != 0) {
//ignore header
cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
}
});
});
});

View File

@ -1,6 +0,0 @@
describe('portfolio', () => {
it('requires connecting', () => {
cy.visit('/portfolio');
cy.get('main[data-testid="portfolio"]').should('exist');
});
});

View File

@ -144,15 +144,15 @@ describe('deal ticket orders', () => {
'Awaiting network confirmation'
);
cy.getByTestId(orderTransactionHash)
.invoke('text')
.should('contain', 'Tx hash: test-tx-hash');
.invoke('attr', 'href')
.should('include', 'https://explorer.fairground.wtf/txs/0xtest-tx-hash');
cy.getByTestId('dialog-close').click();
};
it.skip('cannot place an order if market is suspended');
it.skip('cannot place an order if size is 0');
it.skip('cannot place an order expiry date is invalid');
it.skip('unsuccessfull order due to no collateral');
it.skip('unsuccessful order due to no collateral');
});
describe('deal ticket validation', () => {

View File

@ -1,75 +0,0 @@
/// <reference types="cypress" />
const webpackPreprocessor = require('@cypress/webpack-preprocessor');
const webpack = require('webpack');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
on(
'file:preprocessor',
webpackPreprocessor({
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
plugins: [
new TsconfigPathsPlugin({
configFile: config.env.tsConfig,
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
}),
],
fallback: {
path: require.resolve('path-browserify'),
},
},
module: {
rules: [
{
test: /\.([jt])sx?$/,
loader: 'ts-loader',
exclude: [/node_modules/],
options: {
configFile: config.env.tsConfig,
// https://github.com/TypeStrong/ts-loader/pull/685
experimentalWatchApi: true,
transpileOnly: true,
},
},
{
test: /\.feature$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/loader',
},
],
},
{
test: /\.features$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/lib/featuresLoader',
},
],
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
typescript: {
enabled: true,
configFile: config.env.tsConfig,
},
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
externals: [nodeExternals()],
},
})
);
};

View File

@ -0,0 +1,134 @@
import type {
Fills,
Fills_party_tradesPaged_edges_node,
} from '@vegaprotocol/fills';
import { Side } from '@vegaprotocol/types';
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
export const generateFills = (override?: PartialDeep<Fills>): Fills => {
const fills: Fills_party_tradesPaged_edges_node[] = [
generateFill({
buyer: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
}),
generateFill({
id: '1',
seller: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
aggressor: Side.Sell,
buyerFee: {
infrastructureFee: '5000',
},
market: {
name: 'Apples Daily v3',
positionDecimalPlaces: 2,
},
}),
generateFill({
id: '2',
seller: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
aggressor: Side.Buy,
}),
generateFill({
id: '3',
aggressor: Side.Sell,
market: {
name: 'ETHBTC Quarterly (30 Jun 2022)',
},
buyer: {
id: Cypress.env('VEGA_PUBLIC_KEY'),
},
}),
];
const defaultResult: Fills = {
party: {
id: 'buyer-id',
tradesPaged: {
__typename: 'TradeConnection',
totalCount: 1,
edges: fills.map((f) => {
return {
__typename: 'TradeEdge',
node: f,
cursor: '3',
};
}),
pageInfo: {
__typename: 'PageInfo',
startCursor: '1',
endCursor: '2',
},
},
__typename: 'Party',
},
};
return merge(defaultResult, override);
};
export const generateFill = (
override?: PartialDeep<Fills_party_tradesPaged_edges_node>
) => {
const defaultFill: Fills_party_tradesPaged_edges_node = {
__typename: 'Trade',
id: '0',
createdAt: new Date().toISOString(),
price: '10000000',
size: '50000',
buyOrder: 'buy-order',
sellOrder: 'sell-order',
aggressor: Side.Buy,
buyer: {
__typename: 'Party',
id: 'buyer-id',
},
seller: {
__typename: 'Party',
id: 'seller-id',
},
buyerFee: {
__typename: 'TradeFee',
makerFee: '100',
infrastructureFee: '100',
liquidityFee: '100',
},
sellerFee: {
__typename: 'TradeFee',
makerFee: '200',
infrastructureFee: '200',
liquidityFee: '200',
},
market: {
__typename: 'Market',
id: 'market-id',
name: 'UNIDAI Monthly (30 Jun 2022)',
positionDecimalPlaces: 0,
decimalPlaces: 5,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
id: 'instrument-id',
code: 'instrument-code',
product: {
__typename: 'Future',
settlementAsset: {
__typename: 'Asset',
id: 'asset-id',
symbol: 'SYM',
decimals: 18,
},
},
},
},
},
};
return merge(defaultFill, override);
};

View File

@ -1,21 +1,68 @@
import merge from 'lodash/merge';
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
import type { PartialDeep } from 'type-fest';
export interface Market_market {
__typename: 'Market';
id: string;
name: string;
}
export interface Market {
market: Market_market | null;
}
// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries
import type { Market } from '../../../../trading/pages/markets/__generated__/Market';
export const generateMarket = (override?: PartialDeep<Market>): Market => {
const defaultResult = {
const defaultResult: Market = {
market: {
id: 'market-0',
name: 'MARKET NAME',
name: 'ACTIVE MARKET',
tradingMode: MarketTradingMode.Continuous,
state: MarketState.Active,
decimalPlaces: 5,
data: {
market: {
id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35',
__typename: 'Market',
},
markPrice: '13739109',
indicativeVolume: '0',
bestBidVolume: '244',
bestOfferVolume: '100',
bestStaticBidVolume: '482',
bestStaticOfferVolume: '2188',
__typename: 'MarketData',
},
tradableInstrument: {
instrument: {
name: 'BTCUSD Monthly',
code: 'BTCUSD.MF21',
metadata: {
tags: [
'formerly:076BB86A5AA41E3E',
'base:BTC',
'quote:USD',
'class:fx/crypto',
'monthly',
'sector:crypto',
],
__typename: 'InstrumentMetadata',
},
__typename: 'Instrument',
},
__typename: 'TradableInstrument',
},
marketTimestamps: {
open: '2022-06-21T17:18:43.484055236Z',
close: null,
__typename: 'MarketTimestamps',
},
candles: [
{
open: '2095312844',
close: '2090090607',
volume: '4847',
__typename: 'Candle',
},
{
open: '2090090000',
close: '2090090607',
volume: '4847',
__typename: 'Candle',
},
],
__typename: 'Market',
},
};

View File

@ -21,6 +21,7 @@ export const mockTradingPage = (
generateMarket({
market: {
name: `${state.toUpperCase()} MARKET`,
state: state,
},
})
);

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