Compare commits
88
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
091b882139 | ||
|
|
61105ccfdb | ||
|
|
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 | ||
|
|
58cc074538 | ||
|
|
852c9bb8f4 | ||
|
|
efe9b7a0e7 | ||
|
|
695a059dde | ||
|
|
5d7e4d273a | ||
|
|
b137e024c6 | ||
|
|
6651ee52b4 | ||
|
|
0d14722c5d | ||
|
|
4968b537b8 | ||
|
|
5f5c45f3b9 | ||
|
|
6b10df015d | ||
|
|
6d7e24732b | ||
|
|
b33cf04e16 | ||
|
|
66ebb26b0f | ||
|
|
51c88d2fce | ||
|
|
2786a984b0 | ||
|
|
39c53a7326 | ||
|
|
3371e8e2cc | ||
|
|
5843a36851 | ||
|
|
68b4909dff | ||
|
|
da8e382454 | ||
|
|
81ebefc329 | ||
|
|
1ed7d6fda2 | ||
|
|
a2e4f34b0d |
@@ -208,7 +208,7 @@ jobs:
|
|||||||
name: '(CI) console python'
|
name: '(CI) console python'
|
||||||
uses: ./.github/workflows/console-test-run.yml
|
uses: ./.github/workflows/console-test-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }} && ${{ contains(fromJSON(needs.build-sources.outputs.projects), 'trading') }}
|
if: needs.check-e2e-needed.outputs.run-tests == 'true' && contains(needs.build-sources.outputs.projects, 'trading')
|
||||||
with:
|
with:
|
||||||
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
|
github-sha: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
|
|||||||
@@ -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,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} />
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
|||||||
import {
|
import {
|
||||||
LiquidityPriceRangeInfoPanel,
|
LiquidityPriceRangeInfoPanel,
|
||||||
LiquiditySLAParametersInfoPanel,
|
LiquiditySLAParametersInfoPanel,
|
||||||
|
MarginScalingFactorsPanel,
|
||||||
PriceMonitoringBoundsInfoPanel,
|
PriceMonitoringBoundsInfoPanel,
|
||||||
SuccessionLineInfoPanel,
|
SuccessionLineInfoPanel,
|
||||||
getDataSourceSpecForSettlementData,
|
getDataSourceSpecForSettlementData,
|
||||||
@@ -17,7 +18,6 @@ import {
|
|||||||
OracleInfoPanel,
|
OracleInfoPanel,
|
||||||
RiskFactorsInfoPanel,
|
RiskFactorsInfoPanel,
|
||||||
RiskModelInfoPanel,
|
RiskModelInfoPanel,
|
||||||
RiskParametersInfoPanel,
|
|
||||||
SettlementAssetInfoPanel,
|
SettlementAssetInfoPanel,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import { MarketInfoTable } from '@vegaprotocol/markets';
|
import { MarketInfoTable } from '@vegaprotocol/markets';
|
||||||
@@ -69,8 +69,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
|||||||
<MetadataInfoPanel market={market} />
|
<MetadataInfoPanel market={market} />
|
||||||
<h2 className={headerClassName}>{t('Risk model')}</h2>
|
<h2 className={headerClassName}>{t('Risk model')}</h2>
|
||||||
<RiskModelInfoPanel market={market} />
|
<RiskModelInfoPanel market={market} />
|
||||||
<h2 className={headerClassName}>{t('Risk parameters')}</h2>
|
<h2 className={headerClassName}>{t('Margin scaling factors')}</h2>
|
||||||
<RiskParametersInfoPanel market={market} />
|
<MarginScalingFactorsPanel market={market} />
|
||||||
<h2 className={headerClassName}>{t('Risk factors')}</h2>
|
<h2 className={headerClassName}>{t('Risk factors')}</h2>
|
||||||
<RiskFactorsInfoPanel market={market} />
|
<RiskFactorsInfoPanel market={market} />
|
||||||
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
|
{(market.data?.priceMonitoringBounds || []).map((trigger, i) => (
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ export const ErrorBoundary = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GHOST = (
|
export const GHOST = (
|
||||||
<svg
|
<svg
|
||||||
width="56"
|
width="56"
|
||||||
height="85"
|
height="85"
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -22,7 +22,11 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
|||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
|
NX_REFERRALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_GOVERNANCE_TRANSFERS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|
||||||
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ NX_ETHERSCAN_URL=https://etherscan.io
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=false
|
||||||
|
NX_REFERRALS=false
|
||||||
|
NX_VOLUME_DISCOUNTS=false
|
||||||
|
|||||||
@@ -6,3 +6,5 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"rationale": {
|
||||||
|
"title": "Governance cancel transfer proposal",
|
||||||
|
"description": "Rejected cancel transfer proposal"
|
||||||
|
},
|
||||||
|
"terms": {
|
||||||
|
"cancelTransfer": {
|
||||||
|
"changes": {
|
||||||
|
"transferId": "invalid transfer id"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"closingTimestamp": 0,
|
||||||
|
"enactmentTimestamp": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,12 +27,16 @@ import {
|
|||||||
} from '../../../../governance-e2e/src/support/staking.functions';
|
} from '../../../../governance-e2e/src/support/staking.functions';
|
||||||
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
|
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
|
||||||
import {
|
import {
|
||||||
|
depositAsset,
|
||||||
switchVegaWalletPubKey,
|
switchVegaWalletPubKey,
|
||||||
vegaWalletSetSpecifiedApprovalAmount,
|
vegaWalletSetSpecifiedApprovalAmount,
|
||||||
} from '../../support/wallet-functions';
|
} from '../../support/wallet-functions';
|
||||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||||
import { createSuccessorMarketProposalTxBody } from '../../support/proposal.functions';
|
import {
|
||||||
|
createGovernanceTransferProposalTxBody,
|
||||||
|
createSuccessorMarketProposalTxBody,
|
||||||
|
} from '../../support/proposal.functions';
|
||||||
|
|
||||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||||
const participationNotMet = 'token-participation-not-met';
|
const participationNotMet = 'token-participation-not-met';
|
||||||
@@ -51,6 +55,7 @@ const openProposals = 'open-proposals';
|
|||||||
const viewProposalButton = 'view-proposal-btn';
|
const viewProposalButton = 'view-proposal-btn';
|
||||||
const proposalTermsToggle = 'proposal-json-toggle';
|
const proposalTermsToggle = 'proposal-json-toggle';
|
||||||
const marketDataToggle = 'proposal-market-data-toggle';
|
const marketDataToggle = 'proposal-market-data-toggle';
|
||||||
|
const governanceTransferToggle = 'proposal-transfer-details';
|
||||||
const marketProposalType = 'proposal-type';
|
const marketProposalType = 'proposal-type';
|
||||||
|
|
||||||
describe(
|
describe(
|
||||||
@@ -520,5 +525,78 @@ describe(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Able to see governance transfer proposal', function () {
|
||||||
|
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
|
||||||
|
depositAsset(vegaAssetAddress, '1000', 18);
|
||||||
|
cy.getByTestId('currency-title', Cypress.env('txTimeout')).should(
|
||||||
|
'contain.text',
|
||||||
|
'Collateral'
|
||||||
|
);
|
||||||
|
cy.VegaWalletTopUpNetworkAccount('100');
|
||||||
|
cy.VegaWalletSubmitProposal(createGovernanceTransferProposalTxBody());
|
||||||
|
cy.reload();
|
||||||
|
getProposalFromTitle('Governance transfer proposal').within(() => {
|
||||||
|
cy.getByTestId(marketProposalType).should('have.text', 'New transfer');
|
||||||
|
cy.getByTestId(viewProposalButton).click();
|
||||||
|
});
|
||||||
|
cy.getByTestId(marketProposalType).should('have.text', 'New transfer');
|
||||||
|
cy.getByTestId(governanceTransferToggle).click();
|
||||||
|
cy.getByTestId('proposal-transfer-details-table').within(() => {
|
||||||
|
getProposalInformationFromTable('Source Type')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'Network Treasury');
|
||||||
|
getProposalInformationFromTable('Destination')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', Cypress.env('vegaWalletPublicKey'));
|
||||||
|
getProposalInformationFromTable('Asset')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'VEGA');
|
||||||
|
getProposalInformationFromTable('Fraction Of Balance')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', '50%');
|
||||||
|
getProposalInformationFromTable('Amount')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', '100.00');
|
||||||
|
getProposalInformationFromTable('Transfer Type')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'All or nothing');
|
||||||
|
getProposalInformationFromTable('Kind')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'One off');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it(' Able to see cancel transfer proposal - rejected', function () {
|
||||||
|
const proposalPath = 'src/fixtures/proposals/cancel-transfer-raw.json';
|
||||||
|
const enactmentTimestamp =
|
||||||
|
createTenDigitUnixTimeStampForSpecifiedDays(11);
|
||||||
|
const closingTimestamp = createTenDigitUnixTimeStampForSpecifiedDays(10);
|
||||||
|
submitUniqueRawProposal({
|
||||||
|
proposalBody: proposalPath,
|
||||||
|
enactmentTimestamp: enactmentTimestamp,
|
||||||
|
closingTimestamp: closingTimestamp,
|
||||||
|
submit: false,
|
||||||
|
});
|
||||||
|
cy.getByTestId('proposal-submit').should('be.visible').click();
|
||||||
|
cy.getByTestId('dialog-title').should('have.text', 'Proposal rejected');
|
||||||
|
cy.getByTestId('icon-cross').last().click();
|
||||||
|
navigateTo(navigation.proposals);
|
||||||
|
cy.get('[href="/proposals/rejected"]').click();
|
||||||
|
getProposalFromTitle('Governance cancel transfer proposal').within(() => {
|
||||||
|
cy.getByTestId(marketProposalType).should(
|
||||||
|
'have.text',
|
||||||
|
'CancelTransfer'
|
||||||
|
);
|
||||||
|
cy.getByTestId(viewProposalButton).click();
|
||||||
|
});
|
||||||
|
cy.getByTestId(marketProposalType).should('have.text', 'Cancel transfer');
|
||||||
|
getProposalInformationFromTable('Error details')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'Governance transfer invalid transfer id not found');
|
||||||
|
getProposalInformationFromTable('transferId')
|
||||||
|
.invoke('text')
|
||||||
|
.and('eq', 'invalid transfer id');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import { addSeconds, millisecondsToSeconds } from 'date-fns';
|
import { addDays, addSeconds, millisecondsToSeconds } from 'date-fns';
|
||||||
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
|
import type { ProposalSubmissionBody } from '@vegaprotocol/wallet';
|
||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import { upgradeProposalsData } from '../fixtures/mocks/network-upgrade';
|
import { upgradeProposalsData } from '../fixtures/mocks/network-upgrade';
|
||||||
import { proposalsData } from '../fixtures/mocks/proposals';
|
import { proposalsData } from '../fixtures/mocks/proposals';
|
||||||
import { nodeData } from '../fixtures/mocks/nodes';
|
import { nodeData } from '../fixtures/mocks/nodes';
|
||||||
|
import { AccountType, GovernanceTransferType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
export function createUpdateNetworkProposalTxBody(): ProposalSubmissionBody {
|
export function createUpdateNetworkProposalTxBody(): ProposalSubmissionBody {
|
||||||
const MIN_CLOSE_SEC = 5;
|
const MIN_CLOSE_SEC = 5;
|
||||||
@@ -358,6 +359,46 @@ export function createSuccessorMarketProposalTxBody(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createGovernanceTransferProposalTxBody(): ProposalSubmissionBody {
|
||||||
|
const MIN_CLOSE_SEC = 5;
|
||||||
|
const MIN_ENACT_SEC = 7;
|
||||||
|
|
||||||
|
const closingDate = addDays(new Date(), MIN_CLOSE_SEC);
|
||||||
|
const enactmentDate = addDays(closingDate, MIN_ENACT_SEC);
|
||||||
|
const closingTimestamp = millisecondsToSeconds(closingDate.getTime());
|
||||||
|
const enactmentTimestamp = millisecondsToSeconds(enactmentDate.getTime());
|
||||||
|
const destination = Cypress.env('vegaWalletPublicKey');
|
||||||
|
return {
|
||||||
|
proposalSubmission: {
|
||||||
|
rationale: {
|
||||||
|
title: 'Governance transfer proposal',
|
||||||
|
description: 'E2E test for transfer proposal test',
|
||||||
|
},
|
||||||
|
terms: {
|
||||||
|
newTransfer: {
|
||||||
|
changes: {
|
||||||
|
fractionOfBalance: '0.5',
|
||||||
|
amount: '100' + '0'.repeat(18),
|
||||||
|
sourceType: AccountType.ACCOUNT_TYPE_NETWORK_TREASURY,
|
||||||
|
source: '',
|
||||||
|
transferType:
|
||||||
|
GovernanceTransferType.GOVERNANCE_TRANSFER_TYPE_ALL_OR_NOTHING,
|
||||||
|
destinationType: AccountType.ACCOUNT_TYPE_GENERAL,
|
||||||
|
destination,
|
||||||
|
asset:
|
||||||
|
'b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b',
|
||||||
|
oneOff: {
|
||||||
|
deliverOn: '0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closingTimestamp,
|
||||||
|
enactmentTimestamp,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function mockNetworkUpgradeProposal() {
|
export function mockNetworkUpgradeProposal() {
|
||||||
cy.mockGQL((req) => {
|
cy.mockGQL((req) => {
|
||||||
aliasGQLQuery(req, 'Nodes', nodeData);
|
aliasGQLQuery(req, 'Nodes', nodeData);
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ export async function depositAsset(
|
|||||||
) {
|
) {
|
||||||
// Approve asset
|
// Approve asset
|
||||||
const faucet = new Token(assetEthAddress, signer);
|
const faucet = new Token(assetEthAddress, signer);
|
||||||
|
// Wait needed to allow Eth chain to catch up
|
||||||
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
|
cy.wait(4000);
|
||||||
cy.wrap(
|
cy.wrap(
|
||||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||||
transactionTimeout
|
transactionTimeout
|
||||||
|
|||||||
@@ -35,4 +35,6 @@ NX_SUCCESSOR_MARKETS=true
|
|||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=false
|
||||||
|
NX_REFERRALS=false
|
||||||
NX_GOVERNANCE_TRANSFERS=false
|
NX_GOVERNANCE_TRANSFERS=false
|
||||||
|
NX_VOLUME_DISCOUNTS=false
|
||||||
|
|||||||
@@ -35,3 +35,5 @@ NX_SUCCESSOR_MARKETS=false
|
|||||||
NX_METAMASK_SNAPS=false
|
NX_METAMASK_SNAPS=false
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -27,3 +27,5 @@ NX_SUCCESSOR_MARKETS=true
|
|||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -26,3 +26,5 @@ NX_SUCCESSOR_MARKETS=false
|
|||||||
NX_METAMASK_SNAPS=false
|
NX_METAMASK_SNAPS=false
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=false
|
||||||
|
NX_REFERRALS=false
|
||||||
|
NX_VOLUME_DISCOUNTS=false
|
||||||
|
|||||||
@@ -25,3 +25,5 @@ NX_SUCCESSOR_MARKETS=false
|
|||||||
NX_METAMASK_SNAPS=false
|
NX_METAMASK_SNAPS=false
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=false
|
||||||
|
NX_REFERRALS=false
|
||||||
|
NX_VOLUME_DISCOUNTS=false
|
||||||
|
|||||||
@@ -23,4 +23,6 @@ NX_SUCCESSOR_MARKETS=true
|
|||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
NX_GOVERNANCE_TRANSFERS=true
|
NX_GOVERNANCE_TRANSFERS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -28,3 +28,5 @@ NX_SUCCESSOR_MARKETS=true
|
|||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_PRODUCT_PERPETUALS=true
|
NX_PRODUCT_PERPETUALS=true
|
||||||
NX_UPDATE_MARKET_STATE=true
|
NX_UPDATE_MARKET_STATE=true
|
||||||
|
NX_REFERRALS=true
|
||||||
|
NX_VOLUME_DISCOUNTS=true
|
||||||
|
|||||||
@@ -24,3 +24,5 @@ NX_SUCCESSOR_MARKETS=false
|
|||||||
NX_METAMASK_SNAPS=false
|
NX_METAMASK_SNAPS=false
|
||||||
NX_PRODUCT_PERPETUALS=false
|
NX_PRODUCT_PERPETUALS=false
|
||||||
NX_UPDATE_MARKET_STATE=false
|
NX_UPDATE_MARKET_STATE=false
|
||||||
|
NX_REFERRALS=false
|
||||||
|
NX_VOLUME_DISCOUNTS=false
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
|
process.env.TZ = 'GMT';
|
||||||
export default {
|
export default {
|
||||||
displayName: 'governance',
|
displayName: 'governance',
|
||||||
preset: '../../jest.preset.js',
|
preset: '../../jest.preset.js',
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -709,6 +710,8 @@
|
|||||||
"NewMarketProposal": "New market proposal",
|
"NewMarketProposal": "New market proposal",
|
||||||
"UpdateMarketProposal": "Update market proposal",
|
"UpdateMarketProposal": "Update market proposal",
|
||||||
"UpdateMarketStateProposal": "Update market state proposal",
|
"UpdateMarketStateProposal": "Update market state proposal",
|
||||||
|
"UpdateReferralProgramProposal": "Update referral program proposal",
|
||||||
|
"UpdateVolumeDiscountProgramProposal": "Update volume discount program proposal",
|
||||||
"MarketChange": "Market change",
|
"MarketChange": "Market change",
|
||||||
"MarketStateChange": "Market state change",
|
"MarketStateChange": "Market state change",
|
||||||
"MarketDetails": "Market details",
|
"MarketDetails": "Market details",
|
||||||
@@ -716,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",
|
||||||
@@ -732,8 +737,12 @@
|
|||||||
"NewMarketSpotProduct": "New market - spot",
|
"NewMarketSpotProduct": "New market - spot",
|
||||||
"UpdateMarket": "Update market",
|
"UpdateMarket": "Update market",
|
||||||
"UpdateMarketState": "Update market state",
|
"UpdateMarketState": "Update market state",
|
||||||
|
"UpdateReferralProgram": "Update referral 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)",
|
||||||
@@ -890,5 +899,59 @@
|
|||||||
"HowToProposeRawStep2": "2. Use the appropriate proposal template in the docs:",
|
"HowToProposeRawStep2": "2. Use the appropriate proposal template in the docs:",
|
||||||
"HowToProposeRawStep3": "3. Submit on-chain below",
|
"HowToProposeRawStep3": "3. Submit on-chain below",
|
||||||
"proposalTransferDetails": "New governance transfer details",
|
"proposalTransferDetails": "New governance transfer details",
|
||||||
"proposalCancelTransferDetails": "Cancel governance transfer details"
|
"proposalCancelTransferDetails": "Cancel governance transfer details",
|
||||||
|
"BenefitTiers": "Benefit tiers",
|
||||||
|
"BenefitTierMinimumEpochs": "Minimum epochs",
|
||||||
|
"BenefitTierMinimumEpochsDescription": "The minimum number of epochs the party needs to be in the referral set to be eligible for the benefit",
|
||||||
|
"BenefitTierMinimumRunningNotionalTakerVolume": "Minimum running notional taker volume",
|
||||||
|
"BenefitTierMinimumRunningNotionalTakerVolumeDescription": "The minimum running notional for the given benefit tier",
|
||||||
|
"BenefitTierReferralDiscountFactor": "Referral discount factor",
|
||||||
|
"BenefitTierReferralDiscountFactorDescription": "The proportion of the referee's taker fees to be discounted",
|
||||||
|
"BenefitTierReferralRewardFactor": "Referral reward factor",
|
||||||
|
"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",
|
||||||
|
"StakingTierMinimumStakedTokens": "Minimum staked tokens",
|
||||||
|
"StakingTierMinimumStakedTokensDescription": "Required number of governance tokens ($VEGA) a referrer must have staked to receive the multiplier",
|
||||||
|
"StakingTierReferralRewardMultiplier": "Referral reward multiplier",
|
||||||
|
"StakingTierReferralRewardMultiplierDescription": "Multiplier applied to the referral reward factor when calculating referral rewards due to the referrer",
|
||||||
|
"WindowLength": "Window length",
|
||||||
|
"WindowLengthDescription": "Number of epochs over which to evaluate a referral set's running volume",
|
||||||
|
"EndOfProgramTimestamp": "End of program",
|
||||||
|
"EndOfProgramTimestampDescription": "Time after which when the current epoch ends, the programs will end and benefits will be disabled.",
|
||||||
|
"BenefitTierVolumeDiscountFactor": "Volume discount factor",
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,6 +188,8 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
variables: {
|
variables: {
|
||||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||||
|
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||||
|
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
+33
-5
@@ -98,6 +98,16 @@ export const ProposalHeader = ({
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'UpdateReferralProgram': {
|
||||||
|
proposalType = 'UpdateReferralProgram';
|
||||||
|
fallbackTitle = t('UpdateReferralProgramProposal');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'UpdateVolumeDiscountProgram': {
|
||||||
|
proposalType = 'UpdateVolumeDiscountProgram';
|
||||||
|
fallbackTitle = t('UpdateVolumeDiscountProgramProposal');
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'NewAsset': {
|
case 'NewAsset': {
|
||||||
proposalType = 'NewAsset';
|
proposalType = 'NewAsset';
|
||||||
fallbackTitle = t('NewAssetProposal');
|
fallbackTitle = t('NewAssetProposal');
|
||||||
@@ -227,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);
|
||||||
|
|
||||||
@@ -244,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);
|
||||||
|
|
||||||
@@ -253,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;
|
||||||
|
|||||||
+5
-3
@@ -12,12 +12,12 @@ import {
|
|||||||
PriceMonitoringBoundsInfoPanel,
|
PriceMonitoringBoundsInfoPanel,
|
||||||
RiskFactorsInfoPanel,
|
RiskFactorsInfoPanel,
|
||||||
RiskModelInfoPanel,
|
RiskModelInfoPanel,
|
||||||
RiskParametersInfoPanel,
|
|
||||||
SettlementAssetInfoPanel,
|
SettlementAssetInfoPanel,
|
||||||
getDataSourceSpecForSettlementSchedule,
|
getDataSourceSpecForSettlementSchedule,
|
||||||
getDataSourceSpecForSettlementData,
|
getDataSourceSpecForSettlementData,
|
||||||
getDataSourceSpecForTradingTermination,
|
getDataSourceSpecForTradingTermination,
|
||||||
getSigners,
|
getSigners,
|
||||||
|
MarginScalingFactorsPanel,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
@@ -219,8 +219,10 @@ export const ProposalMarketData = ({
|
|||||||
parentMarket={parentMarketData}
|
parentMarket={parentMarketData}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<h2 className={marketDataHeaderStyles}>{t('Risk parameters')}</h2>
|
<h2 className={marketDataHeaderStyles}>
|
||||||
<RiskParametersInfoPanel
|
{t('Margin scaling factors')}
|
||||||
|
</h2>
|
||||||
|
<MarginScalingFactorsPanel
|
||||||
market={marketData}
|
market={marketData}
|
||||||
parentMarket={parentMarketData}
|
parentMarket={parentMarketData}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-referral-program-details';
|
||||||
+156
@@ -0,0 +1,156 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import {
|
||||||
|
formatMinimumRunningNotionalTakerVolume,
|
||||||
|
formatReferralDiscountFactor,
|
||||||
|
formatReferralRewardFactor,
|
||||||
|
formatMinimumStakedTokens,
|
||||||
|
formatReferralRewardMultiplier,
|
||||||
|
ProposalReferralProgramDetails,
|
||||||
|
} from './proposal-referral-program-details';
|
||||||
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
|
|
||||||
|
jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||||
|
useAppState: () => ({
|
||||||
|
appState: {
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.setSystemTime(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ProposalReferralProgramDetails helper functions', () => {
|
||||||
|
it('should format minimum running notional taker volume correctly', () => {
|
||||||
|
const input = '1000';
|
||||||
|
const formatted = formatMinimumRunningNotionalTakerVolume(input);
|
||||||
|
expect(formatted).toBe('1,000');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format referral discount factor correctly', () => {
|
||||||
|
const input = '0.05';
|
||||||
|
const formatted = formatReferralDiscountFactor(input);
|
||||||
|
expect(formatted).toBe('5.00%');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format referral reward factor correctly', () => {
|
||||||
|
const input = '0.1';
|
||||||
|
const formatted = formatReferralRewardFactor(input);
|
||||||
|
expect(formatted).toBe('10.00%');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format minimum staked tokens correctly', () => {
|
||||||
|
const input = '15';
|
||||||
|
const decimals = 18;
|
||||||
|
const formatted = formatMinimumStakedTokens(input, decimals);
|
||||||
|
expect(formatted).toBe('0.000000000000000015');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should format referral reward multiplier correctly', () => {
|
||||||
|
const input = '3';
|
||||||
|
const formatted = formatReferralRewardMultiplier(input);
|
||||||
|
expect(formatted).toBe('3x');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockReferralProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateReferralProgram',
|
||||||
|
benefitTiers: [
|
||||||
|
{
|
||||||
|
minimumEpochs: 6,
|
||||||
|
minimumRunningNotionalTakerVolume: '10000',
|
||||||
|
referralDiscountFactor: '0.001',
|
||||||
|
referralRewardFactor: '0.001',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumEpochs: 24,
|
||||||
|
minimumRunningNotionalTakerVolume: '500000',
|
||||||
|
referralDiscountFactor: '0.005',
|
||||||
|
referralRewardFactor: '0.005',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumEpochs: 48,
|
||||||
|
minimumRunningNotionalTakerVolume: '1000000',
|
||||||
|
referralDiscountFactor: '0.01',
|
||||||
|
referralRewardFactor: '0.01',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
endOfProgram: '2026-10-03T10:34:34Z',
|
||||||
|
windowLength: 3,
|
||||||
|
stakingTiers: [
|
||||||
|
{
|
||||||
|
minimumStakedTokens: '1',
|
||||||
|
referralRewardMultiplier: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumStakedTokens: '2',
|
||||||
|
referralRewardMultiplier: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumStakedTokens: '5',
|
||||||
|
referralRewardMultiplier: '3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('<ProposalReferralProgramDetails />', () => {
|
||||||
|
it('should not render if proposal is null', () => {
|
||||||
|
render(<ProposalReferralProgramDetails proposal={null} />);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-referral-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if __typename is not UpdateReferralProgram', () => {
|
||||||
|
const updateMarketProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateMarket',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
render(<ProposalReferralProgramDetails proposal={updateMarketProposal} />);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-referral-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if there are no relevant fields', () => {
|
||||||
|
const incompleteProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<ProposalReferralProgramDetails proposal={incompleteProposal} />);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-referral-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render relevant fields if present', () => {
|
||||||
|
render(<ProposalReferralProgramDetails proposal={mockReferralProposal} />);
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-referral-program-window-length')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-referral-program-end-of-program-timestamp')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-referral-program-benefit-tiers')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-referral-program-benefit-tiers')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
+232
@@ -0,0 +1,232 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import {
|
||||||
|
KeyValueTable,
|
||||||
|
KeyValueTableRow,
|
||||||
|
RoundedWrapper,
|
||||||
|
Tooltip,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { formatNumber } from '../../../../lib/format-number';
|
||||||
|
import {
|
||||||
|
formatDateWithLocalTimezone,
|
||||||
|
formatNumberPercentage,
|
||||||
|
toBigNum,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
|
|
||||||
|
interface ProposalReferralProgramDetailsProps {
|
||||||
|
proposal: ProposalQuery['proposal'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatEndOfProgramTimestamp = (value: string) => {
|
||||||
|
return formatDateWithLocalTimezone(new Date(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatMinimumRunningNotionalTakerVolume = (value: string) => {
|
||||||
|
return formatNumber(toBigNum(value, 0), 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatReferralDiscountFactor = (value: string) => {
|
||||||
|
return formatNumberPercentage(new BigNumber(value).times(100));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatReferralRewardFactor = (value: string) => {
|
||||||
|
return formatNumberPercentage(new BigNumber(value).times(100));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatMinimumStakedTokens = (value: string, decimals: number) => {
|
||||||
|
return formatNumber(toBigNum(value, decimals));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const formatReferralRewardMultiplier = (value: string) => {
|
||||||
|
return `${value}x`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProposalReferralProgramDetails = ({
|
||||||
|
proposal,
|
||||||
|
}: ProposalReferralProgramDetailsProps) => {
|
||||||
|
const {
|
||||||
|
appState: { decimals },
|
||||||
|
} = useAppState();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
if (proposal?.terms?.change?.__typename !== 'UpdateReferralProgram') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const benefitTiers = proposal?.terms?.change?.benefitTiers;
|
||||||
|
const stakingTiers = proposal?.terms?.change?.stakingTiers;
|
||||||
|
const windowLength = proposal?.terms?.change?.windowLength;
|
||||||
|
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!benefitTiers &&
|
||||||
|
!stakingTiers &&
|
||||||
|
!windowLength &&
|
||||||
|
!endOfProgramTimestamp
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid="proposal-referral-program-details">
|
||||||
|
<RoundedWrapper paddingBottom={true}>
|
||||||
|
{windowLength && (
|
||||||
|
<div data-testid="proposal-referral-program-window-length">
|
||||||
|
<KeyValueTable>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip description={t('WindowLengthDescription')}>
|
||||||
|
<span>{t('WindowLength')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{windowLength}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{endOfProgramTimestamp && (
|
||||||
|
<div
|
||||||
|
className="mb-6"
|
||||||
|
data-testid="proposal-referral-program-end-of-program-timestamp"
|
||||||
|
>
|
||||||
|
<KeyValueTable>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip description={t('EndOfProgramTimestampDescription')}>
|
||||||
|
<span>{t('EndOfProgramTimestamp')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatEndOfProgramTimestamp(endOfProgramTimestamp)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{benefitTiers && (
|
||||||
|
<div
|
||||||
|
className="mb-6"
|
||||||
|
data-testid="proposal-referral-program-benefit-tiers"
|
||||||
|
>
|
||||||
|
<h3 className="mb-3 uppercase font-semibold text-lg">
|
||||||
|
{t('BenefitTiers')}
|
||||||
|
</h3>
|
||||||
|
<KeyValueTable>
|
||||||
|
{benefitTiers
|
||||||
|
.sort((a, b) => a.minimumEpochs - b.minimumEpochs)
|
||||||
|
.map((benefitTier, index) => (
|
||||||
|
<div className="mb-4" key={index}>
|
||||||
|
<h4 className="font-semibold uppercase">
|
||||||
|
Tier {index + 1}
|
||||||
|
</h4>
|
||||||
|
{benefitTier.minimumEpochs && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t('BenefitTierMinimumEpochsDescription')}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierMinimumEpochs')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{benefitTier.minimumEpochs}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{benefitTier.minimumRunningNotionalTakerVolume && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierMinimumRunningNotionalTakerVolumeDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{t('BenefitTierMinimumRunningNotionalTakerVolume')}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatMinimumRunningNotionalTakerVolume(
|
||||||
|
benefitTier.minimumRunningNotionalTakerVolume
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{benefitTier.referralDiscountFactor && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierReferralDiscountFactorDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierReferralDiscountFactor')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatReferralDiscountFactor(
|
||||||
|
benefitTier.referralDiscountFactor
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{benefitTier.referralRewardFactor && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierReferralRewardFactorDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierReferralRewardFactor')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatReferralRewardFactor(
|
||||||
|
benefitTier.referralRewardFactor
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{stakingTiers && (
|
||||||
|
<div data-testid="proposal-referral-program-staking-tiers">
|
||||||
|
<h3 className="mb-3 uppercase font-semibold text-lg">
|
||||||
|
{t('StakingTiers')}
|
||||||
|
</h3>
|
||||||
|
<KeyValueTable>
|
||||||
|
{stakingTiers
|
||||||
|
.sort(
|
||||||
|
(a, b) =>
|
||||||
|
Number(a.minimumStakedTokens) -
|
||||||
|
Number(b.minimumStakedTokens)
|
||||||
|
)
|
||||||
|
.map((stakingTier, index) => (
|
||||||
|
<div className="mb-4" key={index}>
|
||||||
|
{stakingTier.referralRewardMultiplier && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'StakingTierReferralRewardMultiplierDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{t('StakingTierReferralRewardMultiplier')}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatReferralRewardMultiplier(
|
||||||
|
stakingTier.referralRewardMultiplier
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{stakingTier.minimumStakedTokens && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'StakingTierMinimumStakedTokensFactorDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('StakingTierMinimumStakedTokens')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatMinimumStakedTokens(
|
||||||
|
stakingTier.minimumStakedTokens,
|
||||||
|
decimals
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</RoundedWrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
+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>
|
||||||
|
);
|
||||||
|
};
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-volume-discount-program-details';
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { ProposalVolumeDiscountProgramDetails } from './proposal-volume-discount-program-details';
|
||||||
|
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||||
|
|
||||||
|
jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||||
|
useAppState: () => ({
|
||||||
|
appState: {
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockReferralProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateVolumeDiscountProgram',
|
||||||
|
benefitTiers: [
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '10000',
|
||||||
|
volumeDiscountFactor: '0.05',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '50000',
|
||||||
|
volumeDiscountFactor: '0.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '100000',
|
||||||
|
volumeDiscountFactor: '0.15',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '250000',
|
||||||
|
volumeDiscountFactor: '0.2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '500000',
|
||||||
|
volumeDiscountFactor: '0.25',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '1000000',
|
||||||
|
volumeDiscountFactor: '0.3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '1500000',
|
||||||
|
volumeDiscountFactor: '0.35',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
minimumRunningNotionalTakerVolume: '2000000',
|
||||||
|
volumeDiscountFactor: '0.4',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
endOfProgramTimestamp: '1970-01-01T00:00:01.791568493Z',
|
||||||
|
windowLength: 7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ProposalVolumeDiscountProgramDetails', () => {
|
||||||
|
it('should not render if proposal is null', () => {
|
||||||
|
render(<ProposalVolumeDiscountProgramDetails proposal={null} />);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-volume-discount-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if __typename is not UpdateVolumeDiscountProgram', () => {
|
||||||
|
const updateMarketProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateMarket',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
render(
|
||||||
|
<ProposalVolumeDiscountProgramDetails proposal={updateMarketProposal} />
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-volume-discount-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render if there are no relevant fields', () => {
|
||||||
|
const incompleteProposal = generateProposal({
|
||||||
|
terms: {
|
||||||
|
change: {
|
||||||
|
__typename: 'UpdateVolumeDiscountProgram',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<ProposalVolumeDiscountProgramDetails proposal={incompleteProposal} />
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('proposal-volume-discount-program-details')
|
||||||
|
).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render relevant fields if present', () => {
|
||||||
|
render(
|
||||||
|
<ProposalVolumeDiscountProgramDetails proposal={mockReferralProposal} />
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-volume-discount-program-window-length')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByTestId(
|
||||||
|
'proposal-volume-discount-program-end-of-program-timestamp'
|
||||||
|
)
|
||||||
|
).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('proposal-volume-discount-program-benefit-tiers')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
+130
@@ -0,0 +1,130 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import {
|
||||||
|
KeyValueTable,
|
||||||
|
KeyValueTableRow,
|
||||||
|
RoundedWrapper,
|
||||||
|
Tooltip,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
formatEndOfProgramTimestamp,
|
||||||
|
formatMinimumRunningNotionalTakerVolume,
|
||||||
|
} from '../proposal-referral-program-details';
|
||||||
|
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||||
|
import BigNumber from 'bignumber.js';
|
||||||
|
|
||||||
|
interface ProposalReferralProgramDetailsProps {
|
||||||
|
proposal: ProposalQuery['proposal'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const formatVolumeDiscountFactor = (value: string) => {
|
||||||
|
return formatNumberPercentage(new BigNumber(value).times(100));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProposalVolumeDiscountProgramDetails = ({
|
||||||
|
proposal,
|
||||||
|
}: ProposalReferralProgramDetailsProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
if (proposal?.terms?.change?.__typename !== 'UpdateVolumeDiscountProgram') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const benefitTiers = proposal?.terms?.change?.benefitTiers;
|
||||||
|
const windowLength = proposal?.terms?.change?.windowLength;
|
||||||
|
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgramTimestamp;
|
||||||
|
|
||||||
|
if (!benefitTiers && !windowLength && !endOfProgramTimestamp) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div data-testid="proposal-volume-discount-program-details">
|
||||||
|
<RoundedWrapper paddingBottom={true}>
|
||||||
|
{windowLength && (
|
||||||
|
<div data-testid="proposal-volume-discount-program-window-length">
|
||||||
|
<KeyValueTable>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip description={t('WindowLengthDescription')}>
|
||||||
|
<span>{t('WindowLength')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{windowLength}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{endOfProgramTimestamp && (
|
||||||
|
<div
|
||||||
|
className="mb-6"
|
||||||
|
data-testid="proposal-volume-discount-program-end-of-program-timestamp"
|
||||||
|
>
|
||||||
|
<KeyValueTable>
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip description={t('EndOfProgramTimestampDescription')}>
|
||||||
|
<span>{t('EndOfProgramTimestamp')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatEndOfProgramTimestamp(endOfProgramTimestamp)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{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.minimumRunningNotionalTakerVolume) -
|
||||||
|
Number(b.minimumRunningNotionalTakerVolume)
|
||||||
|
)
|
||||||
|
.map((benefitTier, index) => (
|
||||||
|
<div className="mb-4" key={index}>
|
||||||
|
<h4 className="font-semibold uppercase">
|
||||||
|
Tier {index + 1}
|
||||||
|
</h4>
|
||||||
|
{benefitTier.minimumRunningNotionalTakerVolume && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierMinimumRunningNotionalTakerVolumeDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
{t('BenefitTierMinimumRunningNotionalTakerVolume')}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatMinimumRunningNotionalTakerVolume(
|
||||||
|
benefitTier.minimumRunningNotionalTakerVolume
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
{benefitTier.volumeDiscountFactor && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<Tooltip
|
||||||
|
description={t(
|
||||||
|
'BenefitTierVolumeDiscountFactorDescription'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span>{t('BenefitTierVolumeDiscountFactor')}</span>
|
||||||
|
</Tooltip>
|
||||||
|
{formatVolumeDiscountFactor(
|
||||||
|
benefitTier.volumeDiscountFactor
|
||||||
|
)}
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</KeyValueTable>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</RoundedWrapper>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -6,6 +6,8 @@ import { ProposalDescription } from '../proposal-description';
|
|||||||
import { ProposalChangeTable } from '../proposal-change-table';
|
import { ProposalChangeTable } from '../proposal-change-table';
|
||||||
import { ProposalJson } from '../proposal-json';
|
import { ProposalJson } from '../proposal-json';
|
||||||
import { ProposalAssetDetails } from '../proposal-asset-details';
|
import { ProposalAssetDetails } from '../proposal-asset-details';
|
||||||
|
import { ProposalReferralProgramDetails } from '../proposal-referral-program-details';
|
||||||
|
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
|
||||||
import { UserVote } from '../vote-details';
|
import { UserVote } from '../vote-details';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
@@ -25,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'];
|
||||||
@@ -113,6 +116,15 @@ export const Proposal = ({
|
|||||||
// TODO: check minVoterBalance for 'CancelTransfer'
|
// TODO: check minVoterBalance for 'CancelTransfer'
|
||||||
minVoterBalance =
|
minVoterBalance =
|
||||||
networkParams.governance_proposal_freeform_minVoterBalance;
|
networkParams.governance_proposal_freeform_minVoterBalance;
|
||||||
|
break;
|
||||||
|
case 'UpdateReferralProgram':
|
||||||
|
minVoterBalance =
|
||||||
|
networkParams.governance_proposal_referralProgram_minVoterBalance;
|
||||||
|
break;
|
||||||
|
case 'UpdateVolumeDiscountProgram':
|
||||||
|
minVoterBalance =
|
||||||
|
networkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,6 +232,26 @@ export const Proposal = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{proposal.terms.change.__typename === 'UpdateReferralProgram' && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalReferralProgramDetails proposal={proposal} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{proposal.terms.change.__typename === 'UpdateVolumeDiscountProgram' && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalVolumeDiscountProgramDetails proposal={proposal} />
|
||||||
|
</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">
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ export const useProposalNetworkParams = ({
|
|||||||
NetworkParams.governance_proposal_market_requiredMajority,
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
NetworkParams.governance_proposal_market_requiredParticipation,
|
NetworkParams.governance_proposal_market_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_referralProgram_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_referralProgram_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_updateAsset_requiredParticipation,
|
NetworkParams.governance_proposal_updateAsset_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_asset_requiredMajority,
|
NetworkParams.governance_proposal_asset_requiredMajority,
|
||||||
NetworkParams.governance_proposal_asset_requiredParticipation,
|
NetworkParams.governance_proposal_asset_requiredParticipation,
|
||||||
@@ -26,6 +28,10 @@ export const useProposalNetworkParams = ({
|
|||||||
NetworkParams.governance_proposal_updateNetParam_requiredParticipation,
|
NetworkParams.governance_proposal_updateNetParam_requiredParticipation,
|
||||||
NetworkParams.governance_proposal_freeform_requiredMajority,
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
NetworkParams.governance_proposal_freeform_requiredParticipation,
|
||||||
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredParticipation,
|
||||||
|
NetworkParams.governance_proposal_transfer_requiredParticipation,
|
||||||
|
NetworkParams.governance_proposal_transfer_requiredMajority,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const fallback = {
|
const fallback = {
|
||||||
@@ -91,6 +97,30 @@ export const useProposalNetworkParams = ({
|
|||||||
params.governance_proposal_freeform_requiredParticipation
|
params.governance_proposal_freeform_requiredParticipation
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
case 'UpdateReferralProgram':
|
||||||
|
return {
|
||||||
|
requiredMajority:
|
||||||
|
params.governance_proposal_referralProgram_requiredMajority,
|
||||||
|
requiredParticipation: new BigNumber(
|
||||||
|
params.governance_proposal_referralProgram_requiredParticipation
|
||||||
|
),
|
||||||
|
};
|
||||||
|
case 'UpdateVolumeDiscountProgram':
|
||||||
|
return {
|
||||||
|
requiredMajority:
|
||||||
|
params.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
||||||
|
requiredParticipation: new BigNumber(
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,10 +43,48 @@ fragment UpdateMarketState on Proposal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment UpdateReferralProgram on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateReferralProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumEpochs
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
referralDiscountFactor
|
||||||
|
referralRewardFactor
|
||||||
|
}
|
||||||
|
endOfProgram: endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
stakingTiers {
|
||||||
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment UpdateVolumeDiscountProgram on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateVolumeDiscountProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
volumeDiscountFactor
|
||||||
|
}
|
||||||
|
endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query Proposal(
|
query Proposal(
|
||||||
$proposalId: ID!
|
$proposalId: ID!
|
||||||
$includeNewMarketProductField: Boolean!
|
$includeNewMarketProductField: Boolean!
|
||||||
$includeUpdateMarketState: Boolean!
|
$includeUpdateMarketState: Boolean!
|
||||||
|
$includeUpdateReferralProgram: Boolean!
|
||||||
|
$includeUpdateVolumeDiscountProgram: Boolean!
|
||||||
) {
|
) {
|
||||||
proposal(id: $proposalId) {
|
proposal(id: $proposalId) {
|
||||||
id
|
id
|
||||||
@@ -64,6 +102,9 @@ query Proposal(
|
|||||||
errorDetails
|
errorDetails
|
||||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||||
|
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
|
||||||
|
...UpdateVolumeDiscountProgram
|
||||||
|
@include(if: $includeUpdateVolumeDiscountProgram)
|
||||||
terms {
|
terms {
|
||||||
closingDatetime
|
closingDatetime
|
||||||
enactmentDatetime
|
enactmentDatetime
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -36,6 +36,8 @@ export const ProposalContainer = () => {
|
|||||||
NetworkParams.governance_proposal_updateAsset_minVoterBalance,
|
NetworkParams.governance_proposal_updateAsset_minVoterBalance,
|
||||||
NetworkParams.governance_proposal_updateNetParam_minVoterBalance,
|
NetworkParams.governance_proposal_updateNetParam_minVoterBalance,
|
||||||
NetworkParams.governance_proposal_freeform_minVoterBalance,
|
NetworkParams.governance_proposal_freeform_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_referralProgram_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_VolumeDiscountProgram_minVoterBalance,
|
||||||
NetworkParams.spam_protection_voting_min_tokens,
|
NetworkParams.spam_protection_voting_min_tokens,
|
||||||
NetworkParams.governance_proposal_market_requiredMajority,
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||||
@@ -44,6 +46,8 @@ export const ProposalContainer = () => {
|
|||||||
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||||
NetworkParams.governance_proposal_freeform_requiredMajority,
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_referralProgram_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_VolumeDiscountProgram_requiredMajority,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -57,6 +61,8 @@ export const ProposalContainer = () => {
|
|||||||
proposalId: params.proposalId || '',
|
proposalId: params.proposalId || '',
|
||||||
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
||||||
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
||||||
|
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
|
||||||
|
includeUpdateVolumeDiscountProgram: !!FLAGS.VOLUME_DISCOUNTS,
|
||||||
},
|
},
|
||||||
skip: !params.proposalId,
|
skip: !params.proposalId,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -43,6 +43,42 @@ fragment UpdateMarketStates on Proposal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fragment UpdateReferralPrograms on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateReferralProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumEpochs
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
referralDiscountFactor
|
||||||
|
referralRewardFactor
|
||||||
|
}
|
||||||
|
endOfProgram: endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
stakingTiers {
|
||||||
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment UpdateVolumeDiscountPrograms on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateVolumeDiscountProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
volumeDiscountFactor
|
||||||
|
}
|
||||||
|
endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fragment ProposalFields on Proposal {
|
fragment ProposalFields on Proposal {
|
||||||
id
|
id
|
||||||
rationale {
|
rationale {
|
||||||
@@ -127,6 +163,8 @@ fragment ProposalFields on Proposal {
|
|||||||
query Proposals(
|
query Proposals(
|
||||||
$includeNewMarketProductFields: Boolean!
|
$includeNewMarketProductFields: Boolean!
|
||||||
$includeUpdateMarketStates: Boolean!
|
$includeUpdateMarketStates: Boolean!
|
||||||
|
$includeUpdateReferralPrograms: Boolean!
|
||||||
|
$includeUpdateVolumeDiscountPrograms: Boolean!
|
||||||
) {
|
) {
|
||||||
proposalsConnection {
|
proposalsConnection {
|
||||||
edges {
|
edges {
|
||||||
@@ -134,6 +172,9 @@ query Proposals(
|
|||||||
...ProposalFields
|
...ProposalFields
|
||||||
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
||||||
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
||||||
|
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
|
||||||
|
...UpdateVolumeDiscountPrograms
|
||||||
|
@include(if: $includeUpdateVolumeDiscountPrograms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-3
@@ -7,15 +7,21 @@ 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', 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 ProposalFieldsFragment = { __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 } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
|
export type ProposalFieldsFragment = { __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 } } | { __typename?: 'NewSpotMarket' } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateMarketState' } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } | { __typename?: 'UpdateReferralProgram' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
|
||||||
|
|
||||||
export type ProposalsQueryVariables = Types.Exact<{
|
export type ProposalsQueryVariables = Types.Exact<{
|
||||||
includeNewMarketProductFields: Types.Scalars['Boolean'];
|
includeNewMarketProductFields: Types.Scalars['Boolean'];
|
||||||
includeUpdateMarketStates: Types.Scalars['Boolean'];
|
includeUpdateMarketStates: Types.Scalars['Boolean'];
|
||||||
|
includeUpdateReferralPrograms: Types.Scalars['Boolean'];
|
||||||
|
includeUpdateVolumeDiscountPrograms: Types.Scalars['Boolean'];
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
|
||||||
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' } | { __typename?: 'UpdateSpotMarket' } | { __typename?: 'UpdateVolumeDiscountProgram' } }, 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 {
|
||||||
@@ -64,6 +70,44 @@ export const UpdateMarketStatesFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
export const UpdateReferralProgramsFragmentDoc = gql`
|
||||||
|
fragment UpdateReferralPrograms on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateReferralProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumEpochs
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
referralDiscountFactor
|
||||||
|
referralRewardFactor
|
||||||
|
}
|
||||||
|
endOfProgram: endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
stakingTiers {
|
||||||
|
minimumStakedTokens
|
||||||
|
referralRewardMultiplier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export const UpdateVolumeDiscountProgramsFragmentDoc = gql`
|
||||||
|
fragment UpdateVolumeDiscountPrograms on Proposal {
|
||||||
|
terms {
|
||||||
|
change {
|
||||||
|
... on UpdateVolumeDiscountProgram {
|
||||||
|
benefitTiers {
|
||||||
|
minimumRunningNotionalTakerVolume
|
||||||
|
volumeDiscountFactor
|
||||||
|
}
|
||||||
|
endOfProgramTimestamp
|
||||||
|
windowLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
export const ProposalFieldsFragmentDoc = gql`
|
export const ProposalFieldsFragmentDoc = gql`
|
||||||
fragment ProposalFields on Proposal {
|
fragment ProposalFields on Proposal {
|
||||||
id
|
id
|
||||||
@@ -147,20 +191,24 @@ export const ProposalFieldsFragmentDoc = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
export const ProposalsDocument = gql`
|
export const ProposalsDocument = gql`
|
||||||
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!) {
|
query Proposals($includeNewMarketProductFields: Boolean!, $includeUpdateMarketStates: Boolean!, $includeUpdateReferralPrograms: Boolean!, $includeUpdateVolumeDiscountPrograms: Boolean!) {
|
||||||
proposalsConnection {
|
proposalsConnection {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
...ProposalFields
|
...ProposalFields
|
||||||
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
...NewMarketProductFields @include(if: $includeNewMarketProductFields)
|
||||||
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
...UpdateMarketStates @include(if: $includeUpdateMarketStates)
|
||||||
|
...UpdateReferralPrograms @include(if: $includeUpdateReferralPrograms)
|
||||||
|
...UpdateVolumeDiscountPrograms @include(if: $includeUpdateVolumeDiscountPrograms)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
${ProposalFieldsFragmentDoc}
|
${ProposalFieldsFragmentDoc}
|
||||||
${NewMarketProductFieldsFragmentDoc}
|
${NewMarketProductFieldsFragmentDoc}
|
||||||
${UpdateMarketStatesFragmentDoc}`;
|
${UpdateMarketStatesFragmentDoc}
|
||||||
|
${UpdateReferralProgramsFragmentDoc}
|
||||||
|
${UpdateVolumeDiscountProgramsFragmentDoc}`;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* __useProposalsQuery__
|
* __useProposalsQuery__
|
||||||
@@ -176,6 +224,8 @@ ${UpdateMarketStatesFragmentDoc}`;
|
|||||||
* variables: {
|
* variables: {
|
||||||
* includeNewMarketProductFields: // value for 'includeNewMarketProductFields'
|
* includeNewMarketProductFields: // value for 'includeNewMarketProductFields'
|
||||||
* includeUpdateMarketStates: // value for 'includeUpdateMarketStates'
|
* includeUpdateMarketStates: // value for 'includeUpdateMarketStates'
|
||||||
|
* includeUpdateReferralPrograms: // value for 'includeUpdateReferralPrograms'
|
||||||
|
* includeUpdateVolumeDiscountPrograms: // value for 'includeUpdateVolumeDiscountPrograms'
|
||||||
* },
|
* },
|
||||||
* });
|
* });
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export const ProposalsContainer = () => {
|
|||||||
variables: {
|
variables: {
|
||||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||||
|
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||||
|
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ export const RejectedProposalsContainer = () => {
|
|||||||
variables: {
|
variables: {
|
||||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||||
|
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||||
|
includeUpdateVolumeDiscountPrograms: !!FLAGS.VOLUME_DISCOUNTS,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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"]';
|
||||||
@@ -24,11 +23,17 @@ const colUpdatedAt = '[col-id="updatedAt"] button';
|
|||||||
const headers = [
|
const headers = [
|
||||||
'Party',
|
'Party',
|
||||||
'Commitment (tDAI)',
|
'Commitment (tDAI)',
|
||||||
'Share',
|
|
||||||
'Proposed fee',
|
|
||||||
'Market valuation at entry',
|
|
||||||
'Obligation',
|
'Obligation',
|
||||||
'Supplied',
|
'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',
|
||||||
'Status',
|
'Status',
|
||||||
'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)
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ describe('Market trading page', () => {
|
|||||||
// 6002-MDET-003
|
// 6002-MDET-003
|
||||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||||
cy.getByTestId(marketPrice).within(() => {
|
cy.getByTestId(marketPrice).within(() => {
|
||||||
cy.getByTestId(itemHeader).should('have.text', 'Price');
|
cy.getByTestId(itemHeader).should('have.text', 'Mark Price');
|
||||||
cy.getByTestId(itemValue).should('not.be.empty');
|
cy.getByTestId(itemValue).should('not.be.empty');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
import { MarketState } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
const oracleBannerDialogTrigger = 'oracle-banner-dialog-trigger';
|
|
||||||
const oracleBannerStatus = 'oracle-banner-status';
|
|
||||||
const oracleFullProfile = 'oracle-full-profile';
|
|
||||||
|
|
||||||
describe('oracle information', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.mockTradingPage(
|
|
||||||
MarketState.STATE_ACTIVE,
|
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
'COMPROMISED'
|
|
||||||
);
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.wait('@Markets');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show oracle banner', () => {
|
|
||||||
cy.getByTestId(oracleBannerStatus).should('contain.text', 'COMPROMISED');
|
|
||||||
cy.getByTestId(oracleBannerDialogTrigger)
|
|
||||||
.should('contain.text', 'Show more')
|
|
||||||
.click();
|
|
||||||
cy.getByTestId(oracleFullProfile).should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
import { checkSorting } from '@vegaprotocol/cypress';
|
|
||||||
|
|
||||||
const dialogClose = 'dialog-close';
|
|
||||||
|
|
||||||
describe('accounts', { tags: '@smoke' }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should open usage breakdown dialog when clicked on used', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
// 7001-COLL-009
|
|
||||||
cy.get('[col-id="used"]').contains('1.01').click();
|
|
||||||
const headers = ['Market', 'Account type', 'Balance', 'Margin health'];
|
|
||||||
cy.getByTestId('usage-breakdown').within(($headers) => {
|
|
||||||
cy.wrap($headers)
|
|
||||||
.get('.ag-header-cell-text')
|
|
||||||
.each(($header, i) => {
|
|
||||||
cy.wrap($header).should('have.text', headers[i]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
cy.getByTestId(dialogClose).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('sorting by ag-grid columns should work well', () => {
|
|
||||||
before(() => {
|
|
||||||
const dialogs = Cypress.$('[data-testid="dialog-close"]:visible');
|
|
||||||
if (dialogs.length > 0) {
|
|
||||||
dialogs.each((btn) => {
|
|
||||||
cy.wrap(btn).click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cy.contains('Loading...').should('not.exist');
|
|
||||||
});
|
|
||||||
// 7001-COLL-010
|
|
||||||
it('sorting by asset', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
const marketsSortedDefault = ['tBTC', 'tEURO', 'tDAI', 'tBTC'];
|
|
||||||
const marketsSortedAsc = ['tBTC', 'tBTC', 'tDAI', 'tEURO'];
|
|
||||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
|
||||||
checkSorting(
|
|
||||||
'asset.symbol',
|
|
||||||
marketsSortedDefault,
|
|
||||||
marketsSortedAsc,
|
|
||||||
marketsSortedDesc
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sorting by total', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
const marketsSortedDefault = [
|
|
||||||
'1,000.00002',
|
|
||||||
'1,000.01',
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00001',
|
|
||||||
];
|
|
||||||
const marketsSortedAsc = [
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00001',
|
|
||||||
'1,000.00002',
|
|
||||||
'1,000.01',
|
|
||||||
];
|
|
||||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
|
||||||
|
|
||||||
checkSorting(
|
|
||||||
'total',
|
|
||||||
marketsSortedDefault,
|
|
||||||
marketsSortedAsc,
|
|
||||||
marketsSortedDesc
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sorting by used', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
// concat actual value with percentage value
|
|
||||||
// as cypress will pick up the entire cell contes
|
|
||||||
// textContent
|
|
||||||
const marketsSortedDefault = [
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
'0.01' + '0.00%',
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
];
|
|
||||||
const marketsSortedAsc = [
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
'0.00' + '0.00%',
|
|
||||||
'0.01' + '0.00%',
|
|
||||||
];
|
|
||||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
|
||||||
checkSorting(
|
|
||||||
'used',
|
|
||||||
marketsSortedDefault,
|
|
||||||
marketsSortedAsc,
|
|
||||||
marketsSortedDesc
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('sorting by available', () => {
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
const marketsSortedDefault = [
|
|
||||||
'1,000.00002',
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00001',
|
|
||||||
];
|
|
||||||
const marketsSortedAsc = [
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00',
|
|
||||||
'1,000.00001',
|
|
||||||
'1,000.00002',
|
|
||||||
];
|
|
||||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
|
||||||
|
|
||||||
checkSorting(
|
|
||||||
'available',
|
|
||||||
marketsSortedDefault,
|
|
||||||
marketsSortedAsc,
|
|
||||||
marketsSortedDesc
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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,186 +0,0 @@
|
|||||||
import {
|
|
||||||
TIFlist,
|
|
||||||
orderTIFDropDown,
|
|
||||||
toggleLimit,
|
|
||||||
toggleMarket,
|
|
||||||
} from '../support/deal-ticket';
|
|
||||||
|
|
||||||
const tooltipContent = 'tooltip-content';
|
|
||||||
const reduceOnly = 'reduce-only';
|
|
||||||
const postOnly = 'post-only';
|
|
||||||
|
|
||||||
describe('time in force validation', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.clearAllLocalStorage();
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.wait('@Markets');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must have market order set up to IOC by default', function () {
|
|
||||||
// 7002-SORD-030
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
TIFlist.filter((item) => item.code === 'IOC')[0].text
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('must have time in force set to GTC for limit order', function () {
|
|
||||||
// 7002-SORD-031
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
TIFlist.filter((item) => item.code === 'GTC')[0].text
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('selections should be remembered', () => {
|
|
||||||
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTT');
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
TIFlist.filter((item) => item.code === 'IOC')[0].text
|
|
||||||
);
|
|
||||||
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_FOK');
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
TIFlist.filter((item) => item.code === 'GTT')[0].text
|
|
||||||
);
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
TIFlist.filter((item) => item.code === 'FOK')[0].text
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('limit order', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
});
|
|
||||||
const validTIF = TIFlist;
|
|
||||||
validTIF.forEach((tif) => {
|
|
||||||
// 7002-SORD-023
|
|
||||||
// 7002-SORD-024
|
|
||||||
// 7002-SORD-025
|
|
||||||
// 7002-SORD-026
|
|
||||||
// 7002-SORD-027
|
|
||||||
// 7002-SORD-028
|
|
||||||
|
|
||||||
it(`must be able to select ${tif.code}`, function () {
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
tif.text
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('market order', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
const validTIF = TIFlist.filter((tif) => ['FOK', 'IOC'].includes(tif.code));
|
|
||||||
const invalidTIF = TIFlist.filter(
|
|
||||||
(tif) => !['FOK', 'IOC'].includes(tif.code)
|
|
||||||
);
|
|
||||||
|
|
||||||
validTIF.forEach((tif) => {
|
|
||||||
// 7002-SORD-025
|
|
||||||
// 7002-SORD-026
|
|
||||||
|
|
||||||
it(`must be able to select ${tif.code}`, function () {
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
tif.text
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
invalidTIF.forEach((tif) => {
|
|
||||||
// 7002-SORD-023
|
|
||||||
// 7002-SORD-024
|
|
||||||
// 7002-SORD-027
|
|
||||||
// 7002-SORD-028
|
|
||||||
it(`must not be able to select ${tif.code}`, function () {
|
|
||||||
cy.getByTestId(orderTIFDropDown).should('not.contain', tif.text);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('post and reduce - market order', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.getByTestId(toggleMarket).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
const validTIF = TIFlist.filter((tif) => ['FOK', 'IOC'].includes(tif.code));
|
|
||||||
|
|
||||||
validTIF.forEach((tif) => {
|
|
||||||
// 7002-SORD-025
|
|
||||||
// 7002-SORD-026
|
|
||||||
|
|
||||||
it(`post and reduce order market for ${tif.code}`, function () {
|
|
||||||
// 7003-SORD-054
|
|
||||||
// 7003-SORD-055
|
|
||||||
// 7003-SORD-056
|
|
||||||
// 7003-SORD-057
|
|
||||||
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
tif.text
|
|
||||||
);
|
|
||||||
cy.getByTestId(postOnly).should('be.disabled');
|
|
||||||
cy.getByTestId(reduceOnly).should('be.enabled');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('post and reduce - limit order', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.getByTestId(toggleLimit).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
const validTIFLimit = TIFlist.filter((tif) =>
|
|
||||||
['GFA', 'GFN', 'GTC', 'GTT'].includes(tif.code)
|
|
||||||
);
|
|
||||||
|
|
||||||
validTIFLimit.forEach((tif) => {
|
|
||||||
it(`post and reduce order for limit ${tif.code}`, function () {
|
|
||||||
// 7003-SORD-054
|
|
||||||
// 7003-SORD-055
|
|
||||||
// 7003-SORD-056
|
|
||||||
// 7003-SORD-057
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
|
||||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
|
||||||
'have.text',
|
|
||||||
tif.text
|
|
||||||
);
|
|
||||||
cy.getByTestId(postOnly).should('be.enabled');
|
|
||||||
cy.getByTestId(reduceOnly).should('be.disabled');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
it(`can see explanation of what post only and reduce only is/does`, function () {
|
|
||||||
// 7003-SORD-058
|
|
||||||
cy.get('[for="post-only"]').should('have.text', 'Post only').realHover();
|
|
||||||
cy.getByTestId(tooltipContent).should(
|
|
||||||
'contain.text',
|
|
||||||
`"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.`
|
|
||||||
);
|
|
||||||
cy.get('[for="reduce-only"]')
|
|
||||||
.should('have.text', 'Reduce only')
|
|
||||||
.realHover();
|
|
||||||
cy.getByTestId(tooltipContent).should(
|
|
||||||
'contain.text',
|
|
||||||
`"Reduce only" can be used only with non-persistent orders, such as "Fill or Kill" or "Immediate or Cancel".`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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,25 +0,0 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
|
||||||
import { partyAssetsQuery } from '@vegaprotocol/mock';
|
|
||||||
|
|
||||||
describe('Portfolio page', { tags: '@smoke' }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockGQL((req) => {
|
|
||||||
aliasGQLQuery(req, 'PartyAssets', partyAssetsQuery());
|
|
||||||
});
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
});
|
|
||||||
describe('Ledger entries', () => {
|
|
||||||
it('Download form should be properly rendered', () => {
|
|
||||||
// 7007-LEEN-001
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.getByTestId('"Ledger entries"').click();
|
|
||||||
cy.getByTestId('tab-ledger-entries').within(($headers) => {
|
|
||||||
cy.wrap($headers)
|
|
||||||
.getByTestId('ledger-download-button')
|
|
||||||
.should('be.visible');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
const colHeader = '.ag-header-cell-text';
|
|
||||||
const colIdPrice = '[col-id=price]';
|
|
||||||
const colIdSize = '[col-id=size]';
|
|
||||||
const colIdCreatedAt = '[col-id=createdAt]';
|
|
||||||
const tradesTab = 'Trades';
|
|
||||||
const tradesTable = 'tab-trades';
|
|
||||||
|
|
||||||
describe('trades', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.intercept('POST', '/graphql', (req) => {
|
|
||||||
if (req.body.operationName === 'Trades') {
|
|
||||||
req.alias = '@Trades';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.getByTestId(tradesTab).click();
|
|
||||||
cy.wait('@Trades');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show trades', () => {
|
|
||||||
// 6005-THIS-001
|
|
||||||
// 6005-THIS-002
|
|
||||||
cy.getByTestId(tradesTab).should('be.visible');
|
|
||||||
cy.getByTestId(tradesTable).should('be.visible');
|
|
||||||
cy.getByTestId(tradesTable).should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show trades prices', () => {
|
|
||||||
// 6005-THIS-003
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.get(`${colIdPrice} ${colHeader}`)
|
|
||||||
.first()
|
|
||||||
.should('have.text', 'Price');
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.get(colIdPrice)
|
|
||||||
.each(($tradePrice) => {
|
|
||||||
cy.wrap($tradePrice).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show trades sizes', () => {
|
|
||||||
// 6005-THIS-004
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.get(`${colIdSize} ${colHeader}`)
|
|
||||||
.first()
|
|
||||||
.should('have.text', 'Size');
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.get(colIdSize)
|
|
||||||
.each(($tradeSize) => {
|
|
||||||
cy.wrap($tradeSize).invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('show trades date and time', () => {
|
|
||||||
// 6005-THIS-005
|
|
||||||
cy.getByTestId(tradesTable) // order table shares identical col id
|
|
||||||
.find(`${colIdCreatedAt} ${colHeader}`)
|
|
||||||
.should('have.text', 'Created at');
|
|
||||||
const dateTimeRegex = /(\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.get(`.ag-center-cols-container ${colIdCreatedAt}`)
|
|
||||||
.each(($tradeDateTime) => {
|
|
||||||
cy.wrap($tradeDateTime).invoke('text').should('match', dateTimeRegex);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('trades are sorted descending by datetime', () => {
|
|
||||||
// 6005-THIS-006
|
|
||||||
const dateTimes: Date[] = [];
|
|
||||||
cy.getByTestId(tradesTable)
|
|
||||||
.find(colIdCreatedAt)
|
|
||||||
.each(($tradeDateTime, index) => {
|
|
||||||
if (index != 0) {
|
|
||||||
//ignore header
|
|
||||||
dateTimes.push(new Date($tradeDateTime.text()));
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
expect(dateTimes).to.deep.equal(
|
|
||||||
dateTimes.sort((a, b) => b.getTime() - a.getTime())
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('copy price to deal ticket form', () => {
|
|
||||||
// 6005-THIS-007
|
|
||||||
cy.getByTestId('order-type-Limit').click();
|
|
||||||
cy.get(colIdPrice).last().should('be.visible').click();
|
|
||||||
cy.getByTestId('order-price').should('have.value', '171.16898');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -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'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const amountShortName = 'input[name="amount"] + div + span.text-xs';
|
|
||||||
const assetSelection = 'select-asset';
|
|
||||||
const assetBalance = 'asset-balance';
|
|
||||||
const assetOption = 'rich-select-option';
|
|
||||||
const openTransferButton = 'open-transfer';
|
|
||||||
const submitTransferBtn = '[type="submit"]';
|
|
||||||
const toAddressField = '[name="toAddress"]';
|
|
||||||
const transferForm = 'transfer-form';
|
|
||||||
const ASSET_SEPOLIA_TBTC = 2;
|
|
||||||
const collateralTab = 'Collateral';
|
|
||||||
const toastCloseBtn = 'toast-close';
|
|
||||||
const toastContent = 'toast-content';
|
|
||||||
|
|
||||||
describe('withdraw actions', { tags: '@smoke', testIsolation: true }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.getByTestId(collateralTab).click();
|
|
||||||
cy.getByTestId(openTransferButton).click();
|
|
||||||
|
|
||||||
cy.wait('@Accounts');
|
|
||||||
cy.wait('@Assets');
|
|
||||||
cy.mockVegaWalletTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('key to key transfers by select key', () => {
|
|
||||||
// 1003-TRAN-001
|
|
||||||
// 1003-TRAN-006
|
|
||||||
// 1003-TRAN-007
|
|
||||||
// 1003-TRAN-008
|
|
||||||
// 1003-TRAN-009
|
|
||||||
// 1003-TRAN-010
|
|
||||||
// 1003-TRAN-023
|
|
||||||
cy.getByTestId(transferForm).should('be.visible');
|
|
||||||
cy.getByTestId(transferForm).find(toAddressField).select(1);
|
|
||||||
|
|
||||||
cy.getByTestId(assetSelection).click();
|
|
||||||
cy.getByTestId(assetOption);
|
|
||||||
cy.getByTestId(assetBalance).should('not.be.empty');
|
|
||||||
cy.getByTestId(assetOption).should('have.length.gt', 4);
|
|
||||||
|
|
||||||
let optionText: string;
|
|
||||||
cy.getByTestId(assetOption)
|
|
||||||
.eq(2)
|
|
||||||
.invoke('text')
|
|
||||||
.then((text: string) => {
|
|
||||||
optionText = text;
|
|
||||||
cy.getByTestId(assetOption).eq(2).click();
|
|
||||||
cy.getByTestId(assetSelection).should('have.text', optionText);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId(transferForm)
|
|
||||||
.find(amountField)
|
|
||||||
.type('1', { delay: 100, force: true });
|
|
||||||
|
|
||||||
cy.getByTestId(transferForm).find(amountShortName).should('not.be.empty');
|
|
||||||
|
|
||||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
|
||||||
cy.getByTestId(toastContent).should(
|
|
||||||
'contain.text',
|
|
||||||
'Awaiting confirmation'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('key to key transfers by enter manual key', () => {
|
|
||||||
//1003-TRAN-005
|
|
||||||
cy.getByTestId(transferForm).should('be.visible');
|
|
||||||
cy.contains('Enter manually').click();
|
|
||||||
cy.getByTestId(transferForm)
|
|
||||||
.find(toAddressField)
|
|
||||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
|
||||||
cy.getByTestId(transferForm)
|
|
||||||
.find(amountField)
|
|
||||||
.type('1', { delay: 100, force: true });
|
|
||||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
|
||||||
cy.getByTestId(toastContent).should(
|
|
||||||
'contain.text',
|
|
||||||
'Awaiting confirmation'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const formFieldError = 'input-error-text';
|
|
||||||
const toAddressField = 'input[name="to"]';
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const useMaximumAmount = 'use-maximum';
|
|
||||||
const submitWithdrawBtn = 'submit-withdrawal';
|
|
||||||
const ethAddressValue = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
|
||||||
|
|
||||||
const ASSET_SEPOLIA_TBTC = 2;
|
|
||||||
const ASSET_EURO = 1;
|
|
||||||
|
|
||||||
describe('withdraw form validation', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
|
|
||||||
cy.getByTestId('Withdraw').click(); // sidebar item
|
|
||||||
|
|
||||||
// It also requires connection Ethereum wallet
|
|
||||||
connectEthereumWallet('MetaMask');
|
|
||||||
|
|
||||||
cy.wait('@Accounts');
|
|
||||||
cy.wait('@Assets');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('empty fields', () => {
|
|
||||||
cy.getByTestId(submitWithdrawBtn).click();
|
|
||||||
|
|
||||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
|
||||||
// only 2 despite 3 fields because the ethereum address will be auto populated
|
|
||||||
cy.getByTestId(formFieldError).should('have.length', 2);
|
|
||||||
|
|
||||||
// Test for Ethereum address
|
|
||||||
cy.get(toAddressField).should('have.value', ethAddressValue);
|
|
||||||
});
|
|
||||||
it('min amount', () => {
|
|
||||||
// 1002-WITH-010
|
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
|
||||||
cy.get(amountField).clear().type('0');
|
|
||||||
cy.getByTestId(submitWithdrawBtn).click();
|
|
||||||
cy.get('[data-testid="input-error-text"]').should(
|
|
||||||
'contain.text',
|
|
||||||
'Value is below minimum'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
it('max amount', () => {
|
|
||||||
// 1002-WITH-005
|
|
||||||
// 1002-WITH-008
|
|
||||||
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(submitWithdrawBtn).click();
|
|
||||||
cy.get('[data-testid="input-error-text"]').should(
|
|
||||||
'contain.text',
|
|
||||||
'Insufficient amount in account'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can set amount using use maximum button', () => {
|
|
||||||
// 1002-WITH-004
|
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
|
||||||
cy.getByTestId(useMaximumAmount).click();
|
|
||||||
cy.get(amountField).should('have.value', '1000.00001');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe(
|
|
||||||
'withdraw actions',
|
|
||||||
{ tags: '@regression', testIsolation: true },
|
|
||||||
() => {
|
|
||||||
// this is extremely ugly hack, but setting it properly in contract is too much effort for such simple validation
|
|
||||||
|
|
||||||
// 1002-WITH-018
|
|
||||||
|
|
||||||
const withdrawalThreshold =
|
|
||||||
Cypress.env('VEGA_ENV') === 'CUSTOM' ? '0.00' : '100.00';
|
|
||||||
before(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
|
|
||||||
cy.wait('@Accounts');
|
|
||||||
cy.wait('@Assets');
|
|
||||||
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
|
|
||||||
cy.getByTestId('Withdraw').click();
|
|
||||||
|
|
||||||
// It also requires connection Ethereum wallet
|
|
||||||
connectEthereumWallet('MetaMask');
|
|
||||||
|
|
||||||
cy.mockVegaWalletTransaction();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('triggers transaction when submitted', () => {
|
|
||||||
// 1002-WITH-002
|
|
||||||
// 1002-WITH-003
|
|
||||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
|
||||||
cy.getByTestId('BALANCE_AVAILABLE_label').should(
|
|
||||||
'contain.text',
|
|
||||||
'Balance available'
|
|
||||||
);
|
|
||||||
cy.getByTestId('BALANCE_AVAILABLE_value').should(
|
|
||||||
'have.text',
|
|
||||||
'1,000.00001'
|
|
||||||
);
|
|
||||||
cy.getByTestId('WITHDRAWAL_THRESHOLD_label').should(
|
|
||||||
'contain.text',
|
|
||||||
'Delayed withdrawal threshold'
|
|
||||||
);
|
|
||||||
cy.getByTestId('WITHDRAWAL_THRESHOLD_value').should(
|
|
||||||
'contain.text',
|
|
||||||
withdrawalThreshold
|
|
||||||
);
|
|
||||||
cy.getByTestId('DELAY_TIME_label').should('contain.text', 'Delay time');
|
|
||||||
cy.getByTestId('DELAY_TIME_value').should('have.text', 'None');
|
|
||||||
cy.get(amountField).clear().type('10');
|
|
||||||
cy.getByTestId(submitWithdrawBtn).click();
|
|
||||||
cy.getByTestId('toast').should('contain.text', 'Awaiting confirmation');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
@@ -30,11 +30,11 @@ import {
|
|||||||
blockStatisticsQuery,
|
blockStatisticsQuery,
|
||||||
networkParamQuery,
|
networkParamQuery,
|
||||||
liquidityProvisionsQuery,
|
liquidityProvisionsQuery,
|
||||||
liquidityProviderFeeShareQuery,
|
|
||||||
successorMarketQuery,
|
successorMarketQuery,
|
||||||
parentMarketIdQuery,
|
parentMarketIdQuery,
|
||||||
successorMarketIdsQuery,
|
successorMarketIdsQuery,
|
||||||
successorMarketProposalDetailsQuery,
|
successorMarketProposalDetailsQuery,
|
||||||
|
liquidityProvidersQuery,
|
||||||
} from '@vegaprotocol/mock';
|
} from '@vegaprotocol/mock';
|
||||||
import type { PartialDeep } from 'type-fest';
|
import type { PartialDeep } from 'type-fest';
|
||||||
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
|
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
|
||||||
@@ -162,11 +162,7 @@ const mockTradingPage = (
|
|||||||
aliasGQLQuery(req, 'Trades', tradesQuery());
|
aliasGQLQuery(req, 'Trades', tradesQuery());
|
||||||
aliasGQLQuery(req, 'Chart', chartQuery());
|
aliasGQLQuery(req, 'Chart', chartQuery());
|
||||||
aliasGQLQuery(req, 'LiquidityProvisions', liquidityProvisionsQuery());
|
aliasGQLQuery(req, 'LiquidityProvisions', liquidityProvisionsQuery());
|
||||||
aliasGQLQuery(
|
aliasGQLQuery(req, 'LiquidityProviders', liquidityProvidersQuery());
|
||||||
req,
|
|
||||||
'LiquidityProviderFeeShare',
|
|
||||||
liquidityProviderFeeShareQuery
|
|
||||||
);
|
|
||||||
aliasGQLQuery(req, 'Candles', candlesQuery());
|
aliasGQLQuery(req, 'Candles', candlesQuery());
|
||||||
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
|
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
|
||||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||||
|
|||||||
+1
-5
@@ -12,12 +12,11 @@ NX_VEGA_WALLET_URL=http://localhost:1789
|
|||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
|
||||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||||
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
NX_CHROME_EXTENSION_URL=https://chrome.google.com/webstore/detail/vega-wallet-fairground/nmmjkiafpmphlikhefgjbblebfgclikn
|
||||||
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
NX_MOZILLA_EXTENSION_URL=https://addons.mozilla.org/firefox/addon/vega-wallet-fairground
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||||
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
|
|
||||||
# Cosmic elevator flags
|
# Cosmic elevator flags
|
||||||
NX_SUCCESSOR_MARKETS=true
|
NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -26,6 +25,3 @@ NX_ICEBERG_ORDERS=true
|
|||||||
# NX_PRODUCT_PERPETUALS
|
# NX_PRODUCT_PERPETUALS
|
||||||
NX_METAMASK_SNAPS=true
|
NX_METAMASK_SNAPS=true
|
||||||
NX_REFERRALS=true
|
NX_REFERRALS=true
|
||||||
|
|
||||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
|
||||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import MarketPage from '../market';
|
|
||||||
|
|
||||||
export const ClosedMarketPage = () => {
|
|
||||||
return <MarketPage closed />;
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { ClosedMarketPage as default } from './closed-market';
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { FeesContainer } from '../../components/fees-container';
|
||||||
|
|
||||||
|
export const Fees = () => {
|
||||||
|
return (
|
||||||
|
<div className="container p-4 mx-auto">
|
||||||
|
<h1 className="px-4 pb-4 text-2xl">{t('Fees')}</h1>
|
||||||
|
<FeesContainer />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { Fees } from './fees';
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
SidebarButton,
|
||||||
|
SidebarDivider,
|
||||||
|
ViewType,
|
||||||
|
} from '../../components/sidebar';
|
||||||
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
|
||||||
|
export const LiquiditySidebar = () => {
|
||||||
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Deposit}
|
||||||
|
icon={VegaIconNames.DEPOSIT}
|
||||||
|
tooltip={t('Deposit')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Withdraw}
|
||||||
|
icon={VegaIconNames.WITHDRAW}
|
||||||
|
tooltip={t('Withdraw')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Transfer}
|
||||||
|
icon={VegaIconNames.TRANSFER}
|
||||||
|
tooltip={t('Transfer')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarDivider />
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Order}
|
||||||
|
icon={VegaIconNames.TICKET}
|
||||||
|
tooltip={t('Order')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Info}
|
||||||
|
icon={VegaIconNames.BREAKDOWN}
|
||||||
|
tooltip={t('Market specification')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { DocsLinks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
import { ButtonLink, ExternalLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||||
import { MarketProposalNotification } from '@vegaprotocol/proposals';
|
import { MarketProposalNotification } from '@vegaprotocol/proposals';
|
||||||
import type { Market } from '@vegaprotocol/markets';
|
import type { Market } from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
|
addDecimalsFormatNumber,
|
||||||
fromNanoSeconds,
|
fromNanoSeconds,
|
||||||
getExpiryDate,
|
getExpiryDate,
|
||||||
getMarketExpiryDate,
|
getMarketExpiryDate,
|
||||||
@@ -14,9 +15,12 @@ import {
|
|||||||
Last24hVolume,
|
Last24hVolume,
|
||||||
getAsset,
|
getAsset,
|
||||||
getDataSourceSpecForSettlementSchedule,
|
getDataSourceSpecForSettlementSchedule,
|
||||||
|
isMarketInAuction,
|
||||||
marketInfoProvider,
|
marketInfoProvider,
|
||||||
useFundingPeriodsQuery,
|
useFundingPeriodsQuery,
|
||||||
useFundingRate,
|
useFundingRate,
|
||||||
|
useMarketTradingMode,
|
||||||
|
useExternalTwap,
|
||||||
} from '@vegaprotocol/markets';
|
} from '@vegaprotocol/markets';
|
||||||
import { MarketState as State } from '@vegaprotocol/types';
|
import { MarketState as State } from '@vegaprotocol/types';
|
||||||
import { HeaderStat } from '../../components/header';
|
import { HeaderStat } from '../../components/header';
|
||||||
@@ -26,6 +30,7 @@ import { MarketState } from '../../components/market-state';
|
|||||||
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
import { MarketLiquiditySupplied } from '../../components/liquidity-supplied';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
|
import { PriceCell } from '@vegaprotocol/datagrid';
|
||||||
|
|
||||||
interface MarketHeaderStatsProps {
|
interface MarketHeaderStatsProps {
|
||||||
market: Market;
|
market: Market;
|
||||||
@@ -39,33 +44,7 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{market.tradableInstrument.instrument.product.__typename === 'Future' && (
|
<HeaderStat heading={t('Mark Price')} testId="market-price">
|
||||||
<HeaderStat
|
|
||||||
heading={t('Expiry')}
|
|
||||||
description={
|
|
||||||
<ExpiryTooltipContent
|
|
||||||
market={market}
|
|
||||||
explorerUrl={VEGA_EXPLORER_URL}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
testId="market-expiry"
|
|
||||||
>
|
|
||||||
<ExpiryLabel market={market} />
|
|
||||||
</HeaderStat>
|
|
||||||
)}
|
|
||||||
{market.tradableInstrument.instrument.product.__typename ===
|
|
||||||
'Perpetual' && (
|
|
||||||
<HeaderStat
|
|
||||||
heading={`${t('Funding')} / ${t('Countdown')}`}
|
|
||||||
testId="market-funding"
|
|
||||||
>
|
|
||||||
<div className="flex justify-between gap-2">
|
|
||||||
<FundingRate marketId={market.id} />
|
|
||||||
<FundingCountdown marketId={market.id} />
|
|
||||||
</div>
|
|
||||||
</HeaderStat>
|
|
||||||
)}
|
|
||||||
<HeaderStat heading={t('Price')} testId="market-price">
|
|
||||||
<MarketMarkPrice
|
<MarketMarkPrice
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
decimalPlaces={market.decimalPlaces}
|
decimalPlaces={market.decimalPlaces}
|
||||||
@@ -107,7 +86,64 @@ export const MarketHeaderStats = ({ market }: MarketHeaderStatsProps) => {
|
|||||||
<MarketLiquiditySupplied
|
<MarketLiquiditySupplied
|
||||||
marketId={market.id}
|
marketId={market.id}
|
||||||
assetDecimals={asset?.decimals || 0}
|
assetDecimals={asset?.decimals || 0}
|
||||||
|
quantum={asset.quantum}
|
||||||
/>
|
/>
|
||||||
|
{market.tradableInstrument.instrument.product.__typename === 'Future' && (
|
||||||
|
<HeaderStat
|
||||||
|
heading={t('Expiry')}
|
||||||
|
description={
|
||||||
|
<ExpiryTooltipContent
|
||||||
|
market={market}
|
||||||
|
explorerUrl={VEGA_EXPLORER_URL}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
testId="market-expiry"
|
||||||
|
>
|
||||||
|
<ExpiryLabel market={market} />
|
||||||
|
</HeaderStat>
|
||||||
|
)}
|
||||||
|
{market.tradableInstrument.instrument.product.__typename ===
|
||||||
|
'Perpetual' && (
|
||||||
|
<HeaderStat
|
||||||
|
heading={`${t('Funding Rate')} / ${t('Countdown')}`}
|
||||||
|
testId="market-funding"
|
||||||
|
>
|
||||||
|
<div className="flex justify-between gap-2">
|
||||||
|
<FundingRate marketId={market.id} />
|
||||||
|
<FundingCountdown marketId={market.id} />
|
||||||
|
</div>
|
||||||
|
</HeaderStat>
|
||||||
|
)}
|
||||||
|
{market.tradableInstrument.instrument.product.__typename ===
|
||||||
|
'Perpetual' && (
|
||||||
|
<HeaderStat
|
||||||
|
heading={`${t('Index Price')}`}
|
||||||
|
description={
|
||||||
|
<div className="p1">
|
||||||
|
{t(
|
||||||
|
'The external time weighted average price (TWAP) received from the data source defined in the data sourcing specification.'
|
||||||
|
)}
|
||||||
|
{DocsLinks && (
|
||||||
|
<ExternalLink
|
||||||
|
href={DocsLinks.ETH_DATA_SOURCES}
|
||||||
|
className="mt-2"
|
||||||
|
>
|
||||||
|
{t('Find out more')}
|
||||||
|
</ExternalLink>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
testId="index-price"
|
||||||
|
>
|
||||||
|
<IndexPrice
|
||||||
|
marketId={market.id}
|
||||||
|
decimalPlaces={
|
||||||
|
market.tradableInstrument.instrument.product.settlementAsset
|
||||||
|
.decimals
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</HeaderStat>
|
||||||
|
)}
|
||||||
<MarketProposalNotification marketId={market.id} />
|
<MarketProposalNotification marketId={market.id} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -126,6 +162,24 @@ export const FundingRate = ({ marketId }: { marketId: string }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const IndexPrice = ({
|
||||||
|
marketId,
|
||||||
|
decimalPlaces,
|
||||||
|
}: {
|
||||||
|
marketId: string;
|
||||||
|
decimalPlaces?: number;
|
||||||
|
}) => {
|
||||||
|
const { data: externalTwap } = useExternalTwap(marketId);
|
||||||
|
return externalTwap && decimalPlaces ? (
|
||||||
|
<PriceCell
|
||||||
|
value={Number(externalTwap)}
|
||||||
|
valueFormatted={addDecimalsFormatNumber(externalTwap, decimalPlaces)}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const useNow = () => {
|
const useNow = () => {
|
||||||
const [now, setNow] = useState(Date.now());
|
const [now, setNow] = useState(Date.now());
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -136,9 +190,11 @@ const useNow = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const useEvery = (marketId: string) => {
|
const useEvery = (marketId: string) => {
|
||||||
|
const { data: marketTradingMode } = useMarketTradingMode(marketId);
|
||||||
const { data: marketInfo } = useDataProvider({
|
const { data: marketInfo } = useDataProvider({
|
||||||
dataProvider: marketInfoProvider,
|
dataProvider: marketInfoProvider,
|
||||||
variables: { marketId },
|
variables: { marketId },
|
||||||
|
skip: !marketTradingMode || isMarketInAuction(marketTradingMode),
|
||||||
});
|
});
|
||||||
let every: number | undefined = undefined;
|
let every: number | undefined = undefined;
|
||||||
const sourceType =
|
const sourceType =
|
||||||
|
|||||||
@@ -3,16 +3,15 @@ import { addDecimalsFormatNumber, titlefy } from '@vegaprotocol/utils';
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||||
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
|
import { useThrottledDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { AsyncRenderer, ExternalLink, Splash } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink, Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { getAsset, marketDataProvider, useMarket } from '@vegaprotocol/markets';
|
import { getAsset, marketDataProvider, useMarket } from '@vegaprotocol/markets';
|
||||||
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
import { useGlobalStore, usePageTitleStore } from '../../stores';
|
||||||
import { TradeGrid } from './trade-grid';
|
import { TradeGrid } from './trade-grid';
|
||||||
import { TradePanels } from './trade-panels';
|
import { TradePanels } from './trade-panels';
|
||||||
import { useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
import { Links, Routes } from '../../lib/links';
|
import { Links } from '../../lib/links';
|
||||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
const calculatePrice = (markPrice?: string, decimalPlaces?: number) => {
|
||||||
return markPrice && decimalPlaces
|
return markPrice && decimalPlaces
|
||||||
@@ -57,7 +56,7 @@ const TitleUpdater = ({
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
export const MarketPage = () => {
|
||||||
const { marketId } = useParams();
|
const { marketId } = useParams();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const currentRouteId = useGetCurrentRouteId();
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
@@ -68,27 +67,13 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
|||||||
const update = useGlobalStore((store) => store.update);
|
const update = useGlobalStore((store) => store.update);
|
||||||
const lastMarketId = useGlobalStore((store) => store.marketId);
|
const lastMarketId = useGlobalStore((store) => store.marketId);
|
||||||
|
|
||||||
const { data, error, loading } = useMarket(marketId);
|
const { data, loading } = useMarket(marketId);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (
|
|
||||||
data?.state &&
|
|
||||||
[
|
|
||||||
MarketState.STATE_SETTLED,
|
|
||||||
MarketState.STATE_TRADING_TERMINATED,
|
|
||||||
].includes(data.state) &&
|
|
||||||
currentRouteId !== Routes.CLOSED_MARKETS &&
|
|
||||||
marketId
|
|
||||||
) {
|
|
||||||
navigate(Links.CLOSED_MARKETS(marketId));
|
|
||||||
}
|
|
||||||
}, [data?.state, currentRouteId, navigate, marketId]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (data?.id && data.id !== lastMarketId && !closed) {
|
if (data?.id && data.id !== lastMarketId && !closed) {
|
||||||
update({ marketId: data.id });
|
update({ marketId: data.id });
|
||||||
}
|
}
|
||||||
}, [update, lastMarketId, data?.id, closed]);
|
}, [update, lastMarketId, data?.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (largeScreen && view === undefined) {
|
if (largeScreen && view === undefined) {
|
||||||
@@ -97,7 +82,7 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
|||||||
currentRouteId
|
currentRouteId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [setViews, view, currentRouteId, largeScreen, closed]);
|
}, [setViews, view, currentRouteId, largeScreen]);
|
||||||
|
|
||||||
const pinnedAsset = data && getAsset(data);
|
const pinnedAsset = data && getAsset(data);
|
||||||
|
|
||||||
@@ -110,7 +95,15 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
|||||||
}
|
}
|
||||||
}, [largeScreen, data, pinnedAsset]);
|
}, [largeScreen, data, pinnedAsset]);
|
||||||
|
|
||||||
if (!data && marketId) {
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Splash>
|
||||||
|
<Loader />
|
||||||
|
</Splash>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data) {
|
||||||
return (
|
return (
|
||||||
<Splash>
|
<Splash>
|
||||||
<span className="flex flex-col items-center gap-2">
|
<span className="flex flex-col items-center gap-2">
|
||||||
@@ -120,7 +113,7 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
|||||||
<p className="justify-center text-sm">
|
<p className="justify-center text-sm">
|
||||||
{t(`Please choose another market from the`)}{' '}
|
{t(`Please choose another market from the`)}{' '}
|
||||||
<ExternalLink onClick={() => navigate(Links.MARKETS())}>
|
<ExternalLink onClick={() => navigate(Links.MARKETS())}>
|
||||||
market list
|
{t('market list')}
|
||||||
</ExternalLink>
|
</ExternalLink>
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
@@ -129,18 +122,13 @@ export const MarketPage = ({ closed }: { closed?: boolean }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<>
|
||||||
loading={loading}
|
|
||||||
error={error}
|
|
||||||
data={data || undefined}
|
|
||||||
noDataCondition={(data) => false}
|
|
||||||
>
|
|
||||||
<TitleUpdater
|
<TitleUpdater
|
||||||
marketId={data?.id}
|
marketId={data?.id}
|
||||||
marketName={data?.tradableInstrument.instrument.name}
|
marketName={data?.tradableInstrument.instrument.name}
|
||||||
decimalPlaces={data?.decimalPlaces}
|
decimalPlaces={data?.decimalPlaces}
|
||||||
/>
|
/>
|
||||||
{tradeView}
|
{tradeView}
|
||||||
</AsyncRenderer>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { TradingViews } from './trade-views';
|
|||||||
import {
|
import {
|
||||||
MarketSuccessorBanner,
|
MarketSuccessorBanner,
|
||||||
MarketSuccessorProposalBanner,
|
MarketSuccessorProposalBanner,
|
||||||
|
MarketTerminationBanner,
|
||||||
} from '../../components/market-banner';
|
} from '../../components/market-banner';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
@@ -72,10 +73,19 @@ const MainGrid = memo(
|
|||||||
{market &&
|
{market &&
|
||||||
market.tradableInstrument.instrument.product.__typename ===
|
market.tradableInstrument.instrument.product.__typename ===
|
||||||
'Perpetual' ? (
|
'Perpetual' ? (
|
||||||
<Tab id="funding" name={t('Funding')}>
|
<Tab id="funding-history" name={t('Funding history')}>
|
||||||
<TradingViews.funding.component marketId={marketId} />
|
<TradingViews.funding.component marketId={marketId} />
|
||||||
</Tab>
|
</Tab>
|
||||||
) : null}
|
) : null}
|
||||||
|
{market &&
|
||||||
|
market.tradableInstrument.instrument.product.__typename ===
|
||||||
|
'Perpetual' ? (
|
||||||
|
<Tab id="funding-payments" name={t('Funding payments')}>
|
||||||
|
<TradingViews.fundingPayments.component
|
||||||
|
marketId={marketId}
|
||||||
|
/>
|
||||||
|
</Tab>
|
||||||
|
) : null}
|
||||||
</Tabs>
|
</Tabs>
|
||||||
</TradeGridChild>
|
</TradeGridChild>
|
||||||
</ResizableGridPanel>
|
</ResizableGridPanel>
|
||||||
@@ -113,7 +123,7 @@ const MainGrid = memo(
|
|||||||
<Tab
|
<Tab
|
||||||
id="open-orders"
|
id="open-orders"
|
||||||
name={t('Open')}
|
name={t('Open')}
|
||||||
menu={<TradingViews.activeOrders.menu marketId={marketId} />}
|
menu={<TradingViews.activeOrders.menu />}
|
||||||
>
|
>
|
||||||
<TradingViews.orders.component filter={Filter.Open} />
|
<TradingViews.orders.component filter={Filter.Open} />
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -126,7 +136,7 @@ const MainGrid = memo(
|
|||||||
<Tab
|
<Tab
|
||||||
id="orders"
|
id="orders"
|
||||||
name={t('All')}
|
name={t('All')}
|
||||||
menu={<TradingViews.orders.menu marketId={marketId} />}
|
menu={<TradingViews.orders.menu />}
|
||||||
>
|
>
|
||||||
<TradingViews.orders.component />
|
<TradingViews.orders.component />
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -169,6 +179,7 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
|
|||||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<MarketTerminationBanner market={market} />
|
||||||
<OracleBanner marketId={market?.id || ''} />
|
<OracleBanner marketId={market?.id || ''} />
|
||||||
</div>
|
</div>
|
||||||
<div className="min-h-0 p-0.5">
|
<div className="min-h-0 p-0.5">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import classNames from 'classnames';
|
|||||||
import {
|
import {
|
||||||
MarketSuccessorBanner,
|
MarketSuccessorBanner,
|
||||||
MarketSuccessorProposalBanner,
|
MarketSuccessorProposalBanner,
|
||||||
|
MarketTerminationBanner,
|
||||||
} from '../../components/market-banner';
|
} from '../../components/market-banner';
|
||||||
import { FLAGS } from '@vegaprotocol/environment';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||||
<Menu marketId={market?.id || ''} />
|
<Menu />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -59,6 +60,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
|||||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
<MarketTerminationBanner market={market} />
|
||||||
<OracleBanner marketId={market?.id || ''} />
|
<OracleBanner marketId={market?.id || ''} />
|
||||||
</div>
|
</div>
|
||||||
<div>{renderMenu()}</div>
|
<div>{renderMenu()}</div>
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { PositionsContainer } from '../../components/positions-container';
|
|||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { AccountsContainer } from '../../components/accounts-container';
|
||||||
import { LiquidityContainer } from '../../components/liquidity-container';
|
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||||
import { FundingContainer } from '../../components/funding-container';
|
import { FundingContainer } from '../../components/funding-container';
|
||||||
|
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
|
||||||
import type { OrderContainerProps } from '../../components/orders-container';
|
import type { OrderContainerProps } from '../../components/orders-container';
|
||||||
import { OrdersContainer } from '../../components/orders-container';
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||||
@@ -55,6 +56,10 @@ export const TradingViews = {
|
|||||||
label: 'Funding',
|
label: 'Funding',
|
||||||
component: requiresMarket(FundingContainer),
|
component: requiresMarket(FundingContainer),
|
||||||
},
|
},
|
||||||
|
fundingPayments: {
|
||||||
|
label: 'Funding Payments',
|
||||||
|
component: FundingPaymentsContainer,
|
||||||
|
},
|
||||||
orderbook: {
|
orderbook: {
|
||||||
label: 'Orderbook',
|
label: 'Orderbook',
|
||||||
component: requiresMarket(OrderbookContainer),
|
component: requiresMarket(OrderbookContainer),
|
||||||
|
|||||||
@@ -319,7 +319,8 @@ describe('Closed', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('successor marked should be visible', async () => {
|
// eslint-disable-next-line jest/no-disabled-tests
|
||||||
|
it.skip('successor marked should be visible', async () => {
|
||||||
const marketsWithSuccessorID = [
|
const marketsWithSuccessorID = [
|
||||||
{
|
{
|
||||||
__typename: 'MarketEdge' as const,
|
__typename: 'MarketEdge' as const,
|
||||||
@@ -363,12 +364,12 @@ describe('Closed', () => {
|
|||||||
const container = within(
|
const container = within(
|
||||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||||
);
|
);
|
||||||
const cell = container.getAllByRole('gridcell', {
|
|
||||||
name: (_name, element) => element.getAttribute('col-id') === 'code',
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
expect(within(cell).getByTestId('stack-cell-secondary')).toHaveTextContent(
|
const cells = await container.findAllByRole('gridcell');
|
||||||
'PRNT'
|
const cell = cells.find((el) => el.getAttribute('col-id') === 'code');
|
||||||
);
|
|
||||||
|
expect(
|
||||||
|
within(cell as HTMLElement).getByTestId('stack-cell-secondary')
|
||||||
|
).toHaveTextContent('PRNT');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { CellClickedEvent } from 'ag-grid-community';
|
||||||
import compact from 'lodash/compact';
|
import compact from 'lodash/compact';
|
||||||
import { isAfter } from 'date-fns';
|
import { isAfter } from 'date-fns';
|
||||||
import type {
|
import type {
|
||||||
@@ -5,6 +6,7 @@ import type {
|
|||||||
VegaValueFormatterParams,
|
VegaValueFormatterParams,
|
||||||
} from '@vegaprotocol/datagrid';
|
} from '@vegaprotocol/datagrid';
|
||||||
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
|
||||||
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import type { Asset } from '@vegaprotocol/types';
|
import type { Asset } from '@vegaprotocol/types';
|
||||||
@@ -17,13 +19,11 @@ import {
|
|||||||
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
|
import { closedMarketsWithDataProvider, getAsset } from '@vegaprotocol/markets';
|
||||||
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
|
import type { DataSourceFilterFragment } from '@vegaprotocol/markets';
|
||||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||||
|
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||||
import { SettlementDateCell } from './settlement-date-cell';
|
import { SettlementDateCell } from './settlement-date-cell';
|
||||||
import { SettlementPriceCell } from './settlement-price-cell';
|
import { SettlementPriceCell } from './settlement-price-cell';
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import { MarketCodeCell } from './market-code-cell';
|
import { MarketCodeCell } from './market-code-cell';
|
||||||
import { MarketActionsDropdown } from './market-table-actions';
|
import { MarketActionsDropdown } from './market-table-actions';
|
||||||
import type { CellClickedEvent } from 'ag-grid-community';
|
|
||||||
import { useClosedMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
|
||||||
|
|
||||||
type SettlementAsset = Pick<
|
type SettlementAsset = Pick<
|
||||||
Asset,
|
Asset,
|
||||||
@@ -127,7 +127,7 @@ const ClosedMarketsDataGrid = ({
|
|||||||
rowData: Row[];
|
rowData: Row[];
|
||||||
error: Error | undefined;
|
error: Error | undefined;
|
||||||
}) => {
|
}) => {
|
||||||
const handleOnSelect = useClosedMarketClickHandler();
|
const handleOnSelect = useMarketClickHandler();
|
||||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||||
|
|
||||||
const colDefs = useMemo(() => {
|
const colDefs = useMemo(() => {
|
||||||
@@ -302,8 +302,11 @@ const ClosedMarketsDataGrid = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore metaKey exists
|
handleOnSelect(
|
||||||
handleOnSelect(data.id, event ? event.metaKey : false);
|
data.id,
|
||||||
|
// @ts-ignore metaKey exists
|
||||||
|
event ? event.metaKey : false
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { Route, Routes, useParams } from 'react-router-dom';
|
||||||
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { useMarket } from '@vegaprotocol/markets';
|
||||||
|
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import {
|
||||||
|
SidebarButton,
|
||||||
|
SidebarDivider,
|
||||||
|
ViewType,
|
||||||
|
} from '../../components/sidebar';
|
||||||
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
|
||||||
|
export const MarketsSidebar = () => {
|
||||||
|
const { marketId } = useParams();
|
||||||
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
|
const { data } = useMarket(marketId);
|
||||||
|
const active =
|
||||||
|
data &&
|
||||||
|
[MarketState.STATE_ACTIVE, MarketState.STATE_PENDING].includes(data.state);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Deposit}
|
||||||
|
icon={VegaIconNames.DEPOSIT}
|
||||||
|
tooltip={t('Deposit')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Withdraw}
|
||||||
|
icon={VegaIconNames.WITHDRAW}
|
||||||
|
tooltip={t('Withdraw')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Transfer}
|
||||||
|
icon={VegaIconNames.TRANSFER}
|
||||||
|
tooltip={t('Transfer')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<Routes>
|
||||||
|
<Route
|
||||||
|
path=":marketId"
|
||||||
|
element={
|
||||||
|
<>
|
||||||
|
<SidebarDivider />
|
||||||
|
{active && (
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Order}
|
||||||
|
icon={VegaIconNames.TICKET}
|
||||||
|
tooltip={t('Order')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Info}
|
||||||
|
icon={VegaIconNames.BREAKDOWN}
|
||||||
|
tooltip={t('Market specification')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Routes>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
query AccountHistory(
|
|
||||||
$partyId: ID!
|
|
||||||
$assetId: ID!
|
|
||||||
$accountTypes: [AccountType!]
|
|
||||||
$dateRange: DateRange
|
|
||||||
$marketIds: [ID!]
|
|
||||||
) {
|
|
||||||
balanceChanges(
|
|
||||||
filter: {
|
|
||||||
partyIds: [$partyId]
|
|
||||||
accountTypes: $accountTypes
|
|
||||||
assetId: $assetId
|
|
||||||
marketIds: $marketIds
|
|
||||||
}
|
|
||||||
dateRange: $dateRange
|
|
||||||
) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
timestamp
|
|
||||||
partyId
|
|
||||||
balance
|
|
||||||
marketId
|
|
||||||
assetId
|
|
||||||
accountType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query AccountsWithBalance($partyId: ID!, $dateRange: DateRange) {
|
|
||||||
balanceChanges(filter: { partyIds: [$partyId] }, dateRange: $dateRange) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
assetId
|
|
||||||
accountType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,117 +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 AccountHistoryQueryVariables = Types.Exact<{
|
|
||||||
partyId: Types.Scalars['ID'];
|
|
||||||
assetId: Types.Scalars['ID'];
|
|
||||||
accountTypes?: Types.InputMaybe<Array<Types.AccountType> | Types.AccountType>;
|
|
||||||
dateRange?: Types.InputMaybe<Types.DateRange>;
|
|
||||||
marketIds?: Types.InputMaybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type AccountHistoryQuery = { __typename?: 'Query', balanceChanges: { __typename?: 'AggregatedBalanceConnection', edges: Array<{ __typename?: 'AggregatedBalanceEdge', node: { __typename?: 'AggregatedBalance', timestamp: any, partyId?: string | null, balance: string, marketId?: string | null, assetId?: string | null, accountType?: Types.AccountType | null } } | null> } };
|
|
||||||
|
|
||||||
export type AccountsWithBalanceQueryVariables = Types.Exact<{
|
|
||||||
partyId: Types.Scalars['ID'];
|
|
||||||
dateRange?: Types.InputMaybe<Types.DateRange>;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
|
|
||||||
export type AccountsWithBalanceQuery = { __typename?: 'Query', balanceChanges: { __typename?: 'AggregatedBalanceConnection', edges: Array<{ __typename?: 'AggregatedBalanceEdge', node: { __typename?: 'AggregatedBalance', assetId?: string | null, accountType?: Types.AccountType | null } } | null> } };
|
|
||||||
|
|
||||||
|
|
||||||
export const AccountHistoryDocument = gql`
|
|
||||||
query AccountHistory($partyId: ID!, $assetId: ID!, $accountTypes: [AccountType!], $dateRange: DateRange, $marketIds: [ID!]) {
|
|
||||||
balanceChanges(
|
|
||||||
filter: {partyIds: [$partyId], accountTypes: $accountTypes, assetId: $assetId, marketIds: $marketIds}
|
|
||||||
dateRange: $dateRange
|
|
||||||
) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
timestamp
|
|
||||||
partyId
|
|
||||||
balance
|
|
||||||
marketId
|
|
||||||
assetId
|
|
||||||
accountType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useAccountHistoryQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useAccountHistoryQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useAccountHistoryQuery` 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 } = useAccountHistoryQuery({
|
|
||||||
* variables: {
|
|
||||||
* partyId: // value for 'partyId'
|
|
||||||
* assetId: // value for 'assetId'
|
|
||||||
* accountTypes: // value for 'accountTypes'
|
|
||||||
* dateRange: // value for 'dateRange'
|
|
||||||
* marketIds: // value for 'marketIds'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useAccountHistoryQuery(baseOptions: Apollo.QueryHookOptions<AccountHistoryQuery, AccountHistoryQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<AccountHistoryQuery, AccountHistoryQueryVariables>(AccountHistoryDocument, options);
|
|
||||||
}
|
|
||||||
export function useAccountHistoryLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AccountHistoryQuery, AccountHistoryQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<AccountHistoryQuery, AccountHistoryQueryVariables>(AccountHistoryDocument, options);
|
|
||||||
}
|
|
||||||
export type AccountHistoryQueryHookResult = ReturnType<typeof useAccountHistoryQuery>;
|
|
||||||
export type AccountHistoryLazyQueryHookResult = ReturnType<typeof useAccountHistoryLazyQuery>;
|
|
||||||
export type AccountHistoryQueryResult = Apollo.QueryResult<AccountHistoryQuery, AccountHistoryQueryVariables>;
|
|
||||||
export const AccountsWithBalanceDocument = gql`
|
|
||||||
query AccountsWithBalance($partyId: ID!, $dateRange: DateRange) {
|
|
||||||
balanceChanges(filter: {partyIds: [$partyId]}, dateRange: $dateRange) {
|
|
||||||
edges {
|
|
||||||
node {
|
|
||||||
assetId
|
|
||||||
accountType
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* __useAccountsWithBalanceQuery__
|
|
||||||
*
|
|
||||||
* To run a query within a React component, call `useAccountsWithBalanceQuery` and pass it any options that fit your needs.
|
|
||||||
* When your component renders, `useAccountsWithBalanceQuery` 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 } = useAccountsWithBalanceQuery({
|
|
||||||
* variables: {
|
|
||||||
* partyId: // value for 'partyId'
|
|
||||||
* dateRange: // value for 'dateRange'
|
|
||||||
* },
|
|
||||||
* });
|
|
||||||
*/
|
|
||||||
export function useAccountsWithBalanceQuery(baseOptions: Apollo.QueryHookOptions<AccountsWithBalanceQuery, AccountsWithBalanceQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useQuery<AccountsWithBalanceQuery, AccountsWithBalanceQueryVariables>(AccountsWithBalanceDocument, options);
|
|
||||||
}
|
|
||||||
export function useAccountsWithBalanceLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<AccountsWithBalanceQuery, AccountsWithBalanceQueryVariables>) {
|
|
||||||
const options = {...defaultOptions, ...baseOptions}
|
|
||||||
return Apollo.useLazyQuery<AccountsWithBalanceQuery, AccountsWithBalanceQueryVariables>(AccountsWithBalanceDocument, options);
|
|
||||||
}
|
|
||||||
export type AccountsWithBalanceQueryHookResult = ReturnType<typeof useAccountsWithBalanceQuery>;
|
|
||||||
export type AccountsWithBalanceLazyQueryHookResult = ReturnType<typeof useAccountsWithBalanceLazyQuery>;
|
|
||||||
export type AccountsWithBalanceQueryResult = Apollo.QueryResult<AccountsWithBalanceQuery, AccountsWithBalanceQueryVariables>;
|
|
||||||
@@ -1,349 +0,0 @@
|
|||||||
import { addDecimal, fromNanoSeconds } from '@vegaprotocol/utils';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import compact from 'lodash/compact';
|
|
||||||
import uniqBy from 'lodash/uniqBy';
|
|
||||||
import type { ChangeEvent } from 'react';
|
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
|
||||||
import type { AccountHistoryQuery } from './__generated__/AccountHistory';
|
|
||||||
import { useAccountHistoryQuery } from './__generated__/AccountHistory';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
|
||||||
import { useAssetsDataProvider } from '@vegaprotocol/assets';
|
|
||||||
import {
|
|
||||||
AsyncRenderer,
|
|
||||||
Splash,
|
|
||||||
Toggle,
|
|
||||||
TradingButton,
|
|
||||||
TradingDropdown,
|
|
||||||
TradingDropdownContent,
|
|
||||||
TradingDropdownItem,
|
|
||||||
TradingDropdownTrigger,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { AccountTypeMapping } from '@vegaprotocol/types';
|
|
||||||
import { PriceChart } from 'pennant';
|
|
||||||
import 'pennant/dist/style.css';
|
|
||||||
import type { Account } from '@vegaprotocol/accounts';
|
|
||||||
import { accountsDataProvider } from '@vegaprotocol/accounts';
|
|
||||||
import {
|
|
||||||
useLocalStorageSnapshot,
|
|
||||||
useThemeSwitcher,
|
|
||||||
} from '@vegaprotocol/react-helpers';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import { getAsset, type Market } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
export const DateRange = {
|
|
||||||
RANGE_1D: '1D',
|
|
||||||
RANGE_7D: '7D',
|
|
||||||
RANGE_1M: '1M',
|
|
||||||
RANGE_3M: '3M',
|
|
||||||
RANGE_1Y: '1Y',
|
|
||||||
RANGE_YTD: 'YTD',
|
|
||||||
RANGE_ALL: 'All',
|
|
||||||
};
|
|
||||||
|
|
||||||
const dateRangeToggleItems = Object.entries(DateRange).map(([_, value]) => ({
|
|
||||||
label: t(value),
|
|
||||||
value: value,
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const calculateStartDate = (range: string): string | undefined => {
|
|
||||||
const now = new Date();
|
|
||||||
switch (range) {
|
|
||||||
case DateRange.RANGE_1D:
|
|
||||||
return new Date(now.setDate(now.getDate() - 1)).toISOString();
|
|
||||||
case DateRange.RANGE_7D:
|
|
||||||
return new Date(now.setDate(now.getDate() - 7)).toISOString();
|
|
||||||
case DateRange.RANGE_1M:
|
|
||||||
return new Date(now.setMonth(now.getMonth() - 1)).toISOString();
|
|
||||||
case DateRange.RANGE_3M:
|
|
||||||
return new Date(now.setMonth(now.getMonth() - 3)).toISOString();
|
|
||||||
case DateRange.RANGE_1Y:
|
|
||||||
return new Date(now.setFullYear(now.getFullYear() - 1)).toISOString();
|
|
||||||
case DateRange.RANGE_YTD:
|
|
||||||
return new Date(now.setMonth(0)).toISOString();
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccountHistoryContainer = () => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
|
||||||
const { data: assets } = useAssetsDataProvider();
|
|
||||||
|
|
||||||
if (!pubKey) {
|
|
||||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AsyncRenderer loading={!assets} error={undefined} data={assets}>
|
|
||||||
{assets && <AccountHistoryManager pubKey={pubKey} assetData={assets} />}
|
|
||||||
</AsyncRenderer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const AccountHistoryManager = ({
|
|
||||||
pubKey,
|
|
||||||
assetData,
|
|
||||||
}: {
|
|
||||||
pubKey: string;
|
|
||||||
assetData: AssetFieldsFragment[];
|
|
||||||
}) => {
|
|
||||||
const [accountType, setAccountType] = useState<Schema.AccountType>(
|
|
||||||
Schema.AccountType.ACCOUNT_TYPE_GENERAL
|
|
||||||
);
|
|
||||||
|
|
||||||
const variablesForOneTimeQuery = useMemo(
|
|
||||||
() => ({
|
|
||||||
partyId: pubKey,
|
|
||||||
}),
|
|
||||||
[pubKey]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data: accounts } = useDataProvider({
|
|
||||||
dataProvider: accountsDataProvider,
|
|
||||||
variables: variablesForOneTimeQuery,
|
|
||||||
skip: !pubKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
const assetIds = useMemo(
|
|
||||||
() => accounts?.map((e) => e?.asset?.id) || [],
|
|
||||||
[accounts]
|
|
||||||
);
|
|
||||||
|
|
||||||
const assets = useMemo(
|
|
||||||
() =>
|
|
||||||
assetData
|
|
||||||
.filter((a) => assetIds.includes(a.id))
|
|
||||||
.sort((a, b) => a.name.localeCompare(b.name)),
|
|
||||||
[assetData, assetIds]
|
|
||||||
);
|
|
||||||
const [assetId, setAssetId] = useLocalStorageSnapshot(
|
|
||||||
'account-history-active-asset-id'
|
|
||||||
);
|
|
||||||
|
|
||||||
const asset = useMemo(
|
|
||||||
() => assets.find((a) => a.id === assetId) || assets[0],
|
|
||||||
[assetId, assets]
|
|
||||||
);
|
|
||||||
|
|
||||||
const [range, setRange] = useState<typeof DateRange[keyof typeof DateRange]>(
|
|
||||||
DateRange.RANGE_1M
|
|
||||||
);
|
|
||||||
const [market, setMarket] = useState<Market | null>(null);
|
|
||||||
|
|
||||||
const marketFilterCb = useCallback(
|
|
||||||
(item: Market) => {
|
|
||||||
const itemAsset = getAsset(item);
|
|
||||||
return !asset?.id || itemAsset?.id === asset?.id;
|
|
||||||
},
|
|
||||||
[asset?.id]
|
|
||||||
);
|
|
||||||
const markets = useMemo<Market[] | null>(() => {
|
|
||||||
const arr =
|
|
||||||
accounts
|
|
||||||
?.filter((item: Account) => Boolean(item && item.market))
|
|
||||||
.map<Market>((item) => item.market as Market) ?? null;
|
|
||||||
return arr
|
|
||||||
? uniqBy(arr.filter(marketFilterCb), 'id').sort((a, b) =>
|
|
||||||
a.tradableInstrument.instrument.code.localeCompare(
|
|
||||||
b.tradableInstrument.instrument.code
|
|
||||||
)
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
}, [accounts, marketFilterCb]);
|
|
||||||
|
|
||||||
const resolveMarket = useCallback(
|
|
||||||
(m: Market) => {
|
|
||||||
setMarket(m);
|
|
||||||
const itemAsset = getAsset(m);
|
|
||||||
const newAssetId = itemAsset?.id;
|
|
||||||
const newAsset = assets.find((item) => item.id === newAssetId);
|
|
||||||
if ((!asset || (assets && newAssetId !== asset.id)) && newAsset) {
|
|
||||||
setAssetId(newAsset.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[asset, assets, setAssetId]
|
|
||||||
);
|
|
||||||
|
|
||||||
const variables = useMemo(
|
|
||||||
() => ({
|
|
||||||
partyId: pubKey,
|
|
||||||
assetId: asset?.id || '',
|
|
||||||
accountTypes: accountType ? [accountType] : undefined,
|
|
||||||
dateRange:
|
|
||||||
range === 'All' ? undefined : { start: calculateStartDate(range) },
|
|
||||||
marketIds: market?.id ? [market.id] : undefined,
|
|
||||||
}),
|
|
||||||
[pubKey, asset, accountType, range, market?.id]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data } = useAccountHistoryQuery({
|
|
||||||
variables,
|
|
||||||
skip: !asset || !pubKey,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col h-full gap-2">
|
|
||||||
<div className="flex flex-wrap justify-between px-1 pt-2 gap-2">
|
|
||||||
<div className="flex items-center gap-1 shrink-0">
|
|
||||||
<TradingDropdown
|
|
||||||
trigger={
|
|
||||||
<TradingDropdownTrigger>
|
|
||||||
<TradingButton size="small">
|
|
||||||
{accountType
|
|
||||||
? `${
|
|
||||||
AccountTypeMapping[
|
|
||||||
accountType as keyof typeof Schema.AccountType
|
|
||||||
]
|
|
||||||
} Account`
|
|
||||||
: t('Select account type')}
|
|
||||||
</TradingButton>
|
|
||||||
</TradingDropdownTrigger>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TradingDropdownContent>
|
|
||||||
{[
|
|
||||||
Schema.AccountType.ACCOUNT_TYPE_GENERAL,
|
|
||||||
Schema.AccountType.ACCOUNT_TYPE_BOND,
|
|
||||||
Schema.AccountType.ACCOUNT_TYPE_MARGIN,
|
|
||||||
].map((type) => (
|
|
||||||
<TradingDropdownItem
|
|
||||||
key={type}
|
|
||||||
onClick={() => {
|
|
||||||
setAccountType(type as Schema.AccountType);
|
|
||||||
|
|
||||||
// if not a margin account clear any market selection
|
|
||||||
if (type !== Schema.AccountType.ACCOUNT_TYPE_MARGIN) {
|
|
||||||
setMarket(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{AccountTypeMapping[type as keyof typeof Schema.AccountType]}
|
|
||||||
</TradingDropdownItem>
|
|
||||||
))}
|
|
||||||
</TradingDropdownContent>
|
|
||||||
</TradingDropdown>
|
|
||||||
|
|
||||||
<TradingDropdown
|
|
||||||
trigger={
|
|
||||||
<TradingDropdownTrigger>
|
|
||||||
<TradingButton size="small">
|
|
||||||
{asset ? asset.symbol : t('Select asset')}
|
|
||||||
</TradingButton>
|
|
||||||
</TradingDropdownTrigger>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TradingDropdownContent>
|
|
||||||
{assets.map((a) => (
|
|
||||||
<TradingDropdownItem
|
|
||||||
key={a.id}
|
|
||||||
onClick={() => {
|
|
||||||
setAssetId(a.id);
|
|
||||||
|
|
||||||
// if the selected asset is different to the selected market clear the market
|
|
||||||
if (market && a.id !== getAsset(market).id) {
|
|
||||||
setMarket(null);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{a.symbol}
|
|
||||||
</TradingDropdownItem>
|
|
||||||
))}
|
|
||||||
</TradingDropdownContent>
|
|
||||||
</TradingDropdown>
|
|
||||||
<TradingDropdown
|
|
||||||
trigger={
|
|
||||||
<TradingDropdownTrigger>
|
|
||||||
<TradingButton size="small">
|
|
||||||
{market
|
|
||||||
? market.tradableInstrument.instrument.code
|
|
||||||
: t('Select market')}
|
|
||||||
</TradingButton>
|
|
||||||
</TradingDropdownTrigger>
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<TradingDropdownContent>
|
|
||||||
{market && (
|
|
||||||
<TradingDropdownItem key="0" onClick={() => setMarket(null)}>
|
|
||||||
{t('All markets')}
|
|
||||||
</TradingDropdownItem>
|
|
||||||
)}
|
|
||||||
{markets?.map((m) => (
|
|
||||||
<TradingDropdownItem
|
|
||||||
key={m.id}
|
|
||||||
onClick={() => resolveMarket(m)}
|
|
||||||
>
|
|
||||||
{m.tradableInstrument.instrument.code}
|
|
||||||
</TradingDropdownItem>
|
|
||||||
))}
|
|
||||||
</TradingDropdownContent>
|
|
||||||
</TradingDropdown>
|
|
||||||
</div>
|
|
||||||
<div className="justify-items-end">
|
|
||||||
<Toggle
|
|
||||||
id="account-history-date-range"
|
|
||||||
name="account-history-date-range"
|
|
||||||
toggles={dateRangeToggleItems}
|
|
||||||
checkedValue={range}
|
|
||||||
onChange={(e: ChangeEvent<HTMLInputElement>) =>
|
|
||||||
setRange(e.target.value as keyof typeof DateRange)
|
|
||||||
}
|
|
||||||
size="sm"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex-1">
|
|
||||||
{asset && (
|
|
||||||
<div className="h-full">
|
|
||||||
<AccountHistoryChart
|
|
||||||
data={data}
|
|
||||||
accountType={accountType}
|
|
||||||
asset={asset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AccountHistoryChart = ({
|
|
||||||
data,
|
|
||||||
accountType,
|
|
||||||
asset,
|
|
||||||
}: {
|
|
||||||
data: AccountHistoryQuery | undefined;
|
|
||||||
accountType: Schema.AccountType;
|
|
||||||
asset: AssetFieldsFragment;
|
|
||||||
}) => {
|
|
||||||
const { theme } = useThemeSwitcher();
|
|
||||||
const values: { cols: [string, string]; rows: [Date, number][] } | null =
|
|
||||||
useMemo(() => {
|
|
||||||
if (!data?.balanceChanges.edges.length) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const valuesData = compact(data.balanceChanges.edges)
|
|
||||||
.reduce((acc, edge) => {
|
|
||||||
if (edge.node.accountType === accountType) {
|
|
||||||
acc?.push({
|
|
||||||
datetime: fromNanoSeconds(edge.node.timestamp),
|
|
||||||
balance: Number(addDecimal(edge.node.balance, asset.decimals)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}, [] as { datetime: Date; balance: number }[])
|
|
||||||
.reverse();
|
|
||||||
return {
|
|
||||||
cols: ['Date', `${asset.symbol} account balance`],
|
|
||||||
rows: compact(valuesData).map((d) => [d.datetime, d.balance]),
|
|
||||||
};
|
|
||||||
}, [accountType, asset.decimals, asset.symbol, data?.balanceChanges.edges]);
|
|
||||||
|
|
||||||
if (!data || !values?.rows.length) {
|
|
||||||
return <Splash> {t('No account history data')}</Splash>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <PriceChart data={values} theme={theme} />;
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { SidebarButton, ViewType } from '../../components/sidebar';
|
||||||
|
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||||
|
|
||||||
|
export const PortfolioSidebar = () => {
|
||||||
|
const currentRouteId = useGetCurrentRouteId();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Deposit}
|
||||||
|
icon={VegaIconNames.DEPOSIT}
|
||||||
|
tooltip={t('Deposit')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Withdraw}
|
||||||
|
icon={VegaIconNames.WITHDRAW}
|
||||||
|
tooltip={t('Withdraw')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
<SidebarButton
|
||||||
|
view={ViewType.Transfer}
|
||||||
|
icon={VegaIconNames.TRANSFER}
|
||||||
|
tooltip={t('Transfer')}
|
||||||
|
routeId={currentRouteId}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -9,11 +9,12 @@ import { usePageTitleStore } from '../../stores';
|
|||||||
import { AccountsContainer } from '../../components/accounts-container';
|
import { AccountsContainer } from '../../components/accounts-container';
|
||||||
import { DepositsContainer } from '../../components/deposits-container';
|
import { DepositsContainer } from '../../components/deposits-container';
|
||||||
import { FillsContainer } from '../../components/fills-container';
|
import { FillsContainer } from '../../components/fills-container';
|
||||||
|
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
|
||||||
import { PositionsContainer } from '../../components/positions-container';
|
import { PositionsContainer } from '../../components/positions-container';
|
||||||
|
import { PositionsMenu } from '../../components/positions-menu';
|
||||||
import { WithdrawalsContainer } from '../../components/withdrawals-container';
|
import { WithdrawalsContainer } from '../../components/withdrawals-container';
|
||||||
import { OrdersContainer } from '../../components/orders-container';
|
import { OrdersContainer } from '../../components/orders-container';
|
||||||
import { LedgerContainer } from '../../components/ledger-container';
|
import { LedgerContainer } from '../../components/ledger-container';
|
||||||
import { AccountHistoryContainer } from './account-history-container';
|
|
||||||
import {
|
import {
|
||||||
ResizableGrid,
|
ResizableGrid,
|
||||||
ResizableGridPanel,
|
ResizableGridPanel,
|
||||||
@@ -31,7 +32,7 @@ const WithdrawalsIndicator = () => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span className="bg-vega-clight-500 dark:bg-vega-cdark-500 text-default rounded p-1 leading-none">
|
<span className="p-1 leading-none rounded bg-vega-clight-500 dark:bg-vega-cdark-500 text-default">
|
||||||
{ready.length}
|
{ready.length}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@@ -64,11 +65,12 @@ export const Portfolio = () => {
|
|||||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||||
<ResizableGridPanel minSize={75}>
|
<ResizableGridPanel minSize={75}>
|
||||||
<PortfolioGridChild>
|
<PortfolioGridChild>
|
||||||
<Tabs storageKey="console-portfolio-top">
|
<Tabs storageKey="console-portfolio-top-1">
|
||||||
<Tab id="account-history" name={t('Account history')}>
|
<Tab
|
||||||
<AccountHistoryContainer />
|
id="positions"
|
||||||
</Tab>
|
name={t('Positions')}
|
||||||
<Tab id="positions" name={t('Positions')}>
|
menu={<PositionsMenu />}
|
||||||
|
>
|
||||||
<PositionsContainer allKeys />
|
<PositionsContainer allKeys />
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab id="orders" name={t('Orders')}>
|
<Tab id="orders" name={t('Orders')}>
|
||||||
@@ -77,6 +79,9 @@ export const Portfolio = () => {
|
|||||||
<Tab id="fills" name={t('Fills')}>
|
<Tab id="fills" name={t('Fills')}>
|
||||||
<FillsContainer />
|
<FillsContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
|
<Tab id="funding-payments" name={t('Funding payments')}>
|
||||||
|
<FundingPaymentsContainer />
|
||||||
|
</Tab>
|
||||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||||
<LedgerContainer />
|
<LedgerContainer />
|
||||||
</Tab>
|
</Tab>
|
||||||
@@ -123,7 +128,7 @@ interface PortfolioGridChildProps {
|
|||||||
const PortfolioGridChild = ({ children }: PortfolioGridChildProps) => {
|
const PortfolioGridChild = ({ children }: PortfolioGridChildProps) => {
|
||||||
return (
|
return (
|
||||||
<section className="h-full p-1">
|
<section className="h-full p-1">
|
||||||
<div className="border border-default h-full rounded-sm">{children}</div>
|
<div className="h-full border rounded-sm border-default">{children}</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,21 +1,44 @@
|
|||||||
import {
|
import {
|
||||||
Input,
|
Input,
|
||||||
InputError,
|
InputError,
|
||||||
|
Loader,
|
||||||
VegaIcon,
|
VegaIcon,
|
||||||
VegaIconNames,
|
VegaIconNames,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import type { FieldValues } from 'react-hook-form';
|
import type { FieldValues } from 'react-hook-form';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Navigate, useSearchParams } from 'react-router-dom';
|
import { Navigate, useNavigate, useSearchParams } from 'react-router-dom';
|
||||||
|
import type { ButtonHTMLAttributes, MouseEventHandler } from 'react';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
import { Button } from './buttons';
|
import { RainbowButton } from './buttons';
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||||
import { useReferral } from './hooks/use-referral';
|
import { useReferral } from './hooks/use-referral';
|
||||||
import { Routes } from '../../lib/links';
|
import { Routes } from '../../lib/links';
|
||||||
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
|
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import { Statistics } from './referral-statistics';
|
||||||
|
import { useReferralProgram } from './hooks/use-referral-program';
|
||||||
|
|
||||||
|
const RELOAD_DELAY = 3000;
|
||||||
|
|
||||||
|
const validateCode = (value: string) => {
|
||||||
|
const number = +`0x${value}`;
|
||||||
|
if (!value || value.length !== 64) {
|
||||||
|
return t('Code must be 64 characters in length');
|
||||||
|
} else if (Number.isNaN(number)) {
|
||||||
|
return t('Code must be be valid hex');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
export const ApplyCodeForm = () => {
|
export const ApplyCodeForm = () => {
|
||||||
|
const program = useReferralProgram();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const openWalletDialog = useVegaWalletDialogStore(
|
||||||
|
(store) => store.openVegaWalletDialog
|
||||||
|
);
|
||||||
|
|
||||||
const [status, setStatus] = useState<
|
const [status, setStatus] = useState<
|
||||||
'requested' | 'failed' | 'successful' | null
|
'requested' | 'failed' | 'successful' | null
|
||||||
>(null);
|
>(null);
|
||||||
@@ -27,11 +50,17 @@ export const ApplyCodeForm = () => {
|
|||||||
formState: { errors },
|
formState: { errors },
|
||||||
setValue,
|
setValue,
|
||||||
setError,
|
setError,
|
||||||
|
watch,
|
||||||
} = useForm();
|
} = useForm();
|
||||||
const [params] = useSearchParams();
|
const [params] = useSearchParams();
|
||||||
|
|
||||||
const { data: referee } = useReferral(pubKey, 'referee');
|
const { data: referee } = useReferral({ pubKey, role: 'referee' });
|
||||||
const { data: referrer } = useReferral(pubKey, 'referrer');
|
const { data: referrer } = useReferral({ pubKey, role: 'referrer' });
|
||||||
|
|
||||||
|
const codeField = watch('code');
|
||||||
|
const { data: previewData, loading: previewLoading } = useReferral({
|
||||||
|
code: validateCode(codeField) ? codeField : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const code = params.get('code');
|
const code = params.get('code');
|
||||||
@@ -54,7 +83,7 @@ export const ApplyCodeForm = () => {
|
|||||||
if (!res) {
|
if (!res) {
|
||||||
setError('code', {
|
setError('code', {
|
||||||
type: 'required',
|
type: 'required',
|
||||||
message: 'The transaction could not be sent',
|
message: t('The transaction could not be sent'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (res) {
|
if (res) {
|
||||||
@@ -65,9 +94,13 @@ export const ApplyCodeForm = () => {
|
|||||||
if (err.message.includes('user rejected')) {
|
if (err.message.includes('user rejected')) {
|
||||||
setStatus(null);
|
setStatus(null);
|
||||||
} else {
|
} else {
|
||||||
|
setStatus(null);
|
||||||
setError('code', {
|
setError('code', {
|
||||||
type: 'required',
|
type: 'required',
|
||||||
message: 'Your code has been rejected',
|
message:
|
||||||
|
err instanceof Error
|
||||||
|
? err.message
|
||||||
|
: t('Your code has been rejected'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -99,10 +132,21 @@ export const ApplyCodeForm = () => {
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// go to main page when successfully applied
|
||||||
|
useEffect(() => {
|
||||||
|
if (status === 'successful') {
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate(Routes.REFERRALS);
|
||||||
|
}, RELOAD_DELAY);
|
||||||
|
}
|
||||||
|
}, [navigate, status]);
|
||||||
|
|
||||||
|
// go to main page if the current pubkey is already a referrer or referee
|
||||||
if (referee || referrer) {
|
if (referee || referrer) {
|
||||||
return <Navigate to={Routes.REFERRALS} />;
|
return <Navigate to={Routes.REFERRALS} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show "code applied" message when successfully applied
|
||||||
if (status === 'successful') {
|
if (status === 'successful') {
|
||||||
return (
|
return (
|
||||||
<div className="w-1/2 mx-auto">
|
<div className="w-1/2 mx-auto">
|
||||||
@@ -110,61 +154,94 @@ export const ApplyCodeForm = () => {
|
|||||||
<span className="text-vega-green-500">
|
<span className="text-vega-green-500">
|
||||||
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
<VegaIcon name={VegaIconNames.TICK} size={20} />
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
<span className="pt-1">Code applied</span>
|
<span className="pt-1">{t('Code applied')}</span>
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getButtonProps = () => {
|
const getButtonProps = () => {
|
||||||
if (isReadOnly || !pubKey) {
|
if (!pubKey) {
|
||||||
|
return {
|
||||||
|
disabled: false,
|
||||||
|
children: t('Connect wallet'),
|
||||||
|
type: 'button' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
|
||||||
|
onClick: ((event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
openWalletDialog();
|
||||||
|
}) as MouseEventHandler,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isReadOnly) {
|
||||||
return {
|
return {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
children: 'Apply',
|
children: t('Apply a code'),
|
||||||
|
type: 'submit' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'requested') {
|
if (status === 'requested') {
|
||||||
return {
|
return {
|
||||||
disabled: true,
|
disabled: true,
|
||||||
children: 'Confirm in wallet...',
|
children: t('Confirm in wallet...'),
|
||||||
|
type: 'submit' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
children: 'Apply',
|
children: t('Apply a code'),
|
||||||
|
type: 'submit' as ButtonHTMLAttributes<HTMLButtonElement>['type'],
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-1/2 mx-auto">
|
<>
|
||||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
|
||||||
Apply a referral code
|
<h3 className="mb-4 text-2xl text-center calt">
|
||||||
</h3>
|
{t('Apply a referral code')}
|
||||||
<p className="mb-6 text-center">Enter a referral code</p>
|
</h3>
|
||||||
<form
|
<p className="mb-4 text-center text-base">
|
||||||
className={classNames('w-full flex flex-col gap-3', {
|
{t('Enter a referral code to get trading discounts.')}
|
||||||
'animate-shake': Boolean(errors.code),
|
</p>
|
||||||
})}
|
<form
|
||||||
onSubmit={handleSubmit(onSubmit)}
|
className={classNames('w-full flex flex-col gap-4', {
|
||||||
>
|
'animate-shake': Boolean(errors.code),
|
||||||
<label className="flex-grow">
|
})}
|
||||||
<span className="block mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100">
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
Your referral code
|
>
|
||||||
</span>
|
<label>
|
||||||
<Input
|
<span className="sr-only">{t('Your referral code')}</span>
|
||||||
hasError={Boolean(errors.code)}
|
<Input
|
||||||
{...register('code', {
|
hasError={Boolean(errors.code)}
|
||||||
required: 'You have to provide a code to apply it.',
|
{...register('code', {
|
||||||
})}
|
required: t('You have to provide a code to apply it.'),
|
||||||
/>
|
validate: validateCode,
|
||||||
</label>
|
})}
|
||||||
<Button className="w-full" type="submit" {...getButtonProps()} />
|
placeholder="Enter a code"
|
||||||
</form>
|
className="mb-2 bg-vega-clight-900 dark:bg-vega-cdark-700"
|
||||||
{errors.code && (
|
/>
|
||||||
<InputError>{errors.code.message?.toString()}</InputError>
|
</label>
|
||||||
)}
|
<RainbowButton variant="border" {...getButtonProps()} />
|
||||||
</div>
|
</form>
|
||||||
|
{errors.code && (
|
||||||
|
<InputError className="break-words overflow-auto">
|
||||||
|
{errors.code.message?.toString()}
|
||||||
|
</InputError>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{previewLoading && !previewData ? (
|
||||||
|
<div className="mt-10">
|
||||||
|
<Loader />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
{previewData ? (
|
||||||
|
<div className="mt-10">
|
||||||
|
<h2 className="text-2xl mb-5">{t('You are joining')}</h2>
|
||||||
|
<Statistics data={previewData} program={program} as="referee" />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,20 +16,23 @@ export const RainbowButton = ({
|
|||||||
}: RainbowButtonProps & ButtonHTMLAttributes<HTMLButtonElement>) => (
|
}: RainbowButtonProps & ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||||
<button
|
<button
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'bg-rainbow hover:bg-none hover:bg-rainbow enabled:hover:bg-vega-pink-500 rounded-lg overflow-hidden disabled:opacity-40',
|
'bg-rainbow rounded-lg overflow-hidden disabled:opacity-40',
|
||||||
|
'hover:bg-rainbow-180 hover:animate-spin-rainbow',
|
||||||
{
|
{
|
||||||
'px-5 py-3 text-white': variant === 'full',
|
'px-5 py-3 text-white': variant === 'full',
|
||||||
'p-[0.125rem]': variant === 'border',
|
'p-[0.125rem]': variant === 'border',
|
||||||
},
|
}
|
||||||
className
|
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames({
|
className={classNames(
|
||||||
'bg-white dark:bg-vega-cdark-900 text-black dark:text-white px-5 py-3 rounded-[0.35rem] overflow-hidden':
|
{
|
||||||
variant === 'border',
|
'bg-vega-clight-800 dark:bg-vega-cdark-800 text-black dark:text-white px-5 py-3 rounded-[0.35rem] overflow-hidden':
|
||||||
})}
|
variant === 'border',
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
@@ -55,6 +58,20 @@ const DISABLED_RAINBOW_TAB_STYLE = classNames(
|
|||||||
'[&.active]:text-white'
|
'[&.active]:text-white'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const TAB_STYLE = classNames(
|
||||||
|
'inline-block',
|
||||||
|
'bg-transparent',
|
||||||
|
'text-vega-clight-200 dark:text-vega-cdark-200',
|
||||||
|
'hover:text-vega-clight-100 dark:hover:text-vega-cdark-100',
|
||||||
|
'data-[state="active"]:text-black dark:data-[state="active"]:text-white',
|
||||||
|
'data-[state="active"]:border-b-2 data-[state="active"]:border-b-black dark:data-[state="active"]:border-b-white',
|
||||||
|
'[&.active]:text-black dark:[&.active]:text-white',
|
||||||
|
'[&.active]:border-b-2 [&.active]:border-b-black dark:[&.active]:border-b-white',
|
||||||
|
'mx-4 px-0 py-3',
|
||||||
|
'uppercase'
|
||||||
|
);
|
||||||
|
const DISABLED_TAB_STYLE = classNames('pointer-events-none');
|
||||||
|
|
||||||
export const RainbowTabButton = forwardRef<
|
export const RainbowTabButton = forwardRef<
|
||||||
HTMLButtonElement,
|
HTMLButtonElement,
|
||||||
{ disabled?: boolean } & ButtonHTMLAttributes<HTMLButtonElement>
|
{ disabled?: boolean } & ButtonHTMLAttributes<HTMLButtonElement>
|
||||||
@@ -93,6 +110,26 @@ export const RainbowTabLink = ({
|
|||||||
</NavLink>
|
</NavLink>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TabLink = ({
|
||||||
|
to,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
disabled = false,
|
||||||
|
...props
|
||||||
|
}: { disabled?: boolean } & ComponentProps<typeof NavLink>) => (
|
||||||
|
<NavLink
|
||||||
|
to={to}
|
||||||
|
className={classNames(
|
||||||
|
TAB_STYLE,
|
||||||
|
disabled && DISABLED_TAB_STYLE,
|
||||||
|
typeof className === 'string' ? className : undefined
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</NavLink>
|
||||||
|
);
|
||||||
|
|
||||||
export const Button = forwardRef<
|
export const Button = forwardRef<
|
||||||
HTMLButtonElement,
|
HTMLButtonElement,
|
||||||
ComponentProps<typeof TradingButton>
|
ComponentProps<typeof TradingButton>
|
||||||
|
|||||||
@@ -4,3 +4,8 @@ export const GRADIENT =
|
|||||||
|
|
||||||
export const SKY_BACKGROUND =
|
export const SKY_BACKGROUND =
|
||||||
'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat bg-local';
|
'bg-[url(/sky-light.png)] dark:bg-[url(/sky-dark.png)] bg-[40%_0px] bg-[length:1440px] bg-no-repeat bg-local';
|
||||||
|
|
||||||
|
// TODO: Update the links to use the correct referral related pages
|
||||||
|
export const REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
|
||||||
|
export const ABOUT_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
|
||||||
|
export const DISCLAIMER_REFERRAL_DOCS_LINK = 'https://docs.vega.xyz/';
|
||||||
|
|||||||
@@ -19,70 +19,58 @@ import {
|
|||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||||
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
|
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
|
||||||
import { useStakeAvailable } from './hooks/use-stake-available';
|
import { useStakeAvailable } from './hooks/use-stake-available';
|
||||||
|
import {
|
||||||
|
ABOUT_REFERRAL_DOCS_LINK,
|
||||||
|
DISCLAIMER_REFERRAL_DOCS_LINK,
|
||||||
|
} from './constants';
|
||||||
|
import { useReferral } from './hooks/use-referral';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export const CreateCodeContainer = () => {
|
export const CreateCodeContainer = () => {
|
||||||
const { stakeAvailable, requiredStake } = useStakeAvailable();
|
return <CreateCodeForm />;
|
||||||
if (stakeAvailable == null || requiredStake == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CreateCodeForm
|
|
||||||
currentStakeAvailable={stakeAvailable}
|
|
||||||
requiredStake={requiredStake}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CreateCodeForm = ({
|
export const CreateCodeForm = () => {
|
||||||
currentStakeAvailable,
|
|
||||||
requiredStake,
|
|
||||||
}: {
|
|
||||||
currentStakeAvailable: bigint;
|
|
||||||
requiredStake: bigint;
|
|
||||||
}) => {
|
|
||||||
const [dialogOpen, setDialogOpen] = useState(false);
|
const [dialogOpen, setDialogOpen] = useState(false);
|
||||||
const openWalletDialog = useVegaWalletDialogStore(
|
const openWalletDialog = useVegaWalletDialogStore(
|
||||||
(store) => store.openVegaWalletDialog
|
(store) => store.openVegaWalletDialog
|
||||||
);
|
);
|
||||||
const { pubKey } = useVegaWallet();
|
const { pubKey, isReadOnly } = useVegaWallet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-1/2 mx-auto">
|
<div className="w-2/3 max-w-md mx-auto bg-vega-clight-800 dark:bg-vega-cdark-800 p-8 rounded-lg">
|
||||||
<h3 className="mb-5 text-xl text-center uppercase calt">
|
<h3 className="mb-4 text-2xl text-center calt">
|
||||||
Create a referral code
|
{t('Create a referral code')}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="mb-6 text-center">
|
<p className="mb-4 text-center text-base">
|
||||||
Generate a referral code to share with your friends and start earning
|
{t(
|
||||||
commission.
|
'Generate a referral code to share with your friends and start earning commission.'
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<div className="mb-5">
|
|
||||||
<div className="text-center">
|
<div className="w-full flex flex-col">
|
||||||
<RainbowButton
|
<RainbowButton
|
||||||
variant="border"
|
variant="border"
|
||||||
onClick={() => {
|
disabled={isReadOnly}
|
||||||
if (pubKey) {
|
onClick={() => {
|
||||||
setDialogOpen(true);
|
if (pubKey) {
|
||||||
} else {
|
setDialogOpen(true);
|
||||||
openWalletDialog();
|
} else {
|
||||||
}
|
openWalletDialog();
|
||||||
}}
|
}
|
||||||
>
|
}}
|
||||||
{pubKey ? 'Create a referral code' : 'Connect wallet'}
|
>
|
||||||
</RainbowButton>
|
{pubKey ? t('Create a referral code') : t('Connect wallet')}
|
||||||
</div>
|
</RainbowButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Dialog
|
<Dialog
|
||||||
title="Create a referral code"
|
title={t('Create a referral code')}
|
||||||
open={dialogOpen}
|
open={dialogOpen}
|
||||||
onChange={() => setDialogOpen(false)}
|
onChange={() => setDialogOpen(false)}
|
||||||
size="small"
|
size="small"
|
||||||
>
|
>
|
||||||
<CreateCodeDialog
|
<CreateCodeDialog setDialogOpen={setDialogOpen} />
|
||||||
currentStakeAvailable={currentStakeAvailable}
|
|
||||||
setDialogOpen={setDialogOpen}
|
|
||||||
requiredStake={requiredStake}
|
|
||||||
/>
|
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -90,21 +78,21 @@ export const CreateCodeForm = ({
|
|||||||
|
|
||||||
const CreateCodeDialog = ({
|
const CreateCodeDialog = ({
|
||||||
setDialogOpen,
|
setDialogOpen,
|
||||||
currentStakeAvailable,
|
|
||||||
requiredStake,
|
|
||||||
}: {
|
}: {
|
||||||
setDialogOpen: (open: boolean) => void;
|
setDialogOpen: (open: boolean) => void;
|
||||||
currentStakeAvailable: bigint;
|
|
||||||
requiredStake: bigint;
|
|
||||||
}) => {
|
}) => {
|
||||||
const createLink = useLinks(DApp.Governance);
|
const createLink = useLinks(DApp.Governance);
|
||||||
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
const { isReadOnly, pubKey, sendTx } = useVegaWallet();
|
||||||
|
const { refetch } = useReferral({ pubKey, role: 'referrer' });
|
||||||
const [err, setErr] = useState<string | null>(null);
|
const [err, setErr] = useState<string | null>(null);
|
||||||
const [code, setCode] = useState<string | null>(null);
|
const [code, setCode] = useState<string | null>(null);
|
||||||
const [status, setStatus] = useState<
|
const [status, setStatus] = useState<
|
||||||
'idle' | 'loading' | 'success' | 'error'
|
'idle' | 'loading' | 'success' | 'error'
|
||||||
>('idle');
|
>('idle');
|
||||||
|
|
||||||
|
const { stakeAvailable: currentStakeAvailable, requiredStake } =
|
||||||
|
useStakeAvailable();
|
||||||
|
|
||||||
const onSubmit = () => {
|
const onSubmit = () => {
|
||||||
if (isReadOnly || !pubKey) {
|
if (isReadOnly || !pubKey) {
|
||||||
setErr('Not connected');
|
setErr('Not connected');
|
||||||
@@ -140,45 +128,60 @@ const CreateCodeDialog = ({
|
|||||||
const getButtonProps = () => {
|
const getButtonProps = () => {
|
||||||
if (status === 'idle' || status === 'error') {
|
if (status === 'idle' || status === 'error') {
|
||||||
return {
|
return {
|
||||||
children: 'Generate code',
|
children: t('Generate code'),
|
||||||
onClick: () => onSubmit(),
|
onClick: () => onSubmit(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'loading') {
|
if (status === 'loading') {
|
||||||
return {
|
return {
|
||||||
children: 'Confirm in wallet...',
|
children: t('Confirm in wallet...'),
|
||||||
disabled: true,
|
disabled: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
return {
|
return {
|
||||||
children: 'Close',
|
children: t('Close'),
|
||||||
intent: Intent.Success,
|
intent: Intent.Success,
|
||||||
onClick: () => setDialogOpen(false),
|
onClick: () => {
|
||||||
|
refetch();
|
||||||
|
setDialogOpen(false);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Add when network parameters are updated
|
if (!pubKey || currentStakeAvailable == null || requiredStake == null) {
|
||||||
if (
|
return (
|
||||||
currentStakeAvailable === BigInt(0) ||
|
<div className="flex flex-col gap-4">
|
||||||
currentStakeAvailable < requiredStake
|
<p>{t('You must be connected to the Vega wallet.')}</p>
|
||||||
) {
|
<TradingButton
|
||||||
|
intent={Intent.Primary}
|
||||||
|
onClick={() => setDialogOpen(false)}
|
||||||
|
>
|
||||||
|
{t('Close')}
|
||||||
|
</TradingButton>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentStakeAvailable < requiredStake) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
<p>
|
<p>
|
||||||
You need at least{' '}
|
{t('You need at least')}{' '}
|
||||||
{addDecimalsFormatNumber(requiredStake.toString(), 18)} VEGA staked to
|
{addDecimalsFormatNumber(requiredStake.toString(), 18)}{' '}
|
||||||
generate a referral code and participate in the referral program.
|
{t(
|
||||||
|
'VEGA staked to generate a referral code and participate in the referral program.'
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<TradingAnchorButton
|
<TradingAnchorButton
|
||||||
href={createLink(TokenStaticLinks.ASSOCIATE)}
|
href={createLink(TokenStaticLinks.ASSOCIATE)}
|
||||||
intent={Intent.Primary}
|
intent={Intent.Primary}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
>
|
>
|
||||||
Stake some $VEGA now
|
{t('Stake some $VEGA now')}
|
||||||
</TradingAnchorButton>
|
</TradingAnchorButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -188,8 +191,9 @@ const CreateCodeDialog = ({
|
|||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-4">
|
||||||
{(status === 'idle' || status === 'loading' || status === 'error') && (
|
{(status === 'idle' || status === 'loading' || status === 'error') && (
|
||||||
<p>
|
<p>
|
||||||
Generate a referral code to share with your friends and start earning
|
{t(
|
||||||
commission.
|
'Generate a referral code to share with your friends and start earning commission.'
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
{status === 'success' && code && (
|
{status === 'success' && code && (
|
||||||
@@ -204,7 +208,7 @@ const CreateCodeDialog = ({
|
|||||||
className="text-sm no-underline"
|
className="text-sm no-underline"
|
||||||
icon={<VegaIcon name={VegaIconNames.COPY} />}
|
icon={<VegaIcon name={VegaIconNames.COPY} />}
|
||||||
>
|
>
|
||||||
<span>Copy</span>
|
<span>{t('Copy')}</span>
|
||||||
</TradingButton>
|
</TradingButton>
|
||||||
</CopyWithTooltip>
|
</CopyWithTooltip>
|
||||||
</div>
|
</div>
|
||||||
@@ -215,10 +219,13 @@ const CreateCodeDialog = ({
|
|||||||
{...getButtonProps()}
|
{...getButtonProps()}
|
||||||
/>
|
/>
|
||||||
{err && <InputError>{err}</InputError>}
|
{err && <InputError>{err}</InputError>}
|
||||||
{/* TODO: Add links */}
|
|
||||||
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
|
<div className="flex justify-center pt-5 mt-2 text-sm border-t gap-4 text-default border-default">
|
||||||
<ExternalLink>About the referral program</ExternalLink>
|
<ExternalLink href={ABOUT_REFERRAL_DOCS_LINK}>
|
||||||
<ExternalLink>Disclaimer</ExternalLink>
|
{t('About the referral program')}
|
||||||
|
</ExternalLink>
|
||||||
|
<ExternalLink href={DISCLAIMER_REFERRAL_DOCS_LINK}>
|
||||||
|
{t('Disclaimer')}
|
||||||
|
</ExternalLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RainbowButton } from './buttons';
|
|||||||
import { AnimatedDudeWithWire } from './graphics/dude';
|
import { AnimatedDudeWithWire } from './graphics/dude';
|
||||||
import { LayoutWithSky } from './layout';
|
import { LayoutWithSky } from './layout';
|
||||||
import { Routes } from '../../lib/links';
|
import { Routes } from '../../lib/links';
|
||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
|
||||||
export const ErrorBoundary = () => {
|
export const ErrorBoundary = () => {
|
||||||
const error = useRouteError();
|
const error = useRouteError();
|
||||||
@@ -39,7 +40,7 @@ export const ErrorBoundary = () => {
|
|||||||
variant="border"
|
variant="border"
|
||||||
className="text-xs"
|
className="text-xs"
|
||||||
>
|
>
|
||||||
Go back and try again
|
{t('Go back and try again')}
|
||||||
</RainbowButton>
|
</RainbowButton>
|
||||||
</p>
|
</p>
|
||||||
</LayoutWithSky>
|
</LayoutWithSky>
|
||||||
@@ -60,7 +61,7 @@ export const NotFound = () => {
|
|||||||
<h1 className="text-6xl font-alpha calt mb-10">{'Not found'}</h1>
|
<h1 className="text-6xl font-alpha calt mb-10">{'Not found'}</h1>
|
||||||
|
|
||||||
<p className="text-lg mb-10">
|
<p className="text-lg mb-10">
|
||||||
{"The page you're looking for doesn't exists."}
|
{t("The page you're looking for doesn't exists.")}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-lg mb-10">
|
<p className="text-lg mb-10">
|
||||||
@@ -69,7 +70,7 @@ export const NotFound = () => {
|
|||||||
variant="border"
|
variant="border"
|
||||||
className="text-xs"
|
className="text-xs"
|
||||||
>
|
>
|
||||||
Go back and try again
|
{t('Go back and try again')}
|
||||||
</RainbowButton>
|
</RainbowButton>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user