Compare commits

..
204 changed files with 1814 additions and 3300 deletions
-14
View File
@@ -50,20 +50,6 @@ jobs:
projects=[${projects// /,}]
echo PROJECTS=$projects >> $GITHUB_ENV
# Rename required because some of the files contains the colon character (in the dates)
- name: Rename files to allow archive
if: ${{ always() }}
run: |
while read -r file; do
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
- uses: actions/upload-artifact@v3
if: ${{ always() }}
with:
name: logs-${{ matrix.project }}
path: /home/runner/.vegacapsule/testnet/logs
outputs:
projects: ${{ env.PROJECTS }}
+135 -42
View File
@@ -1,57 +1,150 @@
context('Asset page', { tags: '@regression' }, () => {
const columns = ['symbol', 'name', 'id', 'type', 'status', 'actions'];
const hiddenOnMobile = ['id', 'type', 'status'];
describe('Verify elements on page', () => {
before('Navigate to assets page', () => {
cy.visit('/assets');
context('Asset page', { tags: '@regression' }, function () {
before('gather system asset information', function () {
cy.get_asset_information().as('assetsInfo');
});
describe('Verify elements on page', function () {
const assetsNavigation = 'a[href="/assets"]';
const assetHeader = '[data-testid="asset-header"]';
const jsonSection = '.language-json';
before('Navigate to assets page', function () {
cy.visit('/');
cy.get(assetsNavigation).click();
// Check we have enough enough assets
cy.getAssets().then((assets) => {
assert.isAtLeast(
Object.keys(assets).length,
5,
'Ensuring we have at least 5 assets to test'
);
const assetNames = Object.keys(this.assetsInfo);
assert.isAtLeast(
assetNames.length,
5,
'Ensuring we have at least 5 assets to test'
);
});
it('should be able to see assets page sections', function () {
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.should('be.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
});
});
it('should be able to see full assets list', () => {
cy.getAssets().then((assets) => {
Object.values(assets).forEach((asset) => {
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
});
});
columns.forEach((col) => {
cy.get(`[col-id="${col}"]`).should('be.visible');
it('should be able to see all asset details displayed in JSON', function () {
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.next()
.within(() => {
cy.get(jsonSection)
.invoke('text')
.convert_string_json_to_js_object()
.then((assetsListedInJson) => {
const assetInfo = this.assetsInfo[assetName];
assert.equal(assetsListedInJson.name, assetInfo.node.name);
assert.equal(assetsListedInJson.id, assetInfo.node.id);
assert.equal(
assetsListedInJson.decimals,
assetInfo.node.decimals
);
assert.equal(assetsListedInJson.symbol, assetInfo.node.symbol);
assert.equal(
assetsListedInJson.source.__typename,
assetInfo.node.source.__typename
);
if (assetInfo.node.source.__typename == 'ERC20') {
assert.equal(
assetsListedInJson.source.contractAddress,
assetInfo.node.source.contractAddress
);
}
if (assetInfo.node.source.__typename == 'BuiltinAsset') {
assert.equal(
assetsListedInJson.source.maxFaucetAmountMint,
assetInfo.node.source.maxFaucetAmountMint
);
}
let knownAssetTypes = ['BuiltinAsset', 'ERC20'];
assert.include(
knownAssetTypes,
assetInfo.node.source.__typename,
`Checking that current asset type of ${assetInfo.node.source.__typename} /
is one of: ${knownAssetTypes}: /
If fail then we need to add extra tests for un-encountered asset types`
);
});
});
});
});
it('should be able to see assets page displayed in mobile', () => {
cy.switchToMobile();
it('should be able to switch assets between light and dark mode', function () {
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
const themeSwitcher = '[data-testid="theme-switcher"]';
const jsonFields = '.hljs';
const sideMenuBackground = '.absolute';
hiddenOnMobile.forEach((col) => {
cy.get(`[col-id="${col}"]`).should('have.length', 0);
});
cy.getAssets().then((assets) => {
Object.values(assets).forEach((asset) => {
cy.get(`[row-id="${asset.id}"]`).should('be.visible');
// Engage dark mode if not allready set
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
cy.get(themeSwitcher).click();
});
});
// Engage white mode
cy.get(themeSwitcher).click();
// White Mode
cy.get(assetsNavigation)
.should('have.css', 'background-color')
.and('include', whiteThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', whiteThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', whiteThemeSideMenuBackgroundColor);
// Dark Mode
cy.get(themeSwitcher).click();
cy.get(assetsNavigation)
.should('have.css', 'background-color')
.and('include', darkThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', darkThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', darkThemeSideMenuBackgroundColor);
});
it('should open details dialog when clicked on "View details"', () => {
cy.getAssets().then((assets) => {
Object.values(assets).forEach((asset) => {
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
.eq(0)
.should('contain.text', 'View details');
cy.get(`[row-id="${asset.id}"] [col-id="actions"] button`)
.eq(0)
.click();
cy.getByTestId('dialog-content').should('be.visible');
cy.getByTestId('dialog-close').click();
});
it('should be able to see assets page displayed in mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(assetsNavigation).click();
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.should('be.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
});
});
});
@@ -123,7 +123,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
.convert_string_json_to_js_object()
.get_party_accounts_data_from_js_object()
.then((accountsListedInJson) => {
cy.getAssets().then((assetsInfo) => {
cy.get_asset_information().then((assetsInfo) => {
const assetInfo =
assetsInfo[accountsListedInJson[assetInTest].asset.name];
@@ -205,7 +205,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
});
Cypress.Commands.add('get_asset_decimals', (assetID) => {
cy.getAssets().then((assetsInfo) => {
cy.get_asset_information().then((assetsInfo) => {
const assetDecimals = assetsInfo[assetData[assetID].name].decimals;
let decimals = '';
for (let i = 0; i < assetDecimals; i++) decimals += '0';
@@ -21,10 +21,6 @@ Cypress.Commands.add(
}
);
Cypress.Commands.add('switchToMobile', () => {
cy.viewport('iphone-x');
});
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
cy.viewport('iphone-x');
cy.visit('/');
+3 -5
View File
@@ -1,12 +1,10 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.be.devnet1.vega.xyz/websocket
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_ENV=DEVNET
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet1-network.json
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
-19
View File
@@ -9,23 +9,6 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
import type { InMemoryCacheConfig } from '@apollo/client';
import { Footer } from './components/footer/footer';
import { AnnouncementBanner, ExternalLink } from '@vegaprotocol/ui-toolkit';
import {
AssetDetailsDialog,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
const DialogsContainer = () => {
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
return (
<AssetDetailsDialog
assetId={id}
trigger={trigger || null}
asJson={asJson}
open={isOpen}
onChange={setOpen}
/>
);
};
function App() {
const [menuOpen, setMenuOpen] = useState(false);
@@ -73,8 +56,6 @@ function App() {
<Main />
<Footer />
</div>
<DialogsContainer />
</NetworkLoader>
</TendermintWebsocketProvider>
);
@@ -1,6 +1,6 @@
import { useAssetDataProvider } from '@vegaprotocol/assets';
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import { AssetLink } from '../links';
import { useExplorerAssetQuery } from '../links/asset-link/__generated__/Asset';
export type AssetBalanceProps = {
assetId: string;
@@ -17,17 +17,21 @@ const AssetBalance = ({
price,
showAssetLink = true,
}: AssetBalanceProps) => {
const { data: asset } = useAssetDataProvider(assetId);
const { data } = useExplorerAssetQuery({
variables: { id: assetId },
});
const label =
asset && asset.decimals
? addDecimalsFormatNumber(price, asset.decimals)
data && data.asset?.decimals
? addDecimalsFormatNumber(price, data.asset.decimals)
: price;
return (
<div className="inline-block">
<span>{label}</span>{' '}
{showAssetLink && asset?.id ? <AssetLink assetId={assetId} /> : null}
{showAssetLink && data?.asset?.id ? (
<AssetLink id={data.asset.id} />
) : null}
</div>
);
};
@@ -1,28 +0,0 @@
import { render, waitFor } from '@testing-library/react';
import { assetsList } from '../../mocks/assets';
import { AssetsTable } from './assets-table';
describe('AssetsTable', () => {
it('shows loading message on first render', async () => {
const res = render(<AssetsTable data={null} />);
expect(await res.findByText('Loading...')).toBeInTheDocument();
});
it('shows no data message if no assets found', async () => {
const res = render(<AssetsTable data={[]} />);
expect(
await res.findByText('This chain has no assets')
).toBeInTheDocument();
});
it('shows a table/list with all the assets', async () => {
const res = render(<AssetsTable data={assetsList} />);
await waitFor(() => {
const rowA1 = res.container.querySelector('[row-id="123"]');
expect(rowA1).toBeInTheDocument();
const rowA2 = res.container.querySelector('[row-id="456"]');
expect(rowA2).toBeInTheDocument();
});
});
});
@@ -1,115 +0,0 @@
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/react-helpers';
import type { VegaICellRendererParams } from '@vegaprotocol/ui-toolkit';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react';
import { AgGridDynamic as AgGrid } from '@vegaprotocol/ui-toolkit';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
};
export const AssetsTable = ({ data }: AssetsTableProps) => {
const openAssetDetailsDialog = useAssetDetailsDialogStore(
(state) => state.open
);
const ref = useRef<AgGridReact>(null);
const showColumnsOnDesktop = () => {
ref.current?.columnApi.setColumnsVisible(
['id', 'type', 'status'],
window.innerWidth > BREAKPOINT_MD
);
};
useLayoutEffect(() => {
window.addEventListener('resize', showColumnsOnDesktop);
return () => {
window.removeEventListener('resize', showColumnsOnDesktop);
};
}, []);
return (
<AgGrid
ref={ref}
rowData={data}
getRowId={({ data }: { data: AssetFieldsFragment }) => data.id}
overlayNoRowsTemplate={t('This chain has no assets')}
domLayout="autoHeight"
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
suppressCellFocus={true}
onGridReady={() => {
showColumnsOnDesktop();
}}
>
<AgGridColumn headerName={t('Symbol')} field="symbol" />
<AgGridColumn headerName={t('Name')} field="name" />
<AgGridColumn flex="2" headerName={t('ID')} field="id" />
<AgGridColumn
colId="type"
headerName={t('Type')}
field="source.__typename"
valueFormatter={({ value }: { value?: string }) =>
value && AssetTypeMapping[value].value
}
/>
<AgGridColumn
headerName={t('Status')}
field="status"
valueFormatter={({ value }: { value?: string }) =>
value && AssetStatusMapping[value].value
}
/>
<AgGridColumn
colId="actions"
headerName=""
sortable={false}
filter={false}
resizable={false}
wrapText={true}
field="id"
cellRenderer={({
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<div className="pb-1">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(value, e.target as HTMLElement);
}}
>
{t('View details')}
</ButtonLink>{' '}
<span className="max-md:hidden">
<ButtonLink
onClick={(e) => {
openAssetDetailsDialog(
value,
e.target as HTMLElement,
true
);
}}
>
{t('View JSON')}
</ButtonLink>
</span>
</div>
) : (
''
)
}
/>
</AgGrid>
);
};
@@ -0,0 +1,8 @@
query ExplorerAsset($id: ID!) {
asset(id: $id) {
id
name
status
decimals
}
}
@@ -0,0 +1,51 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerAssetQueryVariables = Types.Exact<{
id: Types.Scalars['ID'];
}>;
export type ExplorerAssetQuery = { __typename?: 'Query', asset?: { __typename?: 'Asset', id: string, name: string, status: Types.AssetStatus, decimals: number } | null };
export const ExplorerAssetDocument = gql`
query ExplorerAsset($id: ID!) {
asset(id: $id) {
id
name
status
decimals
}
}
`;
/**
* __useExplorerAssetQuery__
*
* To run a query within a React component, call `useExplorerAssetQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerAssetQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerAssetQuery({
* variables: {
* id: // value for 'id'
* },
* });
*/
export function useExplorerAssetQuery(baseOptions: Apollo.QueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
}
export function useExplorerAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerAssetQuery, ExplorerAssetQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerAssetQuery, ExplorerAssetQueryVariables>(ExplorerAssetDocument, options);
}
export type ExplorerAssetQueryHookResult = ReturnType<typeof useExplorerAssetQuery>;
export type ExplorerAssetLazyQueryHookResult = ReturnType<typeof useExplorerAssetLazyQuery>;
export type ExplorerAssetQueryResult = Apollo.QueryResult<ExplorerAssetQuery, ExplorerAssetQueryVariables>;
@@ -1,37 +1,63 @@
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render, waitFor } from '@testing-library/react';
import { AssetLink } from './asset-link';
import { mockAssetA1 } from '../../../mocks/assets';
import { render } from '@testing-library/react';
import AssetLink from './asset-link';
import { ExplorerAssetDocument } from './__generated__/Asset';
function renderComponent(id: string, mock: MockedResponse[]) {
return (
<MockedProvider mocks={mock} addTypename={false}>
<MockedProvider mocks={mock}>
<MemoryRouter>
<AssetLink assetId={id} />
<AssetLink id={id} />
</MemoryRouter>
</MockedProvider>
);
}
describe('AssetLink', () => {
it('renders the asset id when not found and makes the button disabled', async () => {
describe('Asset link component', () => {
it('Renders the ID at first', () => {
const res = render(renderComponent('123', []));
expect(res.getByText('123')).toBeInTheDocument();
expect(await res.findByTestId('asset-link')).toBeDisabled();
await waitFor(async () => {
expect(await res.queryByText('A ONE')).toBeFalsy();
});
});
it('renders the asset name when found and make the button enabled', async () => {
const res = render(renderComponent('123', [mockAssetA1]));
expect(res.getByText('123')).toBeInTheDocument();
it('Renders the asset name when the query returns a result', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
variables: {
id: '123',
},
},
result: {
data: {
asset: {
id: '123',
name: 'test-label',
status: 'irrelevant-test-data',
decimals: 18,
},
},
},
};
await waitFor(async () => {
expect(await res.findByText('A ONE')).toBeInTheDocument();
expect(await res.findByTestId('asset-link')).not.toBeDisabled();
});
const res = render(renderComponent('123', [mock]));
expect(res.getByText('123')).toBeInTheDocument();
expect(await res.findByText('test-label')).toBeInTheDocument();
});
it('Leaves the asset id when the asset is not found', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
variables: {
id: '123',
},
},
error: new Error('No such asset'),
};
const res = render(renderComponent('123', [mock]));
expect(await res.findByText('123')).toBeInTheDocument();
});
});
@@ -1,35 +1,36 @@
import React from 'react';
import { Routes } from '../../../routes/route-names';
import { useExplorerAssetQuery } from './__generated__/Asset';
import { Link } from 'react-router-dom';
import type { ComponentProps } from 'react';
import Hash from '../hash';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import {
useAssetDataProvider,
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
export type AssetLinkProps = Partial<ComponentProps<typeof ButtonLink>> & {
assetId: string;
export type AssetLinkProps = Partial<ComponentProps<typeof Link>> & {
id: string;
};
/**
* Given an asset ID, it will fetch the asset name and show that,
* with a link to the assets modal. If the name does not come back
* with a link to the assets list. If the name does not come back
* it will use the ID instead.
*/
export const AssetLink = ({ assetId, ...props }: AssetLinkProps) => {
const { data: asset } = useAssetDataProvider(assetId);
const AssetLink = ({ id, ...props }: AssetLinkProps) => {
const { data } = useExplorerAssetQuery({
variables: { id },
});
let label: string = id;
if (data?.asset?.name) {
label = data.asset.name;
}
const open = useAssetDetailsDialogStore((state) => state.open);
const label = asset?.name ? asset.name : assetId;
return (
<ButtonLink
data-testid="asset-link"
disabled={!asset}
onClick={(e) => {
open(assetId, e.target as HTMLElement);
}}
{...props}
>
<Link className="underline" {...props} to={`/${Routes.ASSETS}#${id}`}>
<Hash text={label} />
</ButtonLink>
</Link>
);
};
export default AssetLink;
@@ -2,4 +2,4 @@ export { default as BlockLink } from './block-link/block-link';
export { default as PartyLink } from './party-link/party-link';
export { default as NodeLink } from './node-link/node-link';
export { default as MarketLink } from './market-link/market-link';
export * from './asset-link/asset-link';
export { default as AssetLink } from './asset-link/asset-link';
@@ -2,7 +2,6 @@ query ExplorerMarket($id: ID!) {
market(id: $id) {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
name
@@ -8,7 +8,7 @@ export type ExplorerMarketQueryVariables = Types.Exact<{
}>;
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export type ExplorerMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string, settlementAsset: { __typename?: 'Asset', decimals: number } } } } } | null };
export const ExplorerMarketDocument = gql`
@@ -16,7 +16,6 @@ export const ExplorerMarketDocument = gql`
market(id: $id) {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
name
@@ -55,7 +55,6 @@ describe('Market link component', () => {
market: {
id: '123',
decimalPlaces: 5,
positionDecimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
@@ -19,7 +19,6 @@ fragment ExplorerDeterministicOrderFields on Order {
market {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
name
@@ -3,7 +3,7 @@ import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
export type ExplorerDeterministicOrderFieldsFragment = { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } };
export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
orderId: Types.Scalars['ID'];
@@ -11,7 +11,7 @@ export type ExplorerDeterministicOrderQueryVariables = Types.Exact<{
}>;
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, positionDecimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
export type ExplorerDeterministicOrderQuery = { __typename?: 'Query', orderByID: { __typename?: 'Order', id: string, type?: Types.OrderType | null, reference: string, status: Types.OrderStatus, version: string, createdAt: any, updatedAt?: any | null, expiresAt?: any | null, timeInForce: Types.OrderTimeInForce, price: string, side: Types.Side, remaining: string, size: string, rejectionReason?: Types.OrderRejectionReason | null, party: { __typename?: 'Party', id: string }, market: { __typename?: 'Market', id: string, decimalPlaces: number, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } } };
export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
fragment ExplorerDeterministicOrderFields on Order {
@@ -35,7 +35,6 @@ export const ExplorerDeterministicOrderFieldsFragmentDoc = gql`
market {
id
decimalPlaces
positionDecimalPlaces
tradableInstrument {
instrument {
name
@@ -61,7 +61,6 @@ function renderExistingAmend(id: string, version: number, amend: Amend) {
__typename: 'Market',
id: '789',
state: 'STATUS_ACTIVE',
positionDecimalPlaces: 2,
decimalPlaces: '5',
tradableInstrument: {
instrument: {
@@ -89,7 +88,6 @@ function renderExistingAmend(id: string, version: number, amend: Amend) {
market: {
id: '789',
decimalPlaces: 5,
positionDecimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
@@ -4,7 +4,6 @@ import { MarketLink } from '../links';
import PriceInMarket from '../price-in-market/price-in-market';
import { Time } from '../time';
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
import SizeInMarket from '../size-in-market/size-in-market';
export interface DeterministicOrderDetailsProps {
id: string;
@@ -91,7 +90,7 @@ const DeterministicOrderDetails = ({
<div className="mb-12 md:mb-0">
<h2 className="text-2xl font-bold text-dark mb-4">{t('Size')}</h2>
<h5 className="text-lg font-medium text-gray-500 mb-0">
<SizeInMarket size={o.size} marketId={o.market.id} />
{o.size}
</h5>
</div>
@@ -38,7 +38,6 @@ const mock = {
__typename: 'Market',
id: '789',
state: 'STATE_ACTIVE',
positionDecimalPlaces: 2,
decimalPlaces: 2,
tradableInstrument: {
instrument: {
@@ -90,7 +90,6 @@ describe('Order TX Summary component', () => {
market: {
id: '123',
decimalPlaces: 2,
positionDecimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
@@ -37,7 +37,6 @@ const fullMock = {
market: {
id: '123',
decimalPlaces: 2,
positionDecimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
@@ -1,69 +0,0 @@
import { MemoryRouter } from 'react-router-dom';
import { MockedProvider } from '@apollo/client/testing';
import type { MockedResponse } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import SizeInMarket from './size-in-market';
import type { DecimalSource } from './size-in-market';
import { ExplorerMarketDocument } from '../links/market-link/__generated__/Market';
function renderComponent(
price: string,
marketId: string,
mocks: MockedResponse[],
decimalSource: DecimalSource = 'MARKET'
) {
return (
<MockedProvider mocks={mocks} addTypename={false}>
<MemoryRouter>
<SizeInMarket
marketId={marketId}
size={price}
decimalSource={decimalSource}
/>
</MemoryRouter>
</MockedProvider>
);
}
const fullMock = {
request: {
query: ExplorerMarketDocument,
variables: {
id: '123',
},
},
result: {
data: {
market: {
id: '123',
decimalPlaces: 2,
positionDecimalPlaces: 2,
state: 'irrelevant-test-data',
tradableInstrument: {
instrument: {
name: 'test dai',
product: {
__typename: 'Future',
quoteName: 'dai',
settlementAsset: {
decimals: 18,
},
},
},
},
},
},
},
};
describe('Size in Market component', () => {
it('Renders the raw size when there is no market data', () => {
const res = render(renderComponent('100', '123', []));
expect(res.getByText('100')).toBeInTheDocument();
});
it('Renders the formatted size when market data is fetched', async () => {
const res = render(renderComponent('100', '123', [fullMock]));
expect(await res.findByText('1.00')).toBeInTheDocument();
});
});
@@ -1,41 +0,0 @@
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import { useExplorerMarketQuery } from '../links/market-link/__generated__/Market';
export type DecimalSource = 'MARKET';
export type PriceInMarketProps = {
marketId: string;
size: string | number;
decimalSource?: DecimalSource;
};
/**
* Given a market ID and an order size it will fetch the market
* order size, and format the size accordingly
*/
const SizeInMarket = ({
marketId,
size,
decimalSource = 'MARKET',
}: PriceInMarketProps) => {
const { data } = useExplorerMarketQuery({
variables: { id: marketId },
fetchPolicy: 'cache-first',
});
let label = size;
if (data) {
if (decimalSource === 'MARKET' && data.market?.positionDecimalPlaces) {
label = addDecimalsFormatNumber(size, data.market.positionDecimalPlaces);
}
}
return (
<label>
<span>{label}</span>
</label>
);
};
export default SizeInMarket;
@@ -3,7 +3,6 @@ import React from 'react';
import classnames from 'classnames';
interface TableProps {
allowWrap?: boolean;
children: React.ReactNode;
className?: string;
}
@@ -26,15 +25,8 @@ interface TableCellProps extends ThHTMLAttributes<HTMLTableCellElement> {
modifier?: 'bordered' | 'background';
}
export const Table = ({
allowWrap,
children,
className,
...props
}: TableProps) => {
const classes = allowWrap
? className
: classnames(className, 'overflow-x-auto whitespace-nowrap');
export const Table = ({ children, className, ...props }: TableProps) => {
const classes = classnames(className, 'overflow-x-auto whitespace-nowrap');
return (
<div className={classes}>
<table className="w-full" {...props}>
@@ -45,14 +37,11 @@ export const Table = ({
};
export const TableWithTbody = ({
allowWrap,
children,
className,
...props
}: TableProps) => {
const classes = allowWrap
? className
: classnames(className, 'overflow-x-auto whitespace-nowrap');
const classes = classnames(className, 'overflow-x-auto whitespace-nowrap');
return (
<div className={classes}>
<table className="w-full" {...props}>
@@ -76,7 +76,9 @@ describe('Chain Event: Builtin asset deposit', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
});
@@ -34,7 +34,7 @@ export const TxDetailsChainEventBuiltinDeposit = ({
<TableRow modifier="bordered">
<TableCell>{t('Asset')}</TableCell>
<TableCell>
<AssetLink assetId={deposit.vegaAssetId} /> ({t('built in asset')})
<AssetLink id={deposit.vegaAssetId} /> ({t('built in asset')})
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -82,7 +82,9 @@ describe('Chain Event: Builtin asset withdrawal', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
});
@@ -39,8 +39,8 @@ export const TxDetailsChainEventBuiltinWithdrawal = ({
<TableRow modifier="bordered">
<TableCell>{t('Asset')}</TableCell>
<TableCell>
<AssetLink assetId={withdrawal.vegaAssetId || ''} /> (
{t('built in asset')})
<AssetLink id={withdrawal.vegaAssetId || ''} /> ({t('built in asset')}
)
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -63,7 +63,9 @@ describe('Chain Event: ERC20 Asset Delist', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
});
});
@@ -29,7 +29,7 @@ export const TxDetailsChainEventErc20AssetDelist = ({
<TableRow modifier="bordered">
<TableCell>{t('Removed Vega asset')}</TableCell>
<TableCell>
<AssetLink assetId={assetDelist.vegaAssetId || ''} />
<AssetLink id={assetDelist.vegaAssetId || ''} />
</TableCell>
</TableRow>
</>
@@ -79,8 +79,10 @@ describe('Chain Event: ERC20 Asset limits updated', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('ERC20 asset'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
@@ -51,7 +51,7 @@ export const TxDetailsChainEventErc20AssetLimitsUpdated = ({
<TableRow modifier="bordered">
<TableCell>{t('Vega asset')}</TableCell>
<TableCell>
<AssetLink assetId={assetLimitsUpdated.vegaAssetId} />
<AssetLink id={assetLimitsUpdated.vegaAssetId} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -65,8 +65,10 @@ describe('Chain Event: ERC20 Asset List', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.assetSource}`);
@@ -41,7 +41,7 @@ export const TxDetailsChainEventErc20AssetList = ({
<TableRow modifier="bordered">
<TableCell>{t('Added Vega asset')}</TableCell>
<TableCell>
<AssetLink assetId={assetList.vegaAssetId} />
<AssetLink id={assetList.vegaAssetId} />
</TableCell>
</TableRow>
</>
@@ -75,8 +75,10 @@ describe('Chain Event: ERC20 asset deposit', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Source'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.sourceEthereumAddress}`);
@@ -51,7 +51,7 @@ export const TxDetailsChainEventDeposit = ({
<TableRow modifier="bordered">
<TableCell>{t('Asset')}</TableCell>
<TableCell>
<AssetLink assetId={deposit.vegaAssetId} />
<AssetLink id={deposit.vegaAssetId} />
</TableCell>
</TableRow>
<TableRow modifier="bordered">
@@ -60,8 +60,10 @@ describe('Chain Event: ERC20 asset deposit', () => {
if (!assetLink.parentElement) {
throw new Error('Asset link does not exist');
}
expect(assetLink.parentElement.tagName).toEqual('BUTTON');
expect(assetLink.parentElement.textContent).toEqual(fullMock.vegaAssetId);
expect(assetLink.parentElement.tagName).toEqual('A');
expect(assetLink.parentElement.getAttribute('href')).toEqual(
`/assets#${fullMock.vegaAssetId}`
);
expect(screen.getByText(t('Recipient'))).toBeInTheDocument();
const ethLink = screen.getByText(`${fullMock.targetEthereumAddress}`);
@@ -45,7 +45,7 @@ export const TxDetailsChainEventWithdrawal = ({
<TableRow modifier="bordered">
<TableCell>{t('Asset')}</TableCell>
<TableCell>
<AssetLink assetId={withdrawal.vegaAssetId} />
<AssetLink id={withdrawal.vegaAssetId} />
</TableCell>
</TableRow>
</>
@@ -1,108 +0,0 @@
import { getValues } from './bound-factors';
import type { components } from '../../../../../types/explorer';
type KeyValueBundle = components['schemas']['vegaKeyValueBundle'][];
describe('getValues', () => {
it('handles an empty array by returning a dashed template', () => {
const res = getValues([]);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '-');
expect(res.down).toHaveProperty('value', '-');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '-');
expect(res.up).toHaveProperty('value', '-');
});
it('handles undefined', () => {
const res = getValues(undefined as unknown as KeyValueBundle);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '-');
expect(res.down).toHaveProperty('value', '-');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '-');
expect(res.up).toHaveProperty('value', '-');
});
it('handles a kvb that only has one side (should not happen)', () => {
const k: KeyValueBundle = [
{
key: 'up',
tolerance: '0.1',
value: { vectorVal: { value: ['0.123'] } },
},
];
const res = getValues(k);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '-');
expect(res.down).toHaveProperty('value', '-');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '0.1');
expect(res.up).toHaveProperty('value', '0.123');
});
it('handles a kvb that has a matrixVal instead of a scalarval by ignoring it', () => {
const k: KeyValueBundle = [
{
key: 'up',
tolerance: '0.1',
value: { matrixVal: { value: [{ value: ['0.123'] }] } },
},
];
const res = getValues(k);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '-');
expect(res.down).toHaveProperty('value', '-');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '0.1');
expect(res.up).toHaveProperty('value', '-');
});
it('ignores unexpected extra values in the kvb', () => {
const k: KeyValueBundle = [
{
key: 'up',
tolerance: '0.1',
value: { vectorVal: { value: ['0.123', '0.77'] } },
},
{
key: 'down',
tolerance: '0.001',
value: { vectorVal: { value: ['0.321'] } },
},
];
const res = getValues(k);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '0.001');
expect(res.down).toHaveProperty('value', '0.321');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '0.1');
expect(res.up).toHaveProperty('value', '0.123');
});
it('handles a full kvb', () => {
const k: KeyValueBundle = [
{
key: 'up',
tolerance: '0.1',
value: { vectorVal: { value: ['0.123'] } },
},
{
key: 'down',
tolerance: '0.001',
value: { vectorVal: { value: ['0.321'] } },
},
];
const res = getValues(k);
expect(res).toHaveProperty('down');
expect(res.down).toHaveProperty('tolerance', '0.001');
expect(res.down).toHaveProperty('value', '0.321');
expect(res).toHaveProperty('up');
expect(res.up).toHaveProperty('tolerance', '0.1');
expect(res.up).toHaveProperty('value', '0.123');
});
});
@@ -1,87 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { components } from '../../../../../types/explorer';
import { Table, TableRow, TableHeader, TableCell } from '../../../table';
interface StateVariableProposalBoundFactorsProps {
kvb: readonly components['schemas']['vegaKeyValueBundle'][];
}
/**
* A dumb as rocks function completely tied to what the structure of this variable should be
* @param kvb The key/value bundle
* @returns Object
*/
export function getValues(kvb: StateVariableProposalBoundFactorsProps['kvb']) {
const template = {
up: {
tolerance: '-',
value: '-',
},
down: {
tolerance: '-',
value: '-',
},
};
if (kvb && kvb.length > 0) {
kvb.forEach((v) => {
if (v.key === 'up') {
template.up.tolerance = v.tolerance || '-';
template.up.value = v.value?.vectorVal?.value
? v.value?.vectorVal.value[0]
: '-';
} else if (v.key === 'down') {
template.down.tolerance = v.tolerance || '-';
template.down.value = v.value?.vectorVal?.value
? v.value?.vectorVal.value[0]
: '-';
}
});
}
return template;
}
/**
* State Variable proposals updating Bound Factors. This contains two bundles,
* an up vector and a down vector
*
* This is nearly identical to risk factors.
*/
export const StateVariableProposalBoundFactors = ({
kvb,
}: StateVariableProposalBoundFactorsProps) => {
const v = getValues(kvb);
return (
<Table allowWrap={true} className="w-1/3">
<thead>
<TableRow modifier="bordered">
<TableHeader align="left">{t('Parameter')}</TableHeader>
<TableHeader align="center">{t('New value')}</TableHeader>
<TableHeader align="right">{t('Tolerance')}</TableHeader>
</TableRow>
</thead>
<tbody>
<TableRow modifier="bordered">
<TableCell>{t('Up')}</TableCell>
<TableCell align="right" className="font-mono">
{v.up.value}
</TableCell>
<TableCell align="right" className="font-mono">
{v.up.tolerance}
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Down')}</TableCell>
<TableCell align="right" className="font-mono">
{v.down.value}
</TableCell>
<TableCell align="right" className="font-mono">
{v.down.tolerance}
</TableCell>
</TableRow>
</tbody>
</Table>
);
};
@@ -1,31 +0,0 @@
import type { components } from '../../../../../types/explorer';
import { StateVariableProposalUnknown } from './unknown';
import { StateVariableProposalBoundFactors } from './bound-factors';
import { StateVariableProposalRiskFactors } from './risk-factors';
interface StateVariableProposalWrapperProps {
stateVariable: string | undefined;
kvb: readonly components['schemas']['vegaKeyValueBundle'][] | undefined;
}
/**
* State Variable proposals
*/
export const StateVariableProposalWrapper = ({
stateVariable,
kvb,
}: StateVariableProposalWrapperProps) => {
if (!stateVariable || !kvb || kvb.length === 0) {
return null;
}
if (stateVariable.indexOf('bound-factors') !== -1) {
return <StateVariableProposalBoundFactors kvb={kvb} />;
} else if (stateVariable.indexOf('risk-factors') !== -1) {
return <StateVariableProposalRiskFactors kvb={kvb} />;
} else if (stateVariable.indexOf('probability_of_trading') !== -1) {
return <StateVariableProposalRiskFactors kvb={kvb} />;
} else {
return <StateVariableProposalUnknown kvb={kvb} />;
}
};
@@ -1,124 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import zip from 'lodash/zip';
import type { components } from '../../../../../types/explorer';
import { Table, TableRow, TableHeader, TableCell } from '../../../table';
import { StateVariableProposalUnknown } from './unknown';
interface StateVariableProposalRiskFactorsProps {
kvb: readonly components['schemas']['vegaKeyValueBundle'][];
}
/**
* A dumb as rocks function completely tied to what the structure of this variable should be
*
* @param kvb The key/value bundle
* @returns Object
*/
export function getValues(kvb: StateVariableProposalRiskFactorsProps['kvb']) {
try {
const template = {
bid: {
offsetTolerance: '-',
probabilityTolerance: '-',
offset: [] as Readonly<string[]>,
probability: [] as Readonly<string[]>,
rows: [] as [string | undefined, string | undefined][],
},
ask: {
offsetTolerance: '-',
probabilityTolerance: '-',
offset: [] as Readonly<string[]>,
probability: [] as Readonly<string[]>,
rows: [] as [string | undefined, string | undefined][],
},
};
kvb.forEach((v) => {
if (v.key === 'bidOffset') {
template.bid.offsetTolerance = v.tolerance || '-';
template.bid.offset = v.value?.vectorVal?.value
? v.value?.vectorVal?.value
: ['0'];
} else if (v.key === 'bidProbability') {
template.bid.probabilityTolerance = v.tolerance || '-';
template.bid.probability = v.value?.vectorVal?.value
? v.value?.vectorVal?.value
: ['0'];
} else if (v.key === 'askOffset') {
template.ask.offsetTolerance = v.tolerance || '-';
template.ask.offset = v.value?.vectorVal?.value
? v.value?.vectorVal?.value
: ['0'];
} else if (v.key === 'askProbability') {
template.ask.probabilityTolerance = v.tolerance || '-';
template.ask.probability = v.value?.vectorVal?.value
? v.value?.vectorVal?.value
: ['0'];
}
});
// Bundles up offset and probability in to a row
if (template.bid.offset.length > 0 && template.bid.probability.length > 0) {
template.bid.rows = zip(template.bid.offset, template.bid.probability);
}
if (template.ask.offset.length > 0 && template.ask.probability.length > 0) {
template.ask.rows = zip(template.ask.offset, template.ask.probability);
}
return template;
} catch (e) {
// This will result in the table not being rendered
return null;
}
}
/**
* State Variable proposals updating Risk Factors. This contains two bundles,
* a long vector and a short vector
*/
export const StateVariableProposalRiskFactors = ({
kvb,
}: StateVariableProposalRiskFactorsProps) => {
const v = getValues(kvb);
const all = v ? zip(v.bid.rows, v.ask.rows) : [];
if (all.length === 0) {
// Give up, do a JSON view
return <StateVariableProposalUnknown kvb={kvb} />;
}
return (
<Table allowWrap={true} className="text-xs lg:text-base max-w-2xl">
<thead>
<TableRow modifier="bordered">
<TableHeader align="left">{t('Mid offset')}</TableHeader>
<TableHeader align="right">{t('Bid probability')}</TableHeader>
<TableHeader align="right" className="pl-2">
{t('Ask probability')}
</TableHeader>
</TableRow>
</thead>
<tbody>
{all.map((r) => {
// Simple remapping of the data to protect against undefineds
const row = {
o: r[0] ? r[0][0] : r[1] ? r[1][0] : '-',
b: r[0] ? r[0][1] : '-',
a: r[1] ? r[1][1] : '-',
};
return (
<TableRow key={`${row.o}${row.b}${row.a}`}>
<TableCell align="left">{row.o}</TableCell>
<TableCell align="right" className="font-mono">
{row.b}
</TableCell>
<TableCell align="right" className="pl-2 font-mono">
{row.a}
</TableCell>
</TableRow>
);
})}
</tbody>
</Table>
);
};
@@ -1,168 +0,0 @@
import { getValues, StateVariableProposalRiskFactors } from './risk-factors';
import type { components } from '../../../../../types/explorer';
import { render } from '@testing-library/react';
type kvb = components['schemas']['vegaKeyValueBundle'][];
describe('Risk Factors: getValues', () => {
it('returns null if null is passed in', () => {
const res = getValues(null as unknown as kvb);
expect(res).toBeNull();
});
it('returns a blank template if kvb is empty', () => {
const res = getValues([]);
expect(res).not.toBeNull();
expect(res?.bid.offsetTolerance).toEqual('-');
expect(res?.bid.probabilityTolerance).toEqual('-');
expect(res?.bid.probability).toEqual([]);
expect(res?.bid.offset).toEqual([]);
expect(res?.bid.rows.length).toEqual(0);
expect(res?.ask.offsetTolerance).toEqual('-');
expect(res?.ask.probabilityTolerance).toEqual('-');
expect(res?.ask.probability).toEqual([]);
expect(res?.ask.offset).toEqual([]);
expect(res?.ask.rows.length).toEqual(0);
});
it('parses out a correct bid offset and probability', () => {
const k: kvb = [
{
key: 'bidOffset',
value: { vectorVal: { value: ['1'] } },
},
{
key: 'bidProbability',
value: { vectorVal: { value: ['2'] } },
},
];
const res = getValues(k);
expect(res?.bid.offset).toEqual(['1']);
expect(res?.bid.probability).toEqual(['2']);
expect(res?.bid.rows).toEqual([['1', '2']]);
});
it('parses out a correct ask offset and probability', () => {
const k: kvb = [
{
key: 'askOffset',
value: { vectorVal: { value: ['1'] } },
},
{
key: 'askProbability',
value: { vectorVal: { value: ['2'] } },
},
];
const res = getValues(k);
expect(res?.ask.offset).toEqual(['1']);
expect(res?.ask.probability).toEqual(['2']);
expect(res?.ask.rows).toEqual([['1', '2']]);
});
it('parses out a correct ask/bid offset and probability', () => {
const k: kvb = [
{
key: 'askOffset',
value: { vectorVal: { value: ['1'] } },
},
{
key: 'askProbability',
value: { vectorVal: { value: ['2'] } },
},
{
key: 'bidOffset',
value: { vectorVal: { value: ['3'] } },
},
{
key: 'bidProbability',
value: { vectorVal: { value: ['4'] } },
},
];
const res = getValues(k);
expect(res?.ask.rows).toEqual([['1', '2']]);
expect(res?.bid.rows).toEqual([['3', '4']]);
});
});
describe('Risk Factors: component', () => {
it('renders 3 rows correctly', () => {
const k: kvb = [
{
key: 'askOffset',
value: { vectorVal: { value: ['1.1', '1.2', '1.3'] } },
},
{
key: 'askProbability',
value: { vectorVal: { value: ['2.2', '2.3', '2.4'] } },
},
{
key: 'bidOffset',
value: { vectorVal: { value: ['1.1', '1.2', '1.3'] } },
},
{
key: 'bidProbability',
value: { vectorVal: { value: ['4.4', '4.5', '4.6'] } },
},
];
const screen = render(<StateVariableProposalRiskFactors kvb={k} />);
expect(screen.getByText('Mid offset')).toBeInTheDocument();
expect(screen.getByText('Bid probability')).toBeInTheDocument();
expect(screen.getByText('Ask probability')).toBeInTheDocument();
// First row
expect(screen.getByText('1.1')).toBeInTheDocument();
expect(screen.getByText('2.2')).toBeInTheDocument();
expect(screen.getByText('4.4')).toBeInTheDocument();
// Second row
expect(screen.getByText('1.2')).toBeInTheDocument();
expect(screen.getByText('2.3')).toBeInTheDocument();
expect(screen.getByText('4.5')).toBeInTheDocument();
// Third row
expect(screen.getByText('1.3')).toBeInTheDocument();
expect(screen.getByText('2.4')).toBeInTheDocument();
expect(screen.getByText('4.6')).toBeInTheDocument();
});
it('renders uneven row counts correctly', () => {
const k: kvb = [
{
key: 'askOffset',
value: { vectorVal: { value: ['1.1'] } },
},
{
key: 'askProbability',
value: { vectorVal: { value: ['2.2'] } },
},
{
key: 'bidOffset',
value: { vectorVal: { value: ['1.1', '1.2', '1.3'] } },
},
{
key: 'bidProbability',
value: { vectorVal: { value: ['4.4', '4.5', '4.6'] } },
},
];
const screen = render(<StateVariableProposalRiskFactors kvb={k} />);
expect(screen.getByText('Mid offset')).toBeInTheDocument();
expect(screen.getByText('Bid probability')).toBeInTheDocument();
expect(screen.getByText('Ask probability')).toBeInTheDocument();
// First row, as previous test
expect(screen.getByText('1.1')).toBeInTheDocument();
expect(screen.getByText('2.2')).toBeInTheDocument();
expect(screen.getByText('4.4')).toBeInTheDocument();
// Second row - offset comes from bid, not ask
expect(screen.getByText('1.2')).toBeInTheDocument();
expect(screen.getByText('4.5')).toBeInTheDocument();
// Third row
expect(screen.getByText('1.3')).toBeInTheDocument();
expect(screen.getByText('4.6')).toBeInTheDocument();
// The askOffset levels without a probability render -
expect(screen.getAllByText('-')).toHaveLength(2);
});
});
@@ -1,52 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { components } from '../../../../../types/explorer';
import { Table, TableRow, TableHeader, TableCell } from '../../../table';
import { getValues } from './bound-factors';
interface StateVariableProposalBoundFactorsProps {
kvb: readonly components['schemas']['vegaKeyValueBundle'][];
}
/**
* State Variable proposals updating Bound Factors. This contains two bundles,
* an up vector and a down vector
*
* This is nearly identical to risk factors.
*/
export const StateVariableProposalBoundFactors = ({
kvb,
}: StateVariableProposalBoundFactorsProps) => {
const v = getValues(kvb);
return (
<Table allowWrap={true} className="w-1/3">
<thead>
<TableRow modifier="bordered">
<TableHeader align="left">{t('Parameter')}</TableHeader>
<TableHeader align="center">{t('New value')}</TableHeader>
<TableHeader align="right">{t('Tolerance')}</TableHeader>
</TableRow>
</thead>
<tbody>
<TableRow modifier="bordered">
<TableCell>{t('Up')}</TableCell>
<TableCell align="right" className="font-mono">
{v.up.value}
</TableCell>
<TableCell align="right" className="font-mono">
{v.up.tolerance}
</TableCell>
</TableRow>
<TableRow modifier="bordered">
<TableCell>{t('Down')}</TableCell>
<TableCell align="right" className="font-mono">
{v.down.value}
</TableCell>
<TableCell align="right" className="font-mono">
{v.down.tolerance}
</TableCell>
</TableRow>
</tbody>
</Table>
);
};
@@ -1,16 +0,0 @@
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import type { components } from '../../../../../types/explorer';
interface StateVariableProposalUnknownProps {
kvb: readonly components['schemas']['vegaKeyValueBundle'][];
}
/**
* State Variable proposals of an unknown type. Let's just dump
* it out.
*/
export const StateVariableProposalUnknown = ({
kvb,
}: StateVariableProposalUnknownProps) => {
return <SyntaxHighlighter data={kvb} />;
};
@@ -53,7 +53,7 @@ export const TxDetailsBatch = ({
let index = 0;
return (
<div key={`tx-${index}`}>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -32,7 +32,7 @@ export const TxDetailsChainEvent = ({
}
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<ChainEvent txData={txData} />
</TableWithTbody>
@@ -38,7 +38,7 @@ export const TxDetailsDataSubmission = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -35,7 +35,7 @@ export const TxDetailsDelegate = ({
txData.command.delegateSubmission;
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{d.nodeId ? (
<TableRow modifier="bordered">
@@ -21,8 +21,6 @@ import { TxDetailsDataSubmission } from './tx-data-submission';
import { TxProposalVote } from './tx-proposal-vote';
import { TxDetailsProtocolUpgrade } from './tx-details-protocol-upgrade';
import { TxDetailsIssueSignatures } from './tx-issue-signatures';
import { TxDetailsNodeAnnounce } from './tx-node-announce';
import { TxDetailsStateVariable } from './tx-state-variable-proposal';
interface TxDetailsWrapperProps {
txData: BlockExplorerTransactionResult | undefined;
@@ -70,8 +68,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
// These come from https://github.com/vegaprotocol/vega/blob/develop/core/txn/command.go#L72-L98
switch (txData.type) {
case 'Register new Node':
return TxDetailsNodeAnnounce;
case 'Issue Signatures':
return TxDetailsIssueSignatures;
case 'Submit Order':
@@ -106,8 +102,6 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
return TxDetailsDelegate;
case 'Undelegate':
return TxDetailsUndelegate;
case 'State Variable Proposal':
return TxDetailsStateVariable;
default:
return TxDetailsGeneric;
}
@@ -1,80 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import {
EthExplorerLink,
EthExplorerLinkTypes,
} from '../../links/eth-explorer-link/eth-explorer-link';
import { BlockLink } from '../../links';
type EthKeyRotate = components['schemas']['v1EthereumKeyRotateSubmission'];
interface TxDetailsEthKeyRotateProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* A node is changing ethereum key
*/
export const TxDetailsEthKeyRotate = ({
txData,
pubKey,
blockData,
}: TxDetailsEthKeyRotateProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const k: EthKeyRotate = txData.command;
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{k.targetBlock ? (
<TableRow modifier="bordered">
<TableCell>{t('Target block')}</TableCell>
<TableCell>
<BlockLink height={k.targetBlock} />
</TableCell>
</TableRow>
) : null}
{k.currentAddress ? (
<TableRow modifier="bordered">
<TableCell>{t('Old Address')}</TableCell>
<TableCell>
<EthExplorerLink
type={EthExplorerLinkTypes.address}
id={k.currentAddress}
/>
</TableCell>
</TableRow>
) : null}
{k.newAddress ? (
<TableRow modifier="bordered">
<TableCell>{t('New Address')}</TableCell>
<TableCell>
<EthExplorerLink
type={EthExplorerLinkTypes.address}
id={k.newAddress}
/>
</TableCell>
</TableRow>
) : null}
{k.submitterAddress ? (
<TableRow modifier="bordered">
<TableCell>{t('Submitter address')}</TableCell>
<TableCell>
<EthExplorerLink
type={EthExplorerLinkTypes.address}
id={k.submitterAddress}
/>
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
);
};
@@ -23,7 +23,7 @@ export const TxDetailsGeneric = ({
}
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
</TableWithTbody>
);
@@ -60,7 +60,7 @@ export const TxDetailsHeartbeat = ({
const blockHeight = txData.command.blockHeight || '';
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Node')}</TableCell>
@@ -1,67 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { BlockLink, PartyLink } from '../../links';
type KeyRotate = components['schemas']['v1KeyRotateSubmission'];
interface TxDetailsKeyRotateProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* A node is changing Vega key
*/
export const TxDetailsKeyRotate = ({
txData,
pubKey,
blockData,
}: TxDetailsKeyRotateProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const k: KeyRotate = txData.command;
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{k.targetBlock ? (
<TableRow modifier="bordered">
<TableCell>{t('Target block')}</TableCell>
<TableCell>
<BlockLink height={k.targetBlock} />
</TableCell>
</TableRow>
) : null}
{k.currentPubKeyHash ? (
<TableRow modifier="bordered">
<TableCell>{t('Old Address')}</TableCell>
<TableCell>
<PartyLink id={k.currentPubKeyHash} />
</TableCell>
</TableRow>
) : null}
{k.currentPubKeyHash ? (
<TableRow modifier="bordered">
<TableCell>{t('New Address')}</TableCell>
<TableCell>
<PartyLink id={k.currentPubKeyHash} />
</TableCell>
</TableRow>
) : null}
{k.newPubKeyIndex ? (
<TableRow modifier="bordered">
<TableCell>{t('Key index')}</TableCell>
<TableCell>
<code>{k.newPubKeyIndex}</code>
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
);
};
@@ -36,7 +36,7 @@ export const TxDetailsLiquidityAmendment = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -37,7 +37,7 @@ export const TxDetailsLiquidityCancellation = ({
const marketId: string = cancel.marketId || '-';
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
@@ -35,7 +35,7 @@ export const TxDetailsLiquiditySubmission = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -1,112 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableRow, TableCell, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import {
EthExplorerLink,
EthExplorerLinkTypes,
} from '../../links/eth-explorer-link/eth-explorer-link';
import { PartyLink } from '../../links';
import Hash from '../../links/hash';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
type Command = components['schemas']['v1AnnounceNode'];
interface TxDetailsNodeAnnounceProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* When a new potential validator node comes online, it announces
* itself with this transaction.
*
* Design decisions:
* - Signatures are not rendered. You can still access them via the
* TX details. This is consistent with explorers for other chains
* - The avatar icon is rendered as a link rather than embedding
*/
export const TxDetailsNodeAnnounce = ({
txData,
pubKey,
blockData,
}: TxDetailsNodeAnnounceProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const cmd: Command = txData.command.announceNode;
return (
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{cmd.name ? (
<TableRow modifier="bordered">
<TableCell>{t('Name')}</TableCell>
<TableCell>
<span>{cmd.name}</span>
</TableCell>
</TableRow>
) : null}
{cmd.id ? (
<TableRow modifier="bordered">
<TableCell>{t('ID')}</TableCell>
<TableCell>
<Hash text={cmd.id} />
</TableCell>
</TableRow>
) : null}
{cmd.chainPubKey ? (
<TableRow modifier="bordered">
<TableCell>{t('Chain public key')}</TableCell>
<TableCell>
<Hash text={cmd.chainPubKey} />
</TableCell>
</TableRow>
) : null}
{cmd.ethereumAddress ? (
<TableRow modifier="bordered">
<TableCell>{t('Ethereum Address')}</TableCell>
<TableCell>
<EthExplorerLink
type={EthExplorerLinkTypes.address}
id={cmd.ethereumAddress}
/>
</TableCell>
</TableRow>
) : null}
{cmd.vegaPubKey ? (
<TableRow modifier="bordered">
<TableCell>{t('Vega public key')}</TableCell>
<TableCell>
<PartyLink id={cmd.vegaPubKey} />
</TableCell>
</TableRow>
) : null}
{cmd.avatarUrl ? (
<TableRow modifier="bordered">
<TableCell>{t('Avatar URL')}</TableCell>
<TableCell>
<ExternalLink href={cmd.avatarUrl} rel="noreferrer noopener">
{cmd.avatarUrl}
</ExternalLink>
</TableCell>
</TableRow>
) : null}
{cmd.infoUrl ? (
<TableRow modifier="bordered">
<TableCell>{t('Info link')}</TableCell>
<TableCell>
<ExternalLink href={cmd.infoUrl} rel="noreferrer noopener">
{cmd.infoUrl}
</ExternalLink>
</TableCell>
</TableRow>
) : null}
</TableWithTbody>
);
};
@@ -42,7 +42,7 @@ export const TxDetailsNodeVote = ({
}
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{data && !!data.deposit
? TxDetailsNodeVoteDeposit({ deposit: data })
@@ -29,7 +29,7 @@ export const TxDetailsOrderAmend = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -29,7 +29,7 @@ export const TxDetailsOrderCancel = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -39,7 +39,7 @@ export const TxDetailsOrder = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -32,7 +32,7 @@ export const TxProposalVote = ({
const vote = txData.command.voteSubmission.value ? '👍' : '👎';
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
<TableRow modifier="bordered">
<TableCell>{t('Proposal ID')}</TableCell>
@@ -1,57 +0,0 @@
import {
hackyGetMarketFromStateVariable,
hackyGetVariableFromStateVariable,
} from './tx-state-variable-proposal';
describe('Hacky Get market from state variable', () => {
it('Extracts a market id from a known state variable proposal id', () => {
const knownId =
'84fff099818dc4f5319477f0812b4341565cfb32ccf735beede734e386b8108f_5d69ff4a485a9f963272c8614c1d0d84bb8ea57886f5b11aad53f0ccc77731ba_probability_of_trading';
const res = hackyGetMarketFromStateVariable(knownId);
expect(res).toEqual(
'5d69ff4a485a9f963272c8614c1d0d84bb8ea57886f5b11aad53f0ccc77731ba'
);
});
it('Returns null if the string looks a bit like the known one, but with different segments', () => {
const knownId =
'5d69ff4a485a9f963272c8614c1d0d84bb8ea57886f5b11aad53f0ccc77731ba_probability_of_trading';
const res = hackyGetMarketFromStateVariable(knownId);
expect(res).toEqual(null);
});
it('Handles empty/weird data', () => {
expect(hackyGetMarketFromStateVariable(null as unknown as string)).toEqual(
null
);
expect(hackyGetMarketFromStateVariable('')).toEqual(null);
expect(
hackyGetMarketFromStateVariable(undefined as unknown as string)
).toEqual(null);
expect(hackyGetMarketFromStateVariable(2 as unknown as string)).toEqual(
null
);
});
});
describe('Hacky Get Variable from state variable proposal id', () => {
it('Extracts an variable name from a known state variable proposal id', () => {
const knownId =
'84fff099818dc4f5319477f0812b4341565cfb32ccf735beede734e386b8108f_5d69ff4a485a9f963272c8614c1d0d84bb8ea57886f5b11aad53f0ccc77731ba_probability_of_trading';
const res = hackyGetVariableFromStateVariable(knownId);
expect(res).toEqual('probability of trading');
});
it('Handles empty/weird data', () => {
expect(
hackyGetVariableFromStateVariable(null as unknown as string)
).toEqual(null);
expect(hackyGetVariableFromStateVariable('')).toEqual(null);
expect(
hackyGetVariableFromStateVariable(undefined as unknown as string)
).toEqual(null);
expect(hackyGetVariableFromStateVariable(2 as unknown as string)).toEqual(
null
);
});
});
@@ -1,113 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { TxDetailsShared } from './shared/tx-details-shared';
import { TableCell, TableRow, TableWithTbody } from '../../table';
import { MarketLink } from '../../links';
import type { components } from '../../../../types/explorer';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import { StateVariableProposalWrapper } from './state-variable/data-wrapper';
interface TxDetailsStateVariableProps {
txData: BlockExplorerTransactionResult | undefined;
pubKey: string | undefined;
blockData: TendermintBlocksResponse | undefined;
}
/**
* There is no market ID in the event, but it appears to be encoded in to the variable
* ID so let's pull it out. MarketLink component will handle if it isn't a real market.
*
* Given how liable to break this is, it's wrapped in a try catch
*
* @param stateVarId The full state variable proposal variable name
* @returns null or a string market id
*/
export function hackyGetMarketFromStateVariable(
stateVarId?: string
): string | null {
try {
const res = stateVarId ? stateVarId.split('_')[1] : null;
return res && res.length === 64 ? res : null;
} catch (e) {
return null;
}
}
/**
* There is no event name in the event, but it appears to be encoded in to the variable
* ID so let's pull it out. Will display nothing if it doesn't parse as expected
*
* Given how liable to break this is, it's wrapped in a try catch
*
* @param stateVarId The full state variable proposal variable name
* @returns null or a string variable name
*/
export function hackyGetVariableFromStateVariable(
stateVarId?: string
): string | null {
try {
if (!stateVarId) {
return null;
}
return stateVarId.split('_').slice(2).join(' ').replace('-', ' ');
} catch (e) {
return null;
}
}
/**
* State Variable proposals
*/
export const TxDetailsStateVariable = ({
txData,
pubKey,
blockData,
}: TxDetailsStateVariableProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
const command: components['schemas']['v1StateVariableProposal'] =
txData.command.stateVariableProposal;
const variable = hackyGetVariableFromStateVariable(
command.proposal?.stateVarId
);
const marketId = hackyGetMarketFromStateVariable(
command.proposal?.stateVarId
);
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TxDetailsShared
txData={txData}
pubKey={pubKey}
blockData={blockData}
/>
{marketId ? (
<TableRow modifier="bordered">
<TableCell>{t('Market')}</TableCell>
<TableCell>
<MarketLink id={marketId} />
</TableCell>
</TableRow>
) : null}
<TableRow modifier="bordered">
<TableCell>{t('Variable')}</TableCell>
<TableCell className="capitalize">
<span>{variable}</span>
</TableCell>
</TableRow>
</TableWithTbody>
<section>
<StateVariableProposalWrapper
stateVariable={command.proposal?.stateVarId}
kvb={command.proposal?.kvb}
/>
</section>
</>
);
};
@@ -46,7 +46,7 @@ export const TxDetailsUndelegate = ({
txData.command.undelegateSubmission;
return (
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
{u.nodeId ? (
<TableRow modifier="bordered">
@@ -41,7 +41,7 @@ export const TxDetailsWithdrawSubmission = ({
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
<TableWithTbody className="mb-8">
<TxDetailsShared
txData={txData}
pubKey={pubKey}
@@ -1,4 +0,0 @@
/**
* Equivalent of tailwind's `md` modifier
*/
export const BREAKPOINT_MD = 768;
-134
View File
@@ -1,134 +0,0 @@
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetDocument } from '@vegaprotocol/assets';
import { AssetsDocument } from '@vegaprotocol/assets';
import { AssetStatus } from '@vegaprotocol/types';
const A1: AssetFieldsFragment = {
__typename: 'Asset',
id: '123',
name: 'A ONE',
symbol: 'A1',
decimals: 0,
quantum: '',
status: AssetStatus.STATUS_ENABLED,
source: {
__typename: 'BuiltinAsset',
maxFaucetAmountMint: '',
},
infrastructureFeeAccount: {
__typename: 'AccountBalance',
balance: '',
},
globalRewardPoolAccount: {
__typename: 'AccountBalance',
balance: '',
},
lpFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
makerFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
marketProposerRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
takerFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
};
const A2: AssetFieldsFragment = {
__typename: 'Asset',
id: '456',
name: 'A TWO',
symbol: 'A2',
decimals: 0,
quantum: '',
status: AssetStatus.STATUS_ENABLED,
source: {
__typename: 'BuiltinAsset',
maxFaucetAmountMint: '',
},
infrastructureFeeAccount: {
__typename: 'AccountBalance',
balance: '',
},
globalRewardPoolAccount: {
__typename: 'AccountBalance',
balance: '',
},
lpFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
makerFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
marketProposerRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
takerFeeRewardAccount: {
__typename: 'AccountBalance',
balance: '',
},
};
export const assetsList = [A1, A2];
export const mockAssetsList = {
request: {
query: AssetsDocument,
},
result: {
data: {
assetsConnection: {
__typename: 'AssetsConnection',
edges: [
{
__typename: 'AssetEdge',
node: A1,
},
{
__typename: 'AssetEdge',
node: A2,
},
],
},
},
},
};
export const mockEmptyAssetsList = {
request: {
query: AssetsDocument,
},
result: { data: null },
};
export const mockAssetA1 = {
request: {
query: AssetDocument,
variables: {
assetId: '123',
},
},
result: {
data: {
assetsConnection: {
__typename: 'AssetsConnection',
edges: [
{
__typename: 'AssetEdge',
node: A1,
},
],
},
},
},
};
@@ -0,0 +1,32 @@
fragment AssetsFields on Asset {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
... on BuiltinAsset {
maxFaucetAmountMint
}
}
infrastructureFeeAccount {
type
balance
market {
id
}
}
}
query ExplorerAssets {
assetsConnection {
edges {
node {
...AssetsFields
}
}
}
}
@@ -0,0 +1,73 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type AssetsFieldsFragment = { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string }, infrastructureFeeAccount?: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, market?: { __typename?: 'Market', id: string } | null } | null };
export type ExplorerAssetsQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerAssetsQuery = { __typename?: 'Query', assetsConnection?: { __typename?: 'AssetsConnection', edges?: Array<{ __typename?: 'AssetEdge', node: { __typename?: 'Asset', id: string, name: string, symbol: string, decimals: number, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string }, infrastructureFeeAccount?: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, market?: { __typename?: 'Market', id: string } | null } | null } } | null> | null } | null };
export const AssetsFieldsFragmentDoc = gql`
fragment AssetsFields on Asset {
id
name
symbol
decimals
source {
... on ERC20 {
contractAddress
}
... on BuiltinAsset {
maxFaucetAmountMint
}
}
infrastructureFeeAccount {
type
balance
market {
id
}
}
}
`;
export const ExplorerAssetsDocument = gql`
query ExplorerAssets {
assetsConnection {
edges {
node {
...AssetsFields
}
}
}
}
${AssetsFieldsFragmentDoc}`;
/**
* __useExplorerAssetsQuery__
*
* To run a query within a React component, call `useExplorerAssetsQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerAssetsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useExplorerAssetsQuery({
* variables: {
* },
* });
*/
export function useExplorerAssetsQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>(ExplorerAssetsDocument, options);
}
export function useExplorerAssetsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>(ExplorerAssetsDocument, options);
}
export type ExplorerAssetsQueryHookResult = ReturnType<typeof useExplorerAssetsQuery>;
export type ExplorerAssetsLazyQueryHookResult = ReturnType<typeof useExplorerAssetsLazyQuery>;
export type ExplorerAssetsQueryResult = Apollo.QueryResult<ExplorerAssetsQuery, ExplorerAssetsQueryVariables>;
@@ -1,30 +0,0 @@
import { t } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import { useAssetsDataProvider } from '@vegaprotocol/assets';
import { AssetsTable } from '../../components/assets/assets-table';
export const Assets = () => {
useDocumentTitle(['Assets']);
useScrollToLocation();
const { data, loading, error } = useAssetsDataProvider();
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
<AsyncRenderer
noDataMessage={t('This chain has no assets')}
data={data}
loading={loading}
error={error}
>
<div className="h-full relative">
<AssetsTable data={data} />
</div>
</AsyncRenderer>
</section>
);
};
@@ -0,0 +1,44 @@
import { MockedProvider } from '@apollo/client/testing';
import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import Assets from './index';
import type { MockedResponse } from '@apollo/client/testing';
import { ExplorerAssetDocument } from '../../components/links/asset-link/__generated__/Asset';
function renderComponent(mock: MockedResponse[]) {
return (
<MemoryRouter>
<MockedProvider mocks={mock}>
<Assets />
</MockedProvider>
</MemoryRouter>
);
}
describe('Assets index', () => {
it('Renders loader when loading', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('loader')).toBeInTheDocument();
});
it('Renders EmptyList when loading completes and there are no results', async () => {
const mock = {
request: {
query: ExplorerAssetDocument,
},
result: {
data: {},
},
};
const res = render(renderComponent([mock]));
expect(await res.findByTestId('emptylist')).toBeInTheDocument();
});
});
+53 -1
View File
@@ -1 +1,53 @@
export * from './assets';
import { getNodes, t } from '@vegaprotocol/react-helpers';
import React from 'react';
import { RouteTitle } from '../../components/route-title';
import { SubHeading } from '../../components/sub-heading';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { useExplorerAssetsQuery } from './__generated__/Assets';
import type { AssetsFieldsFragment } from './__generated__/Assets';
import { useScrollToLocation } from '../../hooks/scroll-to-location';
import { useDocumentTitle } from '../../hooks/use-document-title';
import EmptyList from '../../components/empty-list/empty-list';
const Assets = () => {
const { data, loading } = useExplorerAssetsQuery();
useDocumentTitle(['Assets']);
useScrollToLocation();
const assets = getNodes<AssetsFieldsFragment>(data?.assetsConnection);
if (!assets || assets.length === 0) {
if (!loading) {
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
<EmptyList
heading={t('This chain has no assets')}
label={t('0 assets')}
/>
</section>
);
} else {
return <Loader />;
}
}
return (
<section>
<RouteTitle data-testid="assets-header">{t('Assets')}</RouteTitle>
{assets.map((a) => {
return (
<React.Fragment key={a.id}>
<SubHeading data-testid="asset-header" id={a.id}>
{a.name} ({a.symbol})
</SubHeading>
<SyntaxHighlighter data={a} />
</React.Fragment>
);
})}
</section>
);
};
export default Assets;
@@ -71,7 +71,7 @@ export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
/>
</td>
<td className="text-md">
<AssetLink assetId={account.asset.id} />
<AssetLink id={account.asset.id} />
</td>
</TableRow>
);
@@ -1,4 +1,4 @@
import { Assets } from './assets';
import Assets from './assets';
import BlockPage from './blocks';
import Governance from './governance';
import Home from './home';
+9 -1
View File
@@ -1,6 +1,9 @@
const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const theme = require('../../libs/tailwindcss-config/src/theme');
const {
VegaColours,
} = require('../../libs/tailwindcss-config/src/vega-colours');
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
module.exports = {
@@ -11,7 +14,12 @@ module.exports = {
],
darkMode: 'class',
theme: {
extend: theme,
extend: {
...theme,
colors: {
vega: VegaColours,
},
},
},
plugins: [vegaCustomClasses],
};
@@ -39,8 +39,10 @@ export const MarketList = () => {
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
const localData = data?.markets;
return (
<AsyncRenderer loading={loading} error={error} data={data}>
<AsyncRenderer loading={loading} error={error} data={localData}>
<div
className="grow w-full"
style={{ minHeight: 500, overflow: 'hidden' }}
@@ -55,7 +57,7 @@ export const MarketList = () => {
);
},
}}
rowData={data}
rowData={localData}
defaultColDef={{
resizable: true,
sortable: true,
+45 -188
View File
@@ -98,24 +98,13 @@
}
]
},
{
"tranche_id": 53,
"tranche_start": "2023-03-06T00:00:00.000Z",
"tranche_end": "2023-04-06T00:00:00.000Z",
"total_added": "0",
"total_removed": "0",
"locked_amount": "0",
"deposits": [],
"withdrawals": [],
"users": []
},
{
"tranche_id": 49,
"tranche_start": "2022-12-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "70697.358476038783010308",
"locked_amount": "71290.812100647638944706",
"deposits": [
{
"amount": "86666.297",
@@ -181,7 +170,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "1521.2350872507125",
"locked_amount": "1555.5670024420025",
"deposits": [
{
"amount": "2500",
@@ -568,8 +557,8 @@
"tranche_start": "2023-02-01T00:00:00.000Z",
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "328.1417856",
"locked_amount": "35582.71706184775875",
"total_removed": "183.137181525",
"locked_amount": "36100.54097605893375",
"deposits": [
{
"amount": "7500",
@@ -587,11 +576,6 @@
"amount": "183.137181525",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x326ded5446d14472f79d487ece43dd7db760fec5df52e310342930db38fb5de1"
},
{
"amount": "145.004604075",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x6171eb144528a90bd3a51c6aacec83d4f40d63ffee5388a1c0b8318bee460042"
}
],
"users": [
@@ -611,17 +595,11 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0x326ded5446d14472f79d487ece43dd7db760fec5df52e310342930db38fb5de1"
},
{
"amount": "145.004604075",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 34,
"tx": "0x6171eb144528a90bd3a51c6aacec83d4f40d63ffee5388a1c0b8318bee460042"
}
],
"total_tokens": "7500",
"withdrawn_tokens": "328.1417856",
"remaining_tokens": "7171.8582144"
"withdrawn_tokens": "183.137181525",
"remaining_tokens": "7316.862818475"
},
{
"address": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -646,7 +624,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "70632.856031912127156",
"locked_amount": "71225.768204759715901935",
"deposits": [
{
"amount": "129999.45",
@@ -712,7 +690,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "35115.32668061897062",
"locked_amount": "35543.98462709284528",
"deposits": [
{
"amount": "10000",
@@ -905,7 +883,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "2996.519374682902",
"locked_amount": "3030.7572298325725",
"deposits": [
{
"amount": "5000",
@@ -1116,7 +1094,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "97499.58",
"total_removed": "0",
"locked_amount": "12506.4536420561534866824",
"locked_amount": "13088.045251352428418448",
"deposits": [
{
"amount": "97499.58",
@@ -1149,7 +1127,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "135173.4239508",
"total_removed": "98230.390980249184455396",
"locked_amount": "17094.162979544133231208218516",
"locked_amount": "17889.0983018495693667123126",
"deposits": [
{
"amount": "135173.4239508",
@@ -1195,7 +1173,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "32499.86",
"total_removed": "0",
"locked_amount": "5261.2490723107703745224",
"locked_amount": "5505.914618791834455152",
"deposits": [
{
"amount": "32499.86",
@@ -1228,7 +1206,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "10833.29",
"total_removed": "0",
"locked_amount": "1712.4855190846272169196",
"locked_amount": "1792.1217802859475346957",
"deposits": [
{
"amount": "10833.29",
@@ -1261,7 +1239,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "22749.93",
"total_removed": "0",
"locked_amount": "6401.6533470713355214011",
"locked_amount": "6699.35147787958153563",
"deposits": [
{
"amount": "6500",
@@ -1399,8 +1377,8 @@
"tranche_start": "2022-11-01T00:00:00.000Z",
"tranche_end": "2023-05-01T00:00:00.000Z",
"total_added": "22500",
"total_removed": "4140.290726625",
"locked_amount": "9913.1661487108665",
"total_removed": "3995.28612255",
"locked_amount": "10223.86049723756925",
"deposits": [
{
"amount": "7500",
@@ -1429,11 +1407,6 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x3f253311d975a353930c10981b742ac3b0df52da085d437244f077b63ec32953"
},
{
"amount": "145.004604075",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x8050aa26e1965d26aa350efbc83e48c1b95f8c0933f35dee35fbcdd43cb08dcc"
},
{
"amount": "305.3119245",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -1515,12 +1488,6 @@
"tranche_id": 33,
"tx": "0x3f253311d975a353930c10981b742ac3b0df52da085d437244f077b63ec32953"
},
{
"amount": "145.004604075",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 33,
"tx": "0x8050aa26e1965d26aa350efbc83e48c1b95f8c0933f35dee35fbcdd43cb08dcc"
},
{
"amount": "305.3119245",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -1583,8 +1550,8 @@
}
],
"total_tokens": "7500",
"withdrawn_tokens": "4140.290726625",
"remaining_tokens": "3359.709273375"
"withdrawn_tokens": "3995.28612255",
"remaining_tokens": "3504.71387745"
},
{
"address": "0x2539b51EbDE65a75672aBcfE9439a706a99D18D1",
@@ -1609,7 +1576,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "928642.9598472029154",
"locked_amount": "593915.2619183745624309166",
"locked_amount": "607199.059293409416755688",
"deposits": [
{
"amount": "1852091.69",
@@ -1960,7 +1927,7 @@
"tranche_start": "2022-08-01T00:00:00.000Z",
"tranche_end": "2023-02-01T00:00:00.000Z",
"total_added": "42500",
"total_removed": "30000",
"total_removed": "24434.0787288",
"locked_amount": "0",
"deposits": [
{
@@ -1975,11 +1942,6 @@
}
],
"withdrawals": [
{
"amount": "5565.9212712",
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
"tx": "0x1e37e1c281da391a5bb1ce5ad16cdcc78dba1cb97a1e46103dc4b07725f9ab9e"
},
{
"amount": "1982.0652174",
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -2028,12 +1990,6 @@
}
],
"withdrawals": [
{
"amount": "5565.9212712",
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
"tranche_id": 17,
"tx": "0x1e37e1c281da391a5bb1ce5ad16cdcc78dba1cb97a1e46103dc4b07725f9ab9e"
},
{
"amount": "1982.0652174",
"user": "0x0B4e6fcE839B01ef43DA6F890FAC7B1Afb004600",
@@ -2060,8 +2016,8 @@
}
],
"total_tokens": "30000",
"withdrawn_tokens": "30000",
"remaining_tokens": "0"
"withdrawn_tokens": "24434.0787288",
"remaining_tokens": "5565.9212712"
}
]
},
@@ -33602,8 +33558,8 @@
"tranche_start": "2022-03-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "592998.0503546212334",
"locked_amount": "937142.30296588429147888632",
"total_removed": "589730.8667090699299",
"locked_amount": "957554.874604284746674723657",
"deposits": [
{
"amount": "1998.95815",
@@ -33782,11 +33738,6 @@
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x8a19d30e4686bca650aa1c4e84a82d8a16d607e07828706cb758a6e17cab0190"
},
{
"amount": "3267.1836455513035",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x96af4603ea4ac4ce4585d25325fd739e2380e315175e181c8ac5aec899e5861c"
},
{
"amount": "2536.282963529438",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -34142,12 +34093,6 @@
"tranche_id": 1,
"tx": "0x8a19d30e4686bca650aa1c4e84a82d8a16d607e07828706cb758a6e17cab0190"
},
{
"amount": "3267.1836455513035",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tranche_id": 1,
"tx": "0x96af4603ea4ac4ce4585d25325fd739e2380e315175e181c8ac5aec899e5861c"
},
{
"amount": "2536.282963529438",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -34414,8 +34359,8 @@
}
],
"total_tokens": "187637.95",
"withdrawn_tokens": "139761.298849858013",
"remaining_tokens": "47876.651150141987"
"withdrawn_tokens": "136494.1152043067095",
"remaining_tokens": "51143.8347956932905"
},
{
"address": "0x1A71e3ED1996CAbB91bB043f880CE963D601707e",
@@ -34928,8 +34873,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "563236.46299044578477952",
"locked_amount": "8622734.02167102114525126607450796049608",
"total_removed": "560317.67585805941693952",
"locked_amount": "8695115.6900884208257391762291393526601783",
"deposits": [
{
"amount": "16249.93",
@@ -35503,31 +35448,6 @@
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0xb6a33b8b8855789fbc2d0f9545ba5c2258e52f143d90bc75bbe2411bd2f9b4a5"
},
{
"amount": "179.50914756407",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tx": "0x86280b4d0cccc9e251d0f18fce5af974d0e1fb31a1aaf219725a27efb0d607f4"
},
{
"amount": "718.26845",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tx": "0x71e2885c8c466c935c3fdcc7618896e149ebd155ff432f9c95b993c9f134547a"
},
{
"amount": "615.533659014901",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x61b0366935b55d5f021bbf4c0e1c7595e1a8628fe24c135e62e66d82dc9cc021"
},
{
"amount": "429.5754139050275",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tx": "0x2dc4f71aa0fc8695025203f3ecb4f64d0728c6b38f83796442443fdc3edb991a"
},
{
"amount": "975.90046190236934",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tx": "0xb39a9645c363292992430b1ac8bd16052b46a7eb1624e7e8ba86a0d1663585b2"
},
{
"amount": "858.360074993579125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -37153,18 +37073,6 @@
"tranche_id": 2,
"tx": "0xb6a33b8b8855789fbc2d0f9545ba5c2258e52f143d90bc75bbe2411bd2f9b4a5"
},
{
"amount": "615.533659014901",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x61b0366935b55d5f021bbf4c0e1c7595e1a8628fe24c135e62e66d82dc9cc021"
},
{
"amount": "429.5754139050275",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
"tranche_id": 2,
"tx": "0x2dc4f71aa0fc8695025203f3ecb4f64d0728c6b38f83796442443fdc3edb991a"
},
{
"amount": "858.360074993579125",
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
@@ -38253,8 +38161,8 @@
}
],
"total_tokens": "259998.8875",
"withdrawn_tokens": "118529.201449012157375",
"remaining_tokens": "141469.686050987842625"
"withdrawn_tokens": "117484.092376092228875",
"remaining_tokens": "142514.795123907771125"
},
{
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
@@ -38499,12 +38407,6 @@
"tranche_id": 2,
"tx": "0xb0d4e11c4f5aab1c4c65994c14d5272ecb4df9972ddee36ae5389de3731a3e35"
},
{
"amount": "975.90046190236934",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
"tranche_id": 2,
"tx": "0xb39a9645c363292992430b1ac8bd16052b46a7eb1624e7e8ba86a0d1663585b2"
},
{
"amount": "1293.67099136315494",
"user": "0xe3eB4CF43C072658401996b71D43eE26E573562D",
@@ -38705,8 +38607,8 @@
}
],
"total_tokens": "150551.801",
"withdrawn_tokens": "68648.27553278775093",
"remaining_tokens": "81903.52546721224907"
"withdrawn_tokens": "67672.37507088538159",
"remaining_tokens": "82879.42592911461841"
},
{
"address": "0x4d982Ab0823fD2f48e934a7be2bb0a5374a26148",
@@ -38920,12 +38822,6 @@
"tranche_id": 2,
"tx": "0x94fe358ee7c973dd1d28ca4c56250dfaccbcb718673872c8e9cf8651a22a1108"
},
{
"amount": "718.26845",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
"tranche_id": 2,
"tx": "0x71e2885c8c466c935c3fdcc7618896e149ebd155ff432f9c95b993c9f134547a"
},
{
"amount": "1099.300488",
"user": "0xF4c75FdbAe821C6B5DBB9f001bB23cBA58D1bA37",
@@ -39108,8 +39004,8 @@
}
],
"total_tokens": "200000",
"withdrawn_tokens": "90814.016456",
"remaining_tokens": "109185.983544"
"withdrawn_tokens": "90095.748006",
"remaining_tokens": "109904.251994"
},
{
"address": "0x1b956E6c00E238194B331eddEFF72Cb5f28A8d01",
@@ -39575,12 +39471,6 @@
"tranche_id": 2,
"tx": "0xeb5f2401c9402d58fa4a7549ce2045a48a2c3b90ec6e812ca2f0059f1275e213"
},
{
"amount": "179.50914756407",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
"tranche_id": 2,
"tx": "0x86280b4d0cccc9e251d0f18fce5af974d0e1fb31a1aaf219725a27efb0d607f4"
},
{
"amount": "139.3487773806265",
"user": "0xE6CacAE56Cca8dFdB7910b5A13578719D4E57DA0",
@@ -39829,8 +39719,8 @@
}
],
"total_tokens": "12362.05",
"withdrawn_tokens": "5603.4115869825835",
"remaining_tokens": "6758.6384130174165"
"withdrawn_tokens": "5423.9024394185135",
"remaining_tokens": "6938.1475605814865"
},
{
"address": "0xb091D456d0dFCB94dcba6f355379056C5bb995fC",
@@ -40461,8 +40351,8 @@
"tranche_start": "2021-11-05T00:00:00.000Z",
"tranche_end": "2023-05-05T00:00:00.000Z",
"total_added": "14597706.0446472999",
"total_removed": "3923097.864384523185862282",
"locked_amount": "2239007.76583982230552610140543878",
"total_removed": "3921707.576409189114087282",
"locked_amount": "2305830.059961451513978716150659574",
"deposits": [
{
"amount": "129284.449",
@@ -40726,16 +40616,6 @@
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x0c292f2355b5cdc3b389ed35e6e138e593b1db9d91fcd58f132835343724bf90"
},
{
"amount": "853.04613488184474475",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0xe2b3e0f908fa79636a02b919ede475f86857f0588badd6c85249eb97802fc331"
},
{
"amount": "537.24184045222703025",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tx": "0x58aa11462e034da8d46955c61b513f80de89ebcfd89a96860afc3f8da5bfa73e"
},
{
"amount": "8950.14985089483210984",
"user": "0x66827bCD635f2bB1779d68c46aEB16541bCA6ba8",
@@ -43579,18 +43459,6 @@
"tranche_id": 3,
"tx": "0x0c292f2355b5cdc3b389ed35e6e138e593b1db9d91fcd58f132835343724bf90"
},
{
"amount": "853.04613488184474475",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0xe2b3e0f908fa79636a02b919ede475f86857f0588badd6c85249eb97802fc331"
},
{
"amount": "537.24184045222703025",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
"tranche_id": 3,
"tx": "0x58aa11462e034da8d46955c61b513f80de89ebcfd89a96860afc3f8da5bfa73e"
},
{
"amount": "1192.05386354121365675",
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
@@ -45981,8 +45849,8 @@
}
],
"total_tokens": "359123.469575",
"withdrawn_tokens": "303699.67161397448526175",
"remaining_tokens": "55423.79796102551473825"
"withdrawn_tokens": "302309.38363864041348675",
"remaining_tokens": "56814.08593635958651325"
},
{
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
@@ -47320,7 +47188,7 @@
"tranche_end": "2023-04-05T00:00:00.000Z",
"total_added": "5778205.3912159303",
"total_removed": "2730068.739915456784546642",
"locked_amount": "567742.106149360000910747830518459",
"locked_amount": "594143.998694685843745742950916108",
"deposits": [
{
"amount": "552496.6455",
@@ -49302,8 +49170,8 @@
"tranche_start": "2022-06-05T00:00:00.000Z",
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "32520.5457984892685",
"locked_amount": "148495.55483872828913703876509388",
"total_removed": "32476.7515062512685",
"locked_amount": "151730.043498066832591983753120252",
"deposits": [
{
"amount": "3000",
@@ -55972,11 +55840,6 @@
"user": "0x4eD2b3c68BB4fda084ce1591a210F4aC8b71234A",
"tx": "0xc0f953d7ecc45bfc1d7b69ecd491a63e8b00b0d732ff006bee008e917a04179f"
},
{
"amount": "43.794292238",
"user": "0x916E6Ce65e828e725Affdeda2C0a083071188109",
"tx": "0xdad3bca9bdda18abd6bbed7c1d618f60316bafc507591966b7a86bed3dc36b39"
},
{
"amount": "78.261187214",
"user": "0x479F059c46c6873C6B970A92911084b3eA036d56",
@@ -76115,12 +75978,6 @@
}
],
"withdrawals": [
{
"amount": "43.794292238",
"user": "0x916E6Ce65e828e725Affdeda2C0a083071188109",
"tranche_id": 5,
"tx": "0xdad3bca9bdda18abd6bbed7c1d618f60316bafc507591966b7a86bed3dc36b39"
},
{
"amount": "11.442313546",
"user": "0x916E6Ce65e828e725Affdeda2C0a083071188109",
@@ -76135,8 +75992,8 @@
}
],
"total_tokens": "200",
"withdrawn_tokens": "136.120541602",
"remaining_tokens": "63.879458398"
"withdrawn_tokens": "92.326249364",
"remaining_tokens": "107.673750636"
},
{
"address": "0x93D9D57409e0a1fD340b270Cd41368cc66392249",
@@ -866,10 +866,6 @@ context(
cy.get(stakeTokenSubmitButton).should('contain', 'Add 1 $VEGA tokens');
});
after('teardown wallet', function () {
cy.vega_wallet_teardown();
});
});
}
);
@@ -10,16 +10,6 @@ const pendingStake = '[data-testid="pending-stake"]';
const stakedByOperator = '[data-testid="staked-by-operator"]';
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
const stakeShare = '[data-testid="stake-percentage"]';
const stakedByOperatorToolTip = "[data-testid='staked-operator-tooltip']";
const stakedByDelegatesToolTip = "[data-testid='staked-delegates-tooltip']";
const totalStakedToolTip = "[data-testid='total-staked-tooltip']";
const unnormalisedVotingPowerToolTip =
"[data-testid='unnormalised-voting-power-tooltip']";
const normalisedVotingPowerToolTip =
"[data-testid='normalised-voting-power-tooltip']";
const performancePenaltyToolTip = "[data-testid='performance-penalty-tooltip']";
const overstakedPenaltyToolTip = "[data-testid='overstaked-penalty-tooltip']";
const totalPenaltyToolTip = "[data-testid='total-penalty-tooltip']";
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/;
@@ -66,70 +56,37 @@ context('Staking Page - verify elements on page', function () {
});
it('Should be able to see validator stake', function () {
cy.get('[col-id="stake"] > div > span > span')
cy.get('[col-id="stake"] > div > span')
.should('have.length.at.least', 1)
.each(($stake) => {
cy.wrap($stake).should('not.be.empty');
});
});
it('Should be able to see validator stake tooltip', function () {
cy.get('[col-id="stake"] > div > span > span').first().realHover();
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 0.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 0.00');
});
it('Should be able to see validator normalised voting power', function () {
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
cy.get('[col-id="normalisedVotingPower"] > div > span')
.should('have.length.at.least', 1)
.each(($vPower) => {
cy.wrap($vPower).should('not.be.empty');
});
});
it('Should be able to see validator voting power tooltip', function () {
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
.first()
.realHover();
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 0.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 0.10%');
it('Should be able to see validator normalised voting power', function () {
cy.get('[col-id="normalisedVotingPower"] > div > span')
.should('have.length.at.least', 1)
.each(($vPower) => {
cy.wrap($vPower).should('not.be.empty');
});
});
it('Should be able to see validator total penalties', function () {
cy.get('[col-id="totalPenalties"] > div > span > span')
cy.get('[col-id="totalPenalties"] > div > span')
.should('have.length.at.least', 1)
.each(($penalties) => {
cy.wrap($penalties).should('contain.text', '0%');
});
});
it('Should be able to see validator penalties tooltip', function () {
cy.get('[col-id="totalPenalties"] > div > span > span').realHover();
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 100.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 0.00%');
});
it('Should be able to see validator pending stake', function () {
cy.get('[col-id="pendingStake"] > div > span')
.should('have.length.at.least', 1)
-1
View File
@@ -1,5 +1,4 @@
import '@vegaprotocol/cypress';
import 'cypress-real-events/support';
import './common.functions.js';
import './staking.functions.js';
@@ -95,15 +95,8 @@ Cypress.Commands.add('faucet_asset', function (assetEthAddress) {
});
Cypress.Commands.add('vega_wallet_teardown', function () {
cy.get('[data-testid="associated-amount"]')
.should('be.visible')
.invoke('text')
.as('associatedAmount');
cy.get('body').then(($body) => {
if (
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
this.associatedAmount != '0.00'
) {
if ($body.find('[data-testid="eth-wallet-associated-balances"]').length) {
cy.vega_wallet_teardown_vesting(this.vestingContract);
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
}
+1 -1
View File
@@ -7,7 +7,7 @@
"allowSyntheticDefaultImports": true,
"outDir": "../../dist/out-tsc",
"allowJs": true,
"types": ["cypress", "node", "cypress-real-events", "@cypress/grep"]
"types": ["cypress", "node", "@cypress/grep"]
},
"include": ["src/**/*.ts", "src/**/*.js"]
}
@@ -15,7 +15,7 @@ export const ProposalFormTransactionDialog = ({
finalizedProposal,
TransactionDialog,
}: ProposalFormTransactionDialogProps) => {
// Render a custom complete UI if the proposal was rejected otherwise
// Render a custom complete UI if the proposal was rejected other wise
// pass undefined so that the default vega transaction dialog UI gets used
const completeContent = finalizedProposal?.rejectionReason ? (
<p>{finalizedProposal.rejectionReason}</p>
@@ -90,15 +90,22 @@ describe('Raw proposal form', () => {
},
result: {
data: {
proposals: {
__typename: 'Proposal',
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
reference: 'proposal-reference',
state: Schema.ProposalState.STATE_OPEN,
rejectionReason:
Schema.ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
errorDetails: 'error-details',
},
busEvents: [
{
__typename: 'BusEvent',
type: Schema.BusEventType.Proposal,
event: {
__typename: 'Proposal',
id: '2fca514cebf9f465ae31ecb4c5721e3a6f5f260425ded887ca50ba15b81a5d50',
reference: 'proposal-reference',
state: Schema.ProposalState.STATE_OPEN,
rejectionReason:
Schema.ProposalRejectionReason
.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
errorDetails: 'error-details',
},
},
],
},
},
delay: 300,
@@ -126,10 +126,10 @@ export const VotingPowerRenderer = ({ data }: VotingPowerRendererProps) => {
<Tooltip
description={
<>
<div data-testid="unnormalised-voting-power-tooltip">
<div>
{t('unnormalisedVotingPower')}: {data.unnormalisedVotingPower}
</div>
<div data-testid="normalised-voting-power-tooltip">
<div>
{t('normalisedVotingPower')}: {data.normalisedVotingPower}
</div>
</>
@@ -155,13 +155,13 @@ export const TotalStakeRenderer = ({ data }: TotalStakeRendererProps) => {
<Tooltip
description={
<>
<div data-testid="staked-operator-tooltip">
<div>
{t('stakedByOperator')}: {data.stakedByOperator.toString()}
</div>
<div data-testid="staked-delegates-tooltip">
<div>
{t('stakedByDelegates')}: {data.stakedByDelegates.toString()}
</div>
<div data-testid="total-staked-tooltip">
<div>
{t('totalStake')}: <span className="font-bold">{data.stake}</span>
</div>
</>
@@ -191,13 +191,13 @@ export const TotalPenaltiesRenderer = ({
<Tooltip
description={
<>
<div data-testid="performance-penalty-tooltip">
<div>
{t('performancePenalty')}: {data.performancePenalty}
</div>
<div data-testid="overstaked-penalty-tooltip">
<div>
{t('overstakedPenalty')}: {data.overstakingPenalty}
</div>
<div data-testid="total-penalty-tooltip">
<div>
{t('totalPenalties')}:{' '}
<span className="font-bold">{data.totalPenalties}</span>
</div>
+7 -48
View File
@@ -32,14 +32,11 @@ const usdcSymbol = 'fUSDC';
const toastContent = 'toast-content';
const ordersTab = 'Orders';
const depositsTab = 'Deposits';
const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close';
const price = '390';
const size = '0.0005';
const newPrice = '200';
const completeWithdrawalBtn = 'complete-withdrawal';
const submitTransferBtn = '[type="submit"]';
const transferForm = 'transfer-form';
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
@@ -108,39 +105,12 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
});
});
it('can key to key transfers', function () {
cy.visit('/#/portfolio');
cy.get('main[data-testid="/portfolio"]').should('exist');
cy.getByTestId(collateralTab).click();
cy.getByTestId('open-transfer-dialog').click();
cy.getByTestId('transfer-form').should('be.visible');
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
cy.get('select option')
.contains('BTC')
.invoke('index')
.then((index) => {
cy.get(assetSelectField).select(index, { force: true });
});
cy.getByTestId(transferForm)
.find(amountField)
.focus()
.type('1', { delay: 100 });
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(toastContent).should(
'contain.text',
'Transfer completeYour transaction has been confirmed TransferTo 7f9cf0…c255351.00 tBTC'
);
cy.getByTestId(toastCloseBtn).click();
});
it('can not withdrawal because of no MultiSign', function () {
// 1002-WITH-022
// 1002-WITH-023
cy.getByTestId('Withdrawals').click();
cy.getByTestId('withdraw-dialog-button').click();
connectEthereumWallet('Unknown');
cy.get(assetSelectField, txTimeout).select(btcName, { force: true });
cy.get(amountField).clear().type('1');
cy.getByTestId('submit-withdrawal').click();
@@ -148,7 +118,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
'contain.text',
'Funds unlocked'
);
cy.getByTestId(toastCloseBtn).click();
cy.getByTestId('tab-withdrawals').within(() => {
cy.get('.ag-center-cols-container')
.children()
@@ -165,7 +135,6 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
'contain.text',
'Error occurredprocessing response error'
);
cy.getByTestId(toastCloseBtn).click({ multiple: true });
cy.getByTestId(completeWithdrawalBtn).should(
'contain.text',
'Complete withdrawal'
@@ -258,8 +227,8 @@ describe('capsule', { tags: '@slow' }, () => {
});
});
});
// comment because of bug #2695
it.skip('can edit order', function () {
it('can edit order', function () {
cy.getByTestId(ordersTab).click();
cy.getByTestId('edit').first().should('be.visible').click();
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
@@ -284,8 +253,8 @@ describe('capsule', { tags: '@slow' }, () => {
checkIfDataAndTimeOfCreationAndUpdateIsEqual(orderUpdatedAt);
});
});
// comment because of bug #2695
it.skip('can cancel order', function () {
it('can cancel order', function () {
cy.getByTestId(ordersTab).click();
cy.getByTestId('cancel').first().click();
cy.getByTestId(toastContent).should(
@@ -332,6 +301,7 @@ describe('capsule', { tags: '@slow' }, () => {
'contain.text',
'Funds unlocked'
);
cy.getByTestId('tab-withdrawals').within(() => {
cy.get('.ag-center-cols-container')
.children()
@@ -348,19 +318,8 @@ describe('capsule', { tags: '@slow' }, () => {
'contain.text',
'Transaction confirmed'
);
cy.getByTestId(toastCloseBtn).click({ multiple: true });
cy.wrap(null).then(() => {
try {
cy.getByTestId(completeWithdrawalBtn)
.eq(0, txTimeout)
.should('not.exist');
} catch (error) {
console.log(
'Assertion failed, but we are continuing because this is our wait to complete transaction'
);
}
});
cy.getByTestId(completeWithdrawalBtn).eq(0, txTimeout).should('not.exist');
cy.get('[col-id="txHash"]', txTimeout)
.should('have.length.above', 1)
+8 -6
View File
@@ -59,9 +59,11 @@ describe('home', { tags: '@regression' }, () => {
describe('default market found', () => {
it('redirects to a default market with the landing dialog open', () => {
cy.visit('/');
cy.wait('@Markets');
cy.wait('@Market');
cy.get('main[data-testid^="/markets/"]');
cy.get('main', { timeout: 20000 }).then((el) => {
expect(el.attr('data-testid')?.startsWith('/market')).to.equal(true);
}); // Wait for page to be rendered to before checking url
// Overlay should be shown
cy.getByTestId(selectMarketOverlay).should('exist');
@@ -99,7 +101,7 @@ describe('home', { tags: '@regression' }, () => {
// the choose market overlay is no longer showing
cy.contains('Select a market to get started').should('not.exist');
cy.contains('Loading...').should('not.exist');
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-1');
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
});
});
@@ -123,7 +125,7 @@ describe('home', { tags: '@regression' }, () => {
aliasGQLQuery(req, 'MarketsData', data);
});
cy.visit('/');
cy.wait('@Markets');
cy.wait('@Market');
cy.getByTestId(selectMarketOverlay)
.get('table')
.invoke('outerWidth')
@@ -231,7 +233,7 @@ describe('home', { tags: '@regression' }, () => {
cy.window().then((window) => {
window.localStorage.setItem('marketId', 'market-1');
cy.visit('/');
cy.wait('@Markets');
cy.wait('@Market');
cy.location('hash').should('equal', '#/markets/market-1');
cy.getByTestId('dialog-content').should('not.exist');
});
@@ -244,7 +246,7 @@ describe('home', { tags: '@regression' }, () => {
aliasGQLQuery(req, 'Market', null);
});
cy.visit('/');
cy.wait('@Markets');
cy.wait('@Market');
cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.getByTestId('dialog-content').should('not.exist');
});
@@ -11,7 +11,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
cy.getByTestId(marketInfoBtn).click();
cy.wait('@MarketInfo');
});
@@ -237,6 +237,7 @@ describe('market states not accepting orders', { tags: '@smoke' }, function () {
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/markets/market-0');
cy.wait('@Market');
});
it('must display that market is not accepting orders', function () {
cy.getByTestId('place-order').click();
+10 -16
View File
@@ -1,6 +1,6 @@
import * as Schema from '@vegaprotocol/types';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsQuery } from '@vegaprotocol/mock';
import { marketQuery } from '@vegaprotocol/mock';
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
describe('markets table', { tags: '@smoke' }, () => {
@@ -13,6 +13,7 @@ describe('markets table', { tags: '@smoke' }, () => {
);
cy.mockSubscription();
cy.visit('/');
cy.wait('@Market');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@MarketsCandles');
@@ -122,24 +123,17 @@ describe('markets table', { tags: '@smoke' }, () => {
);
cy.mockGQL((req) => {
const override = {
marketsConnection: {
edges: [
{
node: {
tradableInstrument: {
instrument: {
name: `opening auction MARKET`,
},
},
state: Schema.MarketState.STATE_ACTIVE,
tradingMode:
Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
},
market: {
tradableInstrument: {
instrument: {
name: `opening auction MARKET`,
},
],
},
state: Schema.MarketState.STATE_ACTIVE,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
},
};
const market = marketsQuery(override);
const market = marketQuery(override);
aliasGQLQuery(req, 'Market', market);
aliasGQLQuery(req, 'ProposalOfMarket', {
proposal: { terms: { enactmentDatetime: '2023-01-31 12:00:01' } },
@@ -4,15 +4,14 @@ before(() => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/');
cy.wait('@Markets');
cy.wait('@MarketsData');
cy.wait('@Market');
cy.getByTestId('dialog-close').click();
});
describe('Desktop view', { tags: '@smoke' }, () => {
describe('Navbar', () => {
const links = ['Markets', 'Trading', 'Portfolio'];
const hashes = ['#/markets/all', '#/markets/market-1', '#/portfolio'];
const hashes = ['#/markets/all', '#/markets/market-0', '#/portfolio'];
links.forEach((link, index) => {
it(`${link} should be correctly rendered`, () => {
@@ -68,7 +67,7 @@ describe('Mobile view', { tags: '@smoke' }, () => {
cy.getByTestId('button-menu-drawer').click();
cy.getByTestId('menu-drawer').within((el) => {
cy.wrap(el).getByTestId('Trading').click();
cy.location('hash').should('equal', '#/markets/market-1');
cy.location('hash').should('equal', '#/markets/market-0');
});
});
it('Portfolio should be correctly rendered', () => {
@@ -35,7 +35,7 @@ describe('time in force default values', () => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
it('must have market order set up to IOC by default', function () {
@@ -64,7 +64,7 @@ describe('must submit order', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
beforeEach(() => {
@@ -162,7 +162,7 @@ describe(
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
beforeEach(() => {
@@ -231,7 +231,7 @@ describe(
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
beforeEach(() => {
@@ -300,7 +300,7 @@ describe(
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
beforeEach(() => {
@@ -360,7 +360,7 @@ describe('deal ticket validation', { tags: '@smoke' }, () => {
beforeEach(() => {
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
it('must not place an order if wallet is not connected', () => {
@@ -405,7 +405,7 @@ describe('deal ticket size validation', { tags: '@smoke' }, function () {
cy.setVegaWallet();
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
it('must warn if order size input has too many digits after the decimal place', function () {
@@ -440,7 +440,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
cy.mockTradingPage();
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
cy.getByTestId(toggleLimit).click();
});
@@ -452,7 +452,7 @@ describe('limit order validations', { tags: '@smoke' }, () => {
//7002-SORD-018
cy.getByTestId(orderPriceField)
.siblings('label')
.should('have.text', 'Price (DAI)');
.should('have.text', 'Price (BTC)');
});
it('must see warning when placing an order with expiry date in past', () => {
@@ -532,7 +532,7 @@ describe('market order validations', { tags: '@smoke' }, () => {
cy.setVegaWallet();
cy.mockTradingPage();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
cy.getByTestId(toggleMarket).click();
});
@@ -586,7 +586,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
);
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
beforeEach(() => {
@@ -648,7 +648,7 @@ describe('account validation', { tags: '@regression' }, () => {
market: null,
asset: {
__typename: 'Asset',
id: 'asset-0',
id: '5cfa87844724df6069b94e4c8a6f03af21907d7bc251593d08e4251043ee9f7c',
},
},
},
@@ -660,7 +660,7 @@ describe('account validation', { tags: '@regression' }, () => {
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
it('should show an error if your balance is zero', () => {
@@ -670,7 +670,7 @@ describe('account validation', { tags: '@regression' }, () => {
//7002-SORD-003
cy.getByTestId('dealticket-error-message-zero-balance').should(
'have.text',
'Insufficient balance. Deposit ' + 'tDAI'
'Insufficient balance. Deposit ' + 'tBTC'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').should('exist');
});
@@ -696,7 +696,7 @@ describe('account validation', { tags: '@regression' }, () => {
});
cy.mockSubscription();
cy.visit('/#/markets/market-0');
cy.wait('@Markets');
cy.wait('@Market');
});
it('should display info and button for deposit', () => {
@@ -708,7 +708,7 @@ describe('account validation', { tags: '@regression' }, () => {
);
cy.getByTestId('dealticket-warning-margin').should(
'contain.text',
'9,999.99 tDAI currently required, 1,000.00 tDAI available'
'9,999.99 tBTC currently required, 1,000.00 tBTC available'
);
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
cy.getByTestId('dialog-content')
@@ -1,6 +1,3 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import { marketsDataQuery } from '@vegaprotocol/mock';
beforeEach(() => {
cy.mockTradingPage();
cy.mockSubscription();
@@ -20,47 +17,6 @@ describe('positions', { tags: '@smoke' }, () => {
validatePositionsDisplayed();
});
it('renders position among some graphql errors', () => {
const errors = [
{
message: 'no market data for market: market-2',
path: ['market', 'data'],
extensions: {
code: 13,
type: 'Internal',
},
},
];
const marketData = marketsDataQuery();
const edges = marketData.marketsConnection?.edges.map((market) => {
const replace =
market.node.data?.market.id === 'market-2' ? null : market.node.data;
return { ...market, node: { ...market.node, data: replace } };
});
const overrides = {
...marketData,
marketsConnection: { ...marketData.marketsConnection, edges },
};
cy.mockGQL((req) => {
aliasGQLQuery(req, 'MarketsData', overrides, errors);
});
cy.visit('/#/markets/market-0');
const emptyCells = [
'notional',
'markPrice',
'liquidationPrice',
'currentLeverage',
'averageEntryPrice',
];
cy.getByTestId('tab-positions').within(() => {
cy.get('[row-id="market-2"]').within(() => {
emptyCells.forEach((cell) => {
cy.get(`[col-id="${cell}"]`).should('contain.text', '-');
});
});
});
});
function validatePositionsDisplayed() {
cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions').within(() => {
@@ -1,132 +0,0 @@
import { selectAsset } from '../support/helpers';
const formFieldError = 'input-error-text';
const toAddressField = '[name="toAddress"]';
const amountField = 'input[name="amount"]';
const submitTransferBtn = '[type="submit"]';
const transferForm = 'transfer-form';
const errorText = 'input-error-text';
const openTransferDialog = 'open-transfer-dialog';
const closeDialog = 'dialog-close';
const dialogTransferText = 'dialog-transfer-text';
const ASSET_SEPOLIA_TBTC = 2;
const ASSET_EURO = 1;
const toastContent = 'toast-content';
const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close';
describe(
'transfer form validation and transfer from options',
{ tags: '@smoke' },
() => {
before(() => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferDialog).click();
cy.wait('@Accounts');
cy.wait('@Assets');
});
it('empty fields', () => {
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(formFieldError).should('contain.text', 'Required');
// only 2 despite 3 fields because the ethereum address will be auto populated
cy.getByTestId(formFieldError).should('have.length', 3);
});
it('min amount', () => {
// 1002-WITH-010
selectAsset(ASSET_SEPOLIA_TBTC);
cy.get(amountField).clear().type('0');
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(errorText).should(
'contain.text',
'Value is below minimum'
);
});
it('max amount', () => {
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
cy.get(amountField).clear().type('1001', { delay: 100 });
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(errorText).should(
'contain.text',
'You cannot transfer more than your available collateral'
);
});
it('can start transfer from vega wallet', () => {
cy.getByTestId(closeDialog).click();
cy.getByTestId('manage-vega-wallet').click();
cy.getByTestId('wallet-transfer').should('have.text', 'Transfer').click();
cy.getByTestId(dialogTransferText).should(
'contain.text',
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
);
});
it('can start transfer from trading collateral table', () => {
cy.getByTestId(closeDialog).click();
cy.getByTestId('Trading').first().click();
cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferDialog).click();
cy.getByTestId(dialogTransferText).should(
'contain.text',
'Transfer funds to another Vega key from 02ecea…342f65 If you are at all unsure, stop and seek advice.'
);
});
}
);
describe('withdraw actions', { tags: '@regression' }, () => {
beforeEach(() => {
cy.mockWeb3Provider();
cy.mockTradingPage();
cy.mockSubscription();
cy.setVegaWallet();
cy.visit('/#/portfolio');
cy.getByTestId(collateralTab).click();
cy.getByTestId(openTransferDialog).click();
cy.wait('@Accounts');
cy.wait('@Assets');
cy.mockVegaWalletTransaction();
});
it('key to key transfers by select key', function () {
cy.getByTestId(transferForm).should('be.visible');
cy.getByTestId(transferForm).find(toAddressField).select(1);
selectAsset(ASSET_SEPOLIA_TBTC);
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(toastContent).should(
'contain.text',
'Awaiting confirmation'
);
cy.getByTestId(toastCloseBtn).click();
});
it('key to key transfers by enter manual key', function () {
cy.getByTestId(transferForm).should('be.visible');
cy.contains('Enter manually').click();
cy.getByTestId(transferForm)
.find(toAddressField)
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
selectAsset(ASSET_SEPOLIA_TBTC);
cy.getByTestId(transferForm).find(amountField).type('1', { delay: 100 });
cy.getByTestId(transferForm).find(submitTransferBtn).click();
cy.getByTestId(toastContent).should(
'contain.text',
'Awaiting confirmation'
);
cy.getByTestId(toastCloseBtn).click();
});
});
+14 -13
View File
@@ -15,6 +15,7 @@ import {
marketDataQuery,
marketDepthQuery,
marketInfoQuery,
marketQuery,
marketsCandlesQuery,
marketsDataQuery,
marketsQuery,
@@ -27,7 +28,7 @@ import {
withdrawalsQuery,
} from '@vegaprotocol/mock';
import type { PartialDeep } from 'type-fest';
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
import type { MarketDataQuery, MarketQuery } from '@vegaprotocol/market-list';
import type { MarketInfoQuery } from '@vegaprotocol/market-info';
type MarketPageMockData = {
@@ -54,18 +55,17 @@ const marketDataOverride = (
},
});
const marketsDataOverride = (
const marketQueryOverride = (
data: MarketPageMockData
): PartialDeep<MarketsQuery> => ({
marketsConnection: {
edges: [
{
node: {
tradingMode: data.tradingMode,
state: data.state,
},
): PartialDeep<MarketQuery> => ({
market: {
tradableInstrument: {
instrument: {
name: `${data.state?.toUpperCase()} MARKET`,
},
],
},
state: data.state,
tradingMode: data.tradingMode,
},
});
@@ -91,9 +91,10 @@ const mockTradingPage = (
aliasGQLQuery(req, 'Statistics', statisticsQuery());
aliasGQLQuery(
req,
'Markets',
marketsQuery(marketsDataOverride({ state, tradingMode, trigger }))
'Market',
marketQuery(marketQueryOverride({ state, tradingMode, trigger }))
);
aliasGQLQuery(req, 'Markets', marketsQuery());
aliasGQLQuery(
req,
'MarketData',

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