Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88264d890d | ||
|
|
26b78f878b | ||
|
|
054c0377b4 | ||
|
|
29bcbd06fb | ||
|
|
1d721dc748 | ||
|
|
1d71a839b3 | ||
|
|
4b917926c5 | ||
|
|
3a56678403 | ||
|
|
bcb5351dfc | ||
|
|
177e72dd16 | ||
|
|
8221882346 | ||
|
|
b1a8473131 | ||
|
|
f18216f70f | ||
|
|
de2b79416e | ||
|
|
6504912284 | ||
|
|
93643f1737 | ||
|
|
464d5af6be | ||
|
|
b2a62f16bc | ||
|
|
9ec03a04ea | ||
|
|
2a0727ec4b | ||
|
|
0d39c2354c | ||
|
|
ad6f0c5798 | ||
|
|
38d13085fb | ||
|
|
c0f4278b81 | ||
|
|
b2777043b4 | ||
|
|
4d19b55096 | ||
|
|
7ea7362a7d | ||
|
|
bbfe42ddb1 | ||
|
|
b163af3e8a | ||
|
|
e6b3ff456d | ||
|
|
00dbb7dd60 | ||
|
|
82df401611 |
@@ -4,6 +4,5 @@ tmp/*
|
|||||||
.dockerignore
|
.dockerignore
|
||||||
dockerfiles
|
dockerfiles
|
||||||
node_modules
|
node_modules
|
||||||
.git
|
|
||||||
.github
|
.github
|
||||||
.vscode
|
.vscode
|
||||||
|
|||||||
@@ -196,9 +196,9 @@ jobs:
|
|||||||
cypress:
|
cypress:
|
||||||
needs: [build-sources, check-e2e-needed]
|
needs: [build-sources, check-e2e-needed]
|
||||||
name: '(CI) cypress'
|
name: '(CI) cypress'
|
||||||
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
|
|
||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
|
if: needs.check-e2e-needed.outputs.run-tests == 'true' && (contains(needs.build-sources.outputs.projects, 'governance') || contains(needs.build-sources.outputs.projects, 'explorer'))
|
||||||
with:
|
with:
|
||||||
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
projects: ${{ needs.build-sources.outputs.projects-e2e }}
|
||||||
tags: '@smoke'
|
tags: '@smoke'
|
||||||
@@ -287,6 +287,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- run: |
|
- run: |
|
||||||
result="${{ needs.cypress.result }}"
|
result="${{ needs.cypress.result }}"
|
||||||
|
echo "Result: $result"
|
||||||
if [[ $result == "success" || $result == "skipped" ]]; then
|
if [[ $result == "success" || $result == "skipped" ]]; then
|
||||||
exit 0
|
exit 0
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
name: Cypress Console tests -- live environment
|
|
||||||
|
|
||||||
# This workflow runs using provided url
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
url:
|
|
||||||
description: 'Url'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
cypress-run:
|
|
||||||
name: Run Cypress Trading tests -- live environment
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
|
|
||||||
- name: Use Node.js 20
|
|
||||||
id: Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- name: Run Cypress tests
|
|
||||||
uses: cypress-io/github-action@v4
|
|
||||||
with:
|
|
||||||
browser: chrome
|
|
||||||
record: true
|
|
||||||
project: ./apps/trading-e2e
|
|
||||||
config: baseUrl=${{ github.event.inputs.url }}
|
|
||||||
env: grepTags=@live
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
@@ -12,7 +12,6 @@ on:
|
|||||||
options:
|
options:
|
||||||
- explorer-e2e
|
- explorer-e2e
|
||||||
- governance-e2e
|
- governance-e2e
|
||||||
- trading-e2e
|
|
||||||
tags:
|
tags:
|
||||||
description: 'Test tags to run'
|
description: 'Test tags to run'
|
||||||
required: true
|
required: true
|
||||||
|
|||||||
@@ -10,5 +10,5 @@ jobs:
|
|||||||
uses: ./.github/workflows/cypress-run.yml
|
uses: ./.github/workflows/cypress-run.yml
|
||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
projects: '["explorer-e2e","governance-e2e"]'
|
||||||
tags: '@smoke @regression @slow'
|
tags: '@smoke @regression @slow'
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# path to a directory with all packages
|
||||||
|
storage: ../tmp/local-registry/storage
|
||||||
|
|
||||||
|
# a list of other known repositories we can talk to
|
||||||
|
uplinks:
|
||||||
|
npmjs:
|
||||||
|
url: https://registry.yarnpkg.com
|
||||||
|
maxage: 60m
|
||||||
|
|
||||||
|
packages:
|
||||||
|
'**':
|
||||||
|
# give all users (including non-authenticated users) full access
|
||||||
|
# because it is a local registry
|
||||||
|
access: $all
|
||||||
|
publish: $all
|
||||||
|
unpublish: $all
|
||||||
|
|
||||||
|
# if package is not available locally, proxy requests to npm registry
|
||||||
|
proxy: npmjs
|
||||||
|
|
||||||
|
# log settings
|
||||||
|
logs:
|
||||||
|
type: stdout
|
||||||
|
format: pretty
|
||||||
|
level: warn
|
||||||
|
|
||||||
|
publish:
|
||||||
|
allow_offline: true # set offline to true to allow publish offline
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||||
const theme = require('../../libs/tailwindcss-config/src/theme');
|
const { theme } = require('../../libs/tailwindcss-config/src/theme');
|
||||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
const {
|
||||||
|
vegaCustomClasses,
|
||||||
|
} = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
|
setRiskAccepted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
clickOnValidatorFromList,
|
clickOnValidatorFromList,
|
||||||
@@ -57,6 +58,7 @@ context(
|
|||||||
// 1002-STKE-002, 1002-STKE-032
|
// 1002-STKE-002, 1002-STKE-032
|
||||||
before('visit staking tab and connect vega wallet', function () {
|
before('visit staking tab and connect vega wallet', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
setRiskAccepted();
|
||||||
ethereumWalletConnect();
|
ethereumWalletConnect();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
navigateTo,
|
navigateTo,
|
||||||
navigation,
|
navigation,
|
||||||
turnTelemetryOff,
|
turnTelemetryOff,
|
||||||
|
setRiskAccepted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
stakingPageAssociateTokens,
|
stakingPageAssociateTokens,
|
||||||
@@ -57,6 +58,7 @@ context(
|
|||||||
function () {
|
function () {
|
||||||
cy.clearLocalStorage();
|
cy.clearLocalStorage();
|
||||||
turnTelemetryOff();
|
turnTelemetryOff();
|
||||||
|
setRiskAccepted();
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.reload();
|
cy.reload();
|
||||||
waitForSpinner();
|
waitForSpinner();
|
||||||
|
|||||||
@@ -79,23 +79,23 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have information on active nodes', function () {
|
it.skip('should have information on active nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', '1')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'active nodes');
|
.and('contain.text', 'active nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have information on consensus nodes', function () {
|
it.skip('should have information on consensus nodes', function () {
|
||||||
cy.getByTestId('node-information')
|
cy.getByTestId('node-information')
|
||||||
.last()
|
.last()
|
||||||
.should('contain.text', '1')
|
.should('contain.text', '2')
|
||||||
.and('contain.text', 'consensus nodes');
|
.and('contain.text', 'consensus nodes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain link to specific validators', function () {
|
it.skip('should contain link to specific validators', function () {
|
||||||
cy.getByTestId('validators')
|
cy.getByTestId('validators')
|
||||||
.should('have.length', '1')
|
.should('have.length', '2')
|
||||||
.each(($validator) => {
|
.each(($validator) => {
|
||||||
cy.wrap($validator).find('a').should('have.attr', 'href');
|
cy.wrap($validator).find('a').should('have.attr', 'href');
|
||||||
});
|
});
|
||||||
@@ -153,7 +153,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
.invoke('text')
|
.invoke('text')
|
||||||
.should('not.eq', currentBlockHeight);
|
.should('not.eq', currentBlockHeight);
|
||||||
});
|
});
|
||||||
cy.getByTestId('subscription-cell').should('have.text', 'Yes');
|
cy.getByTestId('subscription-cell').should('be.be.visible');
|
||||||
});
|
});
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('node-url-custom').click({ force: true });
|
cy.getByTestId('node-url-custom').click({ force: true });
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||||
import {
|
import {
|
||||||
navigation,
|
navigation,
|
||||||
|
setRiskAccepted,
|
||||||
verifyPageHeader,
|
verifyPageHeader,
|
||||||
verifyTabHighlighted,
|
verifyTabHighlighted,
|
||||||
} from '../../support/common.functions';
|
} from '../../support/common.functions';
|
||||||
@@ -187,6 +188,7 @@ context('Validators Page - verify elements on page', function () {
|
|||||||
before('connect wallets and click on validator', function () {
|
before('connect wallets and click on validator', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/validators');
|
cy.visit('/validators');
|
||||||
|
setRiskAccepted();
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
clickOnValidatorFromList(0);
|
clickOnValidatorFromList(0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { truncateByChars } from '@vegaprotocol/utils';
|
import { truncateByChars } from '@vegaprotocol/utils';
|
||||||
import { waitForSpinner } from '../../support/common.functions';
|
import {
|
||||||
|
setRiskAccepted,
|
||||||
|
waitForSpinner,
|
||||||
|
} from '../../support/common.functions';
|
||||||
import {
|
import {
|
||||||
vegaWalletFaucetAssetsWithoutCheck,
|
vegaWalletFaucetAssetsWithoutCheck,
|
||||||
vegaWalletTeardown,
|
vegaWalletTeardown,
|
||||||
@@ -11,7 +14,6 @@ const connectButton = 'connect-vega-wallet';
|
|||||||
const getVegaLink = 'link';
|
const getVegaLink = 'link';
|
||||||
const dialog = '[role="dialog"]:visible';
|
const dialog = '[role="dialog"]:visible';
|
||||||
const dialogHeader = 'dialog-title';
|
const dialogHeader = 'dialog-title';
|
||||||
const walletDialogHeader = 'wallet-dialog-title';
|
|
||||||
const connectorsList = 'connectors-list';
|
const connectorsList = 'connectors-list';
|
||||||
const dialogCloseBtn = 'dialog-close';
|
const dialogCloseBtn = 'dialog-close';
|
||||||
const accountNo = 'vega-account-truncated';
|
const accountNo = 'vega-account-truncated';
|
||||||
@@ -34,6 +36,7 @@ context(
|
|||||||
() => {
|
() => {
|
||||||
before('visit token home page', () => {
|
before('visit token home page', () => {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
|
setRiskAccepted();
|
||||||
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,17 +66,12 @@ context(
|
|||||||
|
|
||||||
it('should have Connect Vega header visible', () => {
|
it('should have Connect Vega header visible', () => {
|
||||||
cy.get(dialog).within(() => {
|
cy.get(dialog).within(() => {
|
||||||
cy.getByTestId(walletDialogHeader)
|
cy.getByTestId(connectorsList)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.and('have.text', 'Get a Vega wallet');
|
.and(
|
||||||
});
|
'have.text',
|
||||||
});
|
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
|
||||||
|
);
|
||||||
it('should have jsonRpc and hosted connection options visible on list', function () {
|
|
||||||
cy.getByTestId(connectorsList).within(() => {
|
|
||||||
cy.getByTestId('connector-jsonRpc')
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'Use the Desktop App/CLI');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -88,7 +86,6 @@ context(
|
|||||||
before('connect vega wallet', function () {
|
before('connect vega wallet', function () {
|
||||||
cy.mockChainId();
|
cy.mockChainId();
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.wait('@ChainId');
|
|
||||||
cy.connectVegaWallet();
|
cy.connectVegaWallet();
|
||||||
vegaWalletTeardown();
|
vegaWalletTeardown();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -102,6 +102,12 @@ export function turnTelemetryOff() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setRiskAccepted() {
|
||||||
|
cy.window().then((win) =>
|
||||||
|
win.localStorage.setItem('vega_wallet_risk_accepted', 'true')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function dissociateFromSecondWalletKey() {
|
export function dissociateFromSecondWalletKey() {
|
||||||
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
|
||||||
cy.getByTestId('vega-in-wallet')
|
cy.getByTestId('vega-in-wallet')
|
||||||
|
|||||||
@@ -26,9 +26,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
const { token, staking, vesting } = useContracts();
|
const { token, staking, vesting } = useContracts();
|
||||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||||
const vegaConnecting = useEagerConnect();
|
const vegaWalletStatus = useEagerConnect();
|
||||||
|
|
||||||
const loaded = balancesLoaded && !vegaConnecting;
|
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const run = async () => {
|
const run = async () => {
|
||||||
@@ -169,3 +169,5 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
}
|
}
|
||||||
return <Suspense fallback={loading}>{children}</Suspense>;
|
return <Suspense fallback={loading}>{children}</Suspense>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AppLoader.displayName = 'AppLoader';
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
useLinks,
|
||||||
|
DApp,
|
||||||
|
CONSOLE_REWARDS_PAGE,
|
||||||
|
} from '@vegaprotocol/environment';
|
||||||
|
import {
|
||||||
|
ExternalLink,
|
||||||
|
Intent,
|
||||||
|
NotificationBanner,
|
||||||
|
VegaIcon,
|
||||||
|
VegaIconNames,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Trans } from 'react-i18next';
|
||||||
|
import { useMatch } from 'react-router-dom';
|
||||||
|
import Routes from '../../routes/routes';
|
||||||
|
import { type ReactNode } from 'react';
|
||||||
|
|
||||||
|
const ConsoleRewardsLink = ({ children }: { children: ReactNode }) => {
|
||||||
|
const consoleLink = useLinks(DApp.Console);
|
||||||
|
return (
|
||||||
|
<ExternalLink
|
||||||
|
href={consoleLink(CONSOLE_REWARDS_PAGE)}
|
||||||
|
className="underline inline-flex gap-1 items-center"
|
||||||
|
title="Rewards in Console"
|
||||||
|
>
|
||||||
|
<span>{children}</span>
|
||||||
|
<VegaIcon size={12} name={VegaIconNames.OPEN_EXTERNAL} />
|
||||||
|
</ExternalLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RewardsMovedNotification = () => {
|
||||||
|
const onRewardsPage = useMatch(Routes.REWARDS);
|
||||||
|
if (!onRewardsPage) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NotificationBanner intent={Intent.Warning}>
|
||||||
|
<Trans
|
||||||
|
i18nKey="rewardsMovedNotification"
|
||||||
|
components={[<ConsoleRewardsLink>Console</ConsoleRewardsLink>]}
|
||||||
|
/>
|
||||||
|
</NotificationBanner>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ProtocolUpgradeProposalNotification,
|
ProtocolUpgradeProposalNotification,
|
||||||
} from '@vegaprotocol/proposals';
|
} from '@vegaprotocol/proposals';
|
||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
|
|
||||||
const NotificationsContainer = () => {
|
const NotificationsContainer = () => {
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div data-testid="banners">
|
<div data-testid="banners">
|
||||||
|
<RewardsMovedNotification />
|
||||||
<ProtocolUpgradeProposalNotification
|
<ProtocolUpgradeProposalNotification
|
||||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -111,3 +111,4 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
|||||||
</ContractsContext.Provider>
|
</ContractsContext.Provider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
ContractsProvider.displayName = 'ContractsProvider';
|
||||||
|
|||||||
+4
-1
@@ -38,6 +38,7 @@ import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
|||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
import { MarketName } from '../proposal/market-name';
|
import { MarketName } from '../proposal/market-name';
|
||||||
import { Indicator } from '../proposal/indicator';
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
import { type ProposalNode } from '../proposal/proposal-utils';
|
||||||
|
|
||||||
const ProposalTypeTags = ({
|
const ProposalTypeTags = ({
|
||||||
proposal,
|
proposal,
|
||||||
@@ -540,10 +541,12 @@ const BatchProposalStateText = ({
|
|||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
restData,
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
voteState,
|
voteState,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
voteState?: VoteState | null;
|
voteState?: VoteState | null;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -595,7 +598,7 @@ export const ProposalHeader = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<ProposalDetails proposal={proposal} />
|
<ProposalDetails proposal={proposal} />
|
||||||
<VoteBreakdown proposal={proposal} />
|
<VoteBreakdown proposal={proposal} restData={restData} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -91,6 +91,28 @@ export type ProposalNode = {
|
|||||||
proposal: ProposalData;
|
proposal: ProposalData;
|
||||||
proposalType: ProposalNodeType;
|
proposalType: ProposalNodeType;
|
||||||
proposals: SubProposalData[];
|
proposals: SubProposalData[];
|
||||||
|
yes?: [
|
||||||
|
{
|
||||||
|
partyId: string;
|
||||||
|
elsPerMarket?: [
|
||||||
|
{
|
||||||
|
marketId: string;
|
||||||
|
els: string;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
no?: [
|
||||||
|
{
|
||||||
|
partyId: string;
|
||||||
|
elsPerMarket?: [
|
||||||
|
{
|
||||||
|
marketId: string;
|
||||||
|
els: string;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
type SingleProposalNode = ProposalNode & {
|
type SingleProposalNode = ProposalNode & {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
|
|
||||||
<ProposalHeader
|
<ProposalHeader
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
|
restData={restData}
|
||||||
isListItem={false}
|
isListItem={false}
|
||||||
voteState={voteState}
|
voteState={voteState}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
|
||||||
import { MarketName } from '../proposal/market-name';
|
import { MarketName } from '../proposal/market-name';
|
||||||
import { Indicator } from '../proposal/indicator';
|
import { Indicator } from '../proposal/indicator';
|
||||||
|
import { type ProposalNode } from '../proposal/proposal-utils';
|
||||||
|
|
||||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||||
<CompactNumber
|
<CompactNumber
|
||||||
@@ -110,24 +111,64 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
|
|||||||
|
|
||||||
export const VoteBreakdown = ({
|
export const VoteBreakdown = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
restData,
|
||||||
}: {
|
}: {
|
||||||
proposal: Proposal | BatchProposal;
|
proposal: Proposal | BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
}) => {
|
}) => {
|
||||||
if (proposal.__typename === 'Proposal') {
|
if (proposal.__typename === 'Proposal') {
|
||||||
return <VoteBreakdownNormal proposal={proposal} />;
|
return <VoteBreakdownNormal proposal={proposal} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (proposal.__typename === 'BatchProposal') {
|
if (proposal.__typename === 'BatchProposal') {
|
||||||
return <VoteBreakdownBatch proposal={proposal} />;
|
return <VoteBreakdownBatch proposal={proposal} restData={restData} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
const VoteBreakdownBatch = ({
|
||||||
|
proposal,
|
||||||
|
restData,
|
||||||
|
}: {
|
||||||
|
proposal: BatchProposal;
|
||||||
|
restData?: ProposalNode | null;
|
||||||
|
}) => {
|
||||||
const [fullBreakdown, setFullBreakdown] = useState(false);
|
const [fullBreakdown, setFullBreakdown] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const yesELS =
|
||||||
|
restData?.yes?.reduce((all, y) => {
|
||||||
|
if (y.elsPerMarket) {
|
||||||
|
y.elsPerMarket.forEach((item) => {
|
||||||
|
const share = Number(item.els);
|
||||||
|
if (all[item.marketId]) {
|
||||||
|
all[item.marketId].push(share);
|
||||||
|
} else {
|
||||||
|
all[item.marketId] = [share];
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}, {} as Record<string, number[]>) || {};
|
||||||
|
|
||||||
|
const noELS =
|
||||||
|
restData?.no?.reduce((all, y) => {
|
||||||
|
if (y.elsPerMarket) {
|
||||||
|
y.elsPerMarket.forEach((item) => {
|
||||||
|
const share = Number(item.els);
|
||||||
|
if (all[item.marketId]) {
|
||||||
|
all[item.marketId].push(share);
|
||||||
|
} else {
|
||||||
|
all[item.marketId] = [share];
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}, {} as Record<string, number[]>) || {};
|
||||||
|
|
||||||
const voteInfo = useBatchVoteInformation({
|
const voteInfo = useBatchVoteInformation({
|
||||||
terms: compact(
|
terms: compact(
|
||||||
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
proposal.subProposals ? proposal.subProposals.map((p) => p?.terms) : []
|
||||||
@@ -194,6 +235,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
|
yesELS={yesELS}
|
||||||
|
noELS={noELS}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -254,6 +297,8 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
|
|||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
votes={proposal.votes}
|
votes={proposal.votes}
|
||||||
terms={p.terms}
|
terms={p.terms}
|
||||||
|
yesELS={yesELS}
|
||||||
|
noELS={noELS}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -271,17 +316,17 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
indicator,
|
indicator,
|
||||||
|
yesELS,
|
||||||
|
noELS,
|
||||||
}: {
|
}: {
|
||||||
proposal: BatchProposal;
|
proposal: BatchProposal;
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
indicator?: number;
|
indicator?: number;
|
||||||
|
yesELS: Record<string, number[]>;
|
||||||
|
noELS: Record<string, number[]>;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const voteInfo = useVoteInformation({
|
|
||||||
votes,
|
|
||||||
terms,
|
|
||||||
});
|
|
||||||
|
|
||||||
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
|
||||||
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
|
||||||
@@ -294,6 +339,15 @@ const VoteBreakdownBatchSubProposal = ({
|
|||||||
marketId = terms.change.market.id;
|
marketId = terms.change.market.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const voteInfo = useVoteInformation({
|
||||||
|
votes,
|
||||||
|
terms,
|
||||||
|
// yes votes ELS for this specific proposal (market)
|
||||||
|
yesELS: marketId ? yesELS[marketId] : undefined,
|
||||||
|
// no votes ELS for this specific proposal (market)
|
||||||
|
noELS: marketId ? noELS[marketId] : undefined,
|
||||||
|
});
|
||||||
|
|
||||||
const marketName = marketId ? (
|
const marketName = marketId ? (
|
||||||
<>
|
<>
|
||||||
: <MarketName marketId={marketId} />
|
: <MarketName marketId={marketId} />
|
||||||
|
|||||||
@@ -8,13 +8,18 @@ import {
|
|||||||
type VoteFieldsFragment,
|
type VoteFieldsFragment,
|
||||||
} from '../__generated__/Proposals';
|
} from '../__generated__/Proposals';
|
||||||
import { type ProposalChangeType } from '../types';
|
import { type ProposalChangeType } from '../types';
|
||||||
|
import sum from 'lodash/sum';
|
||||||
|
|
||||||
export const useVoteInformation = ({
|
export const useVoteInformation = ({
|
||||||
votes,
|
votes,
|
||||||
terms,
|
terms,
|
||||||
|
yesELS,
|
||||||
|
noELS,
|
||||||
}: {
|
}: {
|
||||||
votes: VoteFieldsFragment;
|
votes: VoteFieldsFragment;
|
||||||
terms: ProposalTermsFieldsFragment;
|
terms: ProposalTermsFieldsFragment;
|
||||||
|
yesELS?: number[];
|
||||||
|
noELS?: number[];
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
appState: { totalSupply, decimals },
|
appState: { totalSupply, decimals },
|
||||||
@@ -31,7 +36,9 @@ export const useVoteInformation = ({
|
|||||||
paramsForChange,
|
paramsForChange,
|
||||||
votes,
|
votes,
|
||||||
totalSupply,
|
totalSupply,
|
||||||
decimals
|
decimals,
|
||||||
|
yesELS,
|
||||||
|
noELS
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -72,7 +79,11 @@ const getVoteData = (
|
|||||||
},
|
},
|
||||||
votes: ProposalFieldsFragment['votes'],
|
votes: ProposalFieldsFragment['votes'],
|
||||||
totalSupply: BigNumber,
|
totalSupply: BigNumber,
|
||||||
decimals: number
|
decimals: number,
|
||||||
|
/** A list of ELS yes votes */
|
||||||
|
yesELS?: number[],
|
||||||
|
/** A list if ELS no votes */
|
||||||
|
noELS?: number[]
|
||||||
) => {
|
) => {
|
||||||
const requiredMajorityPercentage = params.requiredMajority
|
const requiredMajorityPercentage = params.requiredMajority
|
||||||
? new BigNumber(params.requiredMajority).times(100)
|
? new BigNumber(params.requiredMajority).times(100)
|
||||||
@@ -86,17 +97,31 @@ const getVoteData = (
|
|||||||
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
addDecimal(votes.no.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
const noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
let noEquityLikeShareWeight = !votes.no.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.no.totalEquityLikeShareWeight).times(100);
|
||||||
|
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
||||||
|
// it has to be deduced from `elsPerMarket` of `no` votes of given proposal
|
||||||
|
// data. (by REST DATA)
|
||||||
|
if (noELS != null) {
|
||||||
|
const noTotalELS = sum(noELS);
|
||||||
|
noEquityLikeShareWeight = new BigNumber(noTotalELS).times(100);
|
||||||
|
}
|
||||||
|
|
||||||
const yesTokens = new BigNumber(
|
const yesTokens = new BigNumber(
|
||||||
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
addDecimal(votes.yes.totalTokens ?? 0, decimals)
|
||||||
);
|
);
|
||||||
|
|
||||||
const yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
let yesEquityLikeShareWeight = !votes.yes.totalEquityLikeShareWeight
|
||||||
? new BigNumber(0)
|
? new BigNumber(0)
|
||||||
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
: new BigNumber(votes.yes.totalEquityLikeShareWeight).times(100);
|
||||||
|
// there's no meaningful `totalEquityLikeShareWeight` in batch proposals,
|
||||||
|
// it has to be deduced from `elsPerMarket` of `yes` votes of given proposal
|
||||||
|
// data. (by REST DATA)
|
||||||
|
if (noELS != null) {
|
||||||
|
const yesTotalELS = sum(yesELS);
|
||||||
|
yesEquityLikeShareWeight = new BigNumber(yesTotalELS).times(100);
|
||||||
|
}
|
||||||
|
|
||||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
|
|||||||
|
|
||||||
expect(
|
expect(
|
||||||
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
|
||||||
).toHaveTextContent('13.16%');
|
).toHaveTextContent('10.07%');
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
|
||||||
|
|||||||
+13
-3
@@ -185,15 +185,19 @@ export const ConsensusValidatorsTable = ({
|
|||||||
const { rawValidatorScore: previousEpochValidatorScore } =
|
const { rawValidatorScore: previousEpochValidatorScore } =
|
||||||
getLastEpochScoreAndPerformance(previousEpochData, id);
|
getLastEpochScoreAndPerformance(previousEpochData, id);
|
||||||
|
|
||||||
const overstakingPenalty = calculateOverallPenalty(
|
const overstakingPenalty = calculateOverstakedPenalty(
|
||||||
id,
|
id,
|
||||||
allNodesInPreviousEpoch
|
allNodesInPreviousEpoch
|
||||||
);
|
);
|
||||||
const totalPenalty = calculateOverstakedPenalty(
|
const totalPenalty = calculateOverallPenalty(
|
||||||
id,
|
id,
|
||||||
allNodesInPreviousEpoch
|
allNodesInPreviousEpoch
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
|
||||||
|
(node) => node.id === id
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
|
||||||
@@ -239,6 +243,12 @@ export const ConsensusValidatorsTable = ({
|
|||||||
: undefined,
|
: undefined,
|
||||||
[ValidatorFields.MULTISIG_ERROR]:
|
[ValidatorFields.MULTISIG_ERROR]:
|
||||||
multisigStatus?.showMultisigStatusError,
|
multisigStatus?.showMultisigStatusError,
|
||||||
|
[ValidatorFields.MULTISIG_PENALTY]: formatNumberPercentage(
|
||||||
|
new BigNumber(1)
|
||||||
|
.minus(lastEpochDataForNode?.rewardScore?.multisigScore ?? 1)
|
||||||
|
.times(100),
|
||||||
|
2
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -378,7 +388,6 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('StakeDescription').toString(),
|
headerTooltip: t('StakeDescription').toString(),
|
||||||
cellRenderer: TotalStakeRenderer,
|
cellRenderer: TotalStakeRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
sort: 'desc',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.PENDING_STAKE,
|
field: ValidatorFields.PENDING_STAKE,
|
||||||
@@ -400,6 +409,7 @@ export const ConsensusValidatorsTable = ({
|
|||||||
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
|
||||||
cellRenderer: VotingPowerRenderer,
|
cellRenderer: VotingPowerRenderer,
|
||||||
width: 120,
|
width: 120,
|
||||||
|
sort: 'desc',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
field: ValidatorFields.TOTAL_PENALTIES,
|
field: ValidatorFields.TOTAL_PENALTIES,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export enum ValidatorFields {
|
|||||||
PENDING_USER_STAKE = 'pendingUserStake',
|
PENDING_USER_STAKE = 'pendingUserStake',
|
||||||
USER_STAKE_SHARE = 'userStakeShare',
|
USER_STAKE_SHARE = 'userStakeShare',
|
||||||
MULTISIG_ERROR = 'multisigError',
|
MULTISIG_ERROR = 'multisigError',
|
||||||
|
MULTISIG_PENALTY = 'multisigPenalty',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addUserDataToValidator = (
|
export const addUserDataToValidator = (
|
||||||
@@ -327,7 +328,7 @@ interface TotalPenaltiesRendererProps {
|
|||||||
overstakedAmount: string;
|
overstakedAmount: string;
|
||||||
overstakingPenalty: string;
|
overstakingPenalty: string;
|
||||||
totalPenalties: string;
|
totalPenalties: string;
|
||||||
multisigError?: boolean;
|
multisigPenalty: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,11 +347,9 @@ export const TotalPenaltiesRenderer = ({
|
|||||||
<div data-testid="overstaked-penalty-tooltip">
|
<div data-testid="overstaked-penalty-tooltip">
|
||||||
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
||||||
</div>
|
</div>
|
||||||
{data.multisigError && (
|
<div data-testid="multisig-error-tooltip">
|
||||||
<div data-testid="multisig-error-tooltip">
|
{t('multisigPenalty')}: {data.multisigPenalty}
|
||||||
{t('multisigPenalty')}: 100%
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import {
|
|||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
|
||||||
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
|
||||||
|
|
||||||
const statuses = {
|
const statuses = {
|
||||||
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
|
||||||
@@ -105,9 +104,10 @@ export const ValidatorTable = ({
|
|||||||
};
|
};
|
||||||
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
|
||||||
|
|
||||||
const multisigStatus = previousEpochData
|
const previousNodeData =
|
||||||
? getMultisigStatusInfo(previousEpochData)
|
previousEpochData?.epoch.validatorsConnection?.edges?.find(
|
||||||
: undefined;
|
(e) => e?.node.id === node.id
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -293,21 +293,15 @@ export const ValidatorTable = ({
|
|||||||
data-testid="multisig-penalty"
|
data-testid="multisig-penalty"
|
||||||
className="flex gap-2 items-baseline"
|
className="flex gap-2 items-baseline"
|
||||||
>
|
>
|
||||||
{multisigStatus?.zeroScoreNodes.find(
|
|
||||||
(n) => n.id === node.id
|
|
||||||
) ? (
|
|
||||||
<Tooltip
|
|
||||||
description={t('multisigPenaltyThisNodeIndicator')}
|
|
||||||
>
|
|
||||||
<span className="inline-block w-2 h-2 rounded-full bg-vega-red-500"></span>
|
|
||||||
</Tooltip>
|
|
||||||
) : null}
|
|
||||||
<Tooltip description={t('multisigPenaltyDescription')}>
|
<Tooltip description={t('multisigPenaltyDescription')}>
|
||||||
<span>
|
<span>
|
||||||
{formatNumberPercentage(
|
{formatNumberPercentage(
|
||||||
BigNumber(
|
new BigNumber(1)
|
||||||
multisigStatus?.showMultisigStatusError ? 100 : 0
|
.minus(
|
||||||
),
|
previousNodeData?.node.rewardScore?.multisigScore ??
|
||||||
|
1
|
||||||
|
)
|
||||||
|
.times(100),
|
||||||
2
|
2
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||||
const theme = require('../../libs/tailwindcss-config/src/theme');
|
const { theme } = require('../../libs/tailwindcss-config/src/theme');
|
||||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
const {
|
||||||
|
vegaCustomClasses,
|
||||||
|
} = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
|
|
||||||
module.exports = {
|
export default {
|
||||||
content: [
|
content: [
|
||||||
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
|
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
|
||||||
'libs/ui-toolkit/src/utils/shared.ts',
|
'libs/ui-toolkit/src/utils/shared.ts',
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"presets": [
|
|
||||||
[
|
|
||||||
"@nx/react/babel",
|
|
||||||
{
|
|
||||||
"runtime": "automatic"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"plugins": []
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# This file is used by:
|
|
||||||
# 1. autoprefixer to adjust CSS to support the below specified browsers
|
|
||||||
# 2. babel preset-env to adjust included polyfills
|
|
||||||
#
|
|
||||||
# For additional information regarding the format and rule options, please see:
|
|
||||||
# https://github.com/browserslist/browserslist#queries
|
|
||||||
#
|
|
||||||
# If you need to support different browsers in production, you may tweak the list below.
|
|
||||||
|
|
||||||
last 1 Chrome version
|
|
||||||
last 1 Firefox version
|
|
||||||
last 2 Edge major versions
|
|
||||||
last 2 Safari major version
|
|
||||||
last 2 iOS major versions
|
|
||||||
Firefox ESR
|
|
||||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# React Environment Variables
|
|
||||||
# https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables#expanding-environment-variables-in-env
|
|
||||||
|
|
||||||
# Netlify Environment Variables
|
|
||||||
# https://www.netlify.com/docs/continuous-deployment/#environment-variables
|
|
||||||
NX_VERSION=\$npm_package_version
|
|
||||||
NX_REPOSITORY_URL=\$REPOSITORY_URL
|
|
||||||
NX_BRANCH=\$BRANCH
|
|
||||||
NX_PULL_REQUEST=\$PULL_REQUEST
|
|
||||||
NX_HEAD=\$HEAD
|
|
||||||
NX_COMMIT_REF=\$COMMIT_REF
|
|
||||||
NX_CONTEXT=\$CONTEXT
|
|
||||||
NX_REVIEW_ID=\$REVIEW_ID
|
|
||||||
NX_INCOMING_HOOK_TITLE=\$INCOMING_HOOK_TITLE
|
|
||||||
NX_INCOMING_HOOK_URL=\$INCOMING_HOOK_URL
|
|
||||||
NX_INCOMING_HOOK_BODY=\$INCOMING_HOOK_BODY
|
|
||||||
NX_URL=\$URL
|
|
||||||
NX_DEPLOY_URL=\$DEPLOY_URL
|
|
||||||
NX_DEPLOY_PRIME_URL=\$DEPLOY_PRIME_URL
|
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
|
||||||
NX_VEGA_ENV = 'TESTNET'
|
|
||||||
NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
NX_VEGA_ENV=LOCAL
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
|
||||||
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
|
|
||||||
NX_VEGA_ENV=DEVNET
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_EXPLORER_URL=#
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
|
||||||
NX_VEGA_URL=https://api.vega.community/graphql
|
|
||||||
NX_VEGA_ENV=MAINNET
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://etherscan.io
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.vega.xyz
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
|
||||||
NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql
|
|
||||||
NX_VEGA_ENV=STAGNET1
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# App configuration variables
|
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
|
||||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
|
||||||
NX_VEGA_ENV=TESTNET
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\"}
|
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*", "__generated__"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx"],
|
|
||||||
"rules": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["*.js", "*.jsx"],
|
|
||||||
"rules": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
/* eslint-disable */
|
|
||||||
export default {
|
|
||||||
displayName: 'liquidity-provision-dashboard',
|
|
||||||
preset: '../../jest.preset.js',
|
|
||||||
transform: {
|
|
||||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
|
|
||||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
|
|
||||||
},
|
|
||||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
|
||||||
coverageDirectory: '../../coverage/apps/liquidity-provision-dashboard',
|
|
||||||
};
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
const { join } = require('path');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {
|
|
||||||
config: join(__dirname, 'tailwind.config.js'),
|
|
||||||
},
|
|
||||||
autoprefixer: {},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "liquidity-provision-dashboard",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "apps/liquidity-provision-dashboard/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"targets": {
|
|
||||||
"build": {
|
|
||||||
"executor": "@nx/webpack:webpack",
|
|
||||||
"outputs": ["{options.outputPath}"],
|
|
||||||
"defaultConfiguration": "production",
|
|
||||||
"options": {
|
|
||||||
"compiler": "babel",
|
|
||||||
"outputPath": "dist/apps/liquidity-provision-dashboard",
|
|
||||||
"index": "apps/liquidity-provision-dashboard/src/index.html",
|
|
||||||
"baseHref": "/",
|
|
||||||
"main": "apps/liquidity-provision-dashboard/src/main.tsx",
|
|
||||||
"polyfills": "apps/liquidity-provision-dashboard/src/polyfills.ts",
|
|
||||||
"tsConfig": "apps/liquidity-provision-dashboard/tsconfig.app.json",
|
|
||||||
"assets": [
|
|
||||||
"apps/liquidity-provision-dashboard/src/favicon.ico",
|
|
||||||
"apps/liquidity-provision-dashboard/src/assets"
|
|
||||||
],
|
|
||||||
"styles": ["apps/liquidity-provision-dashboard/src/styles.scss"],
|
|
||||||
"scripts": [],
|
|
||||||
"webpackConfig": "@nx/react/plugins/webpack"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"development": {
|
|
||||||
"extractLicenses": false,
|
|
||||||
"optimization": false,
|
|
||||||
"sourceMap": true,
|
|
||||||
"vendorChunk": true
|
|
||||||
},
|
|
||||||
"production": {
|
|
||||||
"fileReplacements": [
|
|
||||||
{
|
|
||||||
"replace": "apps/liquidity-provision-dashboard/src/environments/environment.ts",
|
|
||||||
"with": "apps/liquidity-provision-dashboard/src/environments/environment.prod.ts"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optimization": true,
|
|
||||||
"outputHashing": "all",
|
|
||||||
"sourceMap": false,
|
|
||||||
"namedChunks": false,
|
|
||||||
"extractLicenses": true,
|
|
||||||
"vendorChunk": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serve": {
|
|
||||||
"executor": "@nx/webpack:dev-server",
|
|
||||||
"options": {
|
|
||||||
"buildTarget": "liquidity-provision-dashboard:build",
|
|
||||||
"hmr": true,
|
|
||||||
"port": 4201
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"development": {
|
|
||||||
"buildTarget": "liquidity-provision-dashboard:build:development"
|
|
||||||
},
|
|
||||||
"production": {
|
|
||||||
"buildTarget": "liquidity-provision-dashboard:build:production",
|
|
||||||
"hmr": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nx/eslint:lint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": [
|
|
||||||
"apps/liquidity-provision-dashboard/**/*.{ts,tsx,js,jsx}"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"executor": "@nx/jest:jest",
|
|
||||||
"outputs": [
|
|
||||||
"{workspaceRoot}/coverage/apps/liquidity-provision-dashboard"
|
|
||||||
],
|
|
||||||
"options": {
|
|
||||||
"jestConfig": "apps/liquidity-provision-dashboard/jest.config.ts"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"build-spec": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"outputs": [],
|
|
||||||
"options": {
|
|
||||||
"command": "yarn tsc --project ./apps/liquidity-provision-dashboard/tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": []
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
|
||||||
import { NetworkLoader, useInitializeEnv } from '@vegaprotocol/environment';
|
|
||||||
import { useRoutes } from 'react-router-dom';
|
|
||||||
|
|
||||||
import '../styles.scss';
|
|
||||||
import { Navbar } from './components/navbar';
|
|
||||||
|
|
||||||
import { routerConfig } from './routes/router-config';
|
|
||||||
|
|
||||||
const cache: InMemoryCacheConfig = {
|
|
||||||
typePolicies: {
|
|
||||||
Market: {
|
|
||||||
merge: true,
|
|
||||||
},
|
|
||||||
Party: {
|
|
||||||
merge: true,
|
|
||||||
},
|
|
||||||
Query: {},
|
|
||||||
Account: {
|
|
||||||
keyFields: false,
|
|
||||||
fields: {
|
|
||||||
balanceFormatted: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Node: {
|
|
||||||
keyFields: false,
|
|
||||||
},
|
|
||||||
Instrument: {
|
|
||||||
keyFields: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
const AppRouter = () => useRoutes(routerConfig);
|
|
||||||
|
|
||||||
export function App() {
|
|
||||||
useInitializeEnv();
|
|
||||||
return (
|
|
||||||
<NetworkLoader cache={cache}>
|
|
||||||
<div className="max-h-full min-h-full bg-white">
|
|
||||||
<Navbar />
|
|
||||||
<AppRouter />
|
|
||||||
</div>
|
|
||||||
</NetworkLoader>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import { Intro } from './intro';
|
|
||||||
import { MarketList } from './market-list';
|
|
||||||
|
|
||||||
export function Dashboard() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<div className="px-16 pt-20 pb-12 bg-greys-light-100">
|
|
||||||
<div className="max-w-screen-xl mx-auto">
|
|
||||||
<h1 className="font-alpha calt uppercase text-5xl mb-8">
|
|
||||||
{t('Top liquidity opportunities')}
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<Intro />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="px-16 py-6">
|
|
||||||
<div className="max-w-screen-xl mx-auto">
|
|
||||||
<MarketList />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './dashboard';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './intro';
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
// TODO: add mainnet links once docs have been updated
|
|
||||||
const LINKS = {
|
|
||||||
testnet: [
|
|
||||||
{
|
|
||||||
label: 'Learn about liquidity fees',
|
|
||||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#resources',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Provide liquidity',
|
|
||||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#overview',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'View your liquidity provisions',
|
|
||||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#viewing-existing-liquidity-provisions',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Amend or remove liquidity',
|
|
||||||
url: 'https://docs.vega.xyz/testnet/tutorials/providing-liquidity#amending-a-liquidity-commitment',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
mainnet: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: update this when network switcher is added
|
|
||||||
type Network = 'testnet' | 'mainnet';
|
|
||||||
|
|
||||||
export const Intro = ({ network = 'testnet' }: { network?: Network }) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<p className="font-alpha calt text-2xl font-medium mb-2">
|
|
||||||
{t(
|
|
||||||
'Become a liquidity provider and earn a cut of the fees paid during trading.'
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<div>
|
|
||||||
<ul className="flex flex-wrap">
|
|
||||||
{LINKS[network].map(
|
|
||||||
({ label, url }: { label: string; url: string }) => (
|
|
||||||
<li key={url} className="mr-6">
|
|
||||||
<ExternalLink href={url} rel="noreferrer">
|
|
||||||
{t(label)}
|
|
||||||
</ExternalLink>
|
|
||||||
</li>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './market-list';
|
|
||||||
-296
@@ -1,296 +0,0 @@
|
|||||||
import { DApp, useLinks } from '@vegaprotocol/environment';
|
|
||||||
import { type Market } from '@vegaprotocol/liquidity';
|
|
||||||
import {
|
|
||||||
displayChange,
|
|
||||||
formatWithAsset,
|
|
||||||
useMarketsLiquidity,
|
|
||||||
} from '@vegaprotocol/liquidity';
|
|
||||||
import {
|
|
||||||
addDecimalsFormatNumber,
|
|
||||||
formatNumberPercentage,
|
|
||||||
getExpiryDate,
|
|
||||||
toBigNum,
|
|
||||||
} from '@vegaprotocol/utils';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { type VegaValueFormatterParams } from '@vegaprotocol/datagrid';
|
|
||||||
import { PriceChangeCell } from '@vegaprotocol/datagrid';
|
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
|
||||||
import {
|
|
||||||
AsyncRenderer,
|
|
||||||
Icon,
|
|
||||||
HealthBar,
|
|
||||||
TooltipCellComponent,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import {
|
|
||||||
type GetRowIdParams,
|
|
||||||
type RowClickedEvent,
|
|
||||||
type ColDef,
|
|
||||||
} from 'ag-grid-community';
|
|
||||||
import 'ag-grid-community/styles/ag-grid.css';
|
|
||||||
import 'ag-grid-community/styles/ag-theme-alpine.css';
|
|
||||||
import { useCallback, useState, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { Grid } from '../../grid';
|
|
||||||
import { HealthDialog } from '../../health-dialog';
|
|
||||||
import { Status } from '../../status';
|
|
||||||
import { intentForStatus } from '../../../lib/utils';
|
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
|
||||||
import { getAsset } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
export const MarketList = () => {
|
|
||||||
const { data, error, loading } = useMarketsLiquidity();
|
|
||||||
const [isHealthDialogOpen, setIsHealthDialogOpen] = useState(false);
|
|
||||||
const consoleLink = useLinks(DApp.Console);
|
|
||||||
|
|
||||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
|
||||||
const columnDefs = useMemo<ColDef[]>(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
headerName: t('Market (futures)'),
|
|
||||||
field: 'tradableInstrument.instrument.name',
|
|
||||||
cellRenderer: ({ value, data }: { value: string; data: Market }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<span className="leading-3">{value}</span>
|
|
||||||
<span className="leading-3">{getAsset(data).symbol}</span>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
minWidth: 100,
|
|
||||||
flex: 1,
|
|
||||||
headerTooltip: t('The market name and settlement asset'),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Market Code'),
|
|
||||||
headerTooltip: t(
|
|
||||||
'The market code is a unique identifier for this market'
|
|
||||||
),
|
|
||||||
field: 'tradableInstrument.instrument.code',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Type'),
|
|
||||||
headerTooltip: t('Type'),
|
|
||||||
field: 'tradableInstrument.instrument.product.__typename',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Last Price'),
|
|
||||||
headerTooltip: t('Latest price for this market'),
|
|
||||||
field: 'data.markPrice',
|
|
||||||
valueFormatter: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: VegaValueFormatterParams<Market, 'data.markPrice'>) =>
|
|
||||||
value && data ? formatWithAsset(value, getAsset(data)) : '-',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Change (24h)'),
|
|
||||||
headerTooltip: t('Change in price over the last 24h'),
|
|
||||||
cellRenderer: ({
|
|
||||||
data,
|
|
||||||
}: VegaValueFormatterParams<Market, 'data.candles'>) => {
|
|
||||||
if (data && data.candles) {
|
|
||||||
const prices = data.candles.map((candle) => candle.close);
|
|
||||||
return (
|
|
||||||
<PriceChangeCell
|
|
||||||
candles={prices}
|
|
||||||
decimalPlaces={data?.decimalPlaces}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
} else return <div>{t('-')}</div>;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Volume (24h)'),
|
|
||||||
field: 'dayVolume',
|
|
||||||
valueFormatter: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: VegaValueFormatterParams<Market, 'dayVolume'>) =>
|
|
||||||
value && data
|
|
||||||
? `${addDecimalsFormatNumber(
|
|
||||||
value,
|
|
||||||
getAsset(data).decimals || 0
|
|
||||||
)} (${displayChange(data.volumeChange)})`
|
|
||||||
: '-',
|
|
||||||
headerTooltip: t('The trade volume over the last 24h'),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Total staked by LPs'),
|
|
||||||
field: 'liquidityCommitted',
|
|
||||||
valueFormatter: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: VegaValueFormatterParams<Market, 'liquidityCommitted'>) =>
|
|
||||||
data && value
|
|
||||||
? formatWithAsset(value.toString(), getAsset(data))
|
|
||||||
: '-',
|
|
||||||
headerTooltip: t('The amount of funds allocated to provide liquidity'),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Target stake'),
|
|
||||||
field: 'target',
|
|
||||||
valueFormatter: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: VegaValueFormatterParams<Market, 'target'>) =>
|
|
||||||
data && value ? formatWithAsset(value, getAsset(data)) : '-',
|
|
||||||
headerTooltip: t(
|
|
||||||
'The ideal committed liquidity to operate the market. If total commitment currently below this level then LPs can set the fee level with new commitment.'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('% Target stake met'),
|
|
||||||
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
|
|
||||||
if (data) {
|
|
||||||
const roundedPercentage =
|
|
||||||
parseInt(
|
|
||||||
(data.liquidityCommitted / parseFloat(data.target)).toFixed(0)
|
|
||||||
) * 100;
|
|
||||||
const display = Number.isNaN(roundedPercentage)
|
|
||||||
? 'N/A'
|
|
||||||
: formatNumberPercentage(toBigNum(roundedPercentage, 0), 0);
|
|
||||||
return display;
|
|
||||||
} else return '-';
|
|
||||||
},
|
|
||||||
headerTooltip: t('% Target stake met'),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Fee levels'),
|
|
||||||
field: 'fees',
|
|
||||||
valueFormatter: ({ value }: VegaValueFormatterParams<Market, 'fees'>) =>
|
|
||||||
value ? `${value.factors.liquidityFee}%` : '-',
|
|
||||||
headerTooltip: t('Fee level for this market'),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('Status'),
|
|
||||||
field: 'tradingMode',
|
|
||||||
cellRenderer: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
value: Schema.MarketTradingMode;
|
|
||||||
data: Market;
|
|
||||||
}) => {
|
|
||||||
return <Status trigger={data.data?.trigger} tradingMode={value} />;
|
|
||||||
},
|
|
||||||
headerTooltip: t(
|
|
||||||
'The current market status - those below the target stake mark are most in need of liquidity'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerComponent: () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<span>{t('Health')}</span>{' '}
|
|
||||||
<button
|
|
||||||
onClick={() => setIsHealthDialogOpen(true)}
|
|
||||||
aria-label={t('open tooltip')}
|
|
||||||
>
|
|
||||||
<Icon name="info-sign" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
field: 'tradingMode',
|
|
||||||
cellRenderer: ({
|
|
||||||
value,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
value: Schema.MarketTradingMode;
|
|
||||||
data: Market;
|
|
||||||
}) => (
|
|
||||||
<HealthBar
|
|
||||||
target={data.target}
|
|
||||||
decimals={getAsset(data).decimals || 0}
|
|
||||||
levels={data.feeLevels}
|
|
||||||
intent={intentForStatus(value)}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
sortable: false,
|
|
||||||
cellStyle: { overflow: 'unset' },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Age'),
|
|
||||||
field: 'marketTimestamps.open',
|
|
||||||
headerTooltip: t('Age of the market'),
|
|
||||||
valueFormatter: ({
|
|
||||||
value,
|
|
||||||
}: VegaValueFormatterParams<Market, 'marketTimestamps.open'>) => {
|
|
||||||
return value ? formatDistanceToNow(new Date(value)) : '-';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Closing Time'),
|
|
||||||
field: 'tradableInstrument.instrument.metadata.tags',
|
|
||||||
headerTooltip: t('Closing time of the market'),
|
|
||||||
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
|
|
||||||
let expiry;
|
|
||||||
if (data?.tradableInstrument.instrument.metadata.tags) {
|
|
||||||
expiry = getExpiryDate(
|
|
||||||
data?.tradableInstrument.instrument.metadata.tags,
|
|
||||||
data?.marketTimestamps.close,
|
|
||||||
data?.state
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return expiry ? expiry : '-';
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
|
||||||
<div
|
|
||||||
className="w-full grow"
|
|
||||||
style={{ minHeight: 500, overflow: 'hidden' }}
|
|
||||||
>
|
|
||||||
<Grid
|
|
||||||
gridOptions={{
|
|
||||||
onRowClicked: ({ data }: RowClickedEvent) => {
|
|
||||||
window.open(
|
|
||||||
liquidityDetailsConsoleLink(data.id, consoleLink),
|
|
||||||
'_blank',
|
|
||||||
'noopener,noreferrer'
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
rowData={data}
|
|
||||||
defaultColDef={{
|
|
||||||
resizable: true,
|
|
||||||
sortable: true,
|
|
||||||
unSortIcon: true,
|
|
||||||
cellClass: ['flex', 'flex-col', 'justify-center'],
|
|
||||||
tooltipComponent: TooltipCellComponent,
|
|
||||||
}}
|
|
||||||
columnDefs={columnDefs}
|
|
||||||
getRowId={getRowId}
|
|
||||||
isRowClickable
|
|
||||||
tooltipShowDelay={500}
|
|
||||||
/>
|
|
||||||
<HealthDialog
|
|
||||||
isOpen={isHealthDialogOpen}
|
|
||||||
onChange={() => {
|
|
||||||
setIsHealthDialogOpen(!isHealthDialogOpen);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</AsyncRenderer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const liquidityDetailsConsoleLink = (
|
|
||||||
marketId: string,
|
|
||||||
consoleLink: (url: string | undefined) => string
|
|
||||||
) => consoleLink(`/#/liquidity/${marketId}`);
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
import { useParams } from 'react-router-dom';
|
|
||||||
import { makeDerivedDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
import {
|
|
||||||
getFeeLevels,
|
|
||||||
sumLiquidityCommitted,
|
|
||||||
lpAggregatedDataProvider,
|
|
||||||
} from '@vegaprotocol/liquidity';
|
|
||||||
import { getAsset, marketWithDataProvider } from '@vegaprotocol/markets';
|
|
||||||
import type { MarketWithData } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
import { Market } from './market';
|
|
||||||
import { Header } from './header';
|
|
||||||
import { LPProvidersGrid } from './providers';
|
|
||||||
|
|
||||||
const formatMarket = (market: MarketWithData) => {
|
|
||||||
return {
|
|
||||||
name: market?.tradableInstrument.instrument.name,
|
|
||||||
symbol: getAsset(market).symbol,
|
|
||||||
settlementAsset: getAsset(market),
|
|
||||||
targetStake: market?.data?.targetStake,
|
|
||||||
tradingMode: market?.data?.marketTradingMode,
|
|
||||||
trigger: market?.data?.trigger,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
export const lpDataProvider = makeDerivedDataProvider(
|
|
||||||
[marketWithDataProvider, lpAggregatedDataProvider],
|
|
||||||
([market, lpAggregatedData]) => ({
|
|
||||||
market: { ...formatMarket(market) },
|
|
||||||
liquidityProviders: lpAggregatedData || [],
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const useMarketDetails = (marketId: string | undefined) => {
|
|
||||||
const { data, loading, error } = useDataProvider({
|
|
||||||
dataProvider: lpDataProvider,
|
|
||||||
skipUpdates: true,
|
|
||||||
variables: { marketId: marketId || '' },
|
|
||||||
});
|
|
||||||
|
|
||||||
const liquidityProviders = data?.liquidityProviders || [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: {
|
|
||||||
name: data?.market?.name,
|
|
||||||
symbol: data?.market?.symbol,
|
|
||||||
liquidityProviders: liquidityProviders,
|
|
||||||
feeLevels: getFeeLevels(liquidityProviders),
|
|
||||||
comittedLiquidity: sumLiquidityCommitted(liquidityProviders) || 0,
|
|
||||||
settlementAsset: data?.market?.settlementAsset || {},
|
|
||||||
targetStake: data?.market?.targetStake || '0',
|
|
||||||
tradingMode: data?.market.tradingMode,
|
|
||||||
},
|
|
||||||
error,
|
|
||||||
loading: loading,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
type Params = { marketId: string };
|
|
||||||
|
|
||||||
export const Detail = () => {
|
|
||||||
const { marketId } = useParams<Params>();
|
|
||||||
const { data, loading, error } = useMarketDetails(marketId);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
|
||||||
<div className="bg-greys-light-100 px-16 pb-12 pt-14">
|
|
||||||
<div className="mx-auto max-w-screen-xl">
|
|
||||||
<Header name={data.name} symbol={data.symbol} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="px-16">
|
|
||||||
<div className="mx-auto max-w-screen-xl">
|
|
||||||
<div className="py-12">
|
|
||||||
{marketId && (
|
|
||||||
<Market
|
|
||||||
marketId={marketId}
|
|
||||||
feeLevels={data.feeLevels}
|
|
||||||
comittedLiquidity={data.comittedLiquidity}
|
|
||||||
settlementAsset={data.settlementAsset}
|
|
||||||
targetStake={data.targetStake}
|
|
||||||
tradingMode={data.tradingMode}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h2 className="font-alpha calt mb-4 text-2xl">
|
|
||||||
{t('Current Liquidity Provision')}
|
|
||||||
</h2>
|
|
||||||
<LPProvidersGrid
|
|
||||||
liquidityProviders={data.liquidityProviders}
|
|
||||||
settlementAsset={data.settlementAsset}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AsyncRenderer>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
export const Header = ({
|
|
||||||
name,
|
|
||||||
symbol,
|
|
||||||
}: {
|
|
||||||
name?: string;
|
|
||||||
symbol?: string;
|
|
||||||
}) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="mb-6">
|
|
||||||
<Link to="/">
|
|
||||||
<Icon name="chevron-left" className="mr-2" />
|
|
||||||
<span className="underline font-alpha calt text-lg font-medium">
|
|
||||||
{t('Liquidity opportunities')}
|
|
||||||
</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<h1 className="font-alpha calt text-5xl mb-6">{name}</h1>
|
|
||||||
<p className="font-alpha calt text-4xl">{symbol}</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './header';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './detail';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './last-24h-volume';
|
|
||||||
-108
@@ -1,108 +0,0 @@
|
|||||||
import { useState, useMemo, useRef, useCallback } from 'react';
|
|
||||||
import throttle from 'lodash/throttle';
|
|
||||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
|
||||||
import { useYesterday } from '@vegaprotocol/react-helpers';
|
|
||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import {
|
|
||||||
calcDayVolume,
|
|
||||||
getChange,
|
|
||||||
displayChange,
|
|
||||||
} from '@vegaprotocol/liquidity';
|
|
||||||
|
|
||||||
import type { Candle } from '@vegaprotocol/markets';
|
|
||||||
import { marketCandlesProvider } from '@vegaprotocol/markets';
|
|
||||||
|
|
||||||
const THROTTLE_UPDATE_TIME = 500;
|
|
||||||
|
|
||||||
export const Last24hVolume = ({
|
|
||||||
marketId,
|
|
||||||
decimals,
|
|
||||||
}: {
|
|
||||||
marketId: string;
|
|
||||||
decimals: number;
|
|
||||||
}) => {
|
|
||||||
const [candleVolume, setCandleVolume] = useState<string>();
|
|
||||||
const [volumeChange, setVolumeChange] = useState<string>(' - ');
|
|
||||||
|
|
||||||
const yesterday = useYesterday();
|
|
||||||
|
|
||||||
const yTimestamp = useMemo(() => {
|
|
||||||
return new Date(yesterday).toISOString();
|
|
||||||
}, [yesterday]);
|
|
||||||
|
|
||||||
const variables = useMemo(
|
|
||||||
() => ({
|
|
||||||
marketId: marketId,
|
|
||||||
interval: Schema.Interval.INTERVAL_I1H,
|
|
||||||
since: yTimestamp,
|
|
||||||
}),
|
|
||||||
[marketId, yTimestamp]
|
|
||||||
);
|
|
||||||
|
|
||||||
const variables24hAgo = {
|
|
||||||
marketId: marketId,
|
|
||||||
interval: Schema.Interval.INTERVAL_I1D,
|
|
||||||
since: yTimestamp,
|
|
||||||
};
|
|
||||||
|
|
||||||
const throttledSetCandles = useRef(
|
|
||||||
throttle((data: Candle[]) => {
|
|
||||||
setCandleVolume(calcDayVolume(data));
|
|
||||||
}, THROTTLE_UPDATE_TIME)
|
|
||||||
).current;
|
|
||||||
|
|
||||||
const update = useCallback(
|
|
||||||
({ data }: { data: Candle[] | null }) => {
|
|
||||||
if (data) {
|
|
||||||
throttledSetCandles(data);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[throttledSetCandles]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { data, error } = useDataProvider({
|
|
||||||
dataProvider: marketCandlesProvider,
|
|
||||||
variables: variables,
|
|
||||||
update,
|
|
||||||
skip: !marketId,
|
|
||||||
});
|
|
||||||
|
|
||||||
const throttledSetVolumeChange = useRef(
|
|
||||||
throttle((candles: Candle[]) => {
|
|
||||||
const candle24hAgo = candles?.[0];
|
|
||||||
setVolumeChange(getChange(data || [], candle24hAgo?.close));
|
|
||||||
}, THROTTLE_UPDATE_TIME)
|
|
||||||
).current;
|
|
||||||
|
|
||||||
const updateCandle24hAgo = useCallback(
|
|
||||||
({ data }: { data: Candle[] | null }) => {
|
|
||||||
if (data) {
|
|
||||||
throttledSetVolumeChange(data);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[throttledSetVolumeChange]
|
|
||||||
);
|
|
||||||
|
|
||||||
useDataProvider({
|
|
||||||
dataProvider: marketCandlesProvider,
|
|
||||||
update: updateCandle24hAgo,
|
|
||||||
variables: variables24hAgo,
|
|
||||||
skip: !marketId || !data,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<span className="text-3xl">
|
|
||||||
{!error && candleVolume
|
|
||||||
? addDecimalsFormatNumber(candleVolume, decimals)
|
|
||||||
: '0'}{' '}
|
|
||||||
</span>
|
|
||||||
<span className="text-lg text-greys-light-400">
|
|
||||||
({displayChange(volumeChange)})
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './market';
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
import { useState } from 'react';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Icon, HealthBar } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { formatWithAsset } from '@vegaprotocol/liquidity';
|
|
||||||
|
|
||||||
import type * as Schema from '@vegaprotocol/types';
|
|
||||||
import { HealthDialog } from '../../health-dialog';
|
|
||||||
import { Last24hVolume } from '../last-24h-volume';
|
|
||||||
import { Status } from '../../status';
|
|
||||||
import { intentForStatus } from '../../../lib/utils';
|
|
||||||
|
|
||||||
interface Levels {
|
|
||||||
fee: string;
|
|
||||||
commitmentAmount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface settlementAsset {
|
|
||||||
symbol?: string;
|
|
||||||
decimals?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Market = ({
|
|
||||||
marketId,
|
|
||||||
feeLevels,
|
|
||||||
comittedLiquidity,
|
|
||||||
settlementAsset,
|
|
||||||
targetStake,
|
|
||||||
tradingMode,
|
|
||||||
trigger,
|
|
||||||
}: {
|
|
||||||
marketId: string;
|
|
||||||
feeLevels: Levels[];
|
|
||||||
comittedLiquidity: number;
|
|
||||||
targetStake: string;
|
|
||||||
settlementAsset?: settlementAsset;
|
|
||||||
tradingMode?: Schema.MarketTradingMode;
|
|
||||||
trigger?: Schema.AuctionTrigger;
|
|
||||||
}) => {
|
|
||||||
const [isHealthDialogOpen, setIsHealthDialogOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="border border-greys-light-200 rounded-2xl px-2 py-6">
|
|
||||||
<table className="w-full">
|
|
||||||
<thead>
|
|
||||||
<tr
|
|
||||||
className="text-sm text-greys-light-400 text-left font-alpha calt"
|
|
||||||
style={{ fontFeatureSettings: "'liga' off, 'calt' off" }}
|
|
||||||
>
|
|
||||||
<th className="font-medium px-4">{t('Volume (24h)')}</th>
|
|
||||||
<th className="font-medium px-4">{t('Commited Liquidity')}</th>
|
|
||||||
<th className="font-medium px-4">{t('Status')}</th>
|
|
||||||
<th className="font-medium flex items-center px-4">
|
|
||||||
<span>{t('Health')}</span>{' '}
|
|
||||||
<button
|
|
||||||
onClick={() => setIsHealthDialogOpen(true)}
|
|
||||||
aria-label={t('open tooltip')}
|
|
||||||
className="flex ml-1"
|
|
||||||
>
|
|
||||||
<Icon name="info-sign" />
|
|
||||||
</button>
|
|
||||||
</th>
|
|
||||||
<th className="font-medium">{t('Est. APY')}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td className="px-4">
|
|
||||||
<div>
|
|
||||||
{marketId && settlementAsset?.decimals && (
|
|
||||||
<Last24hVolume
|
|
||||||
marketId={marketId}
|
|
||||||
decimals={settlementAsset.decimals}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td className="px-4">
|
|
||||||
<span className="text-3xl">
|
|
||||||
{comittedLiquidity && settlementAsset
|
|
||||||
? formatWithAsset(`${comittedLiquidity}`, settlementAsset)
|
|
||||||
: '0'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td className="px-4">
|
|
||||||
<Status
|
|
||||||
trigger={trigger}
|
|
||||||
tradingMode={tradingMode}
|
|
||||||
size="large"
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
<td className="px-4">
|
|
||||||
{tradingMode && settlementAsset?.decimals && feeLevels && (
|
|
||||||
<HealthBar
|
|
||||||
target={targetStake}
|
|
||||||
decimals={settlementAsset.decimals}
|
|
||||||
levels={feeLevels}
|
|
||||||
intent={intentForStatus(tradingMode)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td className="px-4">
|
|
||||||
<span className="text-3xl"></span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<HealthDialog
|
|
||||||
isOpen={isHealthDialogOpen}
|
|
||||||
onChange={() => {
|
|
||||||
setIsHealthDialogOpen(!isHealthDialogOpen);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './providers';
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
|
||||||
|
|
||||||
import { type GetRowIdParams, type ColDef } from 'ag-grid-community';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import {
|
|
||||||
type LiquidityProviderFeeShareFieldsFragment,
|
|
||||||
type LiquidityProvisionFieldsFragment,
|
|
||||||
} from '@vegaprotocol/liquidity';
|
|
||||||
import { formatWithAsset } from '@vegaprotocol/liquidity';
|
|
||||||
|
|
||||||
import { Grid } from '../../grid';
|
|
||||||
import { TooltipCellComponent } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
const formatToHours = ({ value }: { value?: string | null }) => {
|
|
||||||
if (!value) {
|
|
||||||
return '-';
|
|
||||||
}
|
|
||||||
|
|
||||||
const MS_IN_HOUR = 1000 * 60 * 60;
|
|
||||||
const created = new Date(value).getTime();
|
|
||||||
const now = new Date().getTime();
|
|
||||||
return `${Math.round(Math.abs(now - created) / MS_IN_HOUR)}h`;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const LPProvidersGrid = ({
|
|
||||||
liquidityProviders,
|
|
||||||
settlementAsset,
|
|
||||||
}: {
|
|
||||||
liquidityProviders: LiquidityProvisionFieldsFragment &
|
|
||||||
LiquidityProviderFeeShareFieldsFragment[];
|
|
||||||
settlementAsset: {
|
|
||||||
decimals?: number;
|
|
||||||
symbol?: string;
|
|
||||||
};
|
|
||||||
}) => {
|
|
||||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.party.id, []);
|
|
||||||
const columnDefs = useMemo<ColDef[]>(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
headerName: t('LPs'),
|
|
||||||
field: 'party.id',
|
|
||||||
flex: 1,
|
|
||||||
minWidth: 100,
|
|
||||||
headerTooltip: t('Liquidity providers'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Duration'),
|
|
||||||
valueFormatter: formatToHours,
|
|
||||||
field: 'createdAt',
|
|
||||||
headerTooltip: t('Time in market'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Equity-like share'),
|
|
||||||
field: 'equityLikeShare',
|
|
||||||
valueFormatter: ({ value }: { value?: string | null }) => {
|
|
||||||
return value
|
|
||||||
? `${parseFloat(parseFloat(value).toFixed(2)) * 100}%`
|
|
||||||
: '';
|
|
||||||
},
|
|
||||||
headerTooltip: t(
|
|
||||||
'The share of the markets liquidity held - the earlier you commit liquidity the greater % fees you earn'
|
|
||||||
),
|
|
||||||
minWidth: 140,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('committed bond'),
|
|
||||||
field: 'commitmentAmount',
|
|
||||||
valueFormatter: ({ value }: { value?: string | null }) =>
|
|
||||||
value ? formatWithAsset(value, settlementAsset) : '0',
|
|
||||||
headerTooltip: t('The amount of funds allocated to provide liquidity'),
|
|
||||||
minWidth: 140,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Margin Req.'),
|
|
||||||
field: 'margin',
|
|
||||||
headerTooltip: t(
|
|
||||||
'Margin required for arising positions based on liquidity commitment'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('24h Fees'),
|
|
||||||
field: 'fees',
|
|
||||||
headerTooltip: t(
|
|
||||||
'Total fees earned by the liquidity provider in the last 24 hours'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
headerName: t('Fee level'),
|
|
||||||
valueFormatter: ({ value }: { value?: string | null }) => `${value}%`,
|
|
||||||
field: 'fee',
|
|
||||||
headerTooltip: t(
|
|
||||||
"The market's liquidity fee, or the percentage of a trade's value which is collected from the price taker for every trade"
|
|
||||||
),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
headerName: t('APY'),
|
|
||||||
field: 'apy',
|
|
||||||
headerTooltip: t(
|
|
||||||
'An annualised estimate based on the total liquidity provision fees and maker fees collected by liquidity providers, the maximum margin needed and maximum commitment (bond) over the course of 7 epochs'
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[settlementAsset]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Grid
|
|
||||||
rowData={liquidityProviders}
|
|
||||||
tooltipShowDelay={500}
|
|
||||||
defaultColDef={{
|
|
||||||
resizable: true,
|
|
||||||
sortable: true,
|
|
||||||
unSortIcon: true,
|
|
||||||
cellClass: ['flex', 'flex-col', 'justify-center'],
|
|
||||||
tooltipComponent: TooltipCellComponent,
|
|
||||||
minWidth: 100,
|
|
||||||
}}
|
|
||||||
columnDefs={columnDefs}
|
|
||||||
getRowId={getRowId}
|
|
||||||
rowHeight={92}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
.ag-theme-alpine {
|
|
||||||
--ag-line-height: 24px;
|
|
||||||
--ag-row-hover-color: transparent;
|
|
||||||
--ag-header-background-color: transparent;
|
|
||||||
--ag-odd-row-background-color: transparent;
|
|
||||||
--ag-header-foreground-color: #626262;
|
|
||||||
--ag-secondary-foreground-color: #626262;
|
|
||||||
--ag-font-size: 16px;
|
|
||||||
--ag-background-color: transparent;
|
|
||||||
--ag-range-selection-border-color: transparent;
|
|
||||||
|
|
||||||
font-family: AlphaLyrae, Helvetica Neue, -apple-system, BlinkMacSystemFont,
|
|
||||||
Segoe UI, Roboto, Arial, Noto Sans, sans-serif, Apple Color Emoji,
|
|
||||||
Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;
|
|
||||||
font-feature-settings: 'liga' off, 'calt' off;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-cell {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-header {
|
|
||||||
border-bottom: 1px solid #a7a7a7;
|
|
||||||
font-size: 15px;
|
|
||||||
line-height: 1em;
|
|
||||||
text-transform: uppercase;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-root-wrapper {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-header-row {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-row {
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #bfccd6;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-root-wrapper-body.ag-layout-normal {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine.row-hover .ag-row:hover {
|
|
||||||
background: #f0f0f0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
import { useRef, useCallback, useEffect } from 'react';
|
|
||||||
import { AgGridReact } from 'ag-grid-react';
|
|
||||||
import {
|
|
||||||
type AgGridReactProps,
|
|
||||||
type AgReactUiProps,
|
|
||||||
type AgGridReact as AgGridReactType,
|
|
||||||
} from 'ag-grid-react';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import 'ag-grid-community/styles/ag-grid.css';
|
|
||||||
import 'ag-grid-community/styles/ag-theme-alpine.css';
|
|
||||||
|
|
||||||
import './grid.scss';
|
|
||||||
|
|
||||||
type Props = (AgGridReactProps | AgReactUiProps) & {
|
|
||||||
isRowClickable?: boolean;
|
|
||||||
style?: React.CSSProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Grid = ({ isRowClickable, ...props }: Props) => {
|
|
||||||
const gridRef = useRef<AgGridReactType | null>(null);
|
|
||||||
|
|
||||||
const resizeGrid = useCallback(() => {
|
|
||||||
gridRef.current?.api?.sizeColumnsToFit();
|
|
||||||
}, [gridRef]);
|
|
||||||
|
|
||||||
const handleOnGridReady = useCallback(() => {
|
|
||||||
resizeGrid();
|
|
||||||
}, [resizeGrid]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
window.addEventListener('resize', resizeGrid);
|
|
||||||
return () => window.removeEventListener('resize', resizeGrid);
|
|
||||||
}, [resizeGrid]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<AgGridReact
|
|
||||||
className={classNames('ag-theme-alpine font-alpha calt h-full', {
|
|
||||||
'row-hover': isRowClickable,
|
|
||||||
})}
|
|
||||||
rowHeight={92}
|
|
||||||
ref={gridRef}
|
|
||||||
onGridReady={handleOnGridReady}
|
|
||||||
suppressRowClickSelection
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './grid';
|
|
||||||
-110
@@ -1,110 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
import { Dialog, HealthBar } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
import { intentForStatus } from '../../lib/utils';
|
|
||||||
|
|
||||||
interface HealthDialogProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onChange: (isOpen: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ROWS = [
|
|
||||||
{
|
|
||||||
key: '1',
|
|
||||||
title: 'Continuous',
|
|
||||||
copy: 'Markets that have committed liquidity equal or greater than the target stake are trading continuously.',
|
|
||||||
data: {
|
|
||||||
status: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
|
|
||||||
target: '171320',
|
|
||||||
decimals: 5,
|
|
||||||
levels: [
|
|
||||||
{ fee: '0.6', commitmentAmount: 150000 },
|
|
||||||
{ fee: '1', commitmentAmount: 150000 },
|
|
||||||
{ fee: '2', commitmentAmount: 30000 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '2',
|
|
||||||
title: 'Monitoring auction (liquidity)',
|
|
||||||
copy: 'Markets below the target stake will see trading suspended and go into liquidity auction.',
|
|
||||||
data: {
|
|
||||||
status: Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
|
||||||
target: '171320',
|
|
||||||
decimals: 5,
|
|
||||||
levels: [
|
|
||||||
{ fee: '0.6', commitmentAmount: 110000 },
|
|
||||||
{ fee: '1', commitmentAmount: 50000 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: '3',
|
|
||||||
title: 'Opening auction',
|
|
||||||
copy: 'A newly created market looking for a target liquidity amount to start trading.',
|
|
||||||
data: {
|
|
||||||
status: Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION,
|
|
||||||
target: '171320',
|
|
||||||
decimals: 3,
|
|
||||||
levels: [
|
|
||||||
{ fee: '0.6', commitmentAmount: 110000 },
|
|
||||||
{ fee: '1', commitmentAmount: 50000 },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
|
|
||||||
return (
|
|
||||||
<Dialog size="large" open={isOpen} onChange={onChange}>
|
|
||||||
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
|
|
||||||
{t('Health')}
|
|
||||||
</h1>
|
|
||||||
<p className="text-lg font-medium font-alpha mb-8">
|
|
||||||
{t(
|
|
||||||
'Market health is a representation of market and liquidity status and how close that market is to moving from one fee level to another.'
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<table className="table-fixed">
|
|
||||||
<thead className="border-b border-greys-light-300">
|
|
||||||
<th className="w-1/2 text-left font-medium font-alpha text-base pb-4 uppercase">
|
|
||||||
{t('Market status')}
|
|
||||||
</th>
|
|
||||||
<th className="w-1/2 text-lef font-medium font-alpha text-base pb-4 uppercase">
|
|
||||||
{t('Liquidity status')}
|
|
||||||
</th>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{ROWS.map((r, index) => {
|
|
||||||
const isFirstRow = index === 0;
|
|
||||||
return (
|
|
||||||
<tr key={r.key}>
|
|
||||||
<td
|
|
||||||
className={classNames('pr-4 pb-10', { 'pt-8': isFirstRow })}
|
|
||||||
>
|
|
||||||
<h2 className="font-medium font-alpha uppercase text-base">
|
|
||||||
{t(r.title)}
|
|
||||||
</h2>
|
|
||||||
<p className="font-medium font-alpha text-lg">{t(r.copy)}</p>
|
|
||||||
</td>
|
|
||||||
<td
|
|
||||||
className={classNames('pl-4 pb-10', { 'pt-8': isFirstRow })}
|
|
||||||
>
|
|
||||||
<HealthBar
|
|
||||||
size="large"
|
|
||||||
levels={r.data.levels}
|
|
||||||
target={r.data.target}
|
|
||||||
decimals={r.data.decimals}
|
|
||||||
intent={intentForStatus(r.data.status)}
|
|
||||||
/>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './health-dialog';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './indicator';
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
import type * as Schema from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
import { getColorForStatus } from '../../lib/utils';
|
|
||||||
|
|
||||||
export const Indicator = ({
|
|
||||||
status,
|
|
||||||
opacity,
|
|
||||||
}: {
|
|
||||||
status?: Schema.MarketTradingMode;
|
|
||||||
opacity?: number;
|
|
||||||
}) => {
|
|
||||||
const backgroundColor = status ? getColorForStatus(status) : undefined;
|
|
||||||
return (
|
|
||||||
<div className="inline-block w-2 h-2 mr-1 rounded-full bg-white overflow-hidden shrink-0">
|
|
||||||
<div
|
|
||||||
className="h-full bg-black"
|
|
||||||
style={{
|
|
||||||
opacity,
|
|
||||||
backgroundColor,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
.ag-theme-alpine {
|
|
||||||
--ag-line-height: 24px;
|
|
||||||
--ag-row-hover-color: transparent;
|
|
||||||
--ag-header-background-color: #f5f5f5;
|
|
||||||
--ag-odd-row-background-color: transparent;
|
|
||||||
--ag-header-foreground-color: #000;
|
|
||||||
--ag-secondary-foreground-color: #fff;
|
|
||||||
--ag-font-family: 'Helvetica Neue';
|
|
||||||
--ag-font-size: 12px;
|
|
||||||
|
|
||||||
font-family: 'Helvetica Neue', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
|
||||||
Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-cell {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-header {
|
|
||||||
border: 1px solid #bfccd6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-root-wrapper {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-row {
|
|
||||||
border: none;
|
|
||||||
border-bottom: 1px solid #bfccd6;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ag-theme-alpine .ag-root-wrapper-body.ag-layout-normal {
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './navbar';
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { Link } from 'react-router-dom';
|
|
||||||
import { VegaLogo } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
export const Navbar = () => {
|
|
||||||
return (
|
|
||||||
<div className="px-8 py-4 flex items-stretch border-b border-greys-light-200">
|
|
||||||
<div className="flex gap-4 mr-4 items-center h-full">
|
|
||||||
<Link to="/">
|
|
||||||
<VegaLogo />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2 ml-auto"></div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './status';
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
import { Lozenge, Tooltip } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import classNames from 'classnames';
|
|
||||||
|
|
||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import { Indicator } from '../indicator';
|
|
||||||
import type { AuctionTrigger } from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
export const Status = ({
|
|
||||||
tradingMode,
|
|
||||||
trigger,
|
|
||||||
size = 'small',
|
|
||||||
}: {
|
|
||||||
tradingMode?: Schema.MarketTradingMode;
|
|
||||||
trigger?: Schema.AuctionTrigger;
|
|
||||||
size?: 'small' | 'large';
|
|
||||||
}) => {
|
|
||||||
const getStatus = () => {
|
|
||||||
if (!tradingMode) return '';
|
|
||||||
if (
|
|
||||||
tradingMode === Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION
|
|
||||||
) {
|
|
||||||
if (
|
|
||||||
trigger &&
|
|
||||||
trigger !== Schema.AuctionTrigger.AUCTION_TRIGGER_UNSPECIFIED
|
|
||||||
) {
|
|
||||||
return `${Schema.MarketTradingModeMapping[tradingMode]} - ${Schema.AuctionTriggerMapping[trigger]}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Schema.MarketTradingModeMapping[tradingMode];
|
|
||||||
};
|
|
||||||
|
|
||||||
const status = getStatus();
|
|
||||||
const tooltipDescription =
|
|
||||||
tradingMode && getTooltipDescription(tradingMode, trigger);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Tooltip description={tooltipDescription}>
|
|
||||||
<div
|
|
||||||
className={classNames('inline-flex whitespace-normal', {
|
|
||||||
'text-base': size === 'large',
|
|
||||||
'text-sm': size === 'small',
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Lozenge className="border border-greys-light-300 bg-greys-light-100 flex items-center">
|
|
||||||
<Indicator status={tradingMode} />
|
|
||||||
{status}
|
|
||||||
</Lozenge>
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTooltipDescription = (
|
|
||||||
status: Schema.MarketTradingMode,
|
|
||||||
trigger?: Schema.AuctionTrigger
|
|
||||||
) => {
|
|
||||||
switch (status) {
|
|
||||||
case Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS:
|
|
||||||
return t(
|
|
||||||
'This is the standard trading mode where trades are executed whenever orders are received'
|
|
||||||
);
|
|
||||||
case Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION:
|
|
||||||
return getMonitoringDescriptionTooltip(trigger);
|
|
||||||
case Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION:
|
|
||||||
return t(
|
|
||||||
'This is a new market in an opening auction to determine a fair mid-price before starting continuous trading.'
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMonitoringDescriptionTooltip = (trigger?: AuctionTrigger) => {
|
|
||||||
switch (trigger) {
|
|
||||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET:
|
|
||||||
return t(
|
|
||||||
`This market is in auction until it reaches sufficient liquidity.`
|
|
||||||
);
|
|
||||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_UNABLE_TO_DEPLOY_LP_ORDERS:
|
|
||||||
return t(
|
|
||||||
`This market may have sufficient liquidity but there are not enough priced limit orders in the order book, which are required to deploy liquidity commitment pegged orders.`
|
|
||||||
);
|
|
||||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_PRICE:
|
|
||||||
return t(`This market is in auction due to high price volatility.`);
|
|
||||||
case Schema.AuctionTrigger.AUCTION_TRIGGER_OPENING:
|
|
||||||
return t(
|
|
||||||
`This is a new market in an opening auction to determine a fair mid-price before starting continuous trading`
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
|
||||||
|
|
||||||
const marketTradingModeStyle = {
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: '#00D46E',
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION]: '#CF0064',
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION]: '#0046CD',
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION]: '#CF0064',
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_NO_TRADING]: '#CF0064',
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE]: '#CF0064',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getColorForStatus = (status: Schema.MarketTradingMode) =>
|
|
||||||
marketTradingModeStyle[status];
|
|
||||||
|
|
||||||
const marketTradingModeIntent = {
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS]: Intent.Success,
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION]: Intent.Danger,
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_OPENING_AUCTION]: Intent.Primary,
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_BATCH_AUCTION]: Intent.Danger,
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_NO_TRADING]: Intent.Danger,
|
|
||||||
[Schema.MarketTradingMode.TRADING_MODE_SUSPENDED_VIA_GOVERNANCE]:
|
|
||||||
Intent.Danger,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const intentForStatus = (status: Schema.MarketTradingMode) => {
|
|
||||||
return marketTradingModeIntent[status];
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './router-config';
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import { t } from '@vegaprotocol/i18n';
|
|
||||||
|
|
||||||
import { Dashboard } from '../components/dashboard';
|
|
||||||
import { Detail } from '../components/detail';
|
|
||||||
|
|
||||||
export const ROUTES = {
|
|
||||||
MARKETS: 'markets',
|
|
||||||
};
|
|
||||||
|
|
||||||
export const routerConfig = [
|
|
||||||
{ path: '/', element: <Dashboard />, icon: '' },
|
|
||||||
{
|
|
||||||
path: ROUTES.MARKETS,
|
|
||||||
name: 'Markets',
|
|
||||||
text: t('Markets'),
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: ':marketId',
|
|
||||||
element: <Detail />,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
icon: 'trade',
|
|
||||||
isNavItem: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
export const environment = {
|
|
||||||
production: true,
|
|
||||||
};
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// This file can be replaced during build by using the `fileReplacements` array.
|
|
||||||
// When building for production, this file is replaced with `environment.prod.ts`.
|
|
||||||
|
|
||||||
export const environment = {
|
|
||||||
production: false,
|
|
||||||
};
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,23 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<title>Liquidity Provision Dashboard</title>
|
|
||||||
<base href="/" />
|
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
|
||||||
<link
|
|
||||||
rel="preload"
|
|
||||||
href="https://static.vega.xyz/AlphaLyrae-Medium.woff2"
|
|
||||||
as="font"
|
|
||||||
type="font/woff2"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://static.vega.xyz/fonts.css" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="root" class="h-full max-h-full min-h-full"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
import { StrictMode } from 'react';
|
|
||||||
import { createRoot } from 'react-dom/client';
|
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
|
||||||
|
|
||||||
import App from './app/app';
|
|
||||||
|
|
||||||
const rootElement = document.getElementById('root');
|
|
||||||
const root = rootElement && createRoot(rootElement);
|
|
||||||
root?.render(
|
|
||||||
<StrictMode>
|
|
||||||
<BrowserRouter>
|
|
||||||
<App />
|
|
||||||
</BrowserRouter>
|
|
||||||
</StrictMode>
|
|
||||||
);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
|
||||||
*
|
|
||||||
* See: https://github.com/zloirock/core-js#babel
|
|
||||||
*/
|
|
||||||
import 'core-js/stable';
|
|
||||||
import 'regenerator-runtime/runtime';
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
@apply h-full;
|
|
||||||
|
|
||||||
font-family: 'Helvetica Neue', Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
const { join } = require('path');
|
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
|
||||||
const theme = require('../../libs/tailwindcss-config/src/theme-lite');
|
|
||||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
|
||||||
const vegaCustomClassesLite = require('../../libs/tailwindcss-config/src/vega-custom-classes-lite');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
content: [
|
|
||||||
join(__dirname, 'src/**/*.{js,ts,jsx,tsx}'),
|
|
||||||
'libs/ui-toolkit/src/utils/shared.ts',
|
|
||||||
...createGlobPatternsForDependencies(__dirname),
|
|
||||||
],
|
|
||||||
darkMode: 'class',
|
|
||||||
theme: {
|
|
||||||
...theme,
|
|
||||||
colors: {
|
|
||||||
...theme.colors,
|
|
||||||
greys: {
|
|
||||||
light: {
|
|
||||||
100: '#F0F0F0',
|
|
||||||
200: '#D2D2D2',
|
|
||||||
300: '#A7A7A7',
|
|
||||||
400: '#626262',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: [vegaCustomClasses, vegaCustomClassesLite],
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"types": [
|
|
||||||
"node",
|
|
||||||
"@nx/react/typings/cssmodule.d.ts",
|
|
||||||
"@nx/react/typings/image.d.ts"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
|
||||||
"../../node_modules/@nx/react/typings/image.d.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"jest.config.ts",
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.test.ts",
|
|
||||||
"**/*.spec.tsx",
|
|
||||||
"**/*.test.tsx",
|
|
||||||
"**/*.spec.js",
|
|
||||||
"**/*.test.js",
|
|
||||||
"**/*.spec.jsx",
|
|
||||||
"**/*.test.jsx"
|
|
||||||
],
|
|
||||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.base.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"jsx": "react-jsx",
|
|
||||||
"allowJs": true,
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"strict": true,
|
|
||||||
"noImplicitOverride": true,
|
|
||||||
"noPropertyAccessFromIndexSignature": false,
|
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noFallthroughCasesInSwitch": true
|
|
||||||
},
|
|
||||||
"include": [],
|
|
||||||
"references": [
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.app.json"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": "./tsconfig.spec.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/out-tsc",
|
|
||||||
"module": "commonjs",
|
|
||||||
"types": [
|
|
||||||
"jest",
|
|
||||||
"node",
|
|
||||||
"@testing-library/jest-dom",
|
|
||||||
"@nx/react/typings/cssmodule.d.ts",
|
|
||||||
"@nx/react/typings/image.d.ts"
|
|
||||||
],
|
|
||||||
"jsx": "react",
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"esModuleInterop": true
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"jest.config.ts",
|
|
||||||
"**/*.test.ts",
|
|
||||||
"**/*.spec.ts",
|
|
||||||
"**/*.test.tsx",
|
|
||||||
"**/*.spec.tsx",
|
|
||||||
"**/*.test.js",
|
|
||||||
"**/*.spec.js",
|
|
||||||
"**/*.test.jsx",
|
|
||||||
"**/*.spec.jsx",
|
|
||||||
"**/*.d.ts"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
|
|
||||||
"../../node_modules/@nx/react/typings/image.d.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
const { join } = require('path');
|
const { join } = require('path');
|
||||||
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
|
||||||
const theme = require('../../libs/tailwindcss-config/src/theme');
|
const { theme } = require('../../libs/tailwindcss-config/src/theme');
|
||||||
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
const {
|
||||||
|
vegaCustomClasses,
|
||||||
|
} = require('../../libs/tailwindcss-config/src/vega-custom-classes');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
content: [
|
content: [
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=''
|
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|
||||||
NX_VEGA_ENV=CUSTOM
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
NX_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
|
||||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
|
||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
|
||||||
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
|
||||||
|
|
||||||
|
|
||||||
# Expose some env vars to cypress environment for market setup
|
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
|
||||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
|
||||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
|
||||||
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
|
||||||
CYPRESS_VEGA_ENV=CUSTOM
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
|
||||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
|
||||||
|
|
||||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
|
||||||
NX_SUCCESSOR_MARKETS=true
|
|
||||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
|
||||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
|
||||||
NX_VEGA_CONFIG_URL=''
|
|
||||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
|
||||||
NX_VEGA_ENV=CUSTOM
|
|
||||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
|
||||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
NX_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
|
||||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
|
||||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
|
||||||
|
|
||||||
|
|
||||||
# Expose some env vars to cypress environment for market setup
|
|
||||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
|
||||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
|
||||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
|
||||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
|
||||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
|
||||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
|
||||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
|
||||||
CYPRESS_VEGA_ENV=CUSTOM
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
|
||||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
|
||||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|
||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
|
||||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
|
||||||
"ignorePatterns": ["!**/*"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
|
||||||
"rules": {
|
|
||||||
"cypress/unsafe-to-chain-command": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"files": ["src/plugins/index.js"],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-undef": "off"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
const { defineConfig } = require('cypress');
|
|
||||||
|
|
||||||
module.exports = defineConfig({
|
|
||||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
|
||||||
e2e: {
|
|
||||||
setupNodeEvents(on, config) {
|
|
||||||
require('cypress-mochawesome-reporter/plugin')(on);
|
|
||||||
require('@cypress/grep/src/plugin')(config);
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
baseUrl: 'http://localhost:4200',
|
|
||||||
fileServerFolder: '.',
|
|
||||||
fixturesFolder: false,
|
|
||||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
|
||||||
supportFile: './src/support/index.js',
|
|
||||||
video: false,
|
|
||||||
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
|
|
||||||
videoUploadOnPasses: false,
|
|
||||||
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
|
|
||||||
chromeWebSecurity: false,
|
|
||||||
projectId: 'et4snf',
|
|
||||||
defaultCommandTimeout: 10000,
|
|
||||||
viewportWidth: 1800,
|
|
||||||
viewportHeight: 900,
|
|
||||||
responseTimeout: 50000,
|
|
||||||
requestTimeout: 20000,
|
|
||||||
retries: 1,
|
|
||||||
testIsolation: false,
|
|
||||||
experimentalMemoryManagement: true,
|
|
||||||
},
|
|
||||||
env: {
|
|
||||||
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
|
||||||
ETHEREUM_CHAIN_ID: 11155111,
|
|
||||||
TRADING_MODE_LINK:
|
|
||||||
'https://docs.vega.xyz/testnet/concepts/trading-on-vega/trading-modes#auction-type-liquidity-monitoring',
|
|
||||||
grepTags: '@regression @smoke @slow',
|
|
||||||
grepFilterSpecs: true,
|
|
||||||
grepOmitFiltered: true,
|
|
||||||
txTimeout: { timeout: 70000 },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
declare module '*.scss';
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "trading-e2e",
|
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
|
||||||
"sourceRoot": "apps/trading-e2e/src",
|
|
||||||
"projectType": "application",
|
|
||||||
"targets": {
|
|
||||||
"e2e": {
|
|
||||||
"executor": "@nx/cypress:cypress",
|
|
||||||
"options": {
|
|
||||||
"cypressConfig": "apps/trading-e2e/cypress.config.js",
|
|
||||||
"devServerTarget": "trading:serve"
|
|
||||||
},
|
|
||||||
"configurations": {
|
|
||||||
"production": {
|
|
||||||
"devServerTarget": "trading:serve:production"
|
|
||||||
},
|
|
||||||
"live": {
|
|
||||||
"devServerTarget": ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"lint": {
|
|
||||||
"executor": "@nx/eslint:lint",
|
|
||||||
"outputs": ["{options.outputFile}"],
|
|
||||||
"options": {
|
|
||||||
"lintFilePatterns": ["apps/trading-e2e/**/*.{js,ts}"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"build": {
|
|
||||||
"executor": "nx:run-commands",
|
|
||||||
"outputs": [],
|
|
||||||
"options": {
|
|
||||||
"command": "yarn tsc --project ./apps/trading-e2e/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tags": [],
|
|
||||||
"implicitDependencies": ["trading"]
|
|
||||||
}
|
|
||||||
Vendored
-20
@@ -1,20 +0,0 @@
|
|||||||
/// <reference types="cypress" />
|
|
||||||
|
|
||||||
declare namespace Cypress {
|
|
||||||
// specify additional properties in the TestConfig object
|
|
||||||
// in our case we will add "tags" property
|
|
||||||
interface SuiteConfigOverrides {
|
|
||||||
/**
|
|
||||||
* List of tags for this test
|
|
||||||
* @example a single tag
|
|
||||||
* it('logs in', { tags: '@smoke' }, () => { ... })
|
|
||||||
* @example multiple tags
|
|
||||||
* it('works', { tags: ['@smoke', '@slow'] }, () => { ... })
|
|
||||||
*/
|
|
||||||
tags?: string | string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Cypress {
|
|
||||||
grep?: (grep?: string, tags?: string, burn?: string) => void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
import { selectAsset } from '../support/helpers';
|
|
||||||
|
|
||||||
const amountField = 'input[name="amount"]';
|
|
||||||
const txTimeout = Cypress.env('txTimeout');
|
|
||||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
|
||||||
const btcName = 0;
|
|
||||||
const vegaName = 4;
|
|
||||||
const btcSymbol = 'tBTC';
|
|
||||||
const vegaSymbol = 'VEGA';
|
|
||||||
const toastContent = 'toast-content';
|
|
||||||
const depositsTab = 'Deposits';
|
|
||||||
const toastCloseBtn = 'toast-close';
|
|
||||||
const completeWithdrawalBtn = 'complete-withdrawal';
|
|
||||||
const depositSubmit = 'deposit-submit';
|
|
||||||
const approveSubmit = 'approve-submit';
|
|
||||||
const dialogContent = 'dialog-content';
|
|
||||||
|
|
||||||
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
|
|
||||||
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.createMarket();
|
|
||||||
cy.get('@markets').then((markets) => {
|
|
||||||
cy.wrap(markets[0]).as('market');
|
|
||||||
});
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can deposit', function () {
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
|
|
||||||
// 1001-DEPO-001
|
|
||||||
// 1001-DEPO-002
|
|
||||||
// 1001-DEPO-003
|
|
||||||
// 1001-DEPO-005
|
|
||||||
// 1001-DEPO-006
|
|
||||||
// 1001-DEPO-007
|
|
||||||
// 1001-DEPO-008
|
|
||||||
// 1001-DEPO-009
|
|
||||||
// 1001-DEPO-010
|
|
||||||
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
|
|
||||||
cy.getByTestId('approve-default').should(
|
|
||||||
'contain.text',
|
|
||||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
|
||||||
);
|
|
||||||
cy.getByTestId(approveSubmit).click();
|
|
||||||
cy.getByTestId('approve-pending').should('exist');
|
|
||||||
cy.getByTestId('approve-confirmed').should('exist');
|
|
||||||
cy.get(amountField).focus();
|
|
||||||
cy.get(amountField).clear().type('10');
|
|
||||||
cy.getByTestId(depositSubmit).click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
`Transaction confirmedYour transaction has been confirmed.View on EtherscanDeposit 10.00 ${btcSymbol}`,
|
|
||||||
{ matchCase: false }
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
|
|
||||||
cy.highlight('deposit verification');
|
|
||||||
|
|
||||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
btcSymbol
|
|
||||||
);
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', btcSymbol);
|
|
||||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.should('have.length.above', 2)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '10.00');
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can not withdrawal because of no MultiSign', function () {
|
|
||||||
// 1002-WITH-022
|
|
||||||
// 1002-WITH-023
|
|
||||||
// 0003-WTXN-011
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.get(amountField).focus();
|
|
||||||
cy.get(amountField).clear().type('1');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Funds unlocked'
|
|
||||||
);
|
|
||||||
// cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.highlight('withdrawals verification');
|
|
||||||
cy.getByTestId('toast-complete-withdrawal').last().click();
|
|
||||||
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Error occurredcannot estimate gas'
|
|
||||||
);
|
|
||||||
cy.getByTestId(completeWithdrawalBtn).should(
|
|
||||||
'contain.text',
|
|
||||||
'Complete withdrawal'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.updateCapsuleMultiSig();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.createMarket();
|
|
||||||
cy.get('@markets').then((markets) => {
|
|
||||||
cy.wrap(markets[0]).as('market');
|
|
||||||
});
|
|
||||||
cy.setOnBoardingViewed();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can withdrawal', function () {
|
|
||||||
// 1002-WITH-0014
|
|
||||||
// 1002-WITH-006
|
|
||||||
// 1002-WITH-009
|
|
||||||
// 1002-WITH-011
|
|
||||||
// 1002-WITH-024
|
|
||||||
// 1002-WITH-012
|
|
||||||
// 1002-WITH-013
|
|
||||||
// 1002-WITH-014
|
|
||||||
// 1002-WITH-015
|
|
||||||
// 1002-WITH-016
|
|
||||||
// 1002-WITH-017
|
|
||||||
// 1002-WITH-019
|
|
||||||
// 1002-WITH-020
|
|
||||||
// 1002-WITH-021
|
|
||||||
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.get(amountField).clear().type('1');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Funds unlocked'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.highlight('withdrawals verification');
|
|
||||||
cy.getByTestId('toast-complete-withdrawal').click();
|
|
||||||
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Transaction confirmed'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastContent, txTimeout)
|
|
||||||
.should('contain.text', 'Funds unlocked')
|
|
||||||
.and('contain.text', 'Your funds have been unlocked for withdrawal.')
|
|
||||||
.and(
|
|
||||||
'contain.text',
|
|
||||||
'View in block explorerYou can save your withdrawal details for extra security.'
|
|
||||||
)
|
|
||||||
.and('contain.text', 'Withdraw 1.00 tBTCComplete withdrawal');
|
|
||||||
cy.getByTestId('toast-withdrawal-details').click();
|
|
||||||
cy.getByTestId(dialogContent)
|
|
||||||
.last()
|
|
||||||
.within(() => {
|
|
||||||
cy.getByTestId('dialog-title').should(
|
|
||||||
'contain.text',
|
|
||||||
'Save withdrawal details'
|
|
||||||
);
|
|
||||||
cy.getByTestId('copy-button').should('be.visible');
|
|
||||||
cy.getByTestId('assetSource_value').should(
|
|
||||||
'have.text',
|
|
||||||
'0xb63D135B0a6854EEb765d69ca36210cC70BECAE0'
|
|
||||||
);
|
|
||||||
cy.getByTestId('amount_value').should('have.text', '100000');
|
|
||||||
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
|
|
||||||
cy.getByTestId('signatures_value')
|
|
||||||
.invoke('text')
|
|
||||||
.should('not.be.empty');
|
|
||||||
cy.getByTestId('targetAddress_value').should(
|
|
||||||
'have.text',
|
|
||||||
ethWalletAddress
|
|
||||||
);
|
|
||||||
cy.getByTestId('creation_value').invoke('text').should('not.be.empty');
|
|
||||||
});
|
|
||||||
cy.getByTestId('close-withdrawal-approval-dialog').click();
|
|
||||||
|
|
||||||
cy.get('.ag-center-cols-container')
|
|
||||||
.find('[col-id="status"]')
|
|
||||||
.eq(0, txTimeout)
|
|
||||||
.should('contain.text', 'Completed');
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]', txTimeout)
|
|
||||||
.should('have.length.above', 1)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '1.00');
|
|
||||||
cy.get('[col-id="details.receiverAddress"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/address/`);
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Completed');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click({ force: true });
|
|
||||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '6.999');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('approved amount is less than deposit', function () {
|
|
||||||
// 1001-DEPO-006
|
|
||||||
// 1001-DEPO-007
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(btcName);
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
|
||||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
|
||||||
cy.contains('Use maximum').should('be.visible');
|
|
||||||
cy.get(amountField).clear().type('20000000');
|
|
||||||
cy.getByTestId(depositSubmit).should('be.visible');
|
|
||||||
cy.getByTestId(depositSubmit).click();
|
|
||||||
cy.getByTestId('input-error-text').should(
|
|
||||||
'contain.text',
|
|
||||||
`You can't deposit more than you have in your Ethereum wallet`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('withdraw - delay verification', function () {
|
|
||||||
// 1001-DEPO-024
|
|
||||||
// 1002-WITH-007
|
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
|
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('Unknown');
|
|
||||||
selectAsset(vegaName);
|
|
||||||
cy.getByTestId('approve-submit').click();
|
|
||||||
cy.getByTestId('approve-confirmed').should(
|
|
||||||
'contain.text',
|
|
||||||
'You approved deposits of up to VEGA'
|
|
||||||
);
|
|
||||||
cy.get(amountField).clear().type('10000');
|
|
||||||
cy.getByTestId('deposit-submit').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
`Your transaction has been confirmed.`,
|
|
||||||
{ matchCase: false }
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
|
|
||||||
cy.highlight('deposit verification');
|
|
||||||
|
|
||||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
vegaSymbol
|
|
||||||
);
|
|
||||||
cy.getByTestId(depositsTab).click();
|
|
||||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', vegaSymbol);
|
|
||||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
|
||||||
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.should('have.length.above', 2)
|
|
||||||
.eq(1)
|
|
||||||
.parent()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="asset.symbol"]').should('have.text', vegaSymbol);
|
|
||||||
cy.get('[col-id="amount"]').should('have.text', '10,000.00');
|
|
||||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
|
||||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
|
||||||
cy.get('[col-id="txHash"]')
|
|
||||||
.find('a')
|
|
||||||
.should('have.attr', 'href')
|
|
||||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
|
||||||
});
|
|
||||||
|
|
||||||
cy.getByTestId('Withdrawals').click(txTimeout);
|
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
|
||||||
selectAsset(1);
|
|
||||||
cy.get(amountField).clear().type('10000');
|
|
||||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
|
||||||
cy.getByTestId('submit-withdrawal').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should(
|
|
||||||
'contain.text',
|
|
||||||
'Your funds have been unlocked'
|
|
||||||
);
|
|
||||||
cy.getByTestId(toastCloseBtn).click();
|
|
||||||
cy.getByTestId(completeWithdrawalBtn).first().should('be.visible').click();
|
|
||||||
cy.getByTestId(toastContent, txTimeout).should('contain.text', 'Delayed');
|
|
||||||
cy.getByTestId('tab-withdrawals').within(() => {
|
|
||||||
cy.get('.ag-center-cols-container')
|
|
||||||
.children()
|
|
||||||
.first()
|
|
||||||
.within(() => {
|
|
||||||
cy.get('[col-id="status"]').contains(
|
|
||||||
/Delayed \(ready in (\d{1,2}:\d{2}:\d{2}:\d{2})\)/
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
|
||||||
|
|
||||||
const connectEthWalletBtn = 'connect-eth-wallet-btn';
|
|
||||||
|
|
||||||
describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
cy.mockWeb3Provider();
|
|
||||||
// Using portfolio withdrawals tab is it requires Ethereum wallet connection
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.setVegaWallet();
|
|
||||||
cy.visit('/#/portfolio');
|
|
||||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
|
||||||
cy.getByTestId('Withdrawals').click();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can connect', () => {
|
|
||||||
// 0004-EWAL-001
|
|
||||||
|
|
||||||
cy.getByTestId('Deposits').click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('exist');
|
|
||||||
cy.getByTestId('web3-connector-MetaMask').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('not.exist');
|
|
||||||
cy.getByTestId('tab-deposits').should('not.be.empty');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('able to disconnect eth wallet', () => {
|
|
||||||
// 0004-EWAL-004
|
|
||||||
// 0004-EWAL-005
|
|
||||||
// 0004-EWAL-006
|
|
||||||
|
|
||||||
cy.getByTestId('Deposits').click();
|
|
||||||
cy.getByTestId('deposit-button').click();
|
|
||||||
connectEthereumWallet('MetaMask');
|
|
||||||
cy.getByTestId('ethereum-address').should('have.text', '0xEe7D…d94F');
|
|
||||||
cy.getByTestId('disconnect-ethereum-wallet')
|
|
||||||
.should('have.text', 'Disconnect')
|
|
||||||
.click();
|
|
||||||
cy.getByTestId(connectEthWalletBtn).should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import { OrderType } from '@vegaprotocol/types';
|
|
||||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
const orderSizeField = 'order-size';
|
|
||||||
const orderPriceField = 'order-price';
|
|
||||||
const orderTIFDropDown = 'order-tif';
|
|
||||||
const placeOrderBtn = 'place-order';
|
|
||||||
|
|
||||||
export const createOrder = (order: OrderSubmission): void => {
|
|
||||||
cy.log('Placing order', order);
|
|
||||||
const { type, side, size, price, timeInForce, expiresAt } = order;
|
|
||||||
|
|
||||||
cy.getByTestId(
|
|
||||||
`order-type-${type === OrderType.TYPE_LIMIT ? 'Limit' : 'Market'}`
|
|
||||||
).click();
|
|
||||||
cy.getByTestId(`order-side-${side}`).click();
|
|
||||||
cy.getByTestId(orderSizeField).clear().type(size);
|
|
||||||
if (price) {
|
|
||||||
cy.getByTestId(orderPriceField).clear().type(price);
|
|
||||||
}
|
|
||||||
cy.getByTestId(orderTIFDropDown).select(timeInForce);
|
|
||||||
if (timeInForce === 'TIME_IN_FORCE_GTT') {
|
|
||||||
if (!expiresAt) {
|
|
||||||
throw new Error('Specify expiresAt if using GTT');
|
|
||||||
}
|
|
||||||
cy.getByTestId('date-picker-field').type(expiresAt);
|
|
||||||
}
|
|
||||||
cy.getByTestId(placeOrderBtn).click();
|
|
||||||
};
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import * as Schema from '@vegaprotocol/types';
|
|
||||||
|
|
||||||
export const orderSizeField = 'order-size';
|
|
||||||
export const orderPriceField = 'order-price';
|
|
||||||
export const orderTIFDropDown = 'order-tif';
|
|
||||||
export const placeOrderBtn = 'place-order';
|
|
||||||
export const toggleShort = 'order-side-SIDE_SELL';
|
|
||||||
export const toggleLong = 'order-side-SIDE_BUY';
|
|
||||||
export const toggleLimit = 'order-type-Limit';
|
|
||||||
export const toggleMarket = 'order-type-Market';
|
|
||||||
|
|
||||||
export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => {
|
|
||||||
return {
|
|
||||||
code: Schema.OrderTimeInForceCode[value],
|
|
||||||
value,
|
|
||||||
text: Schema.OrderTimeInForceMapping[value],
|
|
||||||
};
|
|
||||||
});
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export const connectEthereumWallet = (connectorName: string) => {
|
|
||||||
cy.getByTestId('connect-eth-wallet-btn').should('be.enabled').click();
|
|
||||||
cy.getByTestId('web3-connector-list').should('be.visible');
|
|
||||||
cy.getByTestId(`web3-connector-${connectorName}`).click();
|
|
||||||
};
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
export const selectAsset = (assetIndex: number) => {
|
|
||||||
cy.log(`selecting asset: ${assetIndex}`);
|
|
||||||
cy.getByTestId('select-asset').click();
|
|
||||||
cy.get('[data-testid="rich-select-option"]').eq(assetIndex).click();
|
|
||||||
|
|
||||||
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
|
|
||||||
// against the Ethereum change resolve, we should fix this but for now just force
|
|
||||||
// some wait time
|
|
||||||
// eslint-disable-next-line
|
|
||||||
cy.wait(100);
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
import '@vegaprotocol/cypress';
|
|
||||||
import 'cypress-real-events/support';
|
|
||||||
import registerCypressGrep from '@cypress/grep';
|
|
||||||
import { addMockTradingPage } from './trading';
|
|
||||||
import 'cypress-mochawesome-reporter/register';
|
|
||||||
|
|
||||||
registerCypressGrep();
|
|
||||||
addMockTradingPage();
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
import type {
|
|
||||||
OrdersUpdateSubscription,
|
|
||||||
OrdersUpdateSubscriptionVariables,
|
|
||||||
OrderUpdateFieldsFragment,
|
|
||||||
} from '@vegaprotocol/orders';
|
|
||||||
import type { onMessage } from '@vegaprotocol/cypress';
|
|
||||||
import type { PartialDeep } from 'type-fest';
|
|
||||||
import { orderUpdateSubscription } from '@vegaprotocol/mock';
|
|
||||||
|
|
||||||
const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
|
|
||||||
const getOnOrderUpdate = () => {
|
|
||||||
const onOrderUpdate: onMessage<
|
|
||||||
OrdersUpdateSubscription,
|
|
||||||
OrdersUpdateSubscriptionVariables
|
|
||||||
> = (send) => {
|
|
||||||
sendOrderUpdate.push(send);
|
|
||||||
};
|
|
||||||
return onOrderUpdate;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getSubscriptionMocks = () => ({
|
|
||||||
OrdersUpdate: getOnOrderUpdate(),
|
|
||||||
});
|
|
||||||
|
|
||||||
export function updateOrder(
|
|
||||||
override?: PartialDeep<OrderUpdateFieldsFragment>
|
|
||||||
): void {
|
|
||||||
const update: OrdersUpdateSubscription = orderUpdateSubscription({
|
|
||||||
// @ts-ignore partial deep check failing
|
|
||||||
orders: [override],
|
|
||||||
});
|
|
||||||
if (!sendOrderUpdate) {
|
|
||||||
throw new Error('OrderSub not called');
|
|
||||||
}
|
|
||||||
sendOrderUpdate.forEach((send) => send(update));
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import type {
|
|
||||||
OrderAmendment,
|
|
||||||
OrderAmendmentBody,
|
|
||||||
OrderCancellation,
|
|
||||||
OrderCancellationBody,
|
|
||||||
OrderSubmission,
|
|
||||||
OrderSubmissionBody,
|
|
||||||
Transaction,
|
|
||||||
} from '@vegaprotocol/wallet';
|
|
||||||
|
|
||||||
export const testOrderSubmission = (
|
|
||||||
order: OrderSubmission,
|
|
||||||
expected?: Partial<OrderSubmission>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderSubmissionBody = {
|
|
||||||
orderSubmission: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testOrderAmendment = (
|
|
||||||
order: OrderAmendment,
|
|
||||||
expected?: Partial<OrderAmendment>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderAmendmentBody = {
|
|
||||||
orderAmendment: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testOrderCancellation = (
|
|
||||||
order: OrderCancellation,
|
|
||||||
expected?: Partial<OrderCancellation>
|
|
||||||
) => {
|
|
||||||
const expectedOrder = {
|
|
||||||
...order,
|
|
||||||
...expected,
|
|
||||||
};
|
|
||||||
|
|
||||||
const transaction: OrderCancellationBody = {
|
|
||||||
orderCancellation: expectedOrder,
|
|
||||||
};
|
|
||||||
vegaWalletTransaction(transaction);
|
|
||||||
verifyToast();
|
|
||||||
};
|
|
||||||
|
|
||||||
const vegaWalletTransaction = (transaction: Transaction) => {
|
|
||||||
cy.wait('@VegaWalletTransaction')
|
|
||||||
.its('request')
|
|
||||||
.then((req) => {
|
|
||||||
expect(req.body.params).to.deep.equal({
|
|
||||||
publicKey: Cypress.env('VEGA_PUBLIC_KEY'),
|
|
||||||
sendingMode: 'TYPE_SYNC',
|
|
||||||
transaction,
|
|
||||||
});
|
|
||||||
expect(req.headers.authorization).to.equal(
|
|
||||||
`VWT ${Cypress.env('VEGA_WALLET_API_TOKEN')}`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const verifyToast = () => {
|
|
||||||
cy.getByTestId('toast').should('contain.text', 'Awaiting confirmation');
|
|
||||||
cy.getByTestId('toast')
|
|
||||||
.find('a')
|
|
||||||
.invoke('attr', 'href')
|
|
||||||
.should('include', `${Cypress.env('EXPLORER_URL')}/txs/test-tx-hash`);
|
|
||||||
cy.getByTestId('toast-close').click();
|
|
||||||
};
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user