Compare commits
89
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79a16b8562 | ||
|
|
5661b74082 | ||
|
|
22703d937e | ||
|
|
f780013846 | ||
|
|
73f37c2477 | ||
|
|
607ad06971 | ||
|
|
7aee2a3a7b | ||
|
|
1fdf519780 | ||
|
|
9233778e85 | ||
|
|
7a91f48bcb | ||
|
|
b43ea0f60b | ||
|
|
6e31fb03ae | ||
|
|
d76fa13c5d | ||
|
|
89aeed6305 | ||
|
|
6e677084a3 | ||
|
|
8069aa5ee7 | ||
|
|
b0169a0d39 | ||
|
|
06cfd79415 | ||
|
|
443220283c | ||
|
|
a642bf8ce4 | ||
|
|
20233db706 | ||
|
|
d4f50eb70c | ||
|
|
e5d3f90d45 | ||
|
|
673c896e2f | ||
|
|
deea63fa5e | ||
|
|
d31333538b | ||
|
|
4af9979a21 | ||
|
|
6421cf87c6 | ||
|
|
0ebfab64ff | ||
|
|
142f08343b | ||
|
|
61aa45a9ed | ||
|
|
4c95db5fb3 | ||
|
|
3072b7824f | ||
|
|
0580e90171 | ||
|
|
c440abc77d | ||
|
|
fbafc726a4 | ||
|
|
dd1890d8c6 | ||
|
|
c8e624eaba | ||
|
|
cc6629ad27 | ||
|
|
9838efa00e | ||
|
|
dac7142a98 | ||
|
|
1397aafd25 | ||
|
|
b192603e57 | ||
|
|
8c2c8d987c | ||
|
|
52c96794f7 | ||
|
|
58ce016d4d | ||
|
|
ac16eb06f5 | ||
|
|
3319871617 | ||
|
|
37e850304a | ||
|
|
655131c744 | ||
|
|
f1cb1b9408 | ||
|
|
891ac527a2 | ||
|
|
546087fb9f | ||
|
|
f7a82d33ba | ||
|
|
d85f413e41 | ||
|
|
c7d0025b4f | ||
|
|
6c9272cd53 | ||
|
|
82fb29d541 | ||
|
|
43cd170c77 | ||
|
|
1d39f81dfc | ||
|
|
f5868b6f1b | ||
|
|
20e1522088 | ||
|
|
ae02b153f8 | ||
|
|
fee5f862a7 | ||
|
|
c25362b084 | ||
|
|
7c105d2c6b | ||
|
|
2dfcb02d1e | ||
|
|
a98ab775c3 | ||
|
|
097bf7c08c | ||
|
|
236e35a92b | ||
|
|
abf84b9d45 | ||
|
|
6d2a2ea0a0 | ||
|
|
6ac79e9f6d | ||
|
|
835ee64243 | ||
|
|
e7c3b5054c | ||
|
|
9dc9588a14 | ||
|
|
a0844d41bf | ||
|
|
3d1aa74128 | ||
|
|
2376bfe58f | ||
|
|
72ccf96523 | ||
|
|
f416fa4a25 | ||
|
|
a24e3646cc | ||
|
|
55b092b216 | ||
|
|
ef4cf6a87e | ||
|
|
c9860a3412 | ||
|
|
127bd3faf3 | ||
|
|
1791b3ca6a | ||
|
|
6044b86ff4 | ||
|
|
341d78e761 |
@@ -1,3 +1,2 @@
|
|||||||
* @vegaprotocol/frontend
|
* @vegaprotocol/frontend
|
||||||
* @vegaprotocol/frontend-qa
|
|
||||||
*.graphql @vegaprotocol/core
|
*.graphql @vegaprotocol/core
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ context('Market page', { tags: '@regression' }, function () {
|
|||||||
cy.contains('Test market 1').click();
|
cy.contains('Test market 1').click();
|
||||||
cy.getByTestId(marketHeaders).should('have.text', 'Test market 1');
|
cy.getByTestId(marketHeaders).should('have.text', 'Test market 1');
|
||||||
cy.validate_element_from_table('Name', 'Test market 1');
|
cy.validate_element_from_table('Name', 'Test market 1');
|
||||||
cy.validate_element_from_table('Market ID', this.createdMarketId);
|
|
||||||
cy.validate_element_from_table('Trading Mode', 'Opening auction');
|
cy.validate_element_from_table('Trading Mode', 'Opening auction');
|
||||||
cy.validate_element_from_table('Market Decimal Places', '5');
|
cy.validate_element_from_table('Market Decimal Places', '5');
|
||||||
cy.validate_element_from_table('Position Decimal Places', '5');
|
cy.validate_element_from_table('Position Decimal Places', '5');
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
context('Oracle page', { tags: '@smoke' }, () => {
|
|
||||||
describe('Verify elements on page', () => {
|
|
||||||
before('create market and navigate to oracle page', () => {
|
|
||||||
cy.createMarket();
|
|
||||||
cy.visit('/oracles');
|
|
||||||
});
|
|
||||||
it('should see oracle data', () => {
|
|
||||||
cy.getByTestId('oracle-details').should('have.length.at.least', 2);
|
|
||||||
cy.getByTestId('oracle-details')
|
|
||||||
.should('exist')
|
|
||||||
.eq(0)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('tr')
|
|
||||||
.eq(0)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('th').should('have.text', 'ID');
|
|
||||||
cy.get('a').invoke('text').should('have.length', 64);
|
|
||||||
cy.get('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', '/oracles/');
|
|
||||||
});
|
|
||||||
cy.get('tr')
|
|
||||||
.eq(1)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('th').should('have.text', 'Type');
|
|
||||||
cy.get('td').should('have.text', 'External data');
|
|
||||||
});
|
|
||||||
cy.get('tr')
|
|
||||||
.eq(2)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('th').should('have.text', 'Signer');
|
|
||||||
cy.getByTestId('keytype').should('have.text', 'Vega');
|
|
||||||
cy.get('a').invoke('text').should('have.length', 64);
|
|
||||||
cy.get('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', '/parties/');
|
|
||||||
});
|
|
||||||
cy.get('tr')
|
|
||||||
.eq(3)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('th').should('have.text', 'Settlement for');
|
|
||||||
cy.get('a').invoke('text').should('have.length', 64);
|
|
||||||
cy.get('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', '/markets/');
|
|
||||||
});
|
|
||||||
cy.get('tr')
|
|
||||||
.eq(4)
|
|
||||||
.within(() => {
|
|
||||||
cy.get('th').should('have.text', 'Matched data');
|
|
||||||
cy.get('td').should('have.text', '❌');
|
|
||||||
});
|
|
||||||
cy.get('details')
|
|
||||||
.eq(0)
|
|
||||||
.within(() => {
|
|
||||||
cy.contains('Filter').click();
|
|
||||||
cy.get('.language-json').should('exist');
|
|
||||||
});
|
|
||||||
cy.get('details')
|
|
||||||
.eq(1)
|
|
||||||
.within(() => {
|
|
||||||
cy.contains('JSON').click();
|
|
||||||
cy.get('.language-json').should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -77,7 +77,7 @@
|
|||||||
"executor": "nx:run-commands",
|
"executor": "nx:run-commands",
|
||||||
"options": {
|
"options": {
|
||||||
"commands": [
|
"commands": [
|
||||||
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.72.3/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.73.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
AppFailure,
|
|
||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
|
NodeFailure,
|
||||||
NodeGuard,
|
NodeGuard,
|
||||||
NodeSwitcherDialog,
|
NodeSwitcherDialog,
|
||||||
useEnvironment,
|
useEnvironment,
|
||||||
@@ -31,7 +31,7 @@ function App() {
|
|||||||
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
<NetworkLoader cache={DEFAULT_CACHE_CONFIG}>
|
||||||
<NodeGuard
|
<NodeGuard
|
||||||
skeleton={<div>{t('Loading')}</div>}
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
failure={<AppFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
failure={<NodeFailure title={t(`Node: ${VEGA_URL} is unsuitable`)} />}
|
||||||
>
|
>
|
||||||
<Suspense fallback={splashLoading}>
|
<Suspense fallback={splashLoading}>
|
||||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
export type HashProps = {
|
export type HashProps = {
|
||||||
text: string;
|
text: string;
|
||||||
|
truncate?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -7,10 +8,16 @@ export type HashProps = {
|
|||||||
* are broken when they need to wrap. This will remove the need
|
* are broken when they need to wrap. This will remove the need
|
||||||
* for a lot of the overflow scrolling that currently exists.
|
* for a lot of the overflow scrolling that currently exists.
|
||||||
*/
|
*/
|
||||||
const Hash = ({ text }: HashProps) => {
|
const Hash = ({ text, truncate = false }: HashProps) => {
|
||||||
|
const h = truncate ? text.slice(0, 6) : text;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<code className="break-all font-mono" style={{ wordWrap: 'break-word' }}>
|
<code
|
||||||
{text}
|
title={text}
|
||||||
|
className="break-all font-mono"
|
||||||
|
style={{ wordWrap: 'break-word' }}
|
||||||
|
>
|
||||||
|
{h}
|
||||||
</code>
|
</code>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ query ExplorerMarket($id: ID!) {
|
|||||||
id
|
id
|
||||||
decimalPlaces
|
decimalPlaces
|
||||||
positionDecimalPlaces
|
positionDecimalPlaces
|
||||||
|
state
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
name
|
name
|
||||||
@@ -22,6 +23,5 @@ query ExplorerMarket($id: ID!) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -17,6 +17,7 @@ export const ExplorerMarketDocument = gql`
|
|||||||
id
|
id
|
||||||
decimalPlaces
|
decimalPlaces
|
||||||
positionDecimalPlaces
|
positionDecimalPlaces
|
||||||
|
state
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
name
|
name
|
||||||
@@ -36,7 +37,6 @@ export const ExplorerMarketDocument = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
state
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -63,6 +63,9 @@ describe('Market link component', () => {
|
|||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
quoteName: 'dai',
|
quoteName: 'dai',
|
||||||
|
settlementAsset: {
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ const MarketLink = ({
|
|||||||
}: MarketLinkProps) => {
|
}: MarketLinkProps) => {
|
||||||
const { data, error, loading } = useExplorerMarketQuery({
|
const { data, error, loading } = useExplorerMarketQuery({
|
||||||
variables: { id },
|
variables: { id },
|
||||||
|
fetchPolicy: 'cache-first',
|
||||||
});
|
});
|
||||||
|
|
||||||
let label = <span>{id}</span>;
|
let label = <span>{id}</span>;
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import OracleLink, { getStatusString } from './oracle-link';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
|
||||||
|
describe('getStatusString', () => {
|
||||||
|
it("returns 'Unknown' for undefined status", () => {
|
||||||
|
expect(getStatusString(undefined)).toBe('Unknown');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct string for a known status', () => {
|
||||||
|
expect(getStatusString('STATUS_ACTIVE')).toBe('Active');
|
||||||
|
expect(getStatusString('STATUS_DEACTIVATED')).toBe('Deactivated');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('OracleLink', () => {
|
||||||
|
it('renders the truncated Oracle ID', () => {
|
||||||
|
const id = '123456789';
|
||||||
|
render(
|
||||||
|
<MockedProvider>
|
||||||
|
<MemoryRouter>
|
||||||
|
<OracleLink id={id} />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
const idElement = screen.getByText(id.slice(0, 6));
|
||||||
|
expect(idElement).toBeInTheDocument();
|
||||||
|
expect(idElement).toHaveAttribute('title', id);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the Oracle status', () => {
|
||||||
|
const id = '123';
|
||||||
|
const status = 'STATUS_ACTIVE';
|
||||||
|
render(
|
||||||
|
<MockedProvider>
|
||||||
|
<MemoryRouter>
|
||||||
|
<OracleLink id={id} status={status} data-testid="link" />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('link')).toHaveAttribute('data-status', 'Active');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders the Oracle data indicator', () => {
|
||||||
|
const id = '123';
|
||||||
|
const hasSeenOracleReports = true;
|
||||||
|
render(
|
||||||
|
<MockedProvider>
|
||||||
|
<MemoryRouter>
|
||||||
|
<OracleLink id={id} hasSeenOracleReports={hasSeenOracleReports} />
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('oracle-data-indicator')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -3,20 +3,93 @@ import { Link } from 'react-router-dom';
|
|||||||
|
|
||||||
import type { ComponentProps } from 'react';
|
import type { ComponentProps } from 'react';
|
||||||
import Hash from '../hash';
|
import Hash from '../hash';
|
||||||
|
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
DataSourceSpecStatus,
|
||||||
|
DataSourceSpecStatusMapping,
|
||||||
|
} from '@vegaprotocol/types';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable string for the given status, or a meaningful
|
||||||
|
* default if the status is unrecognised
|
||||||
|
* @param status string status
|
||||||
|
*/
|
||||||
|
export function getStatusString(status: string | undefined): string {
|
||||||
|
if (status && status in DataSourceSpecStatus) {
|
||||||
|
return DataSourceSpecStatusMapping[status as DataSourceSpecStatus];
|
||||||
|
}
|
||||||
|
|
||||||
|
return t('Unknown');
|
||||||
|
}
|
||||||
|
|
||||||
export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & {
|
export type OracleLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||||
|
// The Oracle ID
|
||||||
id: string;
|
id: string;
|
||||||
|
// If available, the oracle status
|
||||||
|
status?: string;
|
||||||
|
// If the oracle has corresponding data in the OracleDataConnection
|
||||||
|
hasSeenOracleReports?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const OracleLink = ({ id, ...props }: OracleLinkProps) => {
|
/**
|
||||||
|
* Given an Oracle ID, renders a data-dense link to the Oracle page. Data density is achieved by:
|
||||||
|
* - Colour coding the link based on the Oracle's status
|
||||||
|
* - Showing a small indicator if the Oracle has matched data
|
||||||
|
* - Showing a tooltip with the Oracle's status and whether it has matched data
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const OracleLink = ({
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
hasSeenOracleReports = false,
|
||||||
|
...props
|
||||||
|
}: OracleLinkProps) => {
|
||||||
|
const bgColour =
|
||||||
|
status === 'STATUS_ACTIVE'
|
||||||
|
? 'bg-yellow-100 hover:bg-yellow-200 border-yellow-200 dark:bg-yellow-200 dark:border-yellow-200 dark:text-gray-900 dark:border-yellow-300'
|
||||||
|
: 'bg-gray-100 hover:bg-gray-200 border-gray-200';
|
||||||
|
const indicatorColour =
|
||||||
|
status === 'STATUS_ACTIVE'
|
||||||
|
? 'bg-yellow-300 hover:bg-yellow-500 dark:bg-yellow-500'
|
||||||
|
: 'bg-gray-300 hover:bg-gray-500';
|
||||||
|
|
||||||
|
const description = (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
<strong>{`Status: `}</strong>
|
||||||
|
{getStatusString(status)}
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>{`Matched data: `}</strong>
|
||||||
|
{hasSeenOracleReports ? (
|
||||||
|
<VegaIcon name={VegaIconNames.TICK} />
|
||||||
|
) : (
|
||||||
|
<VegaIcon name={VegaIconNames.CROSS} />
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Tooltip description={description}>
|
||||||
className="underline font-mono"
|
<Link
|
||||||
{...props}
|
className={`pl-2 pr-2 font-mono dark:text-black ${bgColour} rounded-sm border-solid border-2 relative`}
|
||||||
to={`/${Routes.ORACLES}/${id}`}
|
{...props}
|
||||||
>
|
to={`/${Routes.ORACLES}/${id}`}
|
||||||
<Hash text={id} />
|
data-status={getStatusString(status)}
|
||||||
</Link>
|
>
|
||||||
|
<Hash text={id} truncate={true} />
|
||||||
|
{hasSeenOracleReports ? (
|
||||||
|
<strong
|
||||||
|
data-testid="oracle-data-indicator"
|
||||||
|
className={`absolute top-0 right-0 w-1 h-full font-thin ${indicatorColour}`}
|
||||||
|
title="Oracle has matched data"
|
||||||
|
></strong>
|
||||||
|
) : null}
|
||||||
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ function renderExistingAmend(
|
|||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
quoteName: '123',
|
quoteName: '123',
|
||||||
|
settlementAsset: {
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -124,6 +127,9 @@ function renderExistingAmend(
|
|||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
quoteName: '123',
|
quoteName: '123',
|
||||||
|
settlementAsset: {
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -152,6 +158,9 @@ function renderExistingAmend(
|
|||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
quoteName: 'dai',
|
quoteName: 'dai',
|
||||||
|
settlementAsset: {
|
||||||
|
decimals: 8,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -22,5 +22,9 @@ export const TimeAgo = ({ date, ...props }: TimeAgoProps) => {
|
|||||||
return <>{t('Date unknown')}</>;
|
return <>{t('Date unknown')}</>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <span {...props}>{t(`${distanceToNow} ago`)}</span>;
|
return (
|
||||||
|
<span {...props} title={date} className="underline decoration-dotted">
|
||||||
|
{t(`${distanceToNow} ago`)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
-6
@@ -1,6 +0,0 @@
|
|||||||
query ExplorerSettlementAssetForMarket($id: ID!) {
|
|
||||||
market(id: $id) {
|
|
||||||
id
|
|
||||||
decimalPlaces
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
import * as Types from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
import { gql } from '@apollo/client';
|
|
||||||
import * as Apollo from '@apollo/client';
|
|
||||||
const defaultOptions = {} as const;
|
|
||||||
export type ExplorerSettlementAssetForMarketQueryVariables = Types.Exact<{
|
|
||||||
id: Types.Scalars['ID'];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerSettlementAssetForMarketQuery = { __typename?: 'Query', market?: { __typename?: 'Market', id: string, decimalPlaces: number } | null };
|
|
||||||
|
|
||||||
|
|
||||||
export const ExplorerSettlementAssetForMarketDocument = gql`
|
|
||||||
query ExplorerSettlementAssetForMarket($id: ID!) {
|
|
||||||
market(id: $id) {
|
|
||||||
id
|
|
||||||
decimalPlaces
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useExplorerSettlementAssetForMarketQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useExplorerSettlementAssetForMarketQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useExplorerSettlementAssetForMarketQuery` 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 } = useExplorerSettlementAssetForMarketQuery({
|
|
||||||
* variables: {
|
|
||||||
* id: // value for 'id'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useExplorerSettlementAssetForMarketQuery(baseOptions: Apollo.QueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
|
|
||||||
}
|
|
||||||
export function useExplorerSettlementAssetForMarketLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>(ExplorerSettlementAssetForMarketDocument, options);
|
|
||||||
}
|
|
||||||
export type ExplorerSettlementAssetForMarketQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketQuery>;
|
|
||||||
export type ExplorerSettlementAssetForMarketLazyQueryHookResult = ReturnType<typeof useExplorerSettlementAssetForMarketLazyQuery>;
|
|
||||||
export type ExplorerSettlementAssetForMarketQueryResult = Apollo.QueryResult<ExplorerSettlementAssetForMarketQuery, ExplorerSettlementAssetForMarketQueryVariables>;
|
|
||||||
-131
@@ -1,131 +0,0 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
import { Side } from '@vegaprotocol/types';
|
|
||||||
import type { LiquidityOrder } from '@vegaprotocol/types';
|
|
||||||
import { PeggedReference } from '@vegaprotocol/types';
|
|
||||||
import { LiquidityProvisionDetailsRow } from './liquidity-provision-details-row';
|
|
||||||
import type { VegaSide } from './liquidity-provision-details-row';
|
|
||||||
|
|
||||||
describe('LiquidityProvisionDetails component', () => {
|
|
||||||
function renderComponent(
|
|
||||||
order: LiquidityOrder,
|
|
||||||
side: VegaSide,
|
|
||||||
normaliseProportionsTo: number,
|
|
||||||
marketId: string
|
|
||||||
) {
|
|
||||||
return render(
|
|
||||||
<MockedProvider>
|
|
||||||
<table>
|
|
||||||
<tbody data-testid="container">
|
|
||||||
<LiquidityProvisionDetailsRow
|
|
||||||
order={order}
|
|
||||||
marketId={marketId}
|
|
||||||
normaliseProportionsTo={normaliseProportionsTo}
|
|
||||||
side={side}
|
|
||||||
/>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('renders null for an order with no proportion', () => {
|
|
||||||
const mockOrder = {
|
|
||||||
offset: '1',
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(
|
|
||||||
mockOrder as LiquidityOrder,
|
|
||||||
Side.SIDE_BUY,
|
|
||||||
100,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
expect(res.getByTestId('container')).toBeEmptyDOMElement();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders null for a null order', () => {
|
|
||||||
const res = renderComponent(
|
|
||||||
null as unknown as LiquidityOrder,
|
|
||||||
Side.SIDE_BUY,
|
|
||||||
100,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
expect(res.getByTestId('container')).toBeEmptyDOMElement();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders a row when the order is as expected', () => {
|
|
||||||
const mockOrder = {
|
|
||||||
offset: '1',
|
|
||||||
proportion: 20,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(
|
|
||||||
mockOrder as LiquidityOrder,
|
|
||||||
Side.SIDE_BUY,
|
|
||||||
100,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
// Row test ids and keys are based on the side, reference and proportion
|
|
||||||
expect(res.getByTestId('SIDE_BUY-20-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('+1')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Mid')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('20%')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('normalises offsets when normaliseToProportion is not 100', () => {
|
|
||||||
const mockOrder = {
|
|
||||||
offset: '1',
|
|
||||||
proportion: 20,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(
|
|
||||||
mockOrder as LiquidityOrder,
|
|
||||||
Side.SIDE_SELL,
|
|
||||||
50,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
|
||||||
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Best Bid')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('40%')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles a missing offset gracefully (should not happen)', () => {
|
|
||||||
const mockOrder = {
|
|
||||||
proportion: 20,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_BEST_BID,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(
|
|
||||||
mockOrder as LiquidityOrder,
|
|
||||||
Side.SIDE_SELL,
|
|
||||||
50,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
|
||||||
expect(res.getByTestId('SIDE_SELL-40-')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('-')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles a missing reference gracefully (should not happen)', () => {
|
|
||||||
const mockOrder = {
|
|
||||||
offset: '1',
|
|
||||||
proportion: 20,
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(
|
|
||||||
mockOrder as LiquidityOrder,
|
|
||||||
Side.SIDE_SELL,
|
|
||||||
50,
|
|
||||||
'123'
|
|
||||||
);
|
|
||||||
// Row test ids and keys are based on the side, reference and proportion - and that proportion is scaled
|
|
||||||
expect(res.getByTestId('SIDE_SELL-40-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('40%')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('-')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-73
@@ -1,73 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import type { components } from '../../../../../../types/explorer';
|
|
||||||
import { TableRow } from '../../../../table';
|
|
||||||
import { LiquidityProvisionOffset } from './liquidity-provision-offset';
|
|
||||||
|
|
||||||
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
|
||||||
export type VegaSide = components['schemas']['vegaSide'];
|
|
||||||
|
|
||||||
export type LiquidityProvisionOrder =
|
|
||||||
components['schemas']['vegaLiquidityOrder'];
|
|
||||||
|
|
||||||
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
|
||||||
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
|
||||||
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
|
||||||
PEGGED_REFERENCE_MID: t('Mid'),
|
|
||||||
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
|
||||||
};
|
|
||||||
|
|
||||||
export type LiquidityProvisionDetailsRowProps = {
|
|
||||||
order?: LiquidityProvisionOrder;
|
|
||||||
marketId?: string;
|
|
||||||
side: VegaSide;
|
|
||||||
// If this is
|
|
||||||
normaliseProportionsTo: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Note: offset is formatted by settlement asset on the market, assuming that is available
|
|
||||||
* Note: Due to the mix of references (MID vs BEST_X), it's not possible to correctly order
|
|
||||||
* the orders by their actual distance from a midpoint. This would require us knowing
|
|
||||||
* the best bid (now or at placement) and the mid. Getting the data for *now* would be
|
|
||||||
* misleading for LP submissions in the past. There is no API for getting <mid />
|
|
||||||
* at the time of a transaction.
|
|
||||||
*/
|
|
||||||
export function LiquidityProvisionDetailsRow({
|
|
||||||
normaliseProportionsTo,
|
|
||||||
order,
|
|
||||||
side,
|
|
||||||
marketId,
|
|
||||||
}: LiquidityProvisionDetailsRowProps) {
|
|
||||||
if (!order || !order.proportion) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const proportion =
|
|
||||||
normaliseProportionsTo === 100
|
|
||||||
? order.proportion
|
|
||||||
: Math.round((order.proportion / normaliseProportionsTo) * 100);
|
|
||||||
|
|
||||||
const key = `${side}-${proportion}-${order.offset ? order.offset : ''}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableRow modifier="bordered" key={key} data-testid={key}>
|
|
||||||
<td className="text-right px-2">
|
|
||||||
{order.offset && marketId ? (
|
|
||||||
<LiquidityProvisionOffset
|
|
||||||
offset={order.offset}
|
|
||||||
side={side}
|
|
||||||
marketId={marketId}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
'-'
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="text-center">
|
|
||||||
{order.reference ? LiquidityReferenceLabel[order.reference] : '-'}
|
|
||||||
</td>
|
|
||||||
<td className="text-center">{proportion}%</td>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
-22
@@ -1,22 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
import { LiquidityProvisionMid } from './liquidity-provision-mid';
|
|
||||||
|
|
||||||
describe('LiquidityProvisionMid component', () => {
|
|
||||||
function renderComponent() {
|
|
||||||
return render(
|
|
||||||
<table>
|
|
||||||
<tbody data-testid="container">
|
|
||||||
<LiquidityProvisionMid />
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('renders a basic row that spans the whole table', () => {
|
|
||||||
const res = renderComponent();
|
|
||||||
const display = res.getByTestId('mid-display');
|
|
||||||
expect(res.getByTestId('mid')).toBeInTheDocument();
|
|
||||||
expect(display).toBeInTheDocument();
|
|
||||||
expect(display).toHaveAttribute('colspan', '3');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
import { TableRow } from '../../../../table';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* In a LiquidityProvision table, this row is the midpoint. Above our LP orders on the
|
|
||||||
* buy side, below are LP orders on the sell side. This component simply divides them.
|
|
||||||
*
|
|
||||||
* There is no API that can give us the mid price when the order was created, and even
|
|
||||||
* if there was it isn't clear that would be appropriate for this centre row. So instead
|
|
||||||
* it's a simple divider.
|
|
||||||
*/
|
|
||||||
export function LiquidityProvisionMid() {
|
|
||||||
return (
|
|
||||||
<TableRow modifier="bordered" data-testid="mid">
|
|
||||||
<td data-testid="mid-display" colSpan={3} className="bg-white"></td>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
-67
@@ -1,67 +0,0 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
import { ExplorerSettlementAssetForMarketDocument } from '../__generated__/Explorer-settlement-asset';
|
|
||||||
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
|
||||||
import type { VegaSide } from './liquidity-provision-details-row';
|
|
||||||
import {
|
|
||||||
getFormattedOffset,
|
|
||||||
LiquidityProvisionOffset,
|
|
||||||
} from './liquidity-provision-offset';
|
|
||||||
const decimalsMock: ExplorerSettlementAssetForMarketQuery = {
|
|
||||||
market: {
|
|
||||||
id: '123',
|
|
||||||
__typename: 'Market',
|
|
||||||
decimalPlaces: 5,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('LiquidityProvisionOffset component', () => {
|
|
||||||
function renderComponent(
|
|
||||||
offset: string,
|
|
||||||
side: VegaSide,
|
|
||||||
marketId: string,
|
|
||||||
mocks: MockedResponse[]
|
|
||||||
) {
|
|
||||||
return render(
|
|
||||||
<MockedProvider mocks={mocks}>
|
|
||||||
<LiquidityProvisionOffset
|
|
||||||
offset={offset}
|
|
||||||
side={side}
|
|
||||||
marketId={marketId}
|
|
||||||
/>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
it('renders a simple row before market data comes in', () => {
|
|
||||||
const res = renderComponent('1', 'SIDE_BUY', '123', []);
|
|
||||||
expect(res.getByText('+1')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('replaces unformatted with formatted if the market data comes in', () => {
|
|
||||||
const mock = {
|
|
||||||
request: {
|
|
||||||
query: ExplorerSettlementAssetForMarketDocument,
|
|
||||||
variables: {
|
|
||||||
id: '123',
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: decimalsMock,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const res = renderComponent('1', 'SIDE_BUY', '123', [mock]);
|
|
||||||
expect(res.getByText('+1')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getFormattedOffset returns the unformatted offset if there is not enough data', () => {
|
|
||||||
const res = getFormattedOffset('1', {});
|
|
||||||
expect(res).toEqual('1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('getFormattedOffset decimal formats a number if it comes in with market data', () => {
|
|
||||||
const res = getFormattedOffset('1', decimalsMock);
|
|
||||||
expect(res).toEqual('0.00001');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-59
@@ -1,59 +0,0 @@
|
|||||||
import { useExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
|
||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
|
||||||
import type { ExplorerSettlementAssetForMarketQuery } from '../__generated__/Explorer-settlement-asset';
|
|
||||||
import type { VegaSide } from './liquidity-provision-details-row';
|
|
||||||
|
|
||||||
export type LiquidityProvisionOffsetProps = {
|
|
||||||
side: VegaSide;
|
|
||||||
offset: string;
|
|
||||||
marketId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Correctly formats an LP's offset according to the market settlement decimal places.
|
|
||||||
* Initially this will appear unformatted, then when the query loads in the proper formatted
|
|
||||||
* value will be displayed
|
|
||||||
*
|
|
||||||
* @see getFormattedOffset
|
|
||||||
*/
|
|
||||||
export function LiquidityProvisionOffset({
|
|
||||||
side,
|
|
||||||
offset,
|
|
||||||
marketId,
|
|
||||||
}: LiquidityProvisionOffsetProps) {
|
|
||||||
const { data } = useExplorerSettlementAssetForMarketQuery({
|
|
||||||
variables: {
|
|
||||||
id: marketId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// getFormattedOffset handles missing results/loading states
|
|
||||||
const formattedOffset = getFormattedOffset(offset, data);
|
|
||||||
|
|
||||||
const label = side === 'SIDE_BUY' ? '+' : '-';
|
|
||||||
const className = side === 'SIDE_BUY' ? 'text-vega-green' : 'text-vega-pink';
|
|
||||||
return <span className={className}>{`${label}${formattedOffset}`}</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the work of formatting the number now we have the market decimal places.
|
|
||||||
* If no market data is assigned (i.e. during loading, or if the market doesn't exist)
|
|
||||||
* this function will return the unformatted number
|
|
||||||
*
|
|
||||||
* @see LiquidityProvisionOffset
|
|
||||||
* @param data the result of a ExplorerSettlementAssetForMarketQuery
|
|
||||||
* @param offset the unformatted offset
|
|
||||||
* @returns string the offset of this lp order formatted with the settlement decimal places
|
|
||||||
*/
|
|
||||||
export function getFormattedOffset(
|
|
||||||
offset: string,
|
|
||||||
data?: ExplorerSettlementAssetForMarketQuery
|
|
||||||
) {
|
|
||||||
const decimals = data?.market?.decimalPlaces;
|
|
||||||
|
|
||||||
if (!decimals) {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
return addDecimalsFormatNumber(offset, decimals);
|
|
||||||
}
|
|
||||||
-209
@@ -1,209 +0,0 @@
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
import type { LiquidityOrder } from '@vegaprotocol/types';
|
|
||||||
import { PeggedReference } from '@vegaprotocol/types';
|
|
||||||
import type { LiquiditySubmission } from '../tx-liquidity-submission';
|
|
||||||
import {
|
|
||||||
LiquidityProvisionDetails,
|
|
||||||
sumProportions,
|
|
||||||
} from './liquidity-provision-details';
|
|
||||||
|
|
||||||
function mockProportion(proportion: number): LiquidityOrder {
|
|
||||||
return {
|
|
||||||
proportion,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
offset: '1',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
describe('sumProportions function', () => {
|
|
||||||
it('returns 0 if the side is undefined', () => {
|
|
||||||
const side: LiquidityOrder[] = undefined as unknown as LiquidityOrder[];
|
|
||||||
const res = sumProportions(side);
|
|
||||||
|
|
||||||
expect(res).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns 0 if the side is empty', () => {
|
|
||||||
const side: LiquidityOrder[] = [];
|
|
||||||
const res = sumProportions(side);
|
|
||||||
|
|
||||||
expect(res).toEqual(0);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sums 1 item correctly (under 100%)', () => {
|
|
||||||
const side: LiquidityOrder[] = [mockProportion(10)];
|
|
||||||
const res = sumProportions(side);
|
|
||||||
|
|
||||||
expect(res).toEqual(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sums 2 item correctly (exactly 100%)', () => {
|
|
||||||
const side: LiquidityOrder[] = [mockProportion(50), mockProportion(50)];
|
|
||||||
const res = sumProportions(side);
|
|
||||||
|
|
||||||
expect(res).toEqual(100);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sums 3 item correctly to over 100%', () => {
|
|
||||||
const side: LiquidityOrder[] = [
|
|
||||||
mockProportion(20),
|
|
||||||
mockProportion(40),
|
|
||||||
mockProportion(50),
|
|
||||||
];
|
|
||||||
const res = sumProportions(side);
|
|
||||||
|
|
||||||
expect(res).toEqual(110);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('LiquidityProvisionDetails component', () => {
|
|
||||||
function renderComponent(provision: LiquiditySubmission) {
|
|
||||||
return render(
|
|
||||||
<MockedProvider>
|
|
||||||
<LiquidityProvisionDetails provision={provision} />
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
it('handles an LP with no buys or sells by returning empty (should never happen)', () => {
|
|
||||||
const mock: LiquiditySubmission = {};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.container).toBeEmptyDOMElement();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles an LP with no sells by just rendering buys', () => {
|
|
||||||
const mock: LiquiditySubmission = {
|
|
||||||
marketId: '123',
|
|
||||||
buys: [
|
|
||||||
{
|
|
||||||
offset: '1',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles an LP with no buys by just rendering sells', () => {
|
|
||||||
const mock: LiquiditySubmission = {
|
|
||||||
marketId: '123',
|
|
||||||
sells: [
|
|
||||||
{
|
|
||||||
offset: '1',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_SELL-50-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles an LP with sells by just rendering buys', () => {
|
|
||||||
const mock: LiquiditySubmission = {
|
|
||||||
marketId: '123',
|
|
||||||
buys: [
|
|
||||||
{
|
|
||||||
offset: '1',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles an LP with both sides', () => {
|
|
||||||
const mock: LiquiditySubmission = {
|
|
||||||
marketId: '123',
|
|
||||||
buys: [
|
|
||||||
{
|
|
||||||
offset: '1',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
sells: [
|
|
||||||
{
|
|
||||||
offset: '4',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 50,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.getByText('Price offset')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Price reference')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('Proportion')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-1')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_BUY-50-2')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_SELL-50-4')).toBeInTheDocument();
|
|
||||||
expect(res.getByTestId('SIDE_SELL-50-2')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('normalises proportions when they do not total 100%', () => {
|
|
||||||
const mock: LiquiditySubmission = {
|
|
||||||
marketId: '123',
|
|
||||||
buys: [
|
|
||||||
{
|
|
||||||
offset: '1',
|
|
||||||
proportion: 25,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
offset: '2',
|
|
||||||
proportion: 30,
|
|
||||||
reference: PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const res = renderComponent(mock);
|
|
||||||
expect(res.getByText('45%')).toBeInTheDocument();
|
|
||||||
expect(res.getByText('55%')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
-94
@@ -1,94 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import type { components } from '../../../../../types/explorer';
|
|
||||||
import type { LiquiditySubmission } from '../tx-liquidity-submission';
|
|
||||||
import { TableRow } from '../../../table';
|
|
||||||
import { LiquidityProvisionMid } from './components/liquidity-provision-mid';
|
|
||||||
import { LiquidityProvisionDetailsRow } from './components/liquidity-provision-details-row';
|
|
||||||
import { Side } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
|
||||||
|
|
||||||
export type LiquidityProvisionOrder =
|
|
||||||
components['schemas']['vegaLiquidityOrder'];
|
|
||||||
|
|
||||||
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
|
||||||
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
|
||||||
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
|
||||||
PEGGED_REFERENCE_MID: t('Mid'),
|
|
||||||
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a side of a liquidity provision order, returns the total
|
|
||||||
* It should be 100%, but it isn't always and if it isn't the proportion
|
|
||||||
* reported for each order should be scaled
|
|
||||||
*
|
|
||||||
* @returns number
|
|
||||||
*/
|
|
||||||
export function sumProportions(
|
|
||||||
side: LiquiditySubmission['buys'] | LiquiditySubmission['sells']
|
|
||||||
): number {
|
|
||||||
if (!side || side.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return side.reduce((total, o) => total + (o.proportion || 0), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type LiquidityProvisionDetailsProps = {
|
|
||||||
provision: LiquiditySubmission;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders a table displaying all buys and sells in this LP. It is valid for there
|
|
||||||
* to be no buys or sells.
|
|
||||||
*
|
|
||||||
* It might seem logical to turn proportions in to values based on the total commitment
|
|
||||||
* but based on the current API structure it is awkward, and given that non-LP orders
|
|
||||||
* will change the amount that is actually deployed vs assigned to a level, we decided
|
|
||||||
* not to bother going down that route.
|
|
||||||
*/
|
|
||||||
export function LiquidityProvisionDetails({
|
|
||||||
provision,
|
|
||||||
}: LiquidityProvisionDetailsProps) {
|
|
||||||
if (!provision.buys?.length && !provision.sells?.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// We need to do some additional calcs if these aren't both 100
|
|
||||||
const buyTotal = sumProportions(provision.buys);
|
|
||||||
const sellTotal = sumProportions(provision.sells);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<th className="px-2 pb-1">{t('Price offset')}</th>
|
|
||||||
<th className="px-2 pb-1">{t('Price reference')}</th>
|
|
||||||
<th className="px-2 pb-1">{t('Proportion')}</th>
|
|
||||||
</TableRow>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{provision.buys?.map((b, i) => (
|
|
||||||
<LiquidityProvisionDetailsRow
|
|
||||||
order={b}
|
|
||||||
marketId={provision.marketId}
|
|
||||||
side={Side.SIDE_BUY}
|
|
||||||
key={`SIDE_BUY-${i}`}
|
|
||||||
normaliseProportionsTo={buyTotal}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<LiquidityProvisionMid />
|
|
||||||
{provision.sells?.map((s, i) => (
|
|
||||||
<LiquidityProvisionDetailsRow
|
|
||||||
order={s}
|
|
||||||
marketId={provision.marketId}
|
|
||||||
side={Side.SIDE_SELL}
|
|
||||||
key={`SIDE_SELL-${i}`}
|
|
||||||
normaliseProportionsTo={sellTotal}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import type { components } from '../../../../../types/explorer';
|
||||||
import { TableCell, TableRow } from '../../../table';
|
import { TableCell, TableRow } from '../../../table';
|
||||||
import type { VegaPeggedReference } from '../liquidity-provision/liquidity-provision-details';
|
|
||||||
import { Side, PeggedReferenceMapping } from '@vegaprotocol/types';
|
import { Side, PeggedReferenceMapping } from '@vegaprotocol/types';
|
||||||
import { useExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
import { useExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
||||||
import type { ExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
import type { ExplorerMarketQuery } from '../../../links/market-link/__generated__/Market';
|
||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||||
|
|
||||||
|
export type VegaPeggedReference = components['schemas']['vegaPeggedReference'];
|
||||||
|
|
||||||
|
export const LiquidityReferenceLabel: Record<VegaPeggedReference, string> = {
|
||||||
|
PEGGED_REFERENCE_BEST_ASK: t('Best Ask'),
|
||||||
|
PEGGED_REFERENCE_BEST_BID: t('Best Bid'),
|
||||||
|
PEGGED_REFERENCE_MID: t('Mid'),
|
||||||
|
PEGGED_REFERENCE_UNSPECIFIED: '-',
|
||||||
|
};
|
||||||
|
|
||||||
export interface TxDetailsOrderProps {
|
export interface TxDetailsOrderProps {
|
||||||
offset: string;
|
offset: string;
|
||||||
reference: VegaPeggedReference;
|
reference: VegaPeggedReference;
|
||||||
|
|||||||
@@ -33,6 +33,14 @@ const AccountType: Record<AccountTypes, string> = {
|
|||||||
ACCOUNT_TYPE_HOLDING: 'Holding',
|
ACCOUNT_TYPE_HOLDING: 'Holding',
|
||||||
ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: 'Bonus Distribution',
|
ACCOUNT_TYPE_LIQUIDITY_FEES_BONUS_DISTRIBUTION: 'Bonus Distribution',
|
||||||
ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees',
|
ACCOUNT_TYPE_LP_LIQUIDITY_FEES: 'LP Liquidity Fees',
|
||||||
|
ACCOUNT_TYPE_NETWORK_TREASURY: 'Network Treasury',
|
||||||
|
ACCOUNT_TYPE_VESTING_REWARDS: 'Vesting Rewards',
|
||||||
|
ACCOUNT_TYPE_VESTED_REWARDS: 'Vested Rewards',
|
||||||
|
ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: 'Reward Average Position',
|
||||||
|
ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: 'Reward Relative Return',
|
||||||
|
ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: 'Reward Return Volatility',
|
||||||
|
ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: 'Reward Validator Ranking',
|
||||||
|
ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD: 'Pending Fee Referral Reward',
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TransferParticipantsProps {
|
interface TransferParticipantsProps {
|
||||||
|
|||||||
@@ -1,10 +1,50 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableWithTbody } from '../../table';
|
import { TableWithTbody } from '../../table';
|
||||||
|
import { defaultAbiCoder, base64 } from 'ethers/lib/utils';
|
||||||
|
import { ChainEvent } from './chain-events';
|
||||||
|
import { BigNumber } from 'ethers';
|
||||||
|
|
||||||
|
import type { AbiType } from '../../../lib/encoders/abis/abi-types';
|
||||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||||
import { ChainEvent } from './chain-events';
|
|
||||||
|
interface AbiOutput {
|
||||||
|
type: AbiType;
|
||||||
|
internalType: AbiType;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes the b64/ABIcoded result from an eth cal
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function decodeEthCallResult(
|
||||||
|
data: BlockExplorerTransactionResult
|
||||||
|
): string {
|
||||||
|
const ethResult = data.command.chainEvent?.contractCall.result;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Decode the result string: base64 => uint8array
|
||||||
|
const data = base64.decode(ethResult);
|
||||||
|
|
||||||
|
// Parse the escaped ABI in to an object
|
||||||
|
const abi = JSON.parse(
|
||||||
|
'[{"inputs":[],"name":"latestAnswer","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"}]'
|
||||||
|
);
|
||||||
|
// Pull the expected types out of the Oracles ABI
|
||||||
|
const types: AbiType[] = abi[0].outputs.map((o: AbiOutput) => o.type);
|
||||||
|
|
||||||
|
const rawResult = defaultAbiCoder.decode(types, data);
|
||||||
|
|
||||||
|
// Finally, convert the resulting BigNumber in to a string
|
||||||
|
const res = BigNumber.from(rawResult[0]).toString();
|
||||||
|
return res;
|
||||||
|
} catch (e) {
|
||||||
|
return '-';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface TxDetailsChainEventProps {
|
interface TxDetailsChainEventProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import { TxDetailsOrderAmend } from './tx-order-amend';
|
|||||||
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
|
import { TxDetailsWithdrawSubmission } from './tx-withdraw-submission';
|
||||||
import { TxDetailsDelegate } from './tx-delegation';
|
import { TxDetailsDelegate } from './tx-delegation';
|
||||||
import { TxDetailsUndelegate } from './tx-undelegation';
|
import { TxDetailsUndelegate } from './tx-undelegation';
|
||||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
|
||||||
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
|
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
|
||||||
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
|
import { TxDetailsLiquidityCancellation } from './tx-liquidity-cancel';
|
||||||
import { TxDetailsDataSubmission } from './tx-data-submission';
|
import { TxDetailsDataSubmission } from './tx-data-submission';
|
||||||
@@ -28,6 +27,7 @@ import { TxDetailsStateVariable } from './tx-state-variable-proposal';
|
|||||||
import { TxProposal } from './tx-proposal';
|
import { TxProposal } from './tx-proposal';
|
||||||
import { TxDetailsTransfer } from './tx-transfer';
|
import { TxDetailsTransfer } from './tx-transfer';
|
||||||
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
import { TxDetailsStopOrderSubmission } from './tx-stop-order-submission';
|
||||||
|
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||||
|
|
||||||
interface TxDetailsWrapperProps {
|
interface TxDetailsWrapperProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@@ -107,7 +107,7 @@ function getTransactionComponent(txData?: BlockExplorerTransactionResult) {
|
|||||||
return TxDetailsWithdrawSubmission;
|
return TxDetailsWithdrawSubmission;
|
||||||
case 'Liquidity Provision Order':
|
case 'Liquidity Provision Order':
|
||||||
return TxDetailsLiquiditySubmission;
|
return TxDetailsLiquiditySubmission;
|
||||||
case 'Amend LiquidityProvision Order':
|
case 'Amend Liquidity Provision Order':
|
||||||
return TxDetailsLiquidityAmendment;
|
return TxDetailsLiquidityAmendment;
|
||||||
case 'Cancel LiquidityProvision Order':
|
case 'Cancel LiquidityProvision Order':
|
||||||
return TxDetailsLiquidityCancellation;
|
return TxDetailsLiquidityCancellation;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint
|
|||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||||
import type { components } from '../../../../types/explorer';
|
import type { components } from '../../../../types/explorer';
|
||||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
|
||||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
@@ -40,40 +39,32 @@ export const TxDetailsLiquidityAmendment = ({
|
|||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||||
<TxDetailsShared
|
<TableRow modifier="bordered">
|
||||||
txData={txData}
|
<TableCell>{t('Market')}</TableCell>
|
||||||
pubKey={pubKey}
|
<TableCell>
|
||||||
blockData={blockData}
|
<MarketLink id={marketId} />
|
||||||
/>
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
{amendment.commitmentAmount ? (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Market')}</TableCell>
|
<TableCell>{t('Commitment amount')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<MarketLink id={marketId} />
|
<PriceInMarket
|
||||||
|
price={amendment.commitmentAmount}
|
||||||
|
marketId={marketId}
|
||||||
|
decimalSource="SETTLEMENT_ASSET"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{amendment.commitmentAmount ? (
|
) : null}
|
||||||
<TableRow modifier="bordered">
|
{amendment.fee ? (
|
||||||
<TableCell>{t('Commitment amount')}</TableCell>
|
<TableRow modifier="bordered">
|
||||||
<TableCell>
|
<TableCell>{t('Fee')}</TableCell>
|
||||||
<PriceInMarket
|
<TableCell>{fee}%</TableCell>
|
||||||
price={amendment.commitmentAmount}
|
</TableRow>
|
||||||
marketId={marketId}
|
) : null}
|
||||||
decimalSource="SETTLEMENT_ASSET"
|
</TableWithTbody>
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
) : null}
|
|
||||||
{amendment.fee ? (
|
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableCell>{t('Fee')}</TableCell>
|
|
||||||
<TableCell>{fee}%</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
) : null}
|
|
||||||
</TableWithTbody>
|
|
||||||
|
|
||||||
<LiquidityProvisionDetails provision={amendment} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
import { render } from '@testing-library/react';
|
|
||||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
|
||||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
|
||||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
|
||||||
import { MockedProvider } from '@apollo/client/testing';
|
|
||||||
import { MemoryRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
describe('TxDetailsLiquiditySubmission', () => {
|
|
||||||
const mockTxData = {
|
|
||||||
hash: 'test',
|
|
||||||
command: {
|
|
||||||
liquidityProvisionSubmission: {
|
|
||||||
marketId: 'BTC-USD',
|
|
||||||
commitmentAmount: 100,
|
|
||||||
fee: '0.01',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const mockPubKey = '123';
|
|
||||||
const mockBlockData = {
|
|
||||||
result: {
|
|
||||||
block: {
|
|
||||||
header: {
|
|
||||||
height: '123',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
it('should render the component with correct data', () => {
|
|
||||||
const { getByText } = render(
|
|
||||||
<MockedProvider>
|
|
||||||
<MemoryRouter>
|
|
||||||
<TxDetailsLiquiditySubmission
|
|
||||||
txData={mockTxData as BlockExplorerTransactionResult}
|
|
||||||
pubKey={mockPubKey}
|
|
||||||
blockData={mockBlockData as TendermintBlocksResponse}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(getByText('Market')).toBeInTheDocument();
|
|
||||||
expect(getByText('BTC-USD')).toBeInTheDocument();
|
|
||||||
expect(getByText('Commitment amount')).toBeInTheDocument();
|
|
||||||
expect(getByText('100')).toBeInTheDocument();
|
|
||||||
expect(getByText('Fee')).toBeInTheDocument();
|
|
||||||
expect(getByText('1%')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display awaiting message when tx data is undefined', () => {
|
|
||||||
const { getByText } = render(
|
|
||||||
<MockedProvider>
|
|
||||||
<MemoryRouter>
|
|
||||||
<TxDetailsLiquiditySubmission
|
|
||||||
txData={undefined}
|
|
||||||
pubKey={mockPubKey}
|
|
||||||
blockData={mockBlockData as TendermintBlocksResponse}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
getByText('Awaiting Block Explorer transaction details')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display awaiting message when liquidityProvisionSubmission is undefined', () => {
|
|
||||||
const { getByText } = render(
|
|
||||||
<MockedProvider>
|
|
||||||
<MemoryRouter>
|
|
||||||
<TxDetailsLiquiditySubmission
|
|
||||||
txData={{ command: {} } as BlockExplorerTransactionResult}
|
|
||||||
pubKey={mockPubKey}
|
|
||||||
blockData={mockBlockData as TendermintBlocksResponse}
|
|
||||||
/>
|
|
||||||
</MemoryRouter>
|
|
||||||
</MockedProvider>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(
|
|
||||||
getByText('Awaiting Block Explorer transaction details')
|
|
||||||
).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,31 +1,30 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||||
import { MarketLink } from '../../links/';
|
import { MarketLink } from '../../links';
|
||||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||||
import { TxDetailsShared } from './shared/tx-details-shared';
|
import { TxDetailsShared } from './shared/tx-details-shared';
|
||||||
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||||
import type { components } from '../../../../types/explorer';
|
import type { components } from '../../../../types/explorer';
|
||||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
|
||||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||||
import BigNumber from 'bignumber.js';
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
export type LiquiditySubmission =
|
export type LiquiditySubmission =
|
||||||
components['schemas']['v1LiquidityProvisionSubmission'];
|
components['schemas']['v1LiquidityProvisionSubmission'];
|
||||||
|
|
||||||
interface TxDetailsLiquiditySubmissionProps {
|
interface TxDetailsLiquidityAmendmentProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
pubKey: string | undefined;
|
pubKey: string | undefined;
|
||||||
blockData: TendermintBlocksResponse | undefined;
|
blockData: TendermintBlocksResponse | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Someone cancelled an order
|
* An existing liquidity order is being created.
|
||||||
*/
|
*/
|
||||||
export const TxDetailsLiquiditySubmission = ({
|
export const TxDetailsLiquiditySubmission = ({
|
||||||
txData,
|
txData,
|
||||||
pubKey,
|
pubKey,
|
||||||
blockData,
|
blockData,
|
||||||
}: TxDetailsLiquiditySubmissionProps) => {
|
}: TxDetailsLiquidityAmendmentProps) => {
|
||||||
if (!txData || !txData.command.liquidityProvisionSubmission) {
|
if (!txData || !txData.command.liquidityProvisionSubmission) {
|
||||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||||
}
|
}
|
||||||
@@ -39,40 +38,38 @@ export const TxDetailsLiquiditySubmission = ({
|
|||||||
: '-';
|
: '-';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TxDetailsShared txData={txData} pubKey={pubKey} blockData={blockData} />
|
||||||
<TxDetailsShared
|
<TableRow modifier="bordered">
|
||||||
txData={txData}
|
<TableCell>{t('Market')}</TableCell>
|
||||||
pubKey={pubKey}
|
<TableCell>
|
||||||
blockData={blockData}
|
<MarketLink id={marketId} />
|
||||||
/>
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
{submission.commitmentAmount ? (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableCell>{t('Market')}</TableCell>
|
<TableCell>{t('Commitment amount')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<MarketLink id={marketId} />
|
<PriceInMarket
|
||||||
|
price={submission.commitmentAmount}
|
||||||
|
marketId={marketId}
|
||||||
|
decimalSource="SETTLEMENT_ASSET"
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
{submission.commitmentAmount ? (
|
) : null}
|
||||||
<TableRow modifier="bordered">
|
{submission.fee ? (
|
||||||
<TableCell>{t('Commitment amount')}</TableCell>
|
<TableRow modifier="bordered">
|
||||||
<TableCell>
|
<TableCell>{t('Fee')}</TableCell>
|
||||||
<PriceInMarket
|
<TableCell>{fee}%</TableCell>
|
||||||
price={submission.commitmentAmount}
|
</TableRow>
|
||||||
marketId={marketId}
|
) : null}
|
||||||
decimalSource="SETTLEMENT_ASSET"
|
{submission.reference ? (
|
||||||
/>
|
<TableRow modifier="bordered">
|
||||||
</TableCell>
|
<TableCell>{t('Reference')}</TableCell>
|
||||||
</TableRow>
|
<TableCell>{submission.reference}</TableCell>
|
||||||
) : null}
|
</TableRow>
|
||||||
{submission.fee ? (
|
) : null}
|
||||||
<TableRow modifier="bordered">
|
</TableWithTbody>
|
||||||
<TableCell>{t('Fee')}</TableCell>
|
|
||||||
<TableCell>{fee}%</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
) : null}
|
|
||||||
</TableWithTbody>
|
|
||||||
|
|
||||||
<LiquidityProvisionDetails provision={submission} />
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
SPECIAL_CASE_NETWORK,
|
SPECIAL_CASE_NETWORK,
|
||||||
SPECIAL_CASE_NETWORK_ID,
|
SPECIAL_CASE_NETWORK_ID,
|
||||||
} from '../../links/party-link/party-link';
|
} from '../../links/party-link/party-link';
|
||||||
|
import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
||||||
|
|
||||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||||
|
|
||||||
@@ -63,10 +64,16 @@ export const TxDetailsTransfer = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered" data-testid="type">
|
||||||
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
|
<TableCell {...sharedHeaderProps}>{t('Type')}</TableCell>
|
||||||
<TableCell>{getTypeLabelForTransfer(transfer)}</TableCell>
|
<TableCell>{getTypeLabelForTransfer(transfer)}</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
|
<TableRow modifier="bordered" data-testid="id">
|
||||||
|
<TableCell {...sharedHeaderProps}>{t('Transfer ID')}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{txSignatureToDeterministicId(txData.signature.value)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
<TxDetailsShared
|
<TxDetailsShared
|
||||||
txData={txData}
|
txData={txData}
|
||||||
pubKey={pubKey}
|
pubKey={pubKey}
|
||||||
@@ -74,7 +81,7 @@ export const TxDetailsTransfer = ({
|
|||||||
hideTypeRow={true}
|
hideTypeRow={true}
|
||||||
/>
|
/>
|
||||||
{from ? (
|
{from ? (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered" data-testid="from">
|
||||||
<TableCell>{t('From')}</TableCell>
|
<TableCell>{t('From')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<PartyLink id={from} />
|
<PartyLink id={from} />
|
||||||
@@ -82,7 +89,7 @@ export const TxDetailsTransfer = ({
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
) : null}
|
) : null}
|
||||||
{transfer.to ? (
|
{transfer.to ? (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered" data-testid="to">
|
||||||
<TableCell>{t('To')}</TableCell>
|
<TableCell>{t('To')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<PartyLink id={transfer.to} />
|
<PartyLink id={transfer.to} />
|
||||||
@@ -90,7 +97,7 @@ export const TxDetailsTransfer = ({
|
|||||||
</TableRow>
|
</TableRow>
|
||||||
) : null}
|
) : null}
|
||||||
{transfer.asset && transfer.amount ? (
|
{transfer.asset && transfer.amount ? (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered" data-testid="amount">
|
||||||
<TableCell>{t('Amount')}</TableCell>
|
<TableCell>{t('Amount')}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<SizeInAsset assetId={transfer.asset} size={transfer.amount} />
|
<SizeInAsset assetId={transfer.asset} size={transfer.amount} />
|
||||||
|
|||||||
@@ -18,11 +18,13 @@ import { FilterLabel } from './tx-filter-label';
|
|||||||
export type FilterOption =
|
export type FilterOption =
|
||||||
| 'Amend LiquidityProvision Order'
|
| 'Amend LiquidityProvision Order'
|
||||||
| 'Amend Order'
|
| 'Amend Order'
|
||||||
|
| 'Apply Referral Code'
|
||||||
| 'Batch Market Instructions'
|
| 'Batch Market Instructions'
|
||||||
| 'Cancel LiquidityProvision Order'
|
| 'Cancel LiquidityProvision Order'
|
||||||
| 'Cancel Order'
|
| 'Cancel Order'
|
||||||
| 'Cancel Transfer Funds'
|
| 'Cancel Transfer Funds'
|
||||||
| 'Chain Event'
|
| 'Chain Event'
|
||||||
|
| 'Create Referral Set'
|
||||||
| 'Delegate'
|
| 'Delegate'
|
||||||
| 'Ethereum Key Rotate Submission'
|
| 'Ethereum Key Rotate Submission'
|
||||||
| 'Issue Signatures'
|
| 'Issue Signatures'
|
||||||
@@ -40,6 +42,7 @@ export type FilterOption =
|
|||||||
| 'Submit Order'
|
| 'Submit Order'
|
||||||
| 'Transfer Funds'
|
| 'Transfer Funds'
|
||||||
| 'Undelegate'
|
| 'Undelegate'
|
||||||
|
| 'Update Referral Set'
|
||||||
| 'Validator Heartbeat'
|
| 'Validator Heartbeat'
|
||||||
| 'Vote on Proposal'
|
| 'Vote on Proposal'
|
||||||
| 'Withdraw';
|
| 'Withdraw';
|
||||||
|
|||||||
@@ -104,10 +104,30 @@ export function getLabelForProposal(
|
|||||||
}
|
}
|
||||||
} else if (proposal.terms?.updateMarket) {
|
} else if (proposal.terms?.updateMarket) {
|
||||||
return t('Proposal: Update market');
|
return t('Proposal: Update market');
|
||||||
|
} else if (proposal.terms?.updateSpotMarket) {
|
||||||
|
return t('Proposal: Update spot');
|
||||||
|
} else if (proposal.terms?.updateMarketState) {
|
||||||
|
const type = proposal.terms.updateMarketState.changes?.updateType;
|
||||||
|
if (type === 'MARKET_STATE_UPDATE_TYPE_TERMINATE') {
|
||||||
|
return t('Proposal: Market terminate');
|
||||||
|
} else if (type === 'MARKET_STATE_UPDATE_TYPE_SUSPEND') {
|
||||||
|
return t('Proposal: Market suspend');
|
||||||
|
} else if (type === 'MARKET_STATE_UPDATE_TYPE_RESUME') {
|
||||||
|
return t('Proposal: Market resume');
|
||||||
|
}
|
||||||
|
return t('Proposal: Market state');
|
||||||
} else if (proposal.terms?.updateNetworkParameter) {
|
} else if (proposal.terms?.updateNetworkParameter) {
|
||||||
return t('Proposal: Network parameter');
|
return t('Proposal: Network parameter');
|
||||||
|
} else if (proposal.terms?.updateReferralProgram) {
|
||||||
|
return t('Proposal: Referral program');
|
||||||
|
} else if (proposal.terms?.updateVolumeDiscountProgram) {
|
||||||
|
return t('Proposal: Discount program');
|
||||||
} else if (proposal.terms?.newFreeform) {
|
} else if (proposal.terms?.newFreeform) {
|
||||||
return t('Proposal: Freeform');
|
return t('Proposal: Freeform');
|
||||||
|
} else if (proposal.terms?.newTransfer) {
|
||||||
|
return t('Proposal: Transfer');
|
||||||
|
} else if (proposal.terms?.cancelTransfer) {
|
||||||
|
return t('Proposal: Transfer cancel');
|
||||||
} else {
|
} else {
|
||||||
return t('Proposal');
|
return t('Proposal');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
import { getTypeLabelForTransfer } from './details/tx-transfer';
|
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||||
|
import type { TendermintBlocksResponse } from '../../routes/blocks/tendermint-blocks-response';
|
||||||
import type { components } from '../../../types/explorer';
|
import type { components } from '../../../types/explorer';
|
||||||
|
|
||||||
|
import {
|
||||||
|
TxDetailsTransfer,
|
||||||
|
getTypeLabelForTransfer,
|
||||||
|
} from './details/tx-transfer';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { MemoryRouter } from 'react-router-dom';
|
||||||
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
type Transfer = components['schemas']['commandsv1Transfer'];
|
type Transfer = components['schemas']['commandsv1Transfer'];
|
||||||
|
|
||||||
describe('TX: Transfer: getLabelForTransfer', () => {
|
describe('TX: Transfer: getLabelForTransfer', () => {
|
||||||
@@ -56,3 +65,70 @@ describe('TX: Transfer: getLabelForTransfer', () => {
|
|||||||
expect(getTypeLabelForTransfer(mock)).toEqual('Transfer');
|
expect(getTypeLabelForTransfer(mock)).toEqual('Transfer');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('TxDetailsTransfer', () => {
|
||||||
|
const mockBlockData = {
|
||||||
|
result: {
|
||||||
|
block: {
|
||||||
|
header: {
|
||||||
|
height: '123',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockTxData: Partial<BlockExplorerTransactionResult> = {
|
||||||
|
hash: 'test',
|
||||||
|
submitter:
|
||||||
|
'e1943eea46fed576cf2be42972f3c5515ad3d0ac7ac013f56677c12a53a1b3ed',
|
||||||
|
command: {
|
||||||
|
nonce: '5188810881378065222',
|
||||||
|
blockHeight: '14951513',
|
||||||
|
transfer: {
|
||||||
|
fromAccountType: 'ACCOUNT_TYPE_GENERAL',
|
||||||
|
to: '78432a2808f20b18a46ccc6a917bdc4d63c2b9e7007f777bdcab5a9f462c5ba6',
|
||||||
|
toAccountType: 'ACCOUNT_TYPE_GENERAL',
|
||||||
|
asset:
|
||||||
|
'dd20590509d30d20bdbbe64dc1090c1140c7690121a9b9940bc66f62dfa2e599',
|
||||||
|
amount: '4800000000',
|
||||||
|
reference: '',
|
||||||
|
oneOff: {
|
||||||
|
deliverOn: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
signature: {
|
||||||
|
value:
|
||||||
|
'610c2e196a7d4fed4413b9e82af267b1ff3e30e943df3a3d28096fd60604d430d752fbaf6dd4f84d496be78885bb6118f40560bff7832c06bd7a3d67b718b700',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
it('renders basic transfer details', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<MockedProvider>
|
||||||
|
<MemoryRouter>
|
||||||
|
<TxDetailsTransfer
|
||||||
|
txData={mockTxData as BlockExplorerTransactionResult}
|
||||||
|
pubKey={mockTxData.command.submitter}
|
||||||
|
blockData={mockBlockData as TendermintBlocksResponse}
|
||||||
|
/>
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
|
||||||
|
const id = getByTestId('id');
|
||||||
|
expect(id.children[0].textContent).toEqual('Transfer ID');
|
||||||
|
expect(id.children[1].textContent).toEqual(
|
||||||
|
'51f3bab5eb2637651012507a64d497790a734248792c16e5cf36df8984074fbd'
|
||||||
|
);
|
||||||
|
|
||||||
|
const type = getByTestId('type');
|
||||||
|
expect(type.children[1].textContent).toEqual('Transfer');
|
||||||
|
|
||||||
|
const from = getByTestId('from');
|
||||||
|
expect(from.children[1].textContent).toEqual(mockTxData.submitter);
|
||||||
|
|
||||||
|
const to = getByTestId('to');
|
||||||
|
expect(to.children[1].textContent).toEqual(mockTxData.command.transfer.to);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ describe('Txs infinite list item', () => {
|
|||||||
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
expect(screen.getByText('Missing vital data')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders data correctly', () => {
|
it('renders data even with missing time', () => {
|
||||||
render(
|
render(
|
||||||
<MockedProvider>
|
<MockedProvider>
|
||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
@@ -105,5 +105,33 @@ describe('Txs infinite list item', () => {
|
|||||||
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||||
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||||
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||||
|
expect(screen.getByTestId('tx-time')).toHaveTextContent('-');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders data correctly', () => {
|
||||||
|
render(
|
||||||
|
<MockedProvider>
|
||||||
|
<MemoryRouter>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<TxsInfiniteListItem
|
||||||
|
type="testType"
|
||||||
|
submitter="testPubKey"
|
||||||
|
hash="testTxHash"
|
||||||
|
block="1"
|
||||||
|
code={0}
|
||||||
|
command={{}}
|
||||||
|
createdAt="1970-11-01T18:07:15Z"
|
||||||
|
/>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</MemoryRouter>
|
||||||
|
</MockedProvider>
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('tx-hash')).toHaveTextContent('testTxHash');
|
||||||
|
expect(screen.getByTestId('pub-key')).toHaveTextContent('testPubKey');
|
||||||
|
expect(screen.getByTestId('tx-type')).toHaveTextContent('testType');
|
||||||
|
expect(screen.getByTestId('tx-block')).toHaveTextContent('1');
|
||||||
|
expect(screen.getByTestId('tx-time').textContent).toMatch(/years ago/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { PartyLink } from '../links';
|
|||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
import type { Screen } from '@vegaprotocol/react-helpers';
|
import type { Screen } from '@vegaprotocol/react-helpers';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import { TimeAgo } from '../time-ago';
|
||||||
|
|
||||||
const DEFAULT_TRUNCATE_LENGTH = 7;
|
const DEFAULT_TRUNCATE_LENGTH = 7;
|
||||||
|
|
||||||
@@ -32,6 +33,7 @@ export const TxsInfiniteListItem = ({
|
|||||||
type,
|
type,
|
||||||
block,
|
block,
|
||||||
command,
|
command,
|
||||||
|
createdAt,
|
||||||
}: Partial<BlockExplorerTransactionResult>) => {
|
}: Partial<BlockExplorerTransactionResult>) => {
|
||||||
const { screenSize } = useScreenDimensions();
|
const { screenSize } = useScreenDimensions();
|
||||||
const idTruncateLength = useMemo(
|
const idTruncateLength = useMemo(
|
||||||
@@ -85,6 +87,11 @@ export const TxsInfiniteListItem = ({
|
|||||||
endChars={5}
|
endChars={5}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
|
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||||
|
<td className="text-sm items-center font-mono" data-testid="tx-time">
|
||||||
|
{createdAt ? <TimeAgo date={createdAt} /> : '-'}
|
||||||
|
</td>
|
||||||
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
|||||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||||
import EmptyList from '../empty-list/empty-list';
|
import EmptyList from '../empty-list/empty-list';
|
||||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
|
|
||||||
interface TxsInfiniteListProps {
|
interface TxsInfiniteListProps {
|
||||||
hasMoreTxs: boolean;
|
hasMoreTxs: boolean;
|
||||||
@@ -19,7 +20,16 @@ interface ItemProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Item = ({ tx }: ItemProps) => {
|
const Item = ({ tx }: ItemProps) => {
|
||||||
const { hash, submitter, type, command, block, code, index: blockIndex } = tx;
|
const {
|
||||||
|
hash,
|
||||||
|
submitter,
|
||||||
|
type,
|
||||||
|
command,
|
||||||
|
block,
|
||||||
|
code,
|
||||||
|
createdAt,
|
||||||
|
index: blockIndex,
|
||||||
|
} = tx;
|
||||||
return (
|
return (
|
||||||
<TxsInfiniteListItem
|
<TxsInfiniteListItem
|
||||||
type={type}
|
type={type}
|
||||||
@@ -29,6 +39,7 @@ const Item = ({ tx }: ItemProps) => {
|
|||||||
hash={hash}
|
hash={hash}
|
||||||
block={block}
|
block={block}
|
||||||
index={blockIndex}
|
index={blockIndex}
|
||||||
|
createdAt={createdAt}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -39,6 +50,7 @@ export const TxsInfiniteList = ({
|
|||||||
className,
|
className,
|
||||||
hasFilters = false,
|
hasFilters = false,
|
||||||
}: TxsInfiniteListProps) => {
|
}: TxsInfiniteListProps) => {
|
||||||
|
const { screenSize } = useScreenDimensions();
|
||||||
if (!txs || txs.length === 0) {
|
if (!txs || txs.length === 0) {
|
||||||
if (!areTxsLoading) {
|
if (!areTxsLoading) {
|
||||||
return (
|
return (
|
||||||
@@ -66,6 +78,9 @@ export const TxsInfiniteList = ({
|
|||||||
<th>{t('Type')}</th>
|
<th>{t('Type')}</th>
|
||||||
<th className="text-left">{t('From')}</th>
|
<th className="text-left">{t('From')}</th>
|
||||||
<th>{t('Block')}</th>
|
<th>{t('Block')}</th>
|
||||||
|
{['lg', 'xl', 'xxl', 'xxxl'].includes(screenSize) && (
|
||||||
|
<th>{t('Time')}</th>
|
||||||
|
)}
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
|
|||||||
url.searchParams.append('first', count);
|
url.searchParams.append('first', count);
|
||||||
url.searchParams.append('after', params.after);
|
url.searchParams.append('after', params.after);
|
||||||
} else {
|
} else {
|
||||||
url.searchParams.append('first', count);
|
url.searchParams.append('last', count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hacky fix for param as array
|
// Hacky fix for param as array
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ describe('getTxsDataUrl', () => {
|
|||||||
count: 10,
|
count: 10,
|
||||||
baseUrl: 'https://example.com/transactions',
|
baseUrl: 'https://example.com/transactions',
|
||||||
};
|
};
|
||||||
const expectedUrl = 'https://example.com/transactions?first=10';
|
const expectedUrl = 'https://example.com/transactions?last=10';
|
||||||
|
|
||||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||||
});
|
});
|
||||||
@@ -41,7 +41,7 @@ describe('getTxsDataUrl', () => {
|
|||||||
baseUrl: 'https://example.com/transactions',
|
baseUrl: 'https://example.com/transactions',
|
||||||
};
|
};
|
||||||
const expectedUrl =
|
const expectedUrl =
|
||||||
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
'https://example.com/transactions?last=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||||
|
|
||||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export const ErrorBoundary = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GHOST = (
|
export const GHOST = (
|
||||||
<svg
|
<svg
|
||||||
width="56"
|
width="56"
|
||||||
height="85"
|
height="85"
|
||||||
|
|||||||
@@ -36,30 +36,50 @@ const PERCENTAGE_PARAMS = [
|
|||||||
'governance.proposal.updateNetParam.requiredMajority',
|
'governance.proposal.updateNetParam.requiredMajority',
|
||||||
'governance.proposal.updateNetParam.requiredParticipation',
|
'governance.proposal.updateNetParam.requiredParticipation',
|
||||||
'governance.proposal.updateMarket.minProposerEquityLikeShare',
|
'governance.proposal.updateMarket.minProposerEquityLikeShare',
|
||||||
|
'governance.proposal.VolumeDiscountProgram.requiredMajority',
|
||||||
|
'governance.proposal.VolumeDiscountProgram.requiredParticipation',
|
||||||
|
'governance.proposal.referralProgram.requiredMajority',
|
||||||
|
'governance.proposal.transfer.requiredMajority',
|
||||||
|
'governance.proposal.updateAsset.requiredMajority',
|
||||||
|
'governance.proposal.updateAsset.requiredParticipation',
|
||||||
|
'governance.proposal.transfer.requiredParticipation',
|
||||||
|
'governance.proposal.referralProgram.requiredParticipation',
|
||||||
|
'network.validators.ersatz.rewardFactor',
|
||||||
|
'network.validators.ersatz.multipleOfTendermintValidators',
|
||||||
'validators.vote.required',
|
'validators.vote.required',
|
||||||
];
|
'referralProgram.maxReferralRewardFactor',
|
||||||
|
'referralProgram.maxReferralDiscountFactor',
|
||||||
|
'referralProgram.maxReferralRewardProportion',
|
||||||
|
].map((p) => p.toLowerCase());
|
||||||
|
|
||||||
const BIG_NUMBER_PARAMS = [
|
const BIG_NUMBER_PARAMS = [
|
||||||
'spam.protection.delegation.min.tokens',
|
'spam.protection.delegation.min.tokens',
|
||||||
'validators.delegation.minAmount',
|
'validators.delegation.minAmount',
|
||||||
|
'governance.proposal.transfer.maxAmount',
|
||||||
'reward.staking.delegation.minimumValidatorStake',
|
'reward.staking.delegation.minimumValidatorStake',
|
||||||
'reward.staking.delegation.maxPayoutPerParticipant',
|
'reward.staking.delegation.maxPayoutPerParticipant',
|
||||||
'reward.staking.delegation.maxPayoutPerEpoch',
|
'reward.staking.delegation.maxPayoutPerEpoch',
|
||||||
'spam.protection.voting.min.tokens',
|
'spam.protection.voting.min.tokens',
|
||||||
|
'spam.protection.proposal.min.tokens',
|
||||||
|
'governance.proposal.transfer.minVoterBalance',
|
||||||
'governance.proposal.freeform.minProposerBalance',
|
'governance.proposal.freeform.minProposerBalance',
|
||||||
'governance.proposal.updateNetParam.minVoterBalance',
|
'governance.proposal.updateNetParam.minVoterBalance',
|
||||||
'governance.proposal.updateMarket.minVoterBalance',
|
'governance.proposal.updateMarket.minVoterBalance',
|
||||||
'governance.proposal.asset.minVoterBalance',
|
'governance.proposal.asset.minVoterBalance',
|
||||||
'governance.proposal.updateNetParam.minProposerBalance',
|
'governance.proposal.updateNetParam.minProposerBalance',
|
||||||
'governance.proposal.freeform.minVoterBalance',
|
'governance.proposal.freeform.minVoterBalance',
|
||||||
'spam.protection.proposal.min.tokens',
|
|
||||||
'governance.proposal.updateMarket.minProposerBalance',
|
'governance.proposal.updateMarket.minProposerBalance',
|
||||||
'governance.proposal.asset.minProposerBalance',
|
'governance.proposal.asset.minProposerBalance',
|
||||||
|
'governance.proposal.transfer.minProposerBalance',
|
||||||
'governance.proposal.market.minProposerBalance',
|
'governance.proposal.market.minProposerBalance',
|
||||||
'governance.proposal.market.minVoterBalance',
|
'governance.proposal.market.minVoterBalance',
|
||||||
'governance.proposal.updateAsset.minProposerBalance',
|
'governance.proposal.updateAsset.minProposerBalance',
|
||||||
'governance.proposal.updateAsset.minVoterBalance',
|
'governance.proposal.updateAsset.minVoterBalance',
|
||||||
];
|
'governance.proposal.referralProgram.minProposerBalance',
|
||||||
|
'governance.proposal.referralProgram.minVoterBalance',
|
||||||
|
'governance.proposal.VolumeDiscountProgram.minProposerBalance',
|
||||||
|
'governance.proposal.VolumeDiscountProgram.minVoterBalance',
|
||||||
|
].map((p) => p.toLowerCase());
|
||||||
|
|
||||||
export const renderGroupedParams = (
|
export const renderGroupedParams = (
|
||||||
group: GroupedParams,
|
group: GroupedParams,
|
||||||
@@ -131,12 +151,12 @@ export const NetworkParameterRow = ({
|
|||||||
<div className="pb-2">
|
<div className="pb-2">
|
||||||
<SyntaxHighlighter data={JSON.parse(value)} />
|
<SyntaxHighlighter data={JSON.parse(value)} />
|
||||||
</div>
|
</div>
|
||||||
|
) : BIG_NUMBER_PARAMS.includes(key.toLowerCase()) ? (
|
||||||
|
addDecimalsFormatNumber(Number(value), 18)
|
||||||
|
) : PERCENTAGE_PARAMS.includes(key.toLowerCase()) ? (
|
||||||
|
`${formatNumber(Number(value) * 100, 0)}%`
|
||||||
) : isNaN(Number(value)) ? (
|
) : isNaN(Number(value)) ? (
|
||||||
value
|
value
|
||||||
) : BIG_NUMBER_PARAMS.includes(key) ? (
|
|
||||||
addDecimalsFormatNumber(Number(value), 18)
|
|
||||||
) : PERCENTAGE_PARAMS.includes(key) ? (
|
|
||||||
`${formatNumber(Number(value) * 100, 0)}%`
|
|
||||||
) : (
|
) : (
|
||||||
formatNumber(Number(value), 4)
|
formatNumber(Number(value), 4)
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||||
dataConnection {
|
dataConnection(pagination: { first: 30 }) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
externalData {
|
externalData {
|
||||||
@@ -45,6 +45,16 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
|||||||
operator
|
operator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on DataSourceSpecConfigurationTimeTrigger {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
triggers {
|
||||||
|
initial
|
||||||
|
every
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on DataSourceDefinitionExternal {
|
... on DataSourceDefinitionExternal {
|
||||||
@@ -103,6 +113,23 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on EthCallSpec {
|
||||||
|
abi
|
||||||
|
address
|
||||||
|
requiredConfirmations
|
||||||
|
method
|
||||||
|
filters {
|
||||||
|
key {
|
||||||
|
type
|
||||||
|
name
|
||||||
|
numberDecimalPlaces
|
||||||
|
}
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +139,7 @@ fragment ExplorerOracleDataSource on OracleSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
query ExplorerOracleSpecs {
|
query ExplorerOracleSpecs {
|
||||||
oracleSpecsConnection(pagination: { first: 50 }) {
|
oracleSpecsConnection(pagination: { first: 30 }) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,85 @@
|
|||||||
|
fragment ExplorerOraclePerpetual on Perpetual {
|
||||||
|
dataSourceSpecForSettlementData {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
dataSourceSpecForSettlementSchedule {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment ExplorerOracleFuture on Future {
|
||||||
|
dataSourceSpecForSettlementData {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
dataSourceSpecForTradingTermination {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fragment ExplorerOracleForMarketsMarket on Market {
|
fragment ExplorerOracleForMarketsMarket on Market {
|
||||||
id
|
id
|
||||||
|
state
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
product {
|
product {
|
||||||
... on Future {
|
... on Future {
|
||||||
dataSourceSpecForSettlementData {
|
...ExplorerOracleFuture
|
||||||
id
|
|
||||||
}
|
|
||||||
dataSourceSpecForTradingTermination {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on Perpetual {
|
... on Perpetual {
|
||||||
dataSourceSpecForSettlementData {
|
...ExplorerOraclePerpetual
|
||||||
id
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment ExplorerOracleDataSourceSpec on ExternalDataSourceSpec {
|
||||||
|
spec {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
data {
|
||||||
|
sourceType {
|
||||||
|
... on DataSourceDefinitionInternal {
|
||||||
|
sourceType {
|
||||||
|
... on DataSourceSpecConfigurationTime {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on DataSourceSpecConfigurationTimeTrigger {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
triggers {
|
||||||
|
initial
|
||||||
|
every
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dataSourceSpecForSettlementSchedule {
|
}
|
||||||
id
|
... on DataSourceDefinitionExternal {
|
||||||
|
sourceType {
|
||||||
|
... on EthCallSpec {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
... on DataSourceSpecConfiguration {
|
||||||
|
signers {
|
||||||
|
signer {
|
||||||
|
... on ETHAddress {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
... on PubKey {
|
||||||
|
key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -32,4 +95,27 @@ query ExplorerOracleFormMarkets {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
oracleSpecsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
dataSourceSpec {
|
||||||
|
...ExplorerOracleDataSourceSpec
|
||||||
|
}
|
||||||
|
dataConnection(pagination: { last: 1 }) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
externalData {
|
||||||
|
data {
|
||||||
|
data {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-5
@@ -5,23 +5,23 @@ import * as Apollo from '@apollo/client';
|
|||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
export type ExplorerOracleDataConnectionFragment = { __typename?: 'OracleSpec', dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||||
|
|
||||||
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
export type ExplorerOracleDataSourceFragment = { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } };
|
||||||
|
|
||||||
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type ExplorerOracleSpecsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
export type ExplorerOracleSpecsQuery = { __typename?: 'Query', oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', pageInfo: { __typename?: 'PageInfo', hasNextPage: boolean }, edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||||
|
|
||||||
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
|
export type ExplorerOracleSpecByIdQueryVariables = Types.Exact<{
|
||||||
id: Types.Scalars['ID'];
|
id: Types.Scalars['ID'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger' } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
export type ExplorerOracleSpecByIdQuery = { __typename?: 'Query', oracleSpec?: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, createdAt: any, updatedAt?: any | null, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } | { __typename?: 'EthCallSpec', abi?: Array<string> | null, args?: Array<string> | null, method: string, requiredConfirmations: number, address: string, normalisers?: Array<{ __typename?: 'Normaliser', name: string, expression: string }> | null, trigger: { __typename?: 'EthCallTrigger', trigger: { __typename?: 'EthTimeTrigger', initial?: any | null, every?: number | null, until?: any | null } }, filters?: Array<{ __typename?: 'Filter', key: { __typename?: 'PropertyKey', name?: string | null, type: Types.PropertyKeyType, numberDecimalPlaces?: number | null }, conditions?: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator }> | null }> | null } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', matchedSpecIds?: Array<string> | null, broadcastAt: any, signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null, data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } | null };
|
||||||
|
|
||||||
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
export const ExplorerOracleDataConnectionFragmentDoc = gql`
|
||||||
fragment ExplorerOracleDataConnection on OracleSpec {
|
fragment ExplorerOracleDataConnection on OracleSpec {
|
||||||
dataConnection {
|
dataConnection(pagination: {first: 30}) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
externalData {
|
externalData {
|
||||||
@@ -68,6 +68,16 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
|||||||
operator
|
operator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on DataSourceSpecConfigurationTimeTrigger {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
triggers {
|
||||||
|
initial
|
||||||
|
every
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
... on DataSourceDefinitionExternal {
|
... on DataSourceDefinitionExternal {
|
||||||
@@ -126,6 +136,23 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
... on EthCallSpec {
|
||||||
|
abi
|
||||||
|
address
|
||||||
|
requiredConfirmations
|
||||||
|
method
|
||||||
|
filters {
|
||||||
|
key {
|
||||||
|
type
|
||||||
|
name
|
||||||
|
numberDecimalPlaces
|
||||||
|
}
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +163,7 @@ export const ExplorerOracleDataSourceFragmentDoc = gql`
|
|||||||
${ExplorerOracleDataConnectionFragmentDoc}`;
|
${ExplorerOracleDataConnectionFragmentDoc}`;
|
||||||
export const ExplorerOracleSpecsDocument = gql`
|
export const ExplorerOracleSpecsDocument = gql`
|
||||||
query ExplorerOracleSpecs {
|
query ExplorerOracleSpecs {
|
||||||
oracleSpecsConnection(pagination: {first: 50}) {
|
oracleSpecsConnection(pagination: {first: 30}) {
|
||||||
pageInfo {
|
pageInfo {
|
||||||
hasNextPage
|
hasNextPage
|
||||||
}
|
}
|
||||||
|
|||||||
+110
-13
@@ -3,33 +3,106 @@ import * as Types from '@vegaprotocol/types';
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string } } | { __typename?: 'Spot' } } } };
|
export type ExplorerOraclePerpetualFragment = { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } };
|
||||||
|
|
||||||
|
export type ExplorerOracleFutureFragment = { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } };
|
||||||
|
|
||||||
|
export type ExplorerOracleForMarketsMarketFragment = { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } };
|
||||||
|
|
||||||
|
export type ExplorerOracleDataSourceSpecFragment = { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } };
|
||||||
|
|
||||||
export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type ExplorerOracleFormMarketsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string } } | { __typename?: 'Spot' } } } } }> } | null };
|
export type ExplorerOracleFormMarketsQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, state: Types.MarketState, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', product: { __typename?: 'Future', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForTradingTermination: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Perpetual', dataSourceSpecForSettlementData: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus }, dataSourceSpecForSettlementSchedule: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus } } | { __typename?: 'Spot' } } } } }> } | null, oracleSpecsConnection?: { __typename?: 'OracleSpecsConnection', edges?: Array<{ __typename?: 'OracleSpecEdge', node: { __typename?: 'OracleSpec', dataSourceSpec: { __typename?: 'ExternalDataSourceSpec', spec: { __typename?: 'DataSourceSpec', id: string, status: Types.DataSourceSpecStatus, data: { __typename?: 'DataSourceDefinition', sourceType: { __typename?: 'DataSourceDefinitionExternal', sourceType: { __typename?: 'DataSourceSpecConfiguration', signers?: Array<{ __typename?: 'Signer', signer: { __typename?: 'ETHAddress', address?: string | null } | { __typename?: 'PubKey', key?: string | null } }> | null } | { __typename?: 'EthCallSpec', address: string } } | { __typename?: 'DataSourceDefinitionInternal', sourceType: { __typename?: 'DataSourceSpecConfigurationTime', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null> } | { __typename?: 'DataSourceSpecConfigurationTimeTrigger', conditions: Array<{ __typename?: 'Condition', value?: string | null, operator: Types.ConditionOperator } | null>, triggers: Array<{ __typename?: 'InternalTimeTrigger', initial?: number | null, every?: number | null } | null> } } } } }, dataConnection: { __typename?: 'OracleDataConnection', edges?: Array<{ __typename?: 'OracleDataEdge', node: { __typename?: 'OracleData', externalData: { __typename?: 'ExternalData', data: { __typename?: 'Data', data?: Array<{ __typename?: 'Property', name: string, value: string }> | null } } } } | null> | null } } } | null> | null } | null };
|
||||||
|
|
||||||
|
export const ExplorerOracleFutureFragmentDoc = gql`
|
||||||
|
fragment ExplorerOracleFuture on Future {
|
||||||
|
dataSourceSpecForSettlementData {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
dataSourceSpecForTradingTermination {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const ExplorerOraclePerpetualFragmentDoc = gql`
|
||||||
|
fragment ExplorerOraclePerpetual on Perpetual {
|
||||||
|
dataSourceSpecForSettlementData {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
dataSourceSpecForSettlementSchedule {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const ExplorerOracleForMarketsMarketFragmentDoc = gql`
|
export const ExplorerOracleForMarketsMarketFragmentDoc = gql`
|
||||||
fragment ExplorerOracleForMarketsMarket on Market {
|
fragment ExplorerOracleForMarketsMarket on Market {
|
||||||
id
|
id
|
||||||
|
state
|
||||||
tradableInstrument {
|
tradableInstrument {
|
||||||
instrument {
|
instrument {
|
||||||
product {
|
product {
|
||||||
... on Future {
|
... on Future {
|
||||||
dataSourceSpecForSettlementData {
|
...ExplorerOracleFuture
|
||||||
id
|
|
||||||
}
|
|
||||||
dataSourceSpecForTradingTermination {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on Perpetual {
|
... on Perpetual {
|
||||||
dataSourceSpecForSettlementData {
|
...ExplorerOraclePerpetual
|
||||||
id
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
${ExplorerOracleFutureFragmentDoc}
|
||||||
|
${ExplorerOraclePerpetualFragmentDoc}`;
|
||||||
|
export const ExplorerOracleDataSourceSpecFragmentDoc = gql`
|
||||||
|
fragment ExplorerOracleDataSourceSpec on ExternalDataSourceSpec {
|
||||||
|
spec {
|
||||||
|
id
|
||||||
|
status
|
||||||
|
data {
|
||||||
|
sourceType {
|
||||||
|
... on DataSourceDefinitionInternal {
|
||||||
|
sourceType {
|
||||||
|
... on DataSourceSpecConfigurationTime {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
... on DataSourceSpecConfigurationTimeTrigger {
|
||||||
|
conditions {
|
||||||
|
value
|
||||||
|
operator
|
||||||
|
}
|
||||||
|
triggers {
|
||||||
|
initial
|
||||||
|
every
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dataSourceSpecForSettlementSchedule {
|
}
|
||||||
id
|
... on DataSourceDefinitionExternal {
|
||||||
|
sourceType {
|
||||||
|
... on EthCallSpec {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
... on DataSourceSpecConfiguration {
|
||||||
|
signers {
|
||||||
|
signer {
|
||||||
|
... on ETHAddress {
|
||||||
|
address
|
||||||
|
}
|
||||||
|
... on PubKey {
|
||||||
|
key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,8 +119,32 @@ export const ExplorerOracleFormMarketsDocument = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
oracleSpecsConnection {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
dataSourceSpec {
|
||||||
|
...ExplorerOracleDataSourceSpec
|
||||||
|
}
|
||||||
|
dataConnection(pagination: {last: 1}) {
|
||||||
|
edges {
|
||||||
|
node {
|
||||||
|
externalData {
|
||||||
|
data {
|
||||||
|
data {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
${ExplorerOracleForMarketsMarketFragmentDoc}`;
|
${ExplorerOracleForMarketsMarketFragmentDoc}
|
||||||
|
${ExplorerOracleDataSourceSpecFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useExplorerOracleFormMarketsQuery__
|
* __useExplorerOracleFormMarketsQuery__
|
||||||
|
|||||||
@@ -51,15 +51,26 @@ describe('Oracle Data view', () => {
|
|||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
externalData: {
|
externalData: {
|
||||||
|
__typename: 'ExternalData',
|
||||||
data: {
|
data: {
|
||||||
broadcastAt: '2022-01-01',
|
__typename: 'Data',
|
||||||
|
matchedSpecIds: ['123'],
|
||||||
|
broadcastAt: '2023-01-01T00:00:00Z',
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
__typename: 'Property',
|
||||||
|
name: 'Test-name',
|
||||||
|
value: 'Test-data',
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
} as DataConnection)
|
} as ExplorerOracleDataConnectionFragment['dataConnection'])
|
||||||
);
|
);
|
||||||
expect(res.getByText('Broadcast data')).toBeInTheDocument();
|
expect(res.getByText('Test-name')).toBeInTheDocument();
|
||||||
|
expect(res.getByText('Test-data')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,39 +1,55 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import filter from 'recursive-key-filter';
|
|
||||||
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
import type { ExplorerOracleDataConnectionFragment } from '../__generated__/Oracles';
|
||||||
|
import { TimeAgo } from '../../../components/time-ago';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
|
const cellSpacing = 'px-3';
|
||||||
|
|
||||||
interface OracleDataTypeProps {
|
interface OracleDataTypeProps {
|
||||||
data: ExplorerOracleDataConnectionFragment['dataConnection'];
|
data: ExplorerOracleDataConnectionFragment['dataConnection'];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is data that has matched this oracle, this view will
|
|
||||||
* render the data inside a collapsed element so that it can be viewed.
|
|
||||||
* Currently the data is just rendered as a JSON view, because
|
|
||||||
* that Does The Job, rather than because it's good.
|
|
||||||
*/
|
|
||||||
export function OracleData({ data }: OracleDataTypeProps) {
|
export function OracleData({ data }: OracleDataTypeProps) {
|
||||||
if (!data || !data.edges?.length || data.edges.length > 1) {
|
if (!data || !data.edges?.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<details data-testid="oracle-data">
|
<>
|
||||||
<summary>{t('Broadcast data')}</summary>
|
<h2 className="text-3xl font-bold mb-4 display-5 mt-5">
|
||||||
<ul>
|
{t('Recent data')}
|
||||||
{data.edges.map((d) => {
|
</h2>
|
||||||
if (!d) {
|
<table>
|
||||||
return null;
|
<thead>
|
||||||
}
|
<tr className="text-left">
|
||||||
|
<th className={cellSpacing}>Value</th>
|
||||||
|
<th className={cellSpacing}>Key</th>
|
||||||
|
<th className={cellSpacing}>Date</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.edges.map((d) => {
|
||||||
|
if (!d) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
const broadcastAt = d.node.externalData.data.broadcastAt;
|
||||||
<li key={d.node.externalData.data.broadcastAt}>
|
const node = d.node.externalData.data.data?.at(0);
|
||||||
<SyntaxHighlighter data={filter(d, ['__typename'])} />
|
if (!node || !node.value || !node.name || !node || !broadcastAt) {
|
||||||
</li>
|
return null;
|
||||||
);
|
}
|
||||||
})}
|
|
||||||
</ul>
|
return (
|
||||||
</details>
|
<tr key={d.node.externalData.data.broadcastAt}>
|
||||||
|
<td className={`${cellSpacing} font-mono`}>{node.value}</td>
|
||||||
|
<td className={`${cellSpacing} font-mono`}>{node.name}</td>
|
||||||
|
<td className={cellSpacing}>
|
||||||
|
<TimeAgo date={broadcastAt} />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,13 +55,13 @@ describe('Oracle type view', () => {
|
|||||||
const s = mock('prices.external.whatever');
|
const s = mock('prices.external.whatever');
|
||||||
expect(isInternalSourceType(s)).toEqual(false);
|
expect(isInternalSourceType(s)).toEqual(false);
|
||||||
const res = render(renderWrappedComponent(s));
|
const res = render(renderWrappedComponent(s));
|
||||||
expect(res.getByText('External data')).toBeInTheDocument();
|
expect(res.getByText('External Data')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders External data otherwise', () => {
|
it('Renders External data otherwise', () => {
|
||||||
const s = mock('prices.external.vegaprotocol.builtin.');
|
const s = mock('prices.external.vegaprotocol.builtin.');
|
||||||
expect(isInternalSourceType(s)).toEqual(false);
|
expect(isInternalSourceType(s)).toEqual(false);
|
||||||
const res = render(renderWrappedComponent(s));
|
const res = render(renderWrappedComponent(s));
|
||||||
expect(res.getByText('External data')).toBeInTheDocument();
|
expect(res.getByText('External Data')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -26,6 +26,19 @@ export function isInternalSourceType(s: SourceType) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getExternalType(s: SourceType) {
|
||||||
|
if (s.sourceType.__typename === 'EthCallSpec') {
|
||||||
|
return 'Ethereum Contract Call';
|
||||||
|
} else {
|
||||||
|
return 'External Data';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTypeString(s: SourceType) {
|
||||||
|
const isInternal = isInternalSourceType(s);
|
||||||
|
return isInternal ? 'Internal data' : getExternalType(s);
|
||||||
|
}
|
||||||
|
|
||||||
interface OracleDetailsTypeProps {
|
interface OracleDetailsTypeProps {
|
||||||
sourceType: SourceType;
|
sourceType: SourceType;
|
||||||
}
|
}
|
||||||
@@ -39,14 +52,10 @@ export function OracleDetailsType({ sourceType }: OracleDetailsTypeProps) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isInternal = isInternalSourceType(sourceType);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableHeader scope="row">Type</TableHeader>
|
<TableHeader scope="row">Type</TableHeader>
|
||||||
<TableCell modifier="bordered">
|
<TableCell modifier="bordered">{getTypeString(sourceType)}</TableCell>
|
||||||
{isInternal ? 'Internal data' : 'External data'}
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||||
|
import type { SourceType } from './oracle';
|
||||||
|
import {
|
||||||
|
EthExplorerLink,
|
||||||
|
EthExplorerLinkTypes,
|
||||||
|
} from '../../../components/links/eth-explorer-link/eth-explorer-link';
|
||||||
|
|
||||||
|
interface OracleDetailsEthSourceProps {
|
||||||
|
sourceType: SourceType;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Given an Oracle that sources data from Ethereum, this component will render
|
||||||
|
* a link to the smart contract and some basic details
|
||||||
|
*/
|
||||||
|
export function OracleEthSource({ sourceType }: OracleDetailsEthSourceProps) {
|
||||||
|
if (
|
||||||
|
sourceType.__typename !== 'DataSourceDefinitionExternal' ||
|
||||||
|
sourceType.sourceType.__typename !== 'EthCallSpec'
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const address = sourceType.sourceType.address;
|
||||||
|
|
||||||
|
if (!address) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableHeader scope="row">Ethereum Contract</TableHeader>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
<EthExplorerLink id={address} type={EthExplorerLinkTypes.address} />
|
||||||
|
<span className="mx-3">⇒</span>
|
||||||
|
<code>{sourceType.sourceType.method}</code>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { getConditionsOrFilters, OracleFilter } from './oracle-filter';
|
import { OracleFilter } from './oracle-filter';
|
||||||
import type { Filter } from './oracle-filter';
|
|
||||||
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
||||||
import {
|
import {
|
||||||
ConditionOperator,
|
ConditionOperator,
|
||||||
DataSourceSpecStatus,
|
DataSourceSpecStatus,
|
||||||
PropertyKeyType,
|
PropertyKeyType,
|
||||||
} from '@vegaprotocol/types';
|
} from '@vegaprotocol/types';
|
||||||
|
import type { Condition } from '@vegaprotocol/types';
|
||||||
|
|
||||||
const mockExternalSpec = {
|
type Spec =
|
||||||
|
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
|
||||||
|
|
||||||
|
const mockExternalSpec: Spec = {
|
||||||
sourceType: {
|
sourceType: {
|
||||||
__typename: 'DataSourceSpecConfiguration',
|
__typename: 'DataSourceSpecConfiguration',
|
||||||
filters: [
|
filters: [
|
||||||
@@ -16,12 +19,12 @@ const mockExternalSpec = {
|
|||||||
__typename: 'Filter',
|
__typename: 'Filter',
|
||||||
key: {
|
key: {
|
||||||
type: PropertyKeyType.TYPE_INTEGER,
|
type: PropertyKeyType.TYPE_INTEGER,
|
||||||
name: 'test',
|
name: 'testKey',
|
||||||
},
|
},
|
||||||
conditions: [
|
conditions: [
|
||||||
{
|
{
|
||||||
__typename: 'Condition',
|
__typename: 'Condition',
|
||||||
value: 'test',
|
value: 'testValue',
|
||||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -30,16 +33,6 @@ const mockExternalSpec = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const mockTimeSpec = {
|
|
||||||
__typename: 'DataSourceSpecConfigurationTime',
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
value: '123',
|
|
||||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderComponent(data: ExplorerOracleDataSourceFragment) {
|
function renderComponent(data: ExplorerOracleDataSourceFragment) {
|
||||||
return <OracleFilter data={data} />;
|
return <OracleFilter data={data} />;
|
||||||
}
|
}
|
||||||
@@ -70,11 +63,16 @@ describe('Oracle Filter view', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as ExplorerOracleDataSourceFragment)
|
dataConnection: {
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(res.getByText('Filter')).toBeInTheDocument();
|
// Renders a comprehensible summary of key = value
|
||||||
// Avoids asserting on how the data is presented because it is very rudimentary
|
expect(res.getByText('testKey')).toBeInTheDocument();
|
||||||
|
expect(res.getByText('=')).toBeInTheDocument();
|
||||||
|
expect(res.getByText('testValue')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders conditions if type is DataSourceSpecConfigurationTime', () => {
|
it('Renders conditions if type is DataSourceSpecConfigurationTime', () => {
|
||||||
@@ -88,75 +86,59 @@ describe('Oracle Filter view', () => {
|
|||||||
data: {
|
data: {
|
||||||
sourceType: {
|
sourceType: {
|
||||||
__typename: 'DataSourceDefinitionInternal',
|
__typename: 'DataSourceDefinitionInternal',
|
||||||
sourceType: mockTimeSpec,
|
sourceType: {
|
||||||
|
__typename: 'DataSourceSpecConfigurationTime',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
value: '1',
|
||||||
|
operator: ConditionOperator.OPERATOR_EQUALS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as ExplorerOracleDataSourceFragment)
|
dataConnection: {
|
||||||
|
edges: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(res.getByText('Filter')).toBeInTheDocument();
|
expect(res.getByText('Time')).toBeInTheDocument();
|
||||||
|
expect(res.getByText('=')).toBeInTheDocument();
|
||||||
|
expect(res.getByTitle('1').textContent).toMatch(/1970/);
|
||||||
// Avoids asserting on how the data is presented because it is very rudimentary
|
// Avoids asserting on how the data is presented because it is very rudimentary
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
describe('getConditionsOrFilter', () => {
|
it('DataSourceSpecConfigurationTime handles empty conditions', () => {
|
||||||
it('Returns null if the type is undetermined (not DataSourceSpecConfiguration or DataSourceSpecConfigurationTime', () => {
|
const res = render(
|
||||||
expect(getConditionsOrFilters({})).toBeNull();
|
renderComponent({
|
||||||
});
|
dataSourceSpec: {
|
||||||
|
spec: {
|
||||||
it('Returns the conditions object for time specs', () => {
|
id: 'irrelevant-test-data',
|
||||||
const mock: Filter = {
|
createdAt: 'irrelevant-test-data',
|
||||||
__typename: 'DataSourceSpecConfigurationTime',
|
status: DataSourceSpecStatus.STATUS_ACTIVE,
|
||||||
conditions: [
|
data: {
|
||||||
{
|
sourceType: {
|
||||||
__typename: 'Condition',
|
__typename: 'DataSourceDefinitionInternal',
|
||||||
value: '100',
|
sourceType: {
|
||||||
operator: ConditionOperator.OPERATOR_GREATER_THAN,
|
__typename: 'DataSourceSpecConfigurationTime',
|
||||||
},
|
conditions: [undefined as unknown as Condition],
|
||||||
],
|
},
|
||||||
};
|
},
|
||||||
const res = getConditionsOrFilters(mock);
|
|
||||||
// This ugly construction is due to lazy typing on getConditionsOrFilter
|
|
||||||
if (!res || res.length !== 1 || !res[0] || 'key' in res[0]) {
|
|
||||||
throw new Error(
|
|
||||||
'getConditionsOrFilter did not return conditions on a time spec'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(res[0].__typename).toEqual('Condition');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Returns the filters object for external specs', () => {
|
|
||||||
const mock: Filter = {
|
|
||||||
__typename: 'DataSourceSpecConfiguration',
|
|
||||||
filters: [
|
|
||||||
{
|
|
||||||
__typename: 'Filter',
|
|
||||||
key: {
|
|
||||||
type: PropertyKeyType.TYPE_INTEGER,
|
|
||||||
name: 'test',
|
|
||||||
},
|
|
||||||
conditions: [
|
|
||||||
{
|
|
||||||
__typename: 'Condition',
|
|
||||||
value: 'test',
|
|
||||||
operator: ConditionOperator.OPERATOR_EQUALS,
|
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
},
|
},
|
||||||
],
|
dataConnection: {
|
||||||
};
|
edges: [],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const res = getConditionsOrFilters(mock);
|
// This should never happen, but for coverage sake we test that it does this
|
||||||
// This ugly construction is due to lazy typing on getConditionsOrFilter
|
const ul = res.getByRole('list');
|
||||||
if (!res || res.length !== 1 || !res[0] || 'value' in res[0]) {
|
expect(ul).toBeInTheDocument();
|
||||||
throw new Error(
|
expect(ul).toBeEmptyDOMElement();
|
||||||
'getConditionsOrFilter did not return filters on a external spec'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
expect(res[0].__typename).toEqual('Filter');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,36 +1,15 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import filter from 'recursive-key-filter';
|
|
||||||
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
import type { ExplorerOracleDataSourceFragment } from '../__generated__/Oracles';
|
||||||
|
import { OracleSpecInternalTimeTrigger } from './oracle-spec/internal-time-trigger';
|
||||||
|
import { OracleSpecCondition } from './oracle-spec/condition';
|
||||||
|
import { getCharacterForOperator } from './oracle-spec/operator';
|
||||||
|
|
||||||
interface OracleFilterProps {
|
interface OracleFilterProps {
|
||||||
data: ExplorerOracleDataSourceFragment;
|
data: ExplorerOracleDataSourceFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Filter =
|
|
||||||
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType']['sourceType'];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given the main Filter view just uses a JSON dump view, this function
|
|
||||||
* selects the correct filter to dump in to that view. Internal oracles
|
|
||||||
* (i.e. the Time oracle) have conditions while external data sources
|
|
||||||
* have filters
|
|
||||||
*
|
|
||||||
* @param s A data source
|
|
||||||
* @returns Object an object containing conditions or filters
|
|
||||||
*/
|
|
||||||
export function getConditionsOrFilters(s: Filter) {
|
|
||||||
if (s.__typename === 'DataSourceSpecConfiguration') {
|
|
||||||
return s.filters;
|
|
||||||
} else if (s.__typename === 'DataSourceSpecConfigurationTime') {
|
|
||||||
return s.conditions;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the conditions that this oracle is using to filter
|
* Shows the conditions that this oracle is using to filter
|
||||||
* data sources.
|
* data sources, as a list.
|
||||||
*
|
*
|
||||||
* Renders nothing if there is no data (which will frequently)
|
* Renders nothing if there is no data (which will frequently)
|
||||||
* be the case) and if there is data, currently renders a simple
|
* be the case) and if there is data, currently renders a simple
|
||||||
@@ -42,16 +21,53 @@ export function OracleFilter({ data }: OracleFilterProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const s = data.dataSourceSpec.spec.data.sourceType.sourceType;
|
const s = data.dataSourceSpec.spec.data.sourceType.sourceType;
|
||||||
const f = getConditionsOrFilters(s);
|
if (s.__typename === 'DataSourceSpecConfigurationTime' && s.conditions) {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
{s.conditions
|
||||||
|
.filter((c) => !!c)
|
||||||
|
.map((c) => {
|
||||||
|
if (!c) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<OracleSpecCondition key={c.value} data={c} type={s.__typename} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
} else if (
|
||||||
|
s.__typename === 'DataSourceSpecConfigurationTimeTrigger' &&
|
||||||
|
s.triggers
|
||||||
|
) {
|
||||||
|
return <OracleSpecInternalTimeTrigger data={s} />;
|
||||||
|
} else if (
|
||||||
|
s.__typename === 'EthCallSpec' ||
|
||||||
|
s.__typename === 'DataSourceSpecConfiguration'
|
||||||
|
) {
|
||||||
|
if (s.filters !== null && s.filters && 'filters' in s) {
|
||||||
|
return (
|
||||||
|
<ul>
|
||||||
|
{s.filters.map((f) => {
|
||||||
|
const prop = <code title={f.key.type}>{f.key.name}</code>;
|
||||||
|
|
||||||
if (!f) {
|
if (!f.conditions || f.conditions.length === 0) {
|
||||||
return null;
|
return prop;
|
||||||
|
} else {
|
||||||
|
return f.conditions.map((c) => {
|
||||||
|
return (
|
||||||
|
<li key={`${prop}${c.value}`}>
|
||||||
|
{prop} {getCharacterForOperator(c.operator)}{' '}
|
||||||
|
<code>{c.value ? c.value : '-'}</code>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return null;
|
||||||
<details>
|
|
||||||
<summary>{t('Filter')}</summary>
|
|
||||||
<SyntaxHighlighter data={filter(f, ['__typename'])} />
|
|
||||||
</details>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function renderComponent(id: string, mocks: MockedResponse[]) {
|
|||||||
<MemoryRouter>
|
<MemoryRouter>
|
||||||
<MockedProvider mocks={mocks}>
|
<MockedProvider mocks={mocks}>
|
||||||
<Table>
|
<Table>
|
||||||
<tbody>
|
<tbody data-testid="wrapper">
|
||||||
<OracleMarkets id={id} />
|
<OracleMarkets id={id} />
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
@@ -23,8 +23,7 @@ function renderComponent(id: string, mocks: MockedResponse[]) {
|
|||||||
describe('Oracle Markets component', () => {
|
describe('Oracle Markets component', () => {
|
||||||
it('Renders a row with the market ID initially', () => {
|
it('Renders a row with the market ID initially', () => {
|
||||||
const res = render(renderComponent('123', []));
|
const res = render(renderComponent('123', []));
|
||||||
expect(res.getByText('Market')).toBeInTheDocument();
|
expect(res.getByTestId('wrapper')).toBeEmptyDOMElement();
|
||||||
expect(res.getByText('123')).toBeInTheDocument();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders that this is a termination source for the right market', async () => {
|
it('Renders that this is a termination source for the right market', async () => {
|
||||||
@@ -34,21 +33,58 @@ describe('Oracle Markets component', () => {
|
|||||||
},
|
},
|
||||||
result: {
|
result: {
|
||||||
data: {
|
data: {
|
||||||
|
oracleSpecsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
dataConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
externalData: {
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
name: '123',
|
||||||
|
value: '456',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dataSourceSpec: {
|
||||||
|
spec: {
|
||||||
|
id: '789',
|
||||||
|
state: 'Active',
|
||||||
|
status: 'Active',
|
||||||
|
data: {
|
||||||
|
sourceType: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
marketsConnection: {
|
marketsConnection: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: '123',
|
id: '123',
|
||||||
|
state: 'Active',
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
dataSourceSpecForSettlementData: {
|
dataSourceSpecForSettlementData: {
|
||||||
id: '456',
|
id: '456',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
dataSourceSpecForTradingTermination: {
|
dataSourceSpecForTradingTermination: {
|
||||||
id: '789',
|
id: '789',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -59,15 +95,18 @@ describe('Oracle Markets component', () => {
|
|||||||
node: {
|
node: {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: 'abc',
|
id: 'abc',
|
||||||
|
state: 'Active',
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
dataSourceSpecForSettlementData: {
|
dataSourceSpecForSettlementData: {
|
||||||
id: 'def',
|
id: 'def',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
dataSourceSpecForTradingTermination: {
|
dataSourceSpecForTradingTermination: {
|
||||||
id: 'ghi',
|
id: 'ghi',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -91,21 +130,58 @@ describe('Oracle Markets component', () => {
|
|||||||
},
|
},
|
||||||
result: {
|
result: {
|
||||||
data: {
|
data: {
|
||||||
|
oracleSpecsConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
dataConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
externalData: {
|
||||||
|
data: {
|
||||||
|
data: {
|
||||||
|
name: '123',
|
||||||
|
value: '456',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
dataSourceSpec: {
|
||||||
|
spec: {
|
||||||
|
id: '789',
|
||||||
|
state: 'Active',
|
||||||
|
status: 'Active',
|
||||||
|
data: {
|
||||||
|
sourceType: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
marketsConnection: {
|
marketsConnection: {
|
||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: '123',
|
id: '123',
|
||||||
|
state: 'Active',
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
dataSourceSpecForSettlementData: {
|
dataSourceSpecForSettlementData: {
|
||||||
id: '789',
|
id: '789',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
dataSourceSpecForTradingTermination: {
|
dataSourceSpecForTradingTermination: {
|
||||||
id: '123',
|
id: '123',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -116,15 +192,18 @@ describe('Oracle Markets component', () => {
|
|||||||
node: {
|
node: {
|
||||||
__typename: 'Market',
|
__typename: 'Market',
|
||||||
id: 'abc',
|
id: 'abc',
|
||||||
|
state: 'Active',
|
||||||
tradableInstrument: {
|
tradableInstrument: {
|
||||||
instrument: {
|
instrument: {
|
||||||
product: {
|
product: {
|
||||||
__typename: 'Future',
|
__typename: 'Future',
|
||||||
dataSourceSpecForSettlementData: {
|
dataSourceSpecForSettlementData: {
|
||||||
id: 'def',
|
id: 'def',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
dataSourceSpecForTradingTermination: {
|
dataSourceSpecForTradingTermination: {
|
||||||
id: 'ghi',
|
id: 'ghi',
|
||||||
|
status: 'Active',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { getNodes } from '@vegaprotocol/utils';
|
import { getNodes } from '@vegaprotocol/utils';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { MarketLink } from '../../../components/links';
|
import { MarketLink } from '../../../components/links';
|
||||||
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
import { TableRow, TableCell, TableHeader } from '../../../components/table';
|
||||||
import type { ExplorerOracleForMarketsMarketFragment } from '../__generated__/OraclesForMarkets';
|
import type { ExplorerOracleForMarketsMarketFragment } from '../__generated__/OraclesForMarkets';
|
||||||
@@ -24,38 +23,37 @@ export function OracleMarkets({ id }: OracleMarketsProps) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (markets) {
|
if (markets) {
|
||||||
const m = markets.find((m) => {
|
const m = markets.filter((market) => {
|
||||||
const p = m.tradableInstrument.instrument.product;
|
const p = market.tradableInstrument.instrument.product;
|
||||||
if (
|
if (
|
||||||
((p.__typename === 'Future' || p.__typename === 'Perpetual') &&
|
((p.__typename === 'Future' || p.__typename === 'Perpetual') &&
|
||||||
p.dataSourceSpecForSettlementData.id === id) ||
|
p.dataSourceSpecForSettlementData.id === id) ||
|
||||||
('dataSourceSpecForTradingTermination' in p &&
|
('dataSourceSpecForTradingTermination' in p &&
|
||||||
p.dataSourceSpecForTradingTermination.id === id)
|
p.dataSourceSpecForTradingTermination.id === id) ||
|
||||||
|
(p.__typename === 'Perpetual' &&
|
||||||
|
p.dataSourceSpecForSettlementSchedule.id === id)
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (m && m.id) {
|
if (m && m.length > 0) {
|
||||||
return (
|
return (
|
||||||
<TableRow modifier="bordered">
|
<>
|
||||||
<TableHeader scope="row">{getLabel(id, m)}</TableHeader>
|
{m.map((market) => (
|
||||||
<TableCell modifier="bordered" data-testid={`m-${m.id}`}>
|
<TableRow modifier="bordered" key={`m-${market.id}`}>
|
||||||
<MarketLink id={m.id} />
|
<TableHeader scope="row">{getLabel(id, market)}</TableHeader>
|
||||||
</TableCell>
|
<TableCell modifier="bordered" data-testid={`m-${market.id}`}>
|
||||||
</TableRow>
|
<MarketLink id={market.id} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (
|
return null;
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableHeader scope="row">{t('Market')}</TableHeader>
|
|
||||||
<TableCell modifier="bordered">
|
|
||||||
<span>{id}</span>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getLabel(
|
export function getLabel(
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
|
import { getCharacterForOperator } from './operator';
|
||||||
|
import type { Condition } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export interface OracleSpecCondition {
|
||||||
|
data: Condition;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OracleSpecCondition({ data, type }: OracleSpecCondition) {
|
||||||
|
const c = getCharacterForOperator(data.operator);
|
||||||
|
const value =
|
||||||
|
type === 'DataSourceSpecConfigurationTime' && data.value
|
||||||
|
? fromUnixTime(parseInt(data.value)).toLocaleString()
|
||||||
|
: data.value;
|
||||||
|
const typeLabel =
|
||||||
|
type === 'DataSourceSpecConfigurationTime' ? (
|
||||||
|
<span>{t('Time')}</span>
|
||||||
|
) : (
|
||||||
|
type
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={`${typeLabel}${c}${value}`}>
|
||||||
|
{typeLabel} {c}{' '}
|
||||||
|
{value && (
|
||||||
|
<span
|
||||||
|
title={data.value || value}
|
||||||
|
className="underline decoration-dotted"
|
||||||
|
>
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import type { DataSourceSpecConfigurationTimeTrigger } from '@vegaprotocol/types';
|
||||||
|
import secondsToMinutes from 'date-fns/secondsToMinutes';
|
||||||
|
import fromUnixTime from 'date-fns/fromUnixTime';
|
||||||
|
|
||||||
|
export interface OracleSpecInternalTimeTriggerProps {
|
||||||
|
data: DataSourceSpecConfigurationTimeTrigger;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function OracleSpecInternalTimeTrigger({
|
||||||
|
data,
|
||||||
|
}: OracleSpecInternalTimeTriggerProps) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<span>{t('Time')}</span>,
|
||||||
|
{data.triggers.map((tr) => {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{tr?.initial ? (
|
||||||
|
<span title={`${tr.initial}`}>
|
||||||
|
<strong>{t('starting at')}</strong>{' '}
|
||||||
|
<em className="not-italic underline decoration-dotted">
|
||||||
|
{fromUnixTime(tr.initial).toLocaleString()}
|
||||||
|
</em>
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
{tr?.every ? (
|
||||||
|
<span title={`${tr.every} ${t('seconds')}`}>
|
||||||
|
, <strong>{t('every')}</strong>{' '}
|
||||||
|
<em className="not-italic underline decoration-dotted">
|
||||||
|
{secondsToMinutes(tr.every)} {t('minutes')}
|
||||||
|
</em>{' '}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
''
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import type { ConditionOperator } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
export function getCharacterForOperator(
|
||||||
|
operator: ConditionOperator
|
||||||
|
): React.ReactElement {
|
||||||
|
switch (operator) {
|
||||||
|
case 'OPERATOR_EQUALS':
|
||||||
|
return <span title={t('equals')}>=</span>;
|
||||||
|
case 'OPERATOR_GREATER_THAN':
|
||||||
|
return <span title={t('greater than')}>></span>;
|
||||||
|
case 'OPERATOR_GREATER_THAN_OR_EQUAL':
|
||||||
|
return <span title={t('greater than or equal')}>≥</span>;
|
||||||
|
case 'OPERATOR_LESS_THAN':
|
||||||
|
return <span title={t('less than')}><</span>;
|
||||||
|
case 'OPERATOR_LESS_THAN_OR_EQUAL':
|
||||||
|
return <span title={t('less than or equal')}>≤</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span>{operator}</span>;
|
||||||
|
}
|
||||||
@@ -14,7 +14,9 @@ import { OracleFilter } from './oracle-filter';
|
|||||||
import { OracleDetailsType } from './oracle-details-type';
|
import { OracleDetailsType } from './oracle-details-type';
|
||||||
import { OracleMarkets } from './oracle-markets';
|
import { OracleMarkets } from './oracle-markets';
|
||||||
import { OracleSigners } from './oracle-signers';
|
import { OracleSigners } from './oracle-signers';
|
||||||
import OracleLink from '../../../components/links/oracle-link/oracle-link';
|
import { OracleEthSource } from './oracle-eth-source';
|
||||||
|
import Hash from '../../../components/links/hash';
|
||||||
|
import { getStatusString } from '../../../components/links/oracle-link/oracle-link';
|
||||||
|
|
||||||
export type SourceType =
|
export type SourceType =
|
||||||
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
|
ExplorerOracleDataSourceFragment['dataSourceSpec']['spec']['data']['sourceType'];
|
||||||
@@ -38,10 +40,8 @@ export const OracleDetails = ({
|
|||||||
id,
|
id,
|
||||||
dataSource,
|
dataSource,
|
||||||
dataConnection,
|
dataConnection,
|
||||||
showBroadcasts = false,
|
|
||||||
}: OracleDetailsProps) => {
|
}: OracleDetailsProps) => {
|
||||||
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
|
const sourceType = dataSource.dataSourceSpec.spec.data.sourceType;
|
||||||
const reportsCount: number = dataConnection.edges?.length || 0;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -49,23 +49,27 @@ export const OracleDetails = ({
|
|||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableHeader scope="row">{t('ID')}</TableHeader>
|
<TableHeader scope="row">{t('ID')}</TableHeader>
|
||||||
<TableCell modifier="bordered">
|
<TableCell modifier="bordered">
|
||||||
<OracleLink id={id} />
|
<Hash text={id} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<OracleDetailsType sourceType={sourceType} />
|
<OracleDetailsType sourceType={sourceType} />
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableHeader scope="row">{t('Status')}</TableHeader>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
{getStatusString(dataSource.dataSourceSpec.spec.status)}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
<OracleSigners sourceType={sourceType} />
|
<OracleSigners sourceType={sourceType} />
|
||||||
|
<OracleEthSource sourceType={sourceType} />
|
||||||
<OracleMarkets id={id} />
|
<OracleMarkets id={id} />
|
||||||
<TableRow modifier="bordered">
|
<TableRow modifier="bordered">
|
||||||
<TableHeader scope="row">{t('Matched data')}</TableHeader>
|
<TableHeader scope="row">{t('Filter')}</TableHeader>
|
||||||
<TableCell modifier="bordered">
|
<TableCell modifier="bordered">
|
||||||
{showBroadcasts ? reportsCount : reportsCount > 0 ? '✅' : '❌'}
|
<OracleFilter data={dataSource} />
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
<OracleFilter data={dataSource} />
|
{dataConnection ? <OracleData data={dataConnection} /> : null}
|
||||||
{showBroadcasts && dataConnection ? (
|
|
||||||
<OracleData data={dataConnection} />
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,28 @@
|
|||||||
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import compact from 'lodash/compact';
|
||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { RouteTitle } from '../../../components/route-title';
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
|
|
||||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||||
import { OracleDetails } from '../components/oracle';
|
|
||||||
import { useScrollToLocation } from '../../../hooks/scroll-to-location';
|
import { useScrollToLocation } from '../../../hooks/scroll-to-location';
|
||||||
import filter from 'recursive-key-filter';
|
import { useExplorerOracleFormMarketsQuery } from '../__generated__/OraclesForMarkets';
|
||||||
|
import { MarketLink } from '../../../components/links';
|
||||||
|
import { OracleLink } from '../../../components/links/oracle-link/oracle-link';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { MarketStateMapping } from '@vegaprotocol/types';
|
||||||
|
import type { MarketState } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
const cellSpacing = 'px-3';
|
||||||
|
|
||||||
const Oracles = () => {
|
const Oracles = () => {
|
||||||
const { data, loading, error } = useExplorerOracleSpecsQuery({
|
const { data, loading, error } = useExplorerOracleFormMarketsQuery({
|
||||||
errorPolicy: 'ignore',
|
errorPolicy: 'ignore',
|
||||||
});
|
});
|
||||||
|
|
||||||
useDocumentTitle(['Oracles']);
|
useDocumentTitle(['Oracles']);
|
||||||
useScrollToLocation();
|
useScrollToLocation();
|
||||||
|
|
||||||
|
const [hoveredOracle, setHoveredOracle] = useState('');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
|
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
|
||||||
@@ -30,36 +38,148 @@ const Oracles = () => {
|
|||||||
data.oracleSpecsConnection.edges?.length === 0
|
data.oracleSpecsConnection.edges?.length === 0
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{data?.oracleSpecsConnection?.edges
|
<table className="text-left">
|
||||||
? data.oracleSpecsConnection.edges.map((o) => {
|
<thead>
|
||||||
const id = o?.node.dataSourceSpec.spec.id;
|
<tr>
|
||||||
if (!id) {
|
<th className={cellSpacing}>Market</th>
|
||||||
return null;
|
<th className={cellSpacing}>Type</th>
|
||||||
}
|
<th className={cellSpacing}>State</th>
|
||||||
|
<th className={cellSpacing}>Settlement</th>
|
||||||
|
<th className={cellSpacing}>Termination</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data?.marketsConnection?.edges
|
||||||
|
? data.marketsConnection.edges.map((o) => {
|
||||||
|
let hasSeenOracleReports = false;
|
||||||
|
let settlementOracle = '-';
|
||||||
|
let settlementOracleStatus = '-';
|
||||||
|
let terminationOracle = '-';
|
||||||
|
let terminationOracleStatus = '-';
|
||||||
|
|
||||||
const dataConnection = o?.node.dataConnection;
|
const id = o?.node.id;
|
||||||
|
if (!id) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
if (
|
||||||
<div
|
o.node.tradableInstrument.instrument.product.__typename ===
|
||||||
id={id}
|
'Future'
|
||||||
key={id}
|
) {
|
||||||
className="mb-10"
|
settlementOracle =
|
||||||
data-testid="oracle-details"
|
o.node.tradableInstrument.instrument.product
|
||||||
>
|
.dataSourceSpecForSettlementData.id;
|
||||||
<OracleDetails
|
terminationOracle =
|
||||||
id={id}
|
o.node.tradableInstrument.instrument.product
|
||||||
dataSource={o?.node}
|
.dataSourceSpecForTradingTermination.id;
|
||||||
dataConnection={dataConnection}
|
settlementOracleStatus =
|
||||||
showBroadcasts={false}
|
o.node.tradableInstrument.instrument.product
|
||||||
/>
|
.dataSourceSpecForSettlementData.status;
|
||||||
<details>
|
terminationOracleStatus =
|
||||||
<summary className="pointer">JSON</summary>
|
o.node.tradableInstrument.instrument.product
|
||||||
<SyntaxHighlighter data={filter(o, ['__typename'])} />
|
.dataSourceSpecForTradingTermination.status;
|
||||||
</details>
|
} else if (
|
||||||
</div>
|
o.node.tradableInstrument.instrument.product.__typename ===
|
||||||
);
|
'Perpetual'
|
||||||
})
|
) {
|
||||||
: null}
|
settlementOracle =
|
||||||
|
o.node.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementData.id;
|
||||||
|
terminationOracle =
|
||||||
|
o.node.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementSchedule.id;
|
||||||
|
settlementOracleStatus =
|
||||||
|
o.node.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementData.status;
|
||||||
|
terminationOracleStatus =
|
||||||
|
o.node.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementSchedule.status;
|
||||||
|
}
|
||||||
|
const oracleInformationUnfiltered =
|
||||||
|
data?.oracleSpecsConnection?.edges?.map((e) =>
|
||||||
|
e && e.node ? e.node : undefined
|
||||||
|
) || [];
|
||||||
|
|
||||||
|
const oracleInformation = compact(oracleInformationUnfiltered)
|
||||||
|
.filter(
|
||||||
|
(o) =>
|
||||||
|
o.dataConnection.edges &&
|
||||||
|
o.dataConnection.edges.length > 0 &&
|
||||||
|
(o.dataSourceSpec.spec.id === settlementOracle ||
|
||||||
|
o.dataSourceSpec.spec.id === terminationOracle)
|
||||||
|
)
|
||||||
|
.at(0);
|
||||||
|
if (oracleInformation) {
|
||||||
|
hasSeenOracleReports = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const oracleList = `${settlementOracle} ${terminationOracle}`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr
|
||||||
|
id={id}
|
||||||
|
key={id}
|
||||||
|
className={
|
||||||
|
hoveredOracle.length > 0 &&
|
||||||
|
oracleList.indexOf(hoveredOracle) > -1
|
||||||
|
? 'bg-gray-100 dark:bg-gray-800'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
data-testid="oracle-details"
|
||||||
|
data-oracles={oracleList}
|
||||||
|
>
|
||||||
|
<td className={cellSpacing}>
|
||||||
|
<MarketLink id={id} />
|
||||||
|
</td>
|
||||||
|
<td className={cellSpacing}>
|
||||||
|
{
|
||||||
|
o.node.tradableInstrument.instrument.product
|
||||||
|
.__typename
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td className={cellSpacing}>
|
||||||
|
{MarketStateMapping[o.node.state as MarketState]}
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={
|
||||||
|
hoveredOracle.length > 0 &&
|
||||||
|
hoveredOracle === settlementOracle
|
||||||
|
? `indent-1 ${cellSpacing}`
|
||||||
|
: cellSpacing
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<OracleLink
|
||||||
|
id={settlementOracle}
|
||||||
|
status={settlementOracleStatus}
|
||||||
|
hasSeenOracleReports={hasSeenOracleReports}
|
||||||
|
onMouseOver={() => setHoveredOracle(settlementOracle)}
|
||||||
|
onMouseOut={() => setHoveredOracle('')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td
|
||||||
|
className={
|
||||||
|
hoveredOracle.length > 0 &&
|
||||||
|
hoveredOracle === terminationOracle
|
||||||
|
? `indent-1 ${cellSpacing}`
|
||||||
|
: cellSpacing
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<OracleLink
|
||||||
|
id={terminationOracle}
|
||||||
|
status={terminationOracleStatus}
|
||||||
|
hasSeenOracleReports={hasSeenOracleReports}
|
||||||
|
onMouseOver={() =>
|
||||||
|
setHoveredOracle(terminationOracle)
|
||||||
|
}
|
||||||
|
onMouseOut={() => setHoveredOracle('')}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})
|
||||||
|
: null}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -40,10 +40,9 @@ export const Oracle = () => {
|
|||||||
id={id || ''}
|
id={id || ''}
|
||||||
dataSource={data?.oracleSpec}
|
dataSource={data?.oracleSpec}
|
||||||
dataConnection={data?.oracleSpec.dataConnection}
|
dataConnection={data?.oracleSpec.dataConnection}
|
||||||
showBroadcasts={true}
|
|
||||||
/>
|
/>
|
||||||
<details>
|
<details className="mt-5 cursor-pointer">
|
||||||
<summary className="pointer">JSON</summary>
|
<summary>JSON</summary>
|
||||||
<SyntaxHighlighter data={filter(data, ['__typename'])} />
|
<SyntaxHighlighter data={filter(data, ['__typename'])} />
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
import { BackgroundVideo } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const RestrictedPage = () => {
|
||||||
|
const errorTitle = '451 Unavailable';
|
||||||
|
const errorMessage =
|
||||||
|
'Due to uncertainty about the legal and regulatory status of the content hosted on this site, it is not available to visitors in your jurisdiction.';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<BackgroundVideo className="brightness-50" />
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'max-w-[620px] p-2 mt-[10vh]',
|
||||||
|
'mx-auto my-0',
|
||||||
|
'antialiased text-white',
|
||||||
|
'overflow-hidden relative',
|
||||||
|
'flex flex-col gap-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div>{GHOST}</div>
|
||||||
|
<h1 className="text-[2.7rem] font-alpha calt break-words uppercase">
|
||||||
|
{errorTitle}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm mt-10 overflow-auto font-mono">
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GHOST = (
|
||||||
|
<svg
|
||||||
|
width="56"
|
||||||
|
height="85"
|
||||||
|
viewBox="0 0 150 234"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.43 93.6821L0 104.097V172.435H5.13793V106.2L14.0779 97.3247L10.43 93.6821Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
<path d="M105.328 18.521H7.70703V172.435H105.328V18.521Z" fill="white" />
|
||||||
|
<path d="M38.5364 64.6953H33.3984V69.8258H38.5364V64.6953Z" fill="black" />
|
||||||
|
<path d="M43.6731 69.8257H38.5352V74.9561H43.6731V69.8257Z" fill="black" />
|
||||||
|
<path d="M48.8098 74.9561H43.6719V80.0865H48.8098V74.9561Z" fill="black" />
|
||||||
|
<path d="M38.5364 74.9561H33.3984V80.0865H38.5364V74.9561Z" fill="black" />
|
||||||
|
<path d="M48.8098 64.6953H43.6719V69.8258H48.8098V64.6953Z" fill="black" />
|
||||||
|
<path d="M69.3606 64.6953H64.2227V69.8258H69.3606V64.6953Z" fill="black" />
|
||||||
|
<path d="M74.5012 69.8257H69.3633V74.9561H74.5012V69.8257Z" fill="black" />
|
||||||
|
<path d="M79.6379 74.9561H74.5V80.0865H79.6379V74.9561Z" fill="black" />
|
||||||
|
<path d="M69.3606 74.9561H64.2227V80.0865H69.3606V74.9561Z" fill="black" />
|
||||||
|
<path d="M79.6379 64.6953H74.5V69.8258H79.6379V64.6953Z" fill="black" />
|
||||||
|
<path d="M79.6398 90.3477H33.3984V95.4781H79.6398V90.3477Z" fill="black" />
|
||||||
|
<path d="M48.8098 172.435H43.6719V234H48.8098V172.435Z" fill="black" />
|
||||||
|
<path d="M69.3606 172.435H64.2227V234H69.3606V172.435Z" fill="black" />
|
||||||
|
<path
|
||||||
|
d="M129.87 0.00408026L92.0273 14.3682L111.195 64.7203L149.038 50.3562L129.87 0.00408026Z"
|
||||||
|
fill="#FF0081"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M108.565 21.7836L103.762 23.6064L105.587 28.4024L110.39 26.5795L108.565 21.7836Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M115.189 24.7596L110.387 26.583L112.213 31.3785L117.015 29.5551L115.189 24.7596Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M121.826 27.7348L117.023 29.5576L118.849 34.3536L123.652 32.5307L121.826 27.7348Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M128.447 30.7059L123.645 32.5293L125.471 37.3247L130.273 35.5014L128.447 30.7059Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M135.084 33.6854L130.281 35.5083L132.107 40.3043L136.91 38.4814L135.084 33.6854Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M124.799 21.1156L119.996 22.939L121.822 27.7344L126.625 25.911L124.799 21.1156Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M127.775 14.492L122.973 16.3154L124.799 21.1109L129.601 19.2875L127.775 14.492Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M118.838 34.3499L114.035 36.1733L115.861 40.9688L120.664 39.1454L118.838 34.3499Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M115.857 40.9691L111.055 42.7925L112.881 47.5879L117.683 45.7645L115.857 40.9691Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M129.938 52.3822L139.906 91.5276L100.703 101.429L101.988 106.406L146.174 95.2215L134.922 51.0996L129.938 52.3822Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default RestrictedPage;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
export const Routes = {
|
export const Routes = {
|
||||||
HOME: '/',
|
HOME: '/',
|
||||||
|
RESTRICTED: '/restricted',
|
||||||
TX: 'txs',
|
TX: 'txs',
|
||||||
BLOCKS: 'blocks',
|
BLOCKS: 'blocks',
|
||||||
PARTIES: 'parties',
|
PARTIES: 'parties',
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import { remove0x } from '@vegaprotocol/utils';
|
|||||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||||
import { Disclaimer } from './pages/disclaimer';
|
import { Disclaimer } from './pages/disclaimer';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
import RestrictedPage from './restricted';
|
||||||
|
|
||||||
export type Navigable = {
|
export type Navigable = {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -356,6 +357,14 @@ export const routerConfig: Route[] = [
|
|||||||
...validators,
|
...validators,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: Routes.RESTRICTED,
|
||||||
|
element: <RestrictedPage />,
|
||||||
|
handle: {
|
||||||
|
name: t('Restricted'),
|
||||||
|
text: t('Restricted'),
|
||||||
|
},
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const router = createBrowserRouter(routerConfig);
|
export const router = createBrowserRouter(routerConfig);
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ export interface BlockExplorerTransactionResult {
|
|||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
error?: string;
|
error?: string;
|
||||||
|
// These aren't strictly optional but are new in 0.73.0 so we need to make them optional
|
||||||
|
createdAt?: string;
|
||||||
|
version?: string;
|
||||||
|
pow?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BlockExplorerTransactions {
|
export interface BlockExplorerTransactions {
|
||||||
|
|||||||
+670
-247
File diff suppressed because it is too large
Load Diff
@@ -537,10 +537,10 @@ describe(
|
|||||||
cy.VegaWalletSubmitProposal(createGovernanceTransferProposalTxBody());
|
cy.VegaWalletSubmitProposal(createGovernanceTransferProposalTxBody());
|
||||||
cy.reload();
|
cy.reload();
|
||||||
getProposalFromTitle('Governance transfer proposal').within(() => {
|
getProposalFromTitle('Governance transfer proposal').within(() => {
|
||||||
cy.getByTestId(marketProposalType).should('have.text', 'NewTransfer');
|
cy.getByTestId(marketProposalType).should('have.text', 'New transfer');
|
||||||
cy.getByTestId(viewProposalButton).click();
|
cy.getByTestId(viewProposalButton).click();
|
||||||
});
|
});
|
||||||
cy.getByTestId(marketProposalType).should('have.text', 'NewTransfer');
|
cy.getByTestId(marketProposalType).should('have.text', 'New transfer');
|
||||||
cy.getByTestId(governanceTransferToggle).click();
|
cy.getByTestId(governanceTransferToggle).click();
|
||||||
cy.getByTestId('proposal-transfer-details-table').within(() => {
|
cy.getByTestId('proposal-transfer-details-table').within(() => {
|
||||||
getProposalInformationFromTable('Source Type')
|
getProposalInformationFromTable('Source Type')
|
||||||
@@ -590,7 +590,7 @@ describe(
|
|||||||
);
|
);
|
||||||
cy.getByTestId(viewProposalButton).click();
|
cy.getByTestId(viewProposalButton).click();
|
||||||
});
|
});
|
||||||
cy.getByTestId(marketProposalType).should('have.text', 'CancelTransfer');
|
cy.getByTestId(marketProposalType).should('have.text', 'Cancel transfer');
|
||||||
getProposalInformationFromTable('Error details')
|
getProposalInformationFromTable('Error details')
|
||||||
.invoke('text')
|
.invoke('text')
|
||||||
.and('eq', 'Governance transfer invalid transfer id not found');
|
.and('eq', 'Governance transfer invalid transfer id not found');
|
||||||
|
|||||||
@@ -38,10 +38,10 @@ import {
|
|||||||
NetworkLoader,
|
NetworkLoader,
|
||||||
useInitializeEnv,
|
useInitializeEnv,
|
||||||
NodeGuard,
|
NodeGuard,
|
||||||
AppFailure,
|
|
||||||
NodeSwitcherDialog,
|
NodeSwitcherDialog,
|
||||||
useNodeSwitcherStore,
|
useNodeSwitcherStore,
|
||||||
DocsLinks,
|
DocsLinks,
|
||||||
|
NodeFailure,
|
||||||
} from '@vegaprotocol/environment';
|
} from '@vegaprotocol/environment';
|
||||||
import { ENV } from './config';
|
import { ENV } from './config';
|
||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||||
@@ -106,6 +106,7 @@ const Web3Container = ({
|
|||||||
useEthWithdrawApprovalsManager();
|
useEthWithdrawApprovalsManager();
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||||
store.connectors,
|
store.connectors,
|
||||||
store.initialize,
|
store.initialize,
|
||||||
@@ -184,7 +185,7 @@ const Web3Container = ({
|
|||||||
<TemplateSidebar sidebar={sideBar}>
|
<TemplateSidebar sidebar={sideBar}>
|
||||||
<AppRouter />
|
<AppRouter />
|
||||||
</TemplateSidebar>
|
</TemplateSidebar>
|
||||||
<footer className="p-4 border-t border-neutral-700 break-all">
|
<footer className="p-4 break-all border-t border-neutral-700">
|
||||||
<NetworkInfo />
|
<NetworkInfo />
|
||||||
</footer>
|
</footer>
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
@@ -239,6 +240,9 @@ const AppContainer = () => {
|
|||||||
store.setDialogOpen,
|
store.setDialogOpen,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// Hacky skip all the loading & web3 init for geo restricted users
|
||||||
|
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ENV.dsn && telemetryOn === 'true') {
|
if (ENV.dsn && telemetryOn === 'true') {
|
||||||
Sentry.init({
|
Sentry.init({
|
||||||
@@ -304,6 +308,14 @@ const AppContainer = () => {
|
|||||||
}
|
}
|
||||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||||
|
|
||||||
|
if (isRestricted) {
|
||||||
|
return (
|
||||||
|
<Router>
|
||||||
|
<AppRouter />
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
@@ -312,7 +324,7 @@ const AppContainer = () => {
|
|||||||
<NodeGuard
|
<NodeGuard
|
||||||
skeleton={<div>{t('Loading')}</div>}
|
skeleton={<div>{t('Loading')}</div>}
|
||||||
failure={
|
failure={
|
||||||
<AppFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
|
<NodeFailure title={t('NodeUnsuitable', { url: VEGA_URL })} />
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<AsyncRenderer<EthereumConfig | null>
|
<AsyncRenderer<EthereumConfig | null>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { ENV } from '../../config';
|
|||||||
|
|
||||||
import noIcon from '../../images/token-no-icon.png';
|
import noIcon from '../../images/token-no-icon.png';
|
||||||
import vegaBlack from '../../images/vega_black.png';
|
import vegaBlack from '../../images/vega_black.png';
|
||||||
|
import vegaVesting from '../../images/vega_vesting.png';
|
||||||
import { BigNumber } from '../../lib/bignumber';
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
import type { WalletCardAssetProps } from '../wallet-card';
|
import type { WalletCardAssetProps } from '../wallet-card';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
@@ -102,7 +103,10 @@ export const usePollForDelegations = () => {
|
|||||||
setAccounts(
|
setAccounts(
|
||||||
accounts
|
accounts
|
||||||
.filter(
|
.filter(
|
||||||
(a) => a.type === Schema.AccountType.ACCOUNT_TYPE_GENERAL
|
(a) =>
|
||||||
|
a.type === Schema.AccountType.ACCOUNT_TYPE_GENERAL ||
|
||||||
|
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
|
||||||
|
a.type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
|
||||||
)
|
)
|
||||||
.map((a) => {
|
.map((a) => {
|
||||||
const isVega =
|
const isVega =
|
||||||
@@ -115,14 +119,23 @@ export const usePollForDelegations = () => {
|
|||||||
subheading: isVega ? t('collateral') : a.asset.symbol,
|
subheading: isVega ? t('collateral') : a.asset.symbol,
|
||||||
symbol: a.asset.symbol,
|
symbol: a.asset.symbol,
|
||||||
decimals: a.asset.decimals,
|
decimals: a.asset.decimals,
|
||||||
|
assetId: a.asset.id,
|
||||||
balance: new BigNumber(
|
balance: new BigNumber(
|
||||||
addDecimal(a.balance, a.asset.decimals)
|
addDecimal(a.balance, a.asset.decimals)
|
||||||
),
|
),
|
||||||
image: isVega ? vegaBlack : noIcon,
|
image: isVega
|
||||||
|
? vegaBlack
|
||||||
|
: a.type ===
|
||||||
|
Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS ||
|
||||||
|
a.type ===
|
||||||
|
Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS
|
||||||
|
? vegaVesting
|
||||||
|
: noIcon,
|
||||||
border: isVega,
|
border: isVega,
|
||||||
address: isAssetTypeERC20(a.asset)
|
address: isAssetTypeERC20(a.asset)
|
||||||
? a.asset.source.contractAddress
|
? a.asset.source.contractAddress
|
||||||
: undefined,
|
: undefined,
|
||||||
|
type: a.type,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
import React from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import { useAnimateValue } from '../../hooks/use-animate-value';
|
import { useAnimateValue } from '../../hooks/use-animate-value';
|
||||||
import type { BigNumber } from '../../lib/bignumber';
|
import type { BigNumber } from '../../lib/bignumber';
|
||||||
import { useNumberParts } from '@vegaprotocol/react-helpers';
|
import { useNumberParts } from '@vegaprotocol/react-helpers';
|
||||||
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AnchorButton, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
CONSOLE_TRANSFER_ASSET,
|
||||||
|
DApp,
|
||||||
|
useLinks,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
import { useNetworkParam } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
interface WalletCardProps {
|
interface WalletCardProps {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
@@ -100,8 +109,10 @@ export interface WalletCardAssetProps {
|
|||||||
symbol: string;
|
symbol: string;
|
||||||
balance: BigNumber;
|
balance: BigNumber;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
|
assetId?: string;
|
||||||
border?: boolean;
|
border?: boolean;
|
||||||
subheading?: string;
|
subheading?: string;
|
||||||
|
type?: Schema.AccountType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const WalletCardAsset = ({
|
export const WalletCardAsset = ({
|
||||||
@@ -110,16 +121,37 @@ export const WalletCardAsset = ({
|
|||||||
symbol,
|
symbol,
|
||||||
balance,
|
balance,
|
||||||
decimals,
|
decimals,
|
||||||
|
assetId,
|
||||||
border,
|
border,
|
||||||
subheading,
|
subheading,
|
||||||
|
type,
|
||||||
}: WalletCardAssetProps) => {
|
}: WalletCardAssetProps) => {
|
||||||
const [integers, decimalsPlaces, separator] = useNumberParts(
|
const [integers, decimalsPlaces, separator] = useNumberParts(
|
||||||
balance,
|
balance,
|
||||||
decimals
|
decimals
|
||||||
);
|
);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const consoleLink = useLinks(DApp.Console);
|
||||||
|
const transferAssetLink = (assetId: string) =>
|
||||||
|
consoleLink(CONSOLE_TRANSFER_ASSET.replace(':assetId', assetId));
|
||||||
|
const { param: baseRate } = useNetworkParam('rewards_vesting_baseRate');
|
||||||
|
|
||||||
|
const isRedeemable =
|
||||||
|
type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS && assetId;
|
||||||
|
|
||||||
|
const accountTypeTooltip = useMemo(() => {
|
||||||
|
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTED_REWARDS) {
|
||||||
|
return t('VestedRewardsTooltip');
|
||||||
|
}
|
||||||
|
if (type === Schema.AccountType.ACCOUNT_TYPE_VESTING_REWARDS && baseRate) {
|
||||||
|
return t('VestingRewardsTooltip', { baseRate });
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}, [baseRate, t, type]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-nowrap mt-2 mb-4">
|
<div className="flex flex-nowrap gap-2 mt-2 mb-4">
|
||||||
<img
|
<img
|
||||||
alt="Vega"
|
alt="Vega"
|
||||||
src={image}
|
src={image}
|
||||||
@@ -129,15 +161,37 @@ export const WalletCardAsset = ({
|
|||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
className="flex align-center text-base"
|
className="flex align-center items-baseline text-base gap-2"
|
||||||
data-testid="currency-title"
|
data-testid="currency-title"
|
||||||
>
|
>
|
||||||
<div className="mb-0 px-2 uppercase">{name}</div>
|
<div className="mb-0 uppercase">{name}</div>
|
||||||
<div className="mb-0 uppercase text-neutral-400">
|
<div className="mb-0 uppercase text-neutral-400">
|
||||||
{subheading || symbol}
|
{subheading || symbol}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="px-2 basis-full font-mono" data-testid="currency-value">
|
{type ? (
|
||||||
|
<div className="mb-[2px] flex gap-2 items-baseline">
|
||||||
|
<Tooltip description={accountTypeTooltip}>
|
||||||
|
<span className="px-2 py-1 leading-none text-xs bg-vega-cdark-700 rounded">
|
||||||
|
{Schema.AccountTypeMapping[type]}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{isRedeemable ? (
|
||||||
|
<Tooltip description={t('RedeemRewardsTooltip')}>
|
||||||
|
<AnchorButton
|
||||||
|
variant="primary"
|
||||||
|
size="xs"
|
||||||
|
href={transferAssetLink(assetId)}
|
||||||
|
target="_blank"
|
||||||
|
className="px-2 py-1 leading-none text-xs bg-vega-yellow text-black rounded"
|
||||||
|
>
|
||||||
|
{t('Redeem')}
|
||||||
|
</AnchorButton>
|
||||||
|
</Tooltip>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<div className="basis-full font-mono" data-testid="currency-value">
|
||||||
<span>
|
<span>
|
||||||
{integers}
|
{integers}
|
||||||
{separator}
|
{separator}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"pageTitleRedemptionTranche": "Redeem from Tranche",
|
"pageTitleRedemptionTranche": "Redeem from Tranche",
|
||||||
"pageTitleTranches": "Vesting tranches",
|
"pageTitleTranches": "Vesting tranches",
|
||||||
"pageTitle404": "Page not found",
|
"pageTitle404": "Page not found",
|
||||||
|
"pageTitle451": "451 unavailable",
|
||||||
"pageTitleNotPermitted": "Can not proceed!",
|
"pageTitleNotPermitted": "Can not proceed!",
|
||||||
"pageTitleDisassociate": "Disassociate $VEGA tokens from a Vega key",
|
"pageTitleDisassociate": "Disassociate $VEGA tokens from a Vega key",
|
||||||
"pageTitleProposals": "Proposals",
|
"pageTitleProposals": "Proposals",
|
||||||
@@ -718,6 +719,8 @@
|
|||||||
"UpdateAssetProposal": "Update asset proposal",
|
"UpdateAssetProposal": "Update asset proposal",
|
||||||
"NewFreeformProposal": "New freeform proposal",
|
"NewFreeformProposal": "New freeform proposal",
|
||||||
"NewRawProposal": "New proposal",
|
"NewRawProposal": "New proposal",
|
||||||
|
"NewTransferProposal": "New transfer proposal",
|
||||||
|
"CancelTransferProposal": "Cancel transfer proposal",
|
||||||
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
|
"MARKET_STATE_UPDATE_TYPE_RESUME": "Resume market",
|
||||||
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
|
"MARKET_STATE_UPDATE_TYPE_SUSPEND": "Suspend market",
|
||||||
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
|
"MARKET_STATE_UPDATE_TYPE_TERMINATE": "Terminate market",
|
||||||
@@ -738,6 +741,8 @@
|
|||||||
"UpdateVolumeDiscountProgram": "Update volume discount program",
|
"UpdateVolumeDiscountProgram": "Update volume discount program",
|
||||||
"NewAsset": "New asset",
|
"NewAsset": "New asset",
|
||||||
"UpdateAsset": "Update asset",
|
"UpdateAsset": "Update asset",
|
||||||
|
"NewTransfer": "New transfer",
|
||||||
|
"CancelTransfer": "Cancel transfer",
|
||||||
"AssetID": "Asset ID",
|
"AssetID": "Asset ID",
|
||||||
"Freeform": "Freeform",
|
"Freeform": "Freeform",
|
||||||
"RawProposal": "Let me choose (raw proposal)",
|
"RawProposal": "Let me choose (raw proposal)",
|
||||||
@@ -904,6 +909,14 @@
|
|||||||
"BenefitTierReferralDiscountFactorDescription": "The proportion of the referee's taker fees to be discounted",
|
"BenefitTierReferralDiscountFactorDescription": "The proportion of the referee's taker fees to be discounted",
|
||||||
"BenefitTierReferralRewardFactor": "Referral reward factor",
|
"BenefitTierReferralRewardFactor": "Referral reward factor",
|
||||||
"BenefitTierReferralRewardFactorDescription": "The proportion of the referee's taker fees to be rewarded to the referrer",
|
"BenefitTierReferralRewardFactorDescription": "The proportion of the referee's taker fees to be rewarded to the referrer",
|
||||||
|
"BenefitTierMinimumActivityStreak": "Minimum activity streak",
|
||||||
|
"BenefitTierMinimumActivityStreakDescription": "The minimum number of times the party needs to have completed the activity",
|
||||||
|
"BenefitTierMinimumQuantumBalance": "Minimum quantum balance",
|
||||||
|
"BenefitTierMinimumQuantumBalanceDescription": "The minimum amount of the vesting token to qualify",
|
||||||
|
"BenefitTierVestingMultiplier": "Vesting multiplier",
|
||||||
|
"BenefitTierVestingMultiplierDescription": "Vesting multiplier for the tier",
|
||||||
|
"BenefitTierRewardMultiplier": "Reward multiplier",
|
||||||
|
"BenefitTierRewardMultiplierDescription": "The multiplier",
|
||||||
"StakingTiers": "Staking tiers",
|
"StakingTiers": "Staking tiers",
|
||||||
"StakingTierMinimumStakedTokens": "Minimum staked tokens",
|
"StakingTierMinimumStakedTokens": "Minimum staked tokens",
|
||||||
"StakingTierMinimumStakedTokensDescription": "Required number of governance tokens ($VEGA) a referrer must have staked to receive the multiplier",
|
"StakingTierMinimumStakedTokensDescription": "Required number of governance tokens ($VEGA) a referrer must have staked to receive the multiplier",
|
||||||
@@ -914,5 +927,34 @@
|
|||||||
"EndOfProgramTimestamp": "End of program",
|
"EndOfProgramTimestamp": "End of program",
|
||||||
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
|
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
|
||||||
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
|
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
|
||||||
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier"
|
"BenefitTierVolumeDiscountFactorDescription": "Discount given to those in this benefit tier",
|
||||||
|
"ACCOUNT_TYPE_INSURANCE": "Insurance account",
|
||||||
|
"ACCOUNT_TYPE_GLOBAL_INSURANCE": "Global insurance account",
|
||||||
|
"ACCOUNT_TYPE_SETTLEMENT": "Settlement account",
|
||||||
|
"ACCOUNT_TYPE_MARGIN": "Margin account ",
|
||||||
|
"ACCOUNT_TYPE_GENERAL": "General account",
|
||||||
|
"ACCOUNT_TYPE_FEES_INFRASTRUCTURE": "Infrastructure fees account",
|
||||||
|
"ACCOUNT_TYPE_FEES_LIQUIDITY": "Liquidity fees account",
|
||||||
|
"ACCOUNT_TYPE_FEES_MAKER": "Maker fees account",
|
||||||
|
"ACCOUNT_TYPE_BOND": "Bond account",
|
||||||
|
"ACCOUNT_TYPE_EXTERNAL": "External account",
|
||||||
|
"ACCOUNT_TYPE_GLOBAL_REWARD": "Global reward account",
|
||||||
|
"ACCOUNT_TYPE_PENDING_TRANSFERS": "Pending transfers account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES": "Maker paid fees reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES": "Maker received fees reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES": "Liquidity provider received fees reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS": "Market proposers reward account",
|
||||||
|
"ACCOUNT_TYPE_HOLDING": "Holding account",
|
||||||
|
"ACCOUNT_TYPE_LP_LIQUIDITY_FEES": "Liquidity provider fees account",
|
||||||
|
"ACCOUNT_TYPE_NETWORK_TREASURY": "Network treasury account",
|
||||||
|
"ACCOUNT_TYPE_VESTING_REWARDS": "Vesting rewards account",
|
||||||
|
"ACCOUNT_TYPE_VESTED_REWARDS": "Vested rewards account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_AVERAGE_POSITION": "Average position reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_RELATIVE_RETURN": "Relative return reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY": "Return volatility reward account",
|
||||||
|
"ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING": "Validator ranking reward account",
|
||||||
|
"ACCOUNT_TYPE_PENDING_FEE_REFERRAL_REWARD": "Pending fee referral reward account",
|
||||||
|
"VestingRewardsTooltip": "Vesting rewards will be moved to vested account at a rate of {{baseRate}} per epoch.",
|
||||||
|
"VestedRewardsTooltip": "Vested rewards can be redeemed using Console",
|
||||||
|
"RedeemRewardsTooltip": "Click to redeem vested rewards in Console"
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-3
@@ -12,18 +12,19 @@ import {
|
|||||||
generateProposal,
|
generateProposal,
|
||||||
generateYesVotes,
|
generateYesVotes,
|
||||||
} from '../../test-helpers/generate-proposals';
|
} from '../../test-helpers/generate-proposals';
|
||||||
import { ProposalHeader } from './proposal-header';
|
import { ProposalHeader, NewTransferSummary } from './proposal-header';
|
||||||
import {
|
import {
|
||||||
lastWeek,
|
lastWeek,
|
||||||
nextWeek,
|
nextWeek,
|
||||||
mockWalletContext,
|
mockWalletContext,
|
||||||
createUserVoteQueryMock,
|
createUserVoteQueryMock,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|
||||||
import type { MockedResponse } from '@apollo/client/testing';
|
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { VoteState } from '../vote-details/use-user-vote';
|
import { VoteState } from '../vote-details/use-user-vote';
|
||||||
|
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/proposals', () => ({
|
jest.mock('@vegaprotocol/proposals', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/proposals'),
|
...jest.requireActual('@vegaprotocol/proposals'),
|
||||||
@@ -31,6 +32,7 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
|||||||
code: 'PARENT_CODE',
|
code: 'PARENT_CODE',
|
||||||
parentMarketId: 'PARENT_ID',
|
parentMarketId: 'PARENT_ID',
|
||||||
}),
|
}),
|
||||||
|
useNewTransferProposalDetails: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const renderComponent = (
|
const renderComponent = (
|
||||||
@@ -418,3 +420,78 @@ describe('Proposal header', () => {
|
|||||||
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/proposals');
|
||||||
|
|
||||||
|
describe('<NewTransferSummary />', () => {
|
||||||
|
it('renders null if no details are provided', () => {
|
||||||
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue(null);
|
||||||
|
const { container } = render(<NewTransferSummary proposalId="1" />);
|
||||||
|
expect(container.firstChild).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles OneOffGovernanceTransfer', () => {
|
||||||
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue({
|
||||||
|
kind: { __typename: 'OneOffGovernanceTransfer', deliverOn: null },
|
||||||
|
source: '0x123',
|
||||||
|
sourceType: 'wallet',
|
||||||
|
destination: '0x456',
|
||||||
|
destinationType: 'contract',
|
||||||
|
});
|
||||||
|
const { getByText } = render(<NewTransferSummary proposalId="1" />);
|
||||||
|
const textMatch = (content: string) => content.includes('One off transfer');
|
||||||
|
expect(getByText(textMatch)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles RecurringGovernanceTransfer', () => {
|
||||||
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue({
|
||||||
|
kind: {
|
||||||
|
__typename: 'RecurringGovernanceTransfer',
|
||||||
|
startEpoch: 1,
|
||||||
|
endEpoch: 5,
|
||||||
|
},
|
||||||
|
source: '0x123',
|
||||||
|
sourceType: 'wallet',
|
||||||
|
destination: '0x456',
|
||||||
|
destinationType: 'contract',
|
||||||
|
});
|
||||||
|
const { getByText } = render(<NewTransferSummary proposalId="1" />);
|
||||||
|
const textMatch = (content: string) =>
|
||||||
|
content.includes('Recurring transfer');
|
||||||
|
expect(getByText(textMatch)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to translated sourceType when source is not set', () => {
|
||||||
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue({
|
||||||
|
kind: {
|
||||||
|
__typename: 'RecurringGovernanceTransfer',
|
||||||
|
startEpoch: 1,
|
||||||
|
endEpoch: 5,
|
||||||
|
},
|
||||||
|
source: undefined,
|
||||||
|
sourceType: 'ACCOUNT_TYPE_GENERAL',
|
||||||
|
destination: '0x456',
|
||||||
|
destinationType: 'ACCOUNT_TYPE_GENERAL',
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<NewTransferSummary proposalId="1" />);
|
||||||
|
expect(screen.getByText('General account')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fallback to translated destinationType when destination is not set', () => {
|
||||||
|
(useNewTransferProposalDetails as jest.Mock).mockReturnValue({
|
||||||
|
kind: {
|
||||||
|
__typename: 'RecurringGovernanceTransfer',
|
||||||
|
startEpoch: 1,
|
||||||
|
endEpoch: 5,
|
||||||
|
},
|
||||||
|
source: '0x123',
|
||||||
|
sourceType: 'ACCOUNT_TYPE_GENERAL',
|
||||||
|
destination: undefined,
|
||||||
|
destinationType: 'ACCOUNT_TYPE_GLOBAL_INSURANCE',
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<NewTransferSummary proposalId="1" />);
|
||||||
|
expect(screen.getByText('Global insurance account')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+23
-5
@@ -237,7 +237,11 @@ export const ProposalHeader = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
|
export const SuccessorCode = ({
|
||||||
|
proposalId,
|
||||||
|
}: {
|
||||||
|
proposalId?: string | null;
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const successor = useSuccessorMarketProposalDetails(proposalId);
|
const successor = useSuccessorMarketProposalDetails(proposalId);
|
||||||
|
|
||||||
@@ -254,7 +258,11 @@ const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
|
|||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
|
export const NewTransferSummary = ({
|
||||||
|
proposalId,
|
||||||
|
}: {
|
||||||
|
proposalId?: string | null;
|
||||||
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const details = useNewTransferProposalDetails(proposalId);
|
const details = useNewTransferProposalDetails(proposalId);
|
||||||
|
|
||||||
@@ -263,13 +271,23 @@ const NewTransferSummary = ({ proposalId }: { proposalId?: string | null }) => {
|
|||||||
return (
|
return (
|
||||||
<span>
|
<span>
|
||||||
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
|
{GovernanceTransferKindMapping[details.kind.__typename]}{' '}
|
||||||
{t('transfer from')} <Lozenge>{truncateMiddle(details.source)}</Lozenge>{' '}
|
{t('transfer from')}{' '}
|
||||||
{t('to')} <Lozenge>{truncateMiddle(details.destination)}</Lozenge>
|
<Lozenge>
|
||||||
|
{details.source
|
||||||
|
? truncateMiddle(details.source)
|
||||||
|
: t(details.sourceType)}
|
||||||
|
</Lozenge>{' '}
|
||||||
|
{t('to')}{' '}
|
||||||
|
<Lozenge>
|
||||||
|
{details.destination
|
||||||
|
? truncateMiddle(details.destination)
|
||||||
|
: t(details.destinationType)}
|
||||||
|
</Lozenge>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const CancelTransferSummary = ({
|
export const CancelTransferSummary = ({
|
||||||
proposalId,
|
proposalId,
|
||||||
}: {
|
}: {
|
||||||
proposalId?: string | null;
|
proposalId?: string | null;
|
||||||
|
|||||||
+39
-44
@@ -36,13 +36,13 @@ describe('ProposalReferralProgramDetails helper functions', () => {
|
|||||||
it('should format referral discount factor correctly', () => {
|
it('should format referral discount factor correctly', () => {
|
||||||
const input = '0.05';
|
const input = '0.05';
|
||||||
const formatted = formatReferralDiscountFactor(input);
|
const formatted = formatReferralDiscountFactor(input);
|
||||||
expect(formatted).toBe('5.00%');
|
expect(formatted).toBe('5%');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format referral reward factor correctly', () => {
|
it('should format referral reward factor correctly', () => {
|
||||||
const input = '0.1';
|
const input = '0.1';
|
||||||
const formatted = formatReferralRewardFactor(input);
|
const formatted = formatReferralRewardFactor(input);
|
||||||
expect(formatted).toBe('10.00%');
|
expect(formatted).toBe('10%');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should format minimum staked tokens correctly', () => {
|
it('should format minimum staked tokens correctly', () => {
|
||||||
@@ -63,44 +63,42 @@ const mockReferralProposal = generateProposal({
|
|||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {
|
||||||
__typename: 'UpdateReferralProgram',
|
__typename: 'UpdateReferralProgram',
|
||||||
changes: {
|
benefitTiers: [
|
||||||
benefitTiers: [
|
{
|
||||||
{
|
minimumEpochs: 6,
|
||||||
minimumEpochs: 6,
|
minimumRunningNotionalTakerVolume: '10000',
|
||||||
minimumRunningNotionalTakerVolume: '10000',
|
referralDiscountFactor: '0.001',
|
||||||
referralDiscountFactor: '0.001',
|
referralRewardFactor: '0.001',
|
||||||
referralRewardFactor: '0.001',
|
},
|
||||||
},
|
{
|
||||||
{
|
minimumEpochs: 24,
|
||||||
minimumEpochs: 24,
|
minimumRunningNotionalTakerVolume: '500000',
|
||||||
minimumRunningNotionalTakerVolume: '500000',
|
referralDiscountFactor: '0.005',
|
||||||
referralDiscountFactor: '0.005',
|
referralRewardFactor: '0.005',
|
||||||
referralRewardFactor: '0.005',
|
},
|
||||||
},
|
{
|
||||||
{
|
minimumEpochs: 48,
|
||||||
minimumEpochs: 48,
|
minimumRunningNotionalTakerVolume: '1000000',
|
||||||
minimumRunningNotionalTakerVolume: '1000000',
|
referralDiscountFactor: '0.01',
|
||||||
referralDiscountFactor: '0.01',
|
referralRewardFactor: '0.01',
|
||||||
referralRewardFactor: '0.01',
|
},
|
||||||
},
|
],
|
||||||
],
|
endOfProgram: '2026-10-03T10:34:34Z',
|
||||||
endOfProgramTimestamp: '2026-10-03T10:34:34Z',
|
windowLength: 3,
|
||||||
windowLength: 3,
|
stakingTiers: [
|
||||||
stakingTiers: [
|
{
|
||||||
{
|
minimumStakedTokens: '1',
|
||||||
minimumStakedTokens: '1',
|
referralRewardMultiplier: '1',
|
||||||
referralRewardMultiplier: '1',
|
},
|
||||||
},
|
{
|
||||||
{
|
minimumStakedTokens: '2',
|
||||||
minimumStakedTokens: '2',
|
referralRewardMultiplier: '2',
|
||||||
referralRewardMultiplier: '2',
|
},
|
||||||
},
|
{
|
||||||
{
|
minimumStakedTokens: '5',
|
||||||
minimumStakedTokens: '5',
|
referralRewardMultiplier: '3',
|
||||||
referralRewardMultiplier: '3',
|
},
|
||||||
},
|
],
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -130,10 +128,7 @@ describe('<ProposalReferralProgramDetails />', () => {
|
|||||||
it('should not render if there are no relevant fields', () => {
|
it('should not render if there are no relevant fields', () => {
|
||||||
const incompleteProposal = generateProposal({
|
const incompleteProposal = generateProposal({
|
||||||
terms: {
|
terms: {
|
||||||
change: {
|
change: {},
|
||||||
__typename: 'UpdateReferralProgram',
|
|
||||||
changes: {},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+4
-5
@@ -54,11 +54,10 @@ export const ProposalReferralProgramDetails = ({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const benefitTiers = proposal?.terms?.change?.changes?.benefitTiers;
|
const benefitTiers = proposal?.terms?.change?.benefitTiers;
|
||||||
const stakingTiers = proposal?.terms?.change?.changes?.stakingTiers;
|
const stakingTiers = proposal?.terms?.change?.stakingTiers;
|
||||||
const windowLength = proposal?.terms?.change?.changes?.windowLength;
|
const windowLength = proposal?.terms?.change?.windowLength;
|
||||||
const endOfProgramTimestamp =
|
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
|
||||||
proposal?.terms?.change?.changes?.endOfProgramTimestamp;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!benefitTiers &&
|
!benefitTiers &&
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-update-benefit-tiers-details';
|
||||||
+158
@@ -0,0 +1,158 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { ProposalUpdateBenefitTiers } from './proposal-update-benefit-tiers-details';
|
||||||
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
|
|
||||||
|
jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||||
|
useAppState: () => ({
|
||||||
|
appState: {
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockVestingBenefitTierProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateNetworkParameter',
|
||||||
|
networkParameter: {
|
||||||
|
key: 'blah.blah.benefitTiers',
|
||||||
|
value: JSON.stringify({
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
minimum_quantum_balance: '10000',
|
||||||
|
reward_multiplier: '0.05',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimum_quantum_balance: '500000000000',
|
||||||
|
reward_multiplier: '0.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimum_quantum_balance: '10000000000000',
|
||||||
|
reward_multiplier: '10',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockActivityStreakBenefitTierProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateNetworkParameter',
|
||||||
|
networkParameter: {
|
||||||
|
key: 'blah.blah.benefitTiers',
|
||||||
|
value: JSON.stringify({
|
||||||
|
tiers: [
|
||||||
|
{
|
||||||
|
minimum_activity_streak: '10000',
|
||||||
|
vesting_multiplier: '5',
|
||||||
|
reward_multiplier: '0.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimum_activity_streak: '10000000000000',
|
||||||
|
vesting_multiplier: '100',
|
||||||
|
reward_multiplier: '10',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
describe('ProposalUpdateBenefitTiers', () => {
|
||||||
|
it('should not render if proposal is null', () => {
|
||||||
|
render(<ProposalUpdateBenefitTiers proposal={null} />);
|
||||||
|
expect(screen.queryByTestId('proposal-update-benefit-tiers')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if __typename is not UpdateNetworkParameter', () => {
|
||||||
|
const updateMarketProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateMarket',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
render(<ProposalUpdateBenefitTiers proposal={updateMarketProposal} />);
|
||||||
|
expect(screen.queryByTestId('proposal-update-benefit-tiers')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if there are no relevant fields', () => {
|
||||||
|
const incompleteProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateNetworkParameter',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<ProposalUpdateBenefitTiers proposal={incompleteProposal} />);
|
||||||
|
expect(screen.queryByTestId('proposal-update-benefit-tiers')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if there are relevant fields that are empty', () => {
|
||||||
|
const incompleteProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateNetworkParameter',
|
||||||
|
networkParameter: {
|
||||||
|
key: 'blah.blah.benefitTiers',
|
||||||
|
value: JSON.stringify({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<ProposalUpdateBenefitTiers proposal={incompleteProposal} />);
|
||||||
|
expect(screen.queryByTestId('proposal-update-benefit-tiers')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a valid vesting benefit tier proposal', () => {
|
||||||
|
render(
|
||||||
|
<ProposalUpdateBenefitTiers proposal={mockVestingBenefitTierProposal} />
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3 tiers in the sample data
|
||||||
|
expect(screen.getByText('Tier 1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Tier 2')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Tier 3')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getAllByText('Minimum quantum balance').length).toBe(3);
|
||||||
|
expect(screen.getAllByText('Reward multiplier').length).toBe(3);
|
||||||
|
|
||||||
|
expect(screen.getByText('0.00000000000001')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('0.05x')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByText('0.0000005')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('0.1x')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByText('0.00001')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('10x')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a valid activity streak benefit tier proposal', () => {
|
||||||
|
render(
|
||||||
|
<ProposalUpdateBenefitTiers
|
||||||
|
proposal={mockActivityStreakBenefitTierProposal}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3 tiers in the sample data
|
||||||
|
expect(screen.getByText('Tier 1')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Tier 2')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getAllByText('Minimum activity streak').length).toBe(2);
|
||||||
|
expect(screen.getAllByText('Vesting multiplier').length).toBe(2);
|
||||||
|
expect(screen.getAllByText('Reward multiplier').length).toBe(2);
|
||||||
|
|
||||||
|
expect(screen.getByText('10000')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('5x')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('0.1x')).toBeInTheDocument();
|
||||||
|
|
||||||
|
expect(screen.getByText('10000000000000')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('100x')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('10x')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
+164
@@ -0,0 +1,164 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import {
|
||||||
|
KeyValueTable,
|
||||||
|
KeyValueTableRow,
|
||||||
|
RoundedWrapper,
|
||||||
|
Tooltip,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
formatMinimumStakedTokens,
|
||||||
|
formatReferralRewardMultiplier,
|
||||||
|
} from '../proposal-referral-program-details';
|
||||||
|
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
// These types are not generated as it's not known how dynamic these are
|
||||||
|
type VestingBenefitTier = {
|
||||||
|
minimum_quantum_balance: string;
|
||||||
|
reward_multiplier: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ActivityStreakBenefitTier = {
|
||||||
|
minimum_activity_streak: number;
|
||||||
|
reward_multiplier: string;
|
||||||
|
vesting_multiplier: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BenefitTiers =
|
||||||
|
| Array<ActivityStreakBenefitTier>
|
||||||
|
| Array<VestingBenefitTier>;
|
||||||
|
|
||||||
|
export function getBenefitTiers(json: string): BenefitTiers {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(json);
|
||||||
|
return parsed.tiers;
|
||||||
|
} catch (e) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatVolumeDiscountFactor = (value: string) => {
|
||||||
|
return formatNumberPercentage(new BigNumber(value).times(100));
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ProposalReferralProgramDetailsProps {
|
||||||
|
proposal: ProposalQuery['proposal'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special rendered for network proposals that change any benefit tiers,
|
||||||
|
* which is detected by:
|
||||||
|
* 1) it being a network parameter change
|
||||||
|
* 2) the name of the field ending in `.benefitTiers`
|
||||||
|
*
|
||||||
|
* It only renders known fields so that they can be formatted correctly.
|
||||||
|
*/
|
||||||
|
export const ProposalUpdateBenefitTiers = ({
|
||||||
|
proposal,
|
||||||
|
}: ProposalReferralProgramDetailsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
if (
|
||||||
|
proposal?.terms?.change?.__typename !== 'UpdateNetworkParameter' ||
|
||||||
|
proposal?.terms?.change?.networkParameter.key.slice(-13) !== '.benefitTiers'
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const benefitTiersString = proposal?.terms?.change?.networkParameter.value;
|
||||||
|
const benefitTiers = getBenefitTiers(benefitTiersString);
|
||||||
|
|
||||||
|
if (!benefitTiers) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid="proposal-update-benefit-tiers">
|
||||||
|
<RoundedWrapper paddingBottom={true}>
|
||||||
|
{benefitTiers && (
|
||||||
|
<div
|
||||||
|
className="mb-6"
|
||||||
|
data-testid="proposal-volume-discount-program-benefit-tiers"
|
||||||
|
>
|
||||||
|
<h3 className="mb-3 uppercase font-semibold text-lg">
|
||||||
|
{t('BenefitTiers')}
|
||||||
|
</h3>
|
||||||
|
<KeyValueTable>
|
||||||
|
{benefitTiers
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
Number(a.reward_multiplier) - Number(b.reward_multiplier)
|
||||||
|
)
|
||||||
|
.map((benefitTier, index) => (
|
||||||
|
<div className="mb-4" key={index}>
|
||||||
|
<h4 className="font-semibold uppercase">
|
||||||
|
Tier {index + 1}
|
||||||
|
</h4>
|
||||||
|
{'minimum_activity_streak' in benefitTier && (
|
||||||
|
<KeyValueTableRow
|
||||||
|
data-testid={`mas-${benefitTier.reward_multiplier}`}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierMinimumActivityStreakDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierMinimumActivityStreak')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{benefitTier.minimum_activity_streak}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{'minimum_quantum_balance' in benefitTier && (
|
||||||
|
<KeyValueTableRow
|
||||||
|
data-testid={`mqb-${benefitTier.reward_multiplier}`}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierMinimumQuantumBalanceDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierMinimumQuantumBalance')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
{formatMinimumStakedTokens(
|
||||||
|
benefitTier.minimum_quantum_balance,
|
||||||
|
18
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{'vesting_multiplier' in benefitTier && (
|
||||||
|
<KeyValueTableRow
|
||||||
|
data-testid={`vm-${benefitTier.reward_multiplier}`}
|
||||||
|
>
|
||||||
|
<Tooltip
|
||||||
|
description={t('BenefitTierVestingMultiplier')}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierVestingMultiplier')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatReferralRewardMultiplier(
|
||||||
|
benefitTier.vesting_multiplier
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{'reward_multiplier' in benefitTier && (
|
||||||
|
<KeyValueTableRow
|
||||||
|
data-testid={`rm-${benefitTier.reward_multiplier}`}
|
||||||
|
>
|
||||||
|
<Tooltip description={t('BenefitTierRewardMultiplier')}>
|
||||||
|
<span>{t('BenefitTierRewardMultiplier')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatReferralRewardMultiplier(
|
||||||
|
benefitTier.reward_multiplier
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</RoundedWrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -27,6 +27,7 @@ import {
|
|||||||
ProposalTransferDetails,
|
ProposalTransferDetails,
|
||||||
} from '../proposal-transfer';
|
} from '../proposal-transfer';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||||
|
|
||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: ProposalQuery['proposal'];
|
proposal: ProposalQuery['proposal'];
|
||||||
@@ -243,6 +244,14 @@ export const Proposal = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{proposal.terms.change.__typename === 'UpdateNetworkParameter' &&
|
||||||
|
proposal.terms.change.networkParameter.key.slice(-13) ===
|
||||||
|
'.benefitTiers' && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalUpdateBenefitTiers proposal={proposal} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{governanceTransferDetails}
|
{governanceTransferDetails}
|
||||||
|
|
||||||
<div className="mb-10">
|
<div className="mb-10">
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ export const useProposalNetworkParams = ({
|
|||||||
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
||||||
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
|
||||||
|
NetworkParams.governance_proposal_transfer_requiredParticipation,
|
||||||
|
NetworkParams.governance_proposal_transfer_requiredMajority,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const fallback = {
|
const fallback = {
|
||||||
@@ -111,6 +113,14 @@ export const useProposalNetworkParams = ({
|
|||||||
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
|
params.governance_proposal_VolumeDiscountProgram_requiredParticipation
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
case 'NewTransfer':
|
||||||
|
case 'CancelTransfer':
|
||||||
|
return {
|
||||||
|
requiredMajority: params.governance_proposal_transfer_requiredMajority,
|
||||||
|
requiredParticipation: new BigNumber(
|
||||||
|
params.governance_proposal_transfer_requiredParticipation
|
||||||
|
),
|
||||||
|
};
|
||||||
default:
|
default:
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,19 +47,17 @@ fragment UpdateReferralProgram on Proposal {
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
changes {
|
benefitTiers {
|
||||||
benefitTiers {
|
minimumEpochs
|
||||||
minimumEpochs
|
minimumRunningNotionalTakerVolume
|
||||||
minimumRunningNotionalTakerVolume
|
referralDiscountFactor
|
||||||
referralDiscountFactor
|
referralRewardFactor
|
||||||
referralRewardFactor
|
}
|
||||||
}
|
endOfProgram: endOfProgramTimestamp
|
||||||
endOfProgramTimestamp
|
windowLength
|
||||||
windowLength
|
stakingTiers {
|
||||||
stakingTiers {
|
minimumStakedTokens
|
||||||
minimumStakedTokens
|
referralRewardMultiplier
|
||||||
referralRewardMultiplier
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -47,19 +47,17 @@ fragment UpdateReferralPrograms on Proposal {
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
changes {
|
benefitTiers {
|
||||||
benefitTiers {
|
minimumEpochs
|
||||||
minimumEpochs
|
minimumRunningNotionalTakerVolume
|
||||||
minimumRunningNotionalTakerVolume
|
referralDiscountFactor
|
||||||
referralDiscountFactor
|
referralRewardFactor
|
||||||
referralRewardFactor
|
}
|
||||||
}
|
endOfProgram: endOfProgramTimestamp
|
||||||
endOfProgramTimestamp
|
windowLength
|
||||||
windowLength
|
stakingTiers {
|
||||||
stakingTiers {
|
minimumStakedTokens
|
||||||
minimumStakedTokens
|
referralRewardMultiplier
|
||||||
referralRewardMultiplier
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-15
@@ -7,7 +7,7 @@ export type NewMarketProductFieldsFragment = { __typename?: 'Proposal', terms: {
|
|||||||
|
|
||||||
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
export type UpdateMarketStatesFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||||
|
|
||||||
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
export type UpdateReferralProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } } };
|
||||||
|
|
||||||
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
|
export type UpdateVolumeDiscountProgramsFragment = { __typename?: 'Proposal', terms: { __typename?: 'ProposalTerms', change: { __typename?: 'CancelTransfer' } | { __typename?: 'NewAsset' } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket' } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset' } | { __typename?: 'UpdateMarket' } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter' } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } } };
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ export type ProposalsQueryVariables = Types.Exact<{
|
|||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', changes: { __typename?: 'ReferralProgram', endOfProgramTimestamp: string, windowLength: number, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null, product?: { __typename: 'FutureProduct' } | { __typename: 'PerpetualProduct' } | { __typename: 'SpotProduct' } | null } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState', updateType: Types.MarketUpdateType, price?: string | null, market: { __typename?: 'Market', decimalPlaces: number, id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string, product: { __typename: 'Future', quoteName: string } | { __typename: 'Perpetual', quoteName: string } | { __typename: 'Spot' } } } } } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram', windowLength: number, endOfProgram: any, benefitTiers: Array<{ __typename?: 'BenefitTier', minimumEpochs: number, minimumRunningNotionalTakerVolume: string, referralDiscountFactor: string, referralRewardFactor: string }>, stakingTiers: Array<{ __typename?: 'StakingTier', minimumStakedTokens: string, referralRewardMultiplier: string }> } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram', endOfProgramTimestamp: any, windowLength: number, benefitTiers: Array<{ __typename?: 'VolumeBenefitTier', minimumRunningNotionalTakerVolume: string, volumeDiscountFactor: string }> } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||||
|
|
||||||
export const NewMarketProductFieldsFragmentDoc = gql`
|
export const NewMarketProductFieldsFragmentDoc = gql`
|
||||||
fragment NewMarketProductFields on Proposal {
|
fragment NewMarketProductFields on Proposal {
|
||||||
@@ -75,19 +75,17 @@ export const UpdateReferralProgramsFragmentDoc = gql`
|
|||||||
terms {
|
terms {
|
||||||
change {
|
change {
|
||||||
... on UpdateReferralProgram {
|
... on UpdateReferralProgram {
|
||||||
changes {
|
benefitTiers {
|
||||||
benefitTiers {
|
minimumEpochs
|
||||||
minimumEpochs
|
minimumRunningNotionalTakerVolume
|
||||||
minimumRunningNotionalTakerVolume
|
referralDiscountFactor
|
||||||
referralDiscountFactor
|
referralRewardFactor
|
||||||
referralRewardFactor
|
}
|
||||||
}
|
endOfProgram: endOfProgramTimestamp
|
||||||
endOfProgramTimestamp
|
windowLength
|
||||||
windowLength
|
stakingTiers {
|
||||||
stakingTiers {
|
minimumStakedTokens
|
||||||
minimumStakedTokens
|
referralRewardMultiplier
|
||||||
referralRewardMultiplier
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ export const generateYesVotes = (
|
|||||||
})
|
})
|
||||||
.toString(),
|
.toString(),
|
||||||
},
|
},
|
||||||
|
vestingBalancesSummary: {
|
||||||
|
__typename: 'PartyVestingBalancesSummary',
|
||||||
|
epoch: null,
|
||||||
|
lockedBalances: [],
|
||||||
|
vestingBalances: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
datetime: faker.date.past().toISOString(),
|
datetime: faker.date.past().toISOString(),
|
||||||
};
|
};
|
||||||
@@ -192,6 +198,12 @@ export const generateNoVotes = (
|
|||||||
})
|
})
|
||||||
.toString(),
|
.toString(),
|
||||||
},
|
},
|
||||||
|
vestingBalancesSummary: {
|
||||||
|
__typename: 'PartyVestingBalancesSummary',
|
||||||
|
epoch: null,
|
||||||
|
lockedBalances: [],
|
||||||
|
vestingBalances: [],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
datetime: faker.date.past().toISOString(),
|
datetime: faker.date.past().toISOString(),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||||
|
import type { RouteChildProps } from '..';
|
||||||
|
import { BackgroundVideo } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
const Restricted = ({ name }: RouteChildProps) => {
|
||||||
|
useDocumentTitle(name);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const errorMessage =
|
||||||
|
'Due to uncertainty about the legal and regulatory status of the content hosted on this site, it is not available to visitors in your jurisdiction.';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<BackgroundVideo className="brightness-50" />
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'max-w-[620px] p-2 mt-[10vh]',
|
||||||
|
'mx-auto my-0',
|
||||||
|
'antialiased text-white',
|
||||||
|
'overflow-hidden relative',
|
||||||
|
'flex flex-col gap-2'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div>{GHOST}</div>
|
||||||
|
<h1 className="leading-relaxed mb-0 text-[2.7rem] font-alpha calt break-words uppercase">
|
||||||
|
{t('pageTitle451')}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm mt-10 overflow-auto font-mono">
|
||||||
|
{errorMessage}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const GHOST = (
|
||||||
|
<svg
|
||||||
|
width="56"
|
||||||
|
height="85"
|
||||||
|
viewBox="0 0 150 234"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M10.43 93.6821L0 104.097V172.435H5.13793V106.2L14.0779 97.3247L10.43 93.6821Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
<path d="M105.328 18.521H7.70703V172.435H105.328V18.521Z" fill="white" />
|
||||||
|
<path d="M38.5364 64.6953H33.3984V69.8258H38.5364V64.6953Z" fill="black" />
|
||||||
|
<path d="M43.6731 69.8257H38.5352V74.9561H43.6731V69.8257Z" fill="black" />
|
||||||
|
<path d="M48.8098 74.9561H43.6719V80.0865H48.8098V74.9561Z" fill="black" />
|
||||||
|
<path d="M38.5364 74.9561H33.3984V80.0865H38.5364V74.9561Z" fill="black" />
|
||||||
|
<path d="M48.8098 64.6953H43.6719V69.8258H48.8098V64.6953Z" fill="black" />
|
||||||
|
<path d="M69.3606 64.6953H64.2227V69.8258H69.3606V64.6953Z" fill="black" />
|
||||||
|
<path d="M74.5012 69.8257H69.3633V74.9561H74.5012V69.8257Z" fill="black" />
|
||||||
|
<path d="M79.6379 74.9561H74.5V80.0865H79.6379V74.9561Z" fill="black" />
|
||||||
|
<path d="M69.3606 74.9561H64.2227V80.0865H69.3606V74.9561Z" fill="black" />
|
||||||
|
<path d="M79.6379 64.6953H74.5V69.8258H79.6379V64.6953Z" fill="black" />
|
||||||
|
<path d="M79.6398 90.3477H33.3984V95.4781H79.6398V90.3477Z" fill="black" />
|
||||||
|
<path d="M48.8098 172.435H43.6719V234H48.8098V172.435Z" fill="black" />
|
||||||
|
<path d="M69.3606 172.435H64.2227V234H69.3606V172.435Z" fill="black" />
|
||||||
|
<path
|
||||||
|
d="M129.87 0.00408026L92.0273 14.3682L111.195 64.7203L149.038 50.3562L129.87 0.00408026Z"
|
||||||
|
fill="#FF0081"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M108.565 21.7836L103.762 23.6064L105.587 28.4024L110.39 26.5795L108.565 21.7836Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M115.189 24.7596L110.387 26.583L112.213 31.3785L117.015 29.5551L115.189 24.7596Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M121.826 27.7348L117.023 29.5576L118.849 34.3536L123.652 32.5307L121.826 27.7348Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M128.447 30.7059L123.645 32.5293L125.471 37.3247L130.273 35.5014L128.447 30.7059Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M135.084 33.6854L130.281 35.5083L132.107 40.3043L136.91 38.4814L135.084 33.6854Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M124.799 21.1156L119.996 22.939L121.822 27.7344L126.625 25.911L124.799 21.1156Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M127.775 14.492L122.973 16.3154L124.799 21.1109L129.601 19.2875L127.775 14.492Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M118.838 34.3499L114.035 36.1733L115.861 40.9688L120.664 39.1454L118.838 34.3499Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M115.857 40.9691L111.055 42.7925L112.881 47.5879L117.683 45.7645L115.857 40.9691Z"
|
||||||
|
fill="white"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M129.938 52.3822L139.906 91.5276L100.703 101.429L101.988 106.406L146.174 95.2215L134.922 51.0996L129.938 52.3822Z"
|
||||||
|
fill="black"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Restricted;
|
||||||
@@ -4,6 +4,7 @@ import Home from './token';
|
|||||||
import NotFound from './not-found';
|
import NotFound from './not-found';
|
||||||
import NotPermitted from './not-permitted';
|
import NotPermitted from './not-permitted';
|
||||||
import Routes from './routes';
|
import Routes from './routes';
|
||||||
|
import Restricted from './restricted';
|
||||||
|
|
||||||
const LazyTranches = React.lazy(
|
const LazyTranches = React.lazy(
|
||||||
() =>
|
() =>
|
||||||
@@ -361,8 +362,13 @@ const routerConfig = [
|
|||||||
element: <LazyDisclaimer name="Disclaimer" />,
|
element: <LazyDisclaimer name="Disclaimer" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '*',
|
path: Routes.RESTRICTED,
|
||||||
// Not lazy as loaded when a user first hits the site
|
// Not lazy as loaded when a user first hits the site
|
||||||
|
element: <Restricted name="451 Unavailable" />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '*',
|
||||||
|
// Also not lazy as loaded when a user first hits the site
|
||||||
element: <NotFound name="NotFound" />,
|
element: <NotFound name="NotFound" />,
|
||||||
},
|
},
|
||||||
...redirects,
|
...redirects,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const Routes = {
|
|||||||
PROPOSALS_REJECTED: '/proposals/rejected',
|
PROPOSALS_REJECTED: '/proposals/rejected',
|
||||||
PROTOCOL_UPGRADES: '/protocol-upgrades',
|
PROTOCOL_UPGRADES: '/protocol-upgrades',
|
||||||
NOT_PERMITTED: '/not-permitted',
|
NOT_PERMITTED: '/not-permitted',
|
||||||
|
RESTRICTED: '/restricted',
|
||||||
NOT_FOUND: '/not-found',
|
NOT_FOUND: '/not-found',
|
||||||
CONTRACTS: '/contracts',
|
CONTRACTS: '/contracts',
|
||||||
TOKEN: '/token',
|
TOKEN: '/token',
|
||||||
|
|||||||
@@ -1,147 +0,0 @@
|
|||||||
.pre-loader {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100vh;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: #000;
|
|
||||||
}
|
|
||||||
.pre-loader .pre-loader-center {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.pre-loader .pre-loader-wrapper {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(0) {
|
|
||||||
animation-delay: 0ms;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:first-child {
|
|
||||||
animation-delay: -0.1s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(2) {
|
|
||||||
animation-delay: 0.3s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(3) {
|
|
||||||
animation-delay: -0.45s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(4) {
|
|
||||||
animation-delay: 1s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(5) {
|
|
||||||
animation-delay: -0.75s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(6) {
|
|
||||||
animation-delay: 0.9s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(7) {
|
|
||||||
animation-delay: -1.4s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(8) {
|
|
||||||
animation-delay: 1.6s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(9) {
|
|
||||||
animation-delay: -0.45s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(10) {
|
|
||||||
animation-delay: 1.5s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(11) {
|
|
||||||
animation-delay: -2.75s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(12) {
|
|
||||||
animation-delay: 1.2s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(13) {
|
|
||||||
animation-delay: -1.95s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(14) {
|
|
||||||
animation-delay: 2.8s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(15) {
|
|
||||||
animation-delay: -0.75s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(16) {
|
|
||||||
animation-delay: 4s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(17) {
|
|
||||||
animation-delay: -0.85s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(18) {
|
|
||||||
animation-delay: 1.8s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(19) {
|
|
||||||
animation-delay: -1.9s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(20) {
|
|
||||||
animation-delay: 5s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(21) {
|
|
||||||
animation-delay: -5.25s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(22) {
|
|
||||||
animation-delay: 4.4s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(23) {
|
|
||||||
animation-delay: -5.75s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(24) {
|
|
||||||
animation-delay: 4.8s;
|
|
||||||
animation-direction: reverse;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item:nth-child(25) {
|
|
||||||
animation-delay: -5s;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
.pre-loader .loader-item {
|
|
||||||
animation: flickering 0.4s linear infinite alternate;
|
|
||||||
}
|
|
||||||
@keyframes flickering {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
26% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html.dark .pre-loader .loader-item {
|
|
||||||
background: #fff;
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
.pre-loader {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
min-height: 100vh;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
.loader-item {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: black;
|
|
||||||
}
|
|
||||||
.pre-loader-center {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
.pre-loader-wrapper {
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
@for $i from 0 through 25 {
|
|
||||||
.loader-item:nth-child(#{$i}) {
|
|
||||||
@if $i % 2 == 0 {
|
|
||||||
animation-delay: #{$i * 50 * random(5)}ms;
|
|
||||||
animation-direction: reverse;
|
|
||||||
} @else {
|
|
||||||
animation-delay: #{$i * -50 * random(5)}ms;
|
|
||||||
animation-direction: alternate;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.loader-item {
|
|
||||||
animation: flickering 0.4s linear alternate infinite;
|
|
||||||
}
|
|
||||||
@keyframes flickering {
|
|
||||||
0% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
25% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
26% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
html.dark {
|
|
||||||
.pre-loader {
|
|
||||||
.loader-item {
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -48,8 +48,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
cy.get('@markets').then((markets) => {
|
cy.get('@markets').then((markets) => {
|
||||||
cy.wrap(markets[0]).as('market');
|
cy.wrap(markets[0]).as('market');
|
||||||
});
|
});
|
||||||
|
cy.setOnBoardingViewed();
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.connectVegaWallet();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can deposit', function () {
|
it('can deposit', function () {
|
||||||
@@ -70,6 +70,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
connectEthereumWallet('Unknown');
|
connectEthereumWallet('Unknown');
|
||||||
selectAsset(btcName);
|
selectAsset(btcName);
|
||||||
|
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||||
|
|
||||||
cy.getByTestId('approve-default').should(
|
cy.getByTestId('approve-default').should(
|
||||||
'contain.text',
|
'contain.text',
|
||||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
||||||
@@ -120,7 +122,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
|
|
||||||
cy.getByTestId(collateralTab).click();
|
cy.getByTestId(collateralTab).click();
|
||||||
cy.getByTestId('open-transfer').click();
|
cy.getByTestId('open-transfer').eq(1).click();
|
||||||
cy.getByTestId('transfer-form').should('be.visible');
|
cy.getByTestId('transfer-form').should('be.visible');
|
||||||
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
||||||
cy.get('select option')
|
cy.get('select option')
|
||||||
@@ -147,7 +149,8 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
// 0003-WTXN-011
|
// 0003-WTXN-011
|
||||||
cy.getByTestId('Withdrawals').click();
|
cy.getByTestId('Withdrawals').click();
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
cy.getByTestId('withdraw-dialog-button').click();
|
||||||
selectAsset(0);
|
selectAsset(btcName);
|
||||||
|
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||||
cy.get(amountField).focus();
|
cy.get(amountField).focus();
|
||||||
cy.get(amountField).clear().type('1');
|
cy.get(amountField).clear().type('1');
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
cy.getByTestId('submit-withdrawal').click();
|
||||||
@@ -180,24 +183,21 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
cy.get('@markets').then((markets) => {
|
cy.get('@markets').then((markets) => {
|
||||||
cy.wrap(markets[0]).as('market');
|
cy.wrap(markets[0]).as('market');
|
||||||
});
|
});
|
||||||
|
cy.setOnBoardingViewed();
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows node health', function () {
|
it('shows node health', function () {
|
||||||
// 0006-NETW-010
|
// 0006-NETW-010
|
||||||
|
const regex = /^Operational\d+$/;
|
||||||
const market = this.market;
|
const market = this.market;
|
||||||
cy.visit(`/#/markets/${market.id}`);
|
cy.visit(`/#/markets/${market.id}`);
|
||||||
cy.getByTestId('node-health-trigger').realHover();
|
cy.getByTestId('node-health-trigger').realHover();
|
||||||
cy.getByTestId('node-health')
|
cy.getByTestId('node-health')
|
||||||
.children()
|
.children()
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', 'Operational')
|
.invoke('text')
|
||||||
.then(($el) => {
|
.should('match', regex);
|
||||||
const blockHeight = parseInt($el.text());
|
|
||||||
// block height will increase over the course of the test run so best
|
|
||||||
// we can do here is check that its showing something sensible
|
|
||||||
expect(blockHeight).to.be.greaterThan(0);
|
|
||||||
});
|
|
||||||
cy.getByTestId('node-health')
|
cy.getByTestId('node-health')
|
||||||
.children()
|
.children()
|
||||||
.eq(1)
|
.eq(1)
|
||||||
@@ -239,7 +239,6 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
.should('contain.text', order.size);
|
.should('contain.text', order.size);
|
||||||
|
|
||||||
cy.getByTestId(openOrdersTab).click();
|
cy.getByTestId(openOrdersTab).click();
|
||||||
cy.getByTestId('edit', txTimeout).should('contain.text', 'Edit');
|
|
||||||
cy.getByTestId('tab-open-orders').within(() => {
|
cy.getByTestId('tab-open-orders').within(() => {
|
||||||
cy.get('.ag-center-cols-container')
|
cy.get('.ag-center-cols-container')
|
||||||
.children()
|
.children()
|
||||||
@@ -280,8 +279,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
cy.visit(`/#/markets/${market.id}`);
|
cy.visit(`/#/markets/${market.id}`);
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||||
cy.getByTestId(openOrdersTab).click();
|
cy.getByTestId(openOrdersTab).click();
|
||||||
cy.getByTestId('edit', txTimeout).should('be.visible');
|
cy.getByTestId('edit').first().click();
|
||||||
cy.getByTestId('edit').first().should('be.visible').click();
|
|
||||||
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
cy.getByTestId('dialog-title').should('contain.text', 'Edit order');
|
||||||
cy.get('#limitPrice').focus().clear().type(newPrice);
|
cy.get('#limitPrice').focus().clear().type(newPrice);
|
||||||
cy.getByTestId('edit-order').find('[type="submit"]').click();
|
cy.getByTestId('edit-order').find('[type="submit"]').click();
|
||||||
@@ -350,6 +348,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
cy.getByTestId('withdraw-dialog-button').click();
|
cy.getByTestId('withdraw-dialog-button').click();
|
||||||
connectEthereumWallet('Unknown');
|
connectEthereumWallet('Unknown');
|
||||||
selectAsset(btcName);
|
selectAsset(btcName);
|
||||||
|
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||||
cy.get(amountField).clear().type('1');
|
cy.get(amountField).clear().type('1');
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
cy.getByTestId('submit-withdrawal').click();
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
cy.getByTestId(toastContent, txTimeout).should(
|
||||||
@@ -437,6 +436,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
connectEthereumWallet('Unknown');
|
connectEthereumWallet('Unknown');
|
||||||
selectAsset(btcName);
|
selectAsset(btcName);
|
||||||
|
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
||||||
cy.contains('Use maximum').should('be.visible');
|
cy.contains('Use maximum').should('be.visible');
|
||||||
cy.get(amountField).clear().type('20000000');
|
cy.get(amountField).clear().type('20000000');
|
||||||
|
|||||||
@@ -1,201 +0,0 @@
|
|||||||
import { removeDecimal } from '@vegaprotocol/cypress';
|
|
||||||
import { ethers } from 'ethers';
|
|
||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const assetSelectField = 'select[name="asset"]';
|
|
||||||
const toAddressField = 'input[name="to"]';
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const formFieldError = 'input-error-text';
|
|
||||||
|
|
||||||
const ASSET_EURO = 1;
|
|
||||||
|
|
||||||
describe('deposit form validation', { tags: '@smoke' }, () => {
|
|
||||||
function openDepositForm() {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId('Deposits').click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('MetaMask');
|
|
||||||
cy.wait('@Assets');
|
|
||||||
}
|
|
||||||
|
|
||||||
before(() => {
|
|
||||||
openDepositForm();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('handles empty fields', () => {
|
|
||||||
cy.getByTestId('deposit-submit').click();
|
|
||||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
|
||||||
// once Ethereum wallet is connected and key selected the only field that will
|
|
||||||
// error is the asset select
|
|
||||||
cy.getByTestId(formFieldError).should('have.length', 1);
|
|
||||||
cy.get('[data-testid="input-error-text"][aria-describedby="asset"]').should(
|
|
||||||
'have.length',
|
|
||||||
1
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('unable to select assets not enabled', () => {
|
|
||||||
// Assets not enabled in mocks
|
|
||||||
cy.get(assetSelectField + ' option:contains(Asset 2)').should('not.exist');
|
|
||||||
cy.get(assetSelectField + ' option:contains(Asset 3)').should('not.exist');
|
|
||||||
cy.get(assetSelectField + ' option:contains(Asset 4)').should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('invalid public key when entering address manually', () => {
|
|
||||||
cy.getByTestId('enter-pubkey-manually').click();
|
|
||||||
cy.get(toAddressField).clear().type('INVALID_DEPOSIT_TO_ADDRESS');
|
|
||||||
cy.get(`[data-testid="${formFieldError}"][aria-describedby="to"]`).should(
|
|
||||||
'have.text',
|
|
||||||
'Invalid Vega key'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('invalid amount', () => {
|
|
||||||
mockWeb3DepositCalls({
|
|
||||||
allowance: '1000',
|
|
||||||
depositLifetimeLimit: '1000',
|
|
||||||
balance: '800',
|
|
||||||
deposited: '0',
|
|
||||||
dps: 5,
|
|
||||||
});
|
|
||||||
// Deposit amount smaller than minimum viable for selected asset
|
|
||||||
// Select an amount so that we have a known decimal places value to work with
|
|
||||||
selectAsset(ASSET_EURO);
|
|
||||||
|
|
||||||
cy.get(amountField)
|
|
||||||
.clear()
|
|
||||||
.type('0.00000000000000000000000000000000001')
|
|
||||||
.next(`[data-testid="${formFieldError}"]`)
|
|
||||||
.should('have.text', 'Value is below minimum');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('insufficient funds', () => {
|
|
||||||
// 1001-DEPO-004
|
|
||||||
mockWeb3DepositCalls({
|
|
||||||
allowance: '1000',
|
|
||||||
depositLifetimeLimit: '1000',
|
|
||||||
balance: '800',
|
|
||||||
deposited: '0',
|
|
||||||
dps: 5,
|
|
||||||
});
|
|
||||||
cy.get(amountField)
|
|
||||||
.clear()
|
|
||||||
.type('850')
|
|
||||||
.next(`[data-testid="${formFieldError}"]`)
|
|
||||||
.should(
|
|
||||||
'have.text',
|
|
||||||
"You can't deposit more than you have in your Ethereum wallet, 800 tEURO"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('deposit actions', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/markets/market-1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it.skip('Deposit to trade is visible', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
|
|
||||||
cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
|
|
||||||
cy.contains('[data-testid="deposit"]', 'Deposit').click();
|
|
||||||
cy.getByTestId('deposit-submit').should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function mockWeb3DepositCalls({
|
|
||||||
allowance,
|
|
||||||
depositLifetimeLimit,
|
|
||||||
balance,
|
|
||||||
deposited,
|
|
||||||
dps,
|
|
||||||
}: {
|
|
||||||
allowance: string;
|
|
||||||
depositLifetimeLimit: string;
|
|
||||||
balance: string;
|
|
||||||
deposited: string;
|
|
||||||
dps: number;
|
|
||||||
}) {
|
|
||||||
const assetContractAddress = '0x0158031158bb4df2ad02eaa31e8963e84ea978a4';
|
|
||||||
const collateralBridgeAddress = '0x7fe27d970bc8afc3b11cc8d9737bfb66b1efd799';
|
|
||||||
const toResult = (value: string, dps: number) => {
|
|
||||||
const rawValue = removeDecimal(value, dps);
|
|
||||||
return ethers.utils.hexZeroPad(
|
|
||||||
ethers.utils.hexlify(parseInt(rawValue)),
|
|
||||||
32
|
|
||||||
);
|
|
||||||
};
|
|
||||||
cy.intercept('POST', 'http://localhost:8545', (req) => {
|
|
||||||
// Mock chainId call
|
|
||||||
if (req.body.method === 'eth_chainId') {
|
|
||||||
req.alias = 'eth_chainId';
|
|
||||||
req.reply({
|
|
||||||
id: req.body.id,
|
|
||||||
jsonrpc: req.body.jsonrpc,
|
|
||||||
result: '0xaa36a7', // 11155111 for sepolia chain id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mock deposited amount
|
|
||||||
if (req.body.method === 'eth_getStorageAt') {
|
|
||||||
req.alias = 'eth_getStorageAt';
|
|
||||||
req.reply({
|
|
||||||
id: req.body.id,
|
|
||||||
jsonrpc: req.body.jsonrpc,
|
|
||||||
result: toResult(deposited, dps),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req.body.method === 'eth_call') {
|
|
||||||
// Mock approved amount for asset on collateral bridge
|
|
||||||
if (
|
|
||||||
req.body.params[0].to === assetContractAddress &&
|
|
||||||
req.body.params[0].data ===
|
|
||||||
'0xdd62ed3e000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f0000000000000000000000007fe27d970bc8afc3b11cc8d9737bfb66b1efd799'
|
|
||||||
) {
|
|
||||||
req.alias = 'eth_call_allowance';
|
|
||||||
req.reply({
|
|
||||||
id: req.body.id,
|
|
||||||
jsonrpc: req.body.jsonrpc,
|
|
||||||
result: toResult(allowance, dps),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Mock balance of asset in Ethereum wallet
|
|
||||||
else if (
|
|
||||||
req.body.params[0].to === assetContractAddress &&
|
|
||||||
req.body.params[0].data ===
|
|
||||||
'0x70a08231000000000000000000000000ee7d375bcb50c26d52e1a4a472d8822a2a22d94f'
|
|
||||||
) {
|
|
||||||
req.alias = 'eth_call_balanceOf';
|
|
||||||
req.reply({
|
|
||||||
id: req.body.id,
|
|
||||||
jsonrpc: req.body.jsonrpc,
|
|
||||||
result: toResult(balance, dps),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Mock deposit lifetime limit
|
|
||||||
else if (
|
|
||||||
req.body.params[0].to === collateralBridgeAddress &&
|
|
||||||
req.body.params[0].data ===
|
|
||||||
'0x354a897a0000000000000000000000000158031158bb4df2ad02eaa31e8963e84ea978a4'
|
|
||||||
) {
|
|
||||||
req.alias = 'eth_call_get_deposit_maximum'; // deposit lifetime limit
|
|
||||||
req.reply({
|
|
||||||
id: req.body.id,
|
|
||||||
jsonrpc: req.body.jsonrpc,
|
|
||||||
result: toResult(depositLifetimeLimit, dps),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -69,7 +69,7 @@ describe(
|
|||||||
cy.contains('Something went wrong').should('not.exist');
|
cy.contains('Something went wrong').should('not.exist');
|
||||||
cy.contains('Application error').should('not.exist');
|
cy.contains('Application error').should('not.exist');
|
||||||
cy.getByTestId('tab-liquidity').within(() => {
|
cy.getByTestId('tab-liquidity').within(() => {
|
||||||
cy.get('[col-id="party.id"]').eq(1).should('not.be.empty');
|
cy.get('[col-id="partyId"]').eq(1).should('not.be.empty');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ const marketSummaryBlock = 'header-summary';
|
|||||||
const itemValue = 'item-value';
|
const itemValue = 'item-value';
|
||||||
const itemHeader = 'item-header';
|
const itemHeader = 'item-header';
|
||||||
const colCommitmentAmount = '[col-id="commitmentAmount"]';
|
const colCommitmentAmount = '[col-id="commitmentAmount"]';
|
||||||
const colAverageEntryValuation = '[col-id="averageEntryValuation"]';
|
const colEquityLikeShare = '[col-id="feeShare.equityLikeShare"]';
|
||||||
const colEquityLikeShare = '[col-id="equityLikeShare"]';
|
|
||||||
const colFee = '[col-id="fee"]';
|
const colFee = '[col-id="fee"]';
|
||||||
const colCommitmentAmount_1 = '[col-id="commitmentAmount_1"]';
|
const colCommitmentAmount_1 = '[col-id="commitmentAmount_1"]';
|
||||||
const colBalance = '[col-id="balance"]';
|
const colBalance = '[col-id="balance"]';
|
||||||
@@ -23,13 +22,19 @@ const colUpdatedAt = '[col-id="updatedAt"] button';
|
|||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
'Party',
|
'Party',
|
||||||
'Commitment (tDAI)',
|
|
||||||
'Share',
|
|
||||||
'Proposed fee',
|
|
||||||
'Market valuation at entry',
|
|
||||||
'Obligation',
|
|
||||||
'Supplied',
|
|
||||||
'Status',
|
'Status',
|
||||||
|
'Commitment (tDAI)',
|
||||||
|
'Obligation',
|
||||||
|
'Fee',
|
||||||
|
'Adjusted stake share',
|
||||||
|
'Share',
|
||||||
|
'Live supplied liquidity',
|
||||||
|
'Fees accrued this epoch',
|
||||||
|
'Live time on book',
|
||||||
|
'Live liquidity quality score (%)',
|
||||||
|
'Last time on the book',
|
||||||
|
'Last fee penalty',
|
||||||
|
'Last bond penalty',
|
||||||
'Created',
|
'Created',
|
||||||
'Updated',
|
'Updated',
|
||||||
];
|
];
|
||||||
@@ -64,11 +69,8 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
|||||||
// 5002-LIQP-002
|
// 5002-LIQP-002
|
||||||
cy.get(rowSelector)
|
cy.get(rowSelector)
|
||||||
.first()
|
.first()
|
||||||
.find('[col-id="party.id"]')
|
.find('[col-id="partyId"]')
|
||||||
.should(
|
.should('have.text', '69464e…dc6f');
|
||||||
'have.text',
|
|
||||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get(rowSelector)
|
cy.get(rowSelector)
|
||||||
.first()
|
.first()
|
||||||
@@ -82,12 +84,6 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
|
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
|
||||||
|
|
||||||
// 5002-LIQP-013
|
|
||||||
cy.get(rowSelector)
|
|
||||||
.first()
|
|
||||||
.find(colAverageEntryValuation)
|
|
||||||
.should('have.text', '685,852.93692');
|
|
||||||
|
|
||||||
cy.get(rowSelector)
|
cy.get(rowSelector)
|
||||||
.first()
|
.first()
|
||||||
.find(colCommitmentAmount_1)
|
.find(colCommitmentAmount_1)
|
||||||
@@ -104,7 +100,8 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
|||||||
cy.get(rowSelector).first().find(colCreatedAt).should('not.be.empty');
|
cy.get(rowSelector).first().find(colCreatedAt).should('not.be.empty');
|
||||||
cy.get(rowSelector).first().find(colUpdatedAt).should('not.be.empty');
|
cy.get(rowSelector).first().find(colUpdatedAt).should('not.be.empty');
|
||||||
});
|
});
|
||||||
it.skip('liquidity status column should be sorted properly', () => {
|
|
||||||
|
it('liquidity status column should be sorted properly', () => {
|
||||||
// 5002-LIQP-003
|
// 5002-LIQP-003
|
||||||
const liquidityColDefault = ['Active', 'Pending'];
|
const liquidityColDefault = ['Active', 'Pending'];
|
||||||
const liquidityColAsc = ['Active', 'Pending'];
|
const liquidityColAsc = ['Active', 'Pending'];
|
||||||
@@ -220,11 +217,8 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
// 5002-LIQP-011
|
// 5002-LIQP-011
|
||||||
cy.get(rowSelectorLiquidityActive)
|
cy.get(rowSelectorLiquidityActive)
|
||||||
.first()
|
.first()
|
||||||
.find('[col-id="party.id"]')
|
.find('[col-id="partyId"]')
|
||||||
.should(
|
.should('have.text', '69464e…dc6f');
|
||||||
'have.text',
|
|
||||||
'69464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get(rowSelectorLiquidityActive)
|
cy.get(rowSelectorLiquidityActive)
|
||||||
.first()
|
.first()
|
||||||
@@ -241,12 +235,6 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
.find(colFee)
|
.find(colFee)
|
||||||
.should('have.text', '0.09%');
|
.should('have.text', '0.09%');
|
||||||
|
|
||||||
// 5002-LIQP-013
|
|
||||||
cy.get(rowSelectorLiquidityActive)
|
|
||||||
.first()
|
|
||||||
.find(colAverageEntryValuation)
|
|
||||||
.should('have.text', '685,852.93692');
|
|
||||||
|
|
||||||
cy.get(rowSelectorLiquidityActive)
|
cy.get(rowSelectorLiquidityActive)
|
||||||
.first()
|
.first()
|
||||||
.find(colCommitmentAmount_1)
|
.find(colCommitmentAmount_1)
|
||||||
@@ -277,11 +265,8 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId('Inactive').click();
|
cy.getByTestId('Inactive').click();
|
||||||
cy.get(rowSelectorLiquidityInactive)
|
cy.get(rowSelectorLiquidityInactive)
|
||||||
.first()
|
.first()
|
||||||
.find('[col-id="party.id"]')
|
.find('[col-id="partyId"]')
|
||||||
.should(
|
.should('have.text', 'cc464e…dc6f');
|
||||||
'have.text',
|
|
||||||
'cc464e35bcb8e8a2900ca0f87acaf252d50cf2ab2fc73694845a16b7c8a0dc6f'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.get(rowSelectorLiquidityInactive)
|
cy.get(rowSelectorLiquidityInactive)
|
||||||
.first()
|
.first()
|
||||||
@@ -298,11 +283,6 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
.find(colFee)
|
.find(colFee)
|
||||||
.should('have.text', '0.40%');
|
.should('have.text', '0.40%');
|
||||||
|
|
||||||
cy.get(rowSelectorLiquidityInactive)
|
|
||||||
.first()
|
|
||||||
.find(colAverageEntryValuation)
|
|
||||||
.should('have.text', '685,852.93692');
|
|
||||||
|
|
||||||
cy.get(rowSelectorLiquidityInactive)
|
cy.get(rowSelectorLiquidityInactive)
|
||||||
.first()
|
.first()
|
||||||
.find(colCommitmentAmount_1)
|
.find(colCommitmentAmount_1)
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import {
|
|
||||||
TIFlist,
|
|
||||||
orderPriceField,
|
|
||||||
orderSizeField,
|
|
||||||
orderTIFDropDown,
|
|
||||||
placeOrderBtn,
|
|
||||||
toggleLimit,
|
|
||||||
toggleMarket,
|
|
||||||
} from '../support/deal-ticket';
|
|
||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
|
||||||
import { accountsQuery } from '@vegaprotocol/mock';
|
|
||||||
|
|
||||||
describe('suspended market validation', { tags: '@regression' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.mockTradingPage(
|
|
||||||
Schema.MarketState.STATE_SUSPENDED,
|
|
||||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
|
||||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
|
||||||
);
|
|
||||||
const accounts = accountsQuery();
|
|
||||||
cy.mockGQL((req) => {
|
|
||||||
aliasGQLQuery(req, 'Accounts', accounts);
|
|
||||||
});
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.wait('@Markets');
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.setVegaWallet();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show warning for market order', function () {
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
// 7002-SORD-060
|
|
||||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
|
||||||
cy.getByTestId(placeOrderBtn).click();
|
|
||||||
cy.getByTestId('deal-ticket-error-message-type').should(
|
|
||||||
'have.text',
|
|
||||||
'This market is in auction until it reaches sufficient liquidity. Only limit orders are permitted when market is in auction'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show info for allowed TIF', function () {
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
cy.getByTestId(orderPriceField).clear().type('0.1');
|
|
||||||
cy.getByTestId(orderSizeField).clear().type('1');
|
|
||||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
|
||||||
cy.getByTestId(placeOrderBtn).click();
|
|
||||||
cy.getByTestId('deal-ticket-warning-auction').should(
|
|
||||||
'have.text',
|
|
||||||
'Any orders placed now will not trade until the auction ends'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show warning for not allowed TIF', function () {
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(
|
|
||||||
TIFlist.filter((item) => item.code === 'FOK')[0].value
|
|
||||||
);
|
|
||||||
cy.getByTestId(placeOrderBtn).should('be.enabled');
|
|
||||||
cy.getByTestId(placeOrderBtn).click();
|
|
||||||
cy.getByTestId('deal-ticket-error-message-tif').should(
|
|
||||||
'have.text',
|
|
||||||
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
|
||||||
import { fillsQuery } from '@vegaprotocol/mock';
|
|
||||||
|
|
||||||
const tabFills = 'tab-fills';
|
|
||||||
|
|
||||||
describe('fills', { tags: '@regression' }, () => {
|
|
||||||
// 7005-FILL-001
|
|
||||||
// 7005-FILL-002
|
|
||||||
// 7005-FILL-003
|
|
||||||
// 7005-FILL-004
|
|
||||||
// 7005-FILL-005
|
|
||||||
// 7005-FILL-006
|
|
||||||
// 7005-FILL-007
|
|
||||||
// 7005-FILL-008
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
// Ensure page loads with correct key
|
|
||||||
cy.window().then((window) => {
|
|
||||||
cy.wrap(
|
|
||||||
window.localStorage.setItem(
|
|
||||||
'vega_wallet_key',
|
|
||||||
Cypress.env('VEGA_PUBLIC_KEY')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockGQL((req) => {
|
|
||||||
aliasGQLQuery(
|
|
||||||
req,
|
|
||||||
'Fills',
|
|
||||||
fillsQuery({}, Cypress.env('VEGA_PUBLIC_KEY'))
|
|
||||||
);
|
|
||||||
});
|
|
||||||
cy.mockSubscription();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders fills on portfolio page', () => {
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId('Fills').click();
|
|
||||||
validateFillsDisplayed();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders fills on trading tab', () => {
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.getByTestId('Fills').click();
|
|
||||||
validateFillsDisplayed();
|
|
||||||
});
|
|
||||||
|
|
||||||
function validateFillsDisplayed() {
|
|
||||||
cy.getByTestId(tabFills).should('be.visible');
|
|
||||||
cy.getByTestId(tabFills).contains('Market');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get(
|
|
||||||
'[role="gridcell"][col-id="market.tradableInstrument.instrument.code"]'
|
|
||||||
)
|
|
||||||
.each(($marketSymbol) => {
|
|
||||||
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Size');
|
|
||||||
cy.get(`[col-id='size']`).eq(1).should('contain.text', '+');
|
|
||||||
cy.get(`[col-id='size']`).eq(2).should('contain.text', '-');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get('[role="gridcell"][col-id="size"]')
|
|
||||||
.each(($amount) => {
|
|
||||||
cy.wrap($amount).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Price');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get('[role="gridcell"][col-id="price"]')
|
|
||||||
.each(($prices) => {
|
|
||||||
cy.wrap($prices).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Notional');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get('[role="gridcell"][col-id="price_1"]')
|
|
||||||
.each(($total) => {
|
|
||||||
cy.wrap($total).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Role');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get('[role="gridcell"][col-id="aggressor"]')
|
|
||||||
.each(($role) => {
|
|
||||||
cy.wrap($role)
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
const roles = ['Maker', 'Taker', '-'];
|
|
||||||
expect(roles.indexOf(text.trim())).to.be.greaterThan(-1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Fee');
|
|
||||||
cy.getByTestId(tabFills)
|
|
||||||
.get(
|
|
||||||
'[role="gridcell"][col-id="market.tradableInstrument.instrument.product"]'
|
|
||||||
)
|
|
||||||
.each(($fees) => {
|
|
||||||
cy.wrap($fees).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId(tabFills).contains('Date');
|
|
||||||
const dateTimeRegex =
|
|
||||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
|
||||||
cy.get('[col-id="createdAt"]').each(($tradeDateTime, index) => {
|
|
||||||
if (index != 0) {
|
|
||||||
//ignore header
|
|
||||||
cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -1,529 +0,0 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import type { OrderAmendment, OrderCancellation } from '@vegaprotocol/wallet';
|
|
||||||
import {
|
|
||||||
updateOrder,
|
|
||||||
getSubscriptionMocks,
|
|
||||||
} from '../support/order-update-subscription';
|
|
||||||
import {
|
|
||||||
testOrderCancellation,
|
|
||||||
testOrderAmendment,
|
|
||||||
} from '../support/order-validation';
|
|
||||||
|
|
||||||
const orderSymbol = 'instrument-code';
|
|
||||||
const orderSize = 'size';
|
|
||||||
const orderType = 'type';
|
|
||||||
const orderStatus = 'status';
|
|
||||||
const orderRemaining = 'remaining';
|
|
||||||
const orderPrice = 'price';
|
|
||||||
const orderTimeInForce = 'timeInForce';
|
|
||||||
const orderUpdatedAt = 'updatedAt';
|
|
||||||
const cancelOrderBtn = 'cancel';
|
|
||||||
const cancelAllOrdersBtn = 'cancelAll';
|
|
||||||
const editOrderBtn = 'edit';
|
|
||||||
|
|
||||||
describe('orders list', { tags: '@smoke', testIsolation: true }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const subscriptionMocks = getSubscriptionMocks();
|
|
||||||
cy.spy(subscriptionMocks, 'OrdersUpdate');
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription(subscriptionMocks);
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.getByTestId('All').click();
|
|
||||||
cy.wait('@Markets');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders orders', () => {
|
|
||||||
cy.getByTestId('tab-orders').should('be.visible');
|
|
||||||
cy.getByTestId(cancelAllOrdersBtn).should('be.visible');
|
|
||||||
cy.getByTestId(cancelOrderBtn).should('have.length.at.least', 1);
|
|
||||||
cy.getByTestId(editOrderBtn).should('have.length.at.least', 1);
|
|
||||||
cy.getByTestId('tab-orders').within(() => {
|
|
||||||
cy.get(`[role='rowgroup']`)
|
|
||||||
.first()
|
|
||||||
.within(() => {
|
|
||||||
cy.get(`[col-id='${orderSymbol}']`).each(($symbol) => {
|
|
||||||
cy.wrap($symbol).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderRemaining}']`).each(($remaining) => {
|
|
||||||
cy.wrap($remaining).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderSize}']`).each(($size) => {
|
|
||||||
cy.wrap($size).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderType}']`).each(($type) => {
|
|
||||||
cy.wrap($type).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderStatus}']`).each(($status) => {
|
|
||||||
cy.wrap($status).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderPrice}']`).each(($price) => {
|
|
||||||
cy.wrap($price).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderTimeInForce}']`).each(($timeInForce) => {
|
|
||||||
cy.wrap($timeInForce).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.get(`[col-id='${orderUpdatedAt}']`).each(($dateTime) => {
|
|
||||||
cy.wrap($dateTime).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('partially filled orders should not show close/edit buttons', () => {
|
|
||||||
const partiallyFilledId =
|
|
||||||
'94aead3ca92dc932efcb503631b03a410e2a5d4606cae6083e2406dc38e52f78';
|
|
||||||
|
|
||||||
cy.getByTestId('tab-orders').should('be.visible');
|
|
||||||
cy.get('.ag-header-container').within(() => {
|
|
||||||
cy.get('[col-id="status"]').realHover();
|
|
||||||
cy.get('[col-id="status"] .ag-icon-menu').click();
|
|
||||||
});
|
|
||||||
cy.contains('Partially Filled').click();
|
|
||||||
cy.getByTestId('All').click();
|
|
||||||
|
|
||||||
cy.get(`[row-id="${partiallyFilledId}"]`)
|
|
||||||
.eq(0)
|
|
||||||
.within(() => {
|
|
||||||
cy.get(`[col-id='${orderStatus}']`).should(
|
|
||||||
'have.text',
|
|
||||||
'Partially Filled'
|
|
||||||
);
|
|
||||||
cy.get(`[col-id='${orderRemaining}']`).should('have.text', '7');
|
|
||||||
cy.get(`[col-id='${orderSize}']`).should('have.text', '-10');
|
|
||||||
cy.getByTestId(cancelOrderBtn).should('not.exist');
|
|
||||||
cy.getByTestId(editOrderBtn).should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('orders are sorted by most recent order', () => {
|
|
||||||
// 7003-MORD-002
|
|
||||||
const expectedOrderList = [
|
|
||||||
'BTCUSD.MF21',
|
|
||||||
'SOLUSD',
|
|
||||||
'AAPL.MF21',
|
|
||||||
'BTCUSD.MF21',
|
|
||||||
'BTCUSD.MF21',
|
|
||||||
];
|
|
||||||
cy.get('.ag-header-container').within(() => {
|
|
||||||
cy.get('[col-id="status"]').realHover();
|
|
||||||
cy.get('[col-id="status"] .ag-icon-menu').click();
|
|
||||||
});
|
|
||||||
cy.contains('Reset').click();
|
|
||||||
cy.getByTestId('All').click();
|
|
||||||
|
|
||||||
cy.getByTestId('tab-orders')
|
|
||||||
.get(
|
|
||||||
`.ag-center-cols-container [col-id='${orderSymbol}'] [data-testid="market-code"]`
|
|
||||||
)
|
|
||||||
.should('have.length.at.least', expectedOrderList.length)
|
|
||||||
.then(($symbols) => {
|
|
||||||
const symbolNames: string[] = [];
|
|
||||||
cy.wrap($symbols)
|
|
||||||
.each(($symbol) => {
|
|
||||||
cy.wrap($symbol)
|
|
||||||
.invoke('text')
|
|
||||||
.then((text) => {
|
|
||||||
symbolNames.push(text);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(symbolNames).to.include.ordered.members(expectedOrderList);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('subscribe orders', { tags: '@smoke' }, () => {
|
|
||||||
let orderId = '0';
|
|
||||||
beforeEach(() => {
|
|
||||||
const subscriptionMocks = getSubscriptionMocks();
|
|
||||||
cy.spy(subscriptionMocks, 'OrdersUpdate');
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription(subscriptionMocks);
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.getByTestId('All').click();
|
|
||||||
cy.getByTestId('tab-orders').within(() => {
|
|
||||||
cy.get('[col-id="status"][role="columnheader"]')
|
|
||||||
.focus()
|
|
||||||
.find('.ag-header-cell-menu-button')
|
|
||||||
.click();
|
|
||||||
cy.get('.ag-filter-apply-panel-button').click();
|
|
||||||
});
|
|
||||||
orderId = (parseInt(orderId, 10) + 1).toString();
|
|
||||||
});
|
|
||||||
// 7002-SORD-053
|
|
||||||
// 7002-SORD-040
|
|
||||||
// 7003-MORD-001
|
|
||||||
|
|
||||||
it('must see an active order', () => {
|
|
||||||
// 7002-SORD-041
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Active');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see an expired order', () => {
|
|
||||||
// 7002-SORD-042
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_EXPIRED,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Expired');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a cancelled order', () => {
|
|
||||||
// 7002-SORD-043
|
|
||||||
// NOT COVERED: see the txn that cancelled it and a link to the block explorer, if cancelled by a user transaction.
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_CANCELLED,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Cancelled');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a stopped order', () => {
|
|
||||||
// 7002-SORD-044
|
|
||||||
// NOT COVERED: see an explanation of why stopped
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_STOPPED,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Stopped');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a partially filled order', () => {
|
|
||||||
// 7002-SORD-045
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_PARTIALLY_FILLED,
|
|
||||||
size: '5',
|
|
||||||
remaining: '1',
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should(
|
|
||||||
'have.text',
|
|
||||||
'Partially Filled'
|
|
||||||
);
|
|
||||||
cy.getByTestId(`order-status-${orderId}`)
|
|
||||||
.parentsUntil(`.ag-row`)
|
|
||||||
.siblings(`[col-id=${orderRemaining}]`)
|
|
||||||
.should('have.text', '4');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a filled order', () => {
|
|
||||||
// 7002-SORD-046
|
|
||||||
// 7003-MORD-020
|
|
||||||
// NOT COVERED: Must be able to see/link to all trades that were created from this order
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_FILLED,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should('have.text', 'Filled');
|
|
||||||
cy.get(`[col-id="${orderSymbol}"]`).contains('[title="Future"]', 'Futr');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a rejected order', () => {
|
|
||||||
// 7002-SORD-047
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_REJECTED,
|
|
||||||
rejectionReason: Schema.OrderRejectionReason.ORDER_ERROR_INTERNAL_ERROR,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should(
|
|
||||||
'have.text',
|
|
||||||
'Rejected: Internal error'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a parked order', () => {
|
|
||||||
// 7002-SORD-048
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_PARKED,
|
|
||||||
});
|
|
||||||
cy.getByTestId(`order-status-${orderId}`).should(
|
|
||||||
'have.text',
|
|
||||||
'Parked: Internal error'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see the size of the order and direction/side -', () => {
|
|
||||||
// 7003-MORD-003
|
|
||||||
// 7003-MORD-004
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
size: '15',
|
|
||||||
side: Schema.Side.SIDE_SELL,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[col-id="${orderSize}"]`)
|
|
||||||
.should('have.text', '-15');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see the size of the order and direction/side +', () => {
|
|
||||||
// 7003-MORD-003
|
|
||||||
// 7003-MORD-004
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
size: '5',
|
|
||||||
side: Schema.Side.SIDE_BUY,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[col-id="${orderSize}"]`)
|
|
||||||
.should('have.text', '+5');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('for limit typy must see the Limit price that was set on the order', () => {
|
|
||||||
// 7003-MORD-005
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[col-id="price"]')
|
|
||||||
.should('have.text', '200.00');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a pegged order - ask', () => {
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
side: Schema.Side.SIDE_BUY,
|
|
||||||
peggedOrder: {
|
|
||||||
__typename: 'PeggedOrder',
|
|
||||||
reference: Schema.PeggedReference.PEGGED_REFERENCE_BEST_ASK,
|
|
||||||
offset: '250000',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[col-id="type"]')
|
|
||||||
.should('have.text', 'Ask - 2.50 Peg limit');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a pegged order - bid', () => {
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
side: Schema.Side.SIDE_SELL,
|
|
||||||
peggedOrder: {
|
|
||||||
__typename: 'PeggedOrder',
|
|
||||||
reference: Schema.PeggedReference.PEGGED_REFERENCE_BEST_BID,
|
|
||||||
offset: '100',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[col-id="type"]')
|
|
||||||
.should('have.text', 'Bid + 0.001 Peg limit');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see a pegged order - mid', () => {
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
side: Schema.Side.SIDE_SELL,
|
|
||||||
peggedOrder: {
|
|
||||||
__typename: 'PeggedOrder',
|
|
||||||
reference: Schema.PeggedReference.PEGGED_REFERENCE_MID,
|
|
||||||
offset: '0.5',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[col-id="type"]')
|
|
||||||
.should('have.text', 'Mid + 0.00001 Peg limit');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('for market typy must not see a price for active or parked orders', () => {
|
|
||||||
// 7003-MORD-005
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
type: Schema.OrderType.TYPE_MARKET,
|
|
||||||
status: Schema.OrderStatus.STATUS_PARKED,
|
|
||||||
peggedOrder: null,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[col-id="price"]')
|
|
||||||
.should('have.text', '-');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must see the time in force applied to the order', () => {
|
|
||||||
// 7003-MORD-006
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
type: Schema.OrderType.TYPE_MARKET,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[col-id='${orderTimeInForce}']`)
|
|
||||||
.should('have.text', 'GTC');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('for Active order when is part of a liquidity or peg shape, must not see an option to amend the individual order ', () => {
|
|
||||||
// 7003-MORD-008
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: {},
|
|
||||||
liquidityProvisionId: '6536',
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="cancel"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="edit"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('for Active order when is part of a liquidity, must not see an option to amend the individual order ', () => {
|
|
||||||
// 7003-MORD-008
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: {},
|
|
||||||
liquidityProvisionId: '6536',
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="cancel"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="edit"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('for Active order when is part of a peg shape, must not see an option to amend the individual order ', () => {
|
|
||||||
// 7003-MORD-008
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: {},
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="cancel"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="edit"]`)
|
|
||||||
.should('not.exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('amend and cancel order', { tags: '@smoke' }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
const subscriptionMocks = getSubscriptionMocks();
|
|
||||||
cy.spy(subscriptionMocks, 'OrdersUpdate');
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription(subscriptionMocks);
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.getByTestId('All').click();
|
|
||||||
cy.getByTestId('tab-orders').within(() => {
|
|
||||||
cy.get('[col-id="status"][role="columnheader"]')
|
|
||||||
.focus()
|
|
||||||
.find('.ag-header-cell-menu-button')
|
|
||||||
.click();
|
|
||||||
cy.get('.ag-filter-apply-panel-button').click();
|
|
||||||
});
|
|
||||||
cy.mockVegaWalletTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
const orderId = '1234567890';
|
|
||||||
|
|
||||||
// this test is flakey
|
|
||||||
it('must be able to amend the price of an order', () => {
|
|
||||||
// 7003-MORD-007
|
|
||||||
// 7003-MORD-012
|
|
||||||
// 7003-MORD-014
|
|
||||||
// 7003-MORD-015
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: null,
|
|
||||||
liquidityProvisionId: null,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[data-testid="icon-edit"]')
|
|
||||||
.then(($btn) => {
|
|
||||||
cy.wrap($btn).click();
|
|
||||||
cy.getByTestId('dialog-title').should('have.text', 'Edit order');
|
|
||||||
cy.get('#limitPrice').focus().clear().type('100');
|
|
||||||
cy.getByTestId('edit-order').find('[type="submit"]').click();
|
|
||||||
const order: OrderAmendment = {
|
|
||||||
orderId: orderId,
|
|
||||||
marketId: 'market-0',
|
|
||||||
price: '10000000',
|
|
||||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
|
|
||||||
sizeDelta: 0,
|
|
||||||
};
|
|
||||||
testOrderAmendment(order);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('must be able to cancel an individual order', () => {
|
|
||||||
// 7003-MORD-007
|
|
||||||
// 7003-MORD-009
|
|
||||||
// 7003-MORD-010
|
|
||||||
// 7003-MORD-011
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: null,
|
|
||||||
liquidityProvisionId: null,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find(`[data-testid="icon-cross"]`)
|
|
||||||
.then(($btn) => {
|
|
||||||
cy.wrap($btn).click({ force: true });
|
|
||||||
const order: OrderCancellation = {
|
|
||||||
orderId: orderId,
|
|
||||||
marketId: 'market-0',
|
|
||||||
};
|
|
||||||
testOrderCancellation(order);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('must be able to cancel all orders on all markets', () => {
|
|
||||||
// 7003-MORD-009
|
|
||||||
// 7003-MORD-010
|
|
||||||
// 7003-MORD-011
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: null,
|
|
||||||
liquidityProvisionId: null,
|
|
||||||
});
|
|
||||||
cy.get(`[data-testid="cancelAll"]`)
|
|
||||||
.should('have.text', 'Cancel all')
|
|
||||||
.then(($btn) => {
|
|
||||||
cy.wrap($btn).click({ force: true });
|
|
||||||
const order: OrderCancellation = {};
|
|
||||||
testOrderCancellation(order);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it('must be warned (pre-submit) if the input price has too many digits after the decimal place for the market', () => {
|
|
||||||
// 7003-MORD-013
|
|
||||||
updateOrder({
|
|
||||||
id: orderId,
|
|
||||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
|
||||||
peggedOrder: null,
|
|
||||||
liquidityProvisionId: null,
|
|
||||||
});
|
|
||||||
cy.get(`[row-id=${orderId}]`)
|
|
||||||
.find('[data-testid="icon-edit"]')
|
|
||||||
.then(($btn) => {
|
|
||||||
cy.wrap($btn).click({ force: true });
|
|
||||||
cy.getByTestId('dialog-title').should('have.text', 'Edit order');
|
|
||||||
cy.get('#limitPrice').focus().clear().type('0.111111');
|
|
||||||
cy.getByTestId('edit-order').find('[type="submit"]').click();
|
|
||||||
cy.getByTestId('input-error-text').should(
|
|
||||||
'have.text',
|
|
||||||
'Price accepts up to 5 decimal places'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const includeTransferFeeRadioBtn = 'include-transfer-fee';
|
|
||||||
const manageVegaWallet = 'manage-vega-wallet';
|
|
||||||
const toAddressField = '[name="toAddress"]';
|
|
||||||
const totalTransferfee = 'total-transfer-fee';
|
|
||||||
const transferAmount = 'transfer-amount';
|
|
||||||
const transferForm = 'transfer-form';
|
|
||||||
const transferFee = 'transfer-fee';
|
|
||||||
const walletTransfer = 'wallet-transfer';
|
|
||||||
|
|
||||||
const ASSET_SEPOLIA_TBTC = 2;
|
|
||||||
|
|
||||||
describe.skip(
|
|
||||||
'transfer fees',
|
|
||||||
{ tags: '@regression', testIsolation: true },
|
|
||||||
() => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
|
|
||||||
cy.visit('/');
|
|
||||||
cy.getByTestId(manageVegaWallet).click();
|
|
||||||
cy.getByTestId(walletTransfer).click();
|
|
||||||
|
|
||||||
cy.wait('@Assets');
|
|
||||||
cy.wait('@Accounts');
|
|
||||||
|
|
||||||
cy.mockVegaWalletTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('transfer fees tooltips', () => {
|
|
||||||
// 1003-TRAN-015
|
|
||||||
// 1003-TRAN-016
|
|
||||||
// 1003-TRAN-017
|
|
||||||
// 1003-TRAN-018
|
|
||||||
// 1003-TRAN-019
|
|
||||||
cy.getByTestId(transferForm);
|
|
||||||
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, force: true });
|
|
||||||
|
|
||||||
/// Check Include Transfer Fee tooltip
|
|
||||||
cy.get('label[for="include-transfer-fee"] div').realHover();
|
|
||||||
cy.get('[data-side="bottom"] div')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('not.be.empty');
|
|
||||||
|
|
||||||
//Check Transfer Fee tooltip
|
|
||||||
cy.contains('div', 'Transfer fee').realHover();
|
|
||||||
cy.get('[data-side="bottom"] div')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('not.be.empty');
|
|
||||||
|
|
||||||
//Check Amount to be transferred tooltip
|
|
||||||
cy.contains('div', 'Amount to be transferred').realHover();
|
|
||||||
cy.get('[data-side="bottom"] div')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('not.be.empty');
|
|
||||||
|
|
||||||
//Check Total amount (with fee) tooltip
|
|
||||||
cy.contains('div', 'Total amount (with fee)').realHover();
|
|
||||||
cy.get('[data-side="bottom"] div')
|
|
||||||
.should('be.visible')
|
|
||||||
.should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('transfer fees', () => {
|
|
||||||
// 1003-TRAN-020
|
|
||||||
// 1003-TRAN-021
|
|
||||||
// 1003-TRAN-022
|
|
||||||
// 1003-TRAN-023
|
|
||||||
cy.getByTestId(transferForm);
|
|
||||||
cy.contains('Enter manually').click();
|
|
||||||
|
|
||||||
cy.getByTestId(transferForm)
|
|
||||||
.find(toAddressField)
|
|
||||||
.type(
|
|
||||||
'7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535'
|
|
||||||
);
|
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
|
||||||
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
|
||||||
|
|
||||||
cy.getByTestId(transferForm)
|
|
||||||
.find(amountField)
|
|
||||||
.type('1', { delay: 100, force: true });
|
|
||||||
|
|
||||||
cy.getByTestId(transferFee)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '0.01');
|
|
||||||
cy.getByTestId(transferAmount)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '1.00');
|
|
||||||
cy.getByTestId(totalTransferfee)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '1.01');
|
|
||||||
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
|
||||||
cy.getByTestId(transferFee)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '0.01');
|
|
||||||
cy.getByTestId(transferAmount)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '0.99');
|
|
||||||
cy.getByTestId(totalTransferfee)
|
|
||||||
.should('be.visible')
|
|
||||||
.should('contain.text', '1.00');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const transferText = 'transfer-intro-text';
|
|
||||||
const errorText = 'input-error-text';
|
|
||||||
const formFieldError = 'input-error-text';
|
|
||||||
const keyID = `[data-testid="${transferText}"] > .rounded-md`;
|
|
||||||
const manageVegaWallet = 'manage-vega-wallet';
|
|
||||||
const submitTransferBtn = '[type="submit"]';
|
|
||||||
const toAddressField = '[name="toAddress"]';
|
|
||||||
const transferForm = 'transfer-form';
|
|
||||||
const walletTransfer = 'wallet-transfer';
|
|
||||||
|
|
||||||
const ASSET_EURO = 1;
|
|
||||||
const ASSET_SEPOLIA_TBTC = 2;
|
|
||||||
|
|
||||||
describe(
|
|
||||||
'transfer form validation',
|
|
||||||
{ tags: '@regression', testIsolation: true },
|
|
||||||
() => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.getByTestId(manageVegaWallet).click();
|
|
||||||
cy.getByTestId(walletTransfer).click();
|
|
||||||
|
|
||||||
cy.wait('@Accounts');
|
|
||||||
cy.wait('@Assets');
|
|
||||||
|
|
||||||
cy.mockVegaWalletTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('transfer Text', () => {
|
|
||||||
// 1003-TRAN-003
|
|
||||||
cy.getByTestId(transferText)
|
|
||||||
.should('exist')
|
|
||||||
.get(keyID)
|
|
||||||
.invoke('text')
|
|
||||||
.should('match', /[\w.]{6}…[\w.]{6}/);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('invalid vega key validation', () => {
|
|
||||||
//1003-TRAN-013
|
|
||||||
//1003-TRAN-004
|
|
||||||
cy.getByTestId(transferForm).should('be.visible');
|
|
||||||
cy.contains('Enter manually').click();
|
|
||||||
cy.getByTestId(transferForm).find(toAddressField).type('asd');
|
|
||||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
|
||||||
cy.getByTestId(formFieldError).should('contain.text', 'Invalid Vega key');
|
|
||||||
cy.contains('label', 'Vega key').should('be.visible');
|
|
||||||
cy.contains('label', 'Asset').should('be.visible');
|
|
||||||
cy.contains('label', 'Amount').should('be.visible');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('empty fields', () => {
|
|
||||||
// 1003-TRAN-012
|
|
||||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
|
||||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
|
||||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('min amount', () => {
|
|
||||||
// 1002-WITH-010
|
|
||||||
// 1003-TRAN-014
|
|
||||||
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', () => {
|
|
||||||
// 1003-TRAN-002
|
|
||||||
// 1003-TRAN-011
|
|
||||||
// 1003-TRAN-002
|
|
||||||
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'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user