Compare commits

...
Author SHA1 Message Date
asiaznik 002738af70 chore: skip picking if VEGA_URL set 2024-03-08 14:11:42 +01:00
asiaznik b0bbae4c2e chore: skip picking if VEGA_URL set 2024-03-08 13:53:57 +01:00
asiaznik 635445f23b chore: clear timeout 2024-03-08 13:53:57 +01:00
asiaznik 10e30b9f70 chore: move copy 2024-03-08 13:53:57 +01:00
asiaznik 46c6e27aec chore: remove app failure component 2024-03-08 13:53:57 +01:00
asiaznik 4bd73bfc0f chore: add np to mocks 2024-03-08 13:53:57 +01:00
asiaznik 15aad9db62 chore: add np to mocks 2024-03-08 13:53:57 +01:00
asiaznik 46308b381f chore: i18n texts 2024-03-08 13:53:57 +01:00
asiaznik 938ad08a60 fix(trading): pick the best node to connect, slow notificatio, init error 2024-03-08 13:53:57 +01:00
Ben 177e72dd16 chore(trading): Refactor test fixtures (#5913) 2024-03-07 23:08:31 +01:00
daro-maj 8221882346 chore(trading): fix CI for trading e2e tests (#5945) 2024-03-07 20:23:50 +00:00
Matthew Russell b1a8473131 fix(wallet-react): fix typo in refresh text 2024-03-07 18:00:34 +00:00
Matthew Russell f18216f70f chore(trading,governance,explorer): back merge hotfixes (#5946) 2024-03-07 17:41:58 +00:00
Ben de2b79416e chore(trading): staking reward card e2e (#5915) 2024-03-07 14:11:14 +00:00
6504912284 fix(wallet): wallet connection and error fixes (#5927)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-07 14:01:46 +00:00
m.rayandDariusz Majcherczyk 93643f1737 feat(trading): take profit and stop loss (#5902)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2024-03-07 10:53:16 +00:00
Bartłomiej Głownia 464d5af6be feat(trading): add additional intervals (#5916) 2024-03-07 10:40:33 +00:00
Ben b2a62f16bc chore(trading): fix banners test (#5933) 2024-03-07 09:39:25 +00:00
BenandDariusz Majcherczyk 9ec03a04ea chore(trading): skip market info volume test (#5935)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2024-03-06 15:03:03 +00:00
Artandbwallacee 2a0727ec4b chore(trading): scoped to teams games (#5920)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-06 11:44:33 +00:00
Art 0d39c2354c fix(trading): add team id to games query (#5921) 2024-03-06 10:47:47 +00:00
Matthew Russell ad6f0c5798 feat(trading): add and show key aliases (#5819) 2024-03-06 10:47:16 +00:00
m.ray 38d13085fb chore(trading): remove view as pubkey banner (#5926) 2024-03-05 16:15:51 +00:00
daro-maj c0f4278b81 fix(trading): fix ci cypress issue (#5922) 2024-03-05 16:09:14 +00:00
Ben b2777043b4 chore(trading): update vega and market-sim (#5919) 2024-03-05 15:49:50 +00:00
Matthew Russell 4d19b55096 Merge pull request #5912 from vegaprotocol/main
chore(trading,governance): back merge hotfixes
2024-03-05 04:14:14 -05:00
Matthew Russellandbwallacee 7ea7362a7d fix(trading): market into tab 24hr vol and price fix (#5910)
Co-authored-by: bwallacee <ben@vega.xyz>
2024-03-04 19:08:42 +00:00
Art bbfe42ddb1 fix(governance): rewards moved to console notification (#5904) 2024-03-04 18:09:52 +00:00
Matthew Russell b163af3e8a fix(trading): 24hr price and volume in header (#5908) 2024-03-04 18:00:54 +00:00
daro-maj e6b3ff456d fix(trading): fix tests due to new wallet connection method (#5903) 2024-03-04 15:58:33 +01:00
Matthew Russell 82df401611 fix(governance): incorrect penalty information in validators table and page (#5896) 2024-03-01 22:22:17 +00:00
113 changed files with 2834 additions and 1497 deletions
+2 -1
View File
@@ -196,9 +196,9 @@ jobs:
cypress:
needs: [build-sources, check-e2e-needed]
name: '(CI) cypress'
if: ${{ needs.check-e2e-needed.outputs.run-tests == 'true' }}
uses: ./.github/workflows/cypress-run.yml
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:
projects: ${{ needs.build-sources.outputs.projects-e2e }}
tags: '@smoke'
@@ -287,6 +287,7 @@ jobs:
steps:
- run: |
result="${{ needs.cypress.result }}"
echo "Result: $result"
if [[ $result == "success" || $result == "skipped" ]]; then
exit 0
else
@@ -7,6 +7,7 @@ import {
navigateTo,
navigation,
turnTelemetryOff,
setRiskAccepted,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
@@ -57,6 +58,7 @@ context(
// 1002-STKE-002, 1002-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
setRiskAccepted();
ethereumWalletConnect();
cy.connectVegaWallet();
vegaWalletSetSpecifiedApprovalAmount('1000');
@@ -5,6 +5,7 @@ import {
navigateTo,
navigation,
turnTelemetryOff,
setRiskAccepted,
} from '../../support/common.functions';
import {
stakingPageAssociateTokens,
@@ -57,6 +58,7 @@ context(
function () {
cy.clearLocalStorage();
turnTelemetryOff();
setRiskAccepted();
cy.mockChainId();
cy.reload();
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')
.first()
.should('contain.text', '1')
.should('contain.text', '2')
.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')
.last()
.should('contain.text', '1')
.should('contain.text', '2')
.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')
.should('have.length', '1')
.should('have.length', '2')
.each(($validator) => {
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')
.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('node-url-custom').click({ force: true });
@@ -3,6 +3,7 @@
import { aliasGQLQuery } from '@vegaprotocol/cypress';
import {
navigation,
setRiskAccepted,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
@@ -187,6 +188,7 @@ context('Validators Page - verify elements on page', function () {
before('connect wallets and click on validator', function () {
cy.mockChainId();
cy.visit('/validators');
setRiskAccepted();
cy.connectVegaWallet();
clickOnValidatorFromList(0);
});
@@ -1,5 +1,8 @@
import { truncateByChars } from '@vegaprotocol/utils';
import { waitForSpinner } from '../../support/common.functions';
import {
setRiskAccepted,
waitForSpinner,
} from '../../support/common.functions';
import {
vegaWalletFaucetAssetsWithoutCheck,
vegaWalletTeardown,
@@ -11,7 +14,6 @@ const connectButton = 'connect-vega-wallet';
const getVegaLink = 'link';
const dialog = '[role="dialog"]:visible';
const dialogHeader = 'dialog-title';
const walletDialogHeader = 'wallet-dialog-title';
const connectorsList = 'connectors-list';
const dialogCloseBtn = 'dialog-close';
const accountNo = 'vega-account-truncated';
@@ -34,6 +36,7 @@ context(
() => {
before('visit token home page', () => {
cy.visit('/');
setRiskAccepted();
cy.get(walletContainer, { timeout: 60000 }).should('be.visible');
});
@@ -63,17 +66,12 @@ context(
it('should have Connect Vega header visible', () => {
cy.get(dialog).within(() => {
cy.getByTestId(walletDialogHeader)
cy.getByTestId(connectorsList)
.should('be.visible')
.and('have.text', 'Get a Vega wallet');
});
});
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');
.and(
'have.text',
'Get the Vega WalletGet MetaMask>_Command Line WalletView as public key'
);
});
});
@@ -88,7 +86,6 @@ context(
before('connect vega wallet', function () {
cy.mockChainId();
cy.visit('/');
cy.wait('@ChainId');
cy.connectVegaWallet();
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() {
const secondWalletKey = Cypress.env('vegaWalletPublicKey2Short');
cy.getByTestId('vega-in-wallet')
+4 -2
View File
@@ -26,9 +26,9 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
const { token, staking, vesting } = useContracts();
const setAssociatedBalances = useRefreshAssociatedBalances();
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
const vegaConnecting = useEagerConnect();
const vegaWalletStatus = useEagerConnect();
const loaded = balancesLoaded && !vegaConnecting;
const loaded = balancesLoaded && vegaWalletStatus !== 'connecting';
React.useEffect(() => {
const run = async () => {
@@ -169,3 +169,5 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
}
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,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
interface AppLayoutProps {
children: ReactNode;
@@ -45,8 +46,10 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
const NotificationsContainer = () => {
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<div data-testid="banners">
<RewardsMovedNotification />
<ProtocolUpgradeProposalNotification
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
@@ -111,3 +111,4 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
</ContractsContext.Provider>
);
};
ContractsProvider.displayName = 'ContractsProvider';
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
expect(
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
).toHaveTextContent('13.16%');
).toHaveTextContent('10.07%');
expect(
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
@@ -185,15 +185,19 @@ export const ConsensusValidatorsTable = ({
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakingPenalty = calculateOverallPenalty(
const overstakingPenalty = calculateOverstakedPenalty(
id,
allNodesInPreviousEpoch
);
const totalPenalty = calculateOverstakedPenalty(
const totalPenalty = calculateOverallPenalty(
id,
allNodesInPreviousEpoch
);
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
(node) => node.id === id
);
return {
id,
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
@@ -239,6 +243,12 @@ export const ConsensusValidatorsTable = ({
: undefined,
[ValidatorFields.MULTISIG_ERROR]:
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(),
cellRenderer: TotalStakeRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.PENDING_STAKE,
@@ -400,6 +409,7 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.TOTAL_PENALTIES,
@@ -40,6 +40,7 @@ export enum ValidatorFields {
PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError',
MULTISIG_PENALTY = 'multisigPenalty',
}
export const addUserDataToValidator = (
@@ -327,7 +328,7 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string;
overstakingPenalty: string;
totalPenalties: string;
multisigError?: boolean;
multisigPenalty: string;
};
}
@@ -346,11 +347,9 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty}
</div>
{data.multisigError && (
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: 100%
</div>
)}
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: {data.multisigPenalty}
</div>
</>
}
>
@@ -37,7 +37,6 @@ import {
import type { ReactNode } from 'react';
import type { StakingNodeFieldsFragment } from '../__generated__/Staking';
import type { PreviousEpochQuery } from '../__generated__/PreviousEpoch';
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
const statuses = {
[Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_ERSATZ]: 'status-ersatz',
@@ -105,9 +104,10 @@ export const ValidatorTable = ({
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
const previousNodeData =
previousEpochData?.epoch.validatorsConnection?.edges?.find(
(e) => e?.node.id === node.id
);
return (
<>
@@ -293,21 +293,15 @@ export const ValidatorTable = ({
data-testid="multisig-penalty"
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')}>
<span>
{formatNumberPercentage(
BigNumber(
multisigStatus?.showMultisigStatusError ? 100 : 0
),
new BigNumber(1)
.minus(
previousNodeData?.node.rewardScore?.multisigScore ??
1
)
.times(100),
2
)}
</span>
@@ -1,63 +1,126 @@
import type { InMemoryCacheConfig } from '@apollo/client';
import {
AppFailure,
AppLoader,
NetworkLoader,
NodeFailure,
NodeGuard,
useEnvironment,
useNodeSwitcherStore,
} from '@vegaprotocol/environment';
import { type ReactNode } from 'react';
import { useEffect, type ReactNode, useState } from 'react';
import { Web3Provider } from './web3-provider';
import { useT } from '../../lib/use-t';
import { DataLoader } from './data-loader';
import { WalletProvider } from '@vegaprotocol/wallet-react';
import { useVegaWalletConfig } from '../../lib/hooks/use-vega-wallet-config';
import { Trans } from 'react-i18next';
import { Button, Loader, Splash, VLogo } from '@vegaprotocol/ui-toolkit';
const Failure = ({ reason }: { reason?: ReactNode }) => {
const t = useT();
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
return (
<Splash>
<div className="border border-vega-red m-10 mx-auto w-4/5 max-w-3xl rounded-lg overflow-hidden animate-shake">
<div className="bg-vega-red text-white px-2 py-2 flex gap-1 items-center font-alpha calt uppercase">
<VLogo className="h-4" />
<span className="text-lg">{t('Failed to initialize the app')}</span>
</div>
<div className="p-4 text-left text-sm">
<p className="mb-4">{reason}</p>
<div className="text-center">
<Button className="border-2" onClick={() => setNodeSwitcher(true)}>
{t('Change node')}
</Button>
</div>
</div>
</div>
</Splash>
);
};
const Loading = () => {
const [showSlowNotification, setShowSlowNotification] = useState(false);
const t = useT();
const setNodeSwitcher = useNodeSwitcherStore((store) => store.setDialogOpen);
useEffect(() => {
const to = setTimeout(() => {
if (!showSlowNotification) setShowSlowNotification(true);
}, 5000);
return () => {
clearTimeout(to);
};
}, [showSlowNotification]);
return (
<Splash>
<div className="border border-transparent m-10 mx-auto w-4/5 max-w-3xl rounded-lg overflow-hidden">
<div className="mt-11 p-4 text-left text-sm">
<Loader />
{showSlowNotification && (
<>
<p className="mt-4 text-center">
{t(
"It looks like you're connection is slow, try switching to another node."
)}
</p>
<div className="mt-4 text-center">
<Button
className="border-2"
onClick={() => setNodeSwitcher(true)}
>
{t('Change node')}
</Button>
</div>
</>
)}
</div>
</div>
</Splash>
);
};
export const Bootstrapper = ({ children }: { children: ReactNode }) => {
const t = useT();
const { error, VEGA_URL } = useEnvironment();
const { error, VEGA_URL } = useEnvironment((state) => ({
error: state.error,
VEGA_URL: state.VEGA_URL,
}));
const config = useVegaWalletConfig();
if (!config) {
return <AppLoader />;
}
const ERR_DATA_LOADER = (
<Trans
i18nKey="It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node."
components={[
<span key="vega" className="text-muted">
{VEGA_URL}
</span>,
]}
values={{
VEGA_URL,
}}
/>
);
return (
<NetworkLoader
cache={cacheConfig}
skeleton={<AppLoader />}
failure={
<AppFailure title={t('Could not initialize app')} error={error} />
}
skeleton={<Loading />}
failure={<Failure reason={error} />}
>
<NodeGuard
skeleton={<AppLoader />}
failure={
<NodeFailure
title={t('Node: {{VEGA_URL}} is unsuitable', { VEGA_URL })}
/>
}
<DataLoader
skeleton={<Loading />}
failure={<Failure reason={ERR_DATA_LOADER} />}
>
<DataLoader
skeleton={<AppLoader />}
failure={
<AppFailure
title={t('Could not load market data or asset data')}
error={error}
/>
}
<Web3Provider
skeleton={<Loading />}
failure={<Failure reason={t('Could not configure web3 provider')} />}
>
<Web3Provider
skeleton={<AppLoader />}
failure={
<AppFailure title={t('Could not configure web3 provider')} />
}
>
<WalletProvider config={config}>{children}</WalletProvider>
</Web3Provider>
</DataLoader>
</NodeGuard>
<WalletProvider config={config}>{children}</WalletProvider>
</Web3Provider>
</DataLoader>
</NetworkLoader>
);
};
@@ -30,8 +30,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
setStudies,
setStudySizes,
setOverlays,
state,
setState,
} = useChartSettings();
const pennantChart = (
@@ -68,10 +66,6 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
onIntervalChange={(newInterval) => {
setInterval(fromTradingViewResolution(newInterval));
}}
onAutoSaveNeeded={(data) => {
setState(data);
}}
state={state}
/>
);
}
@@ -6,7 +6,12 @@ export const SUPPORTED_INTERVALS = [
Interval.INTERVAL_I1M,
Interval.INTERVAL_I5M,
Interval.INTERVAL_I15M,
Interval.INTERVAL_I30M,
Interval.INTERVAL_I1H,
Interval.INTERVAL_I4H,
Interval.INTERVAL_I6H,
Interval.INTERVAL_I8H,
Interval.INTERVAL_I12H,
Interval.INTERVAL_I1D,
Interval.INTERVAL_I7D,
] as const;
@@ -9,7 +9,6 @@ type StudySizes = { [S in Study]?: number };
export type Chartlib = 'pennant' | 'tradingview';
interface StoredSettings {
state: object | undefined; // Don't see a better type provided from TradingView type definitions
chartlib: Chartlib;
// For interval we use the enum from @vegaprotocol/types, this is to make mapping between different
// chart types easier and more consistent
@@ -30,7 +29,6 @@ const STUDY_ORDER: Study[] = [
];
export const DEFAULT_CHART_SETTINGS = {
state: undefined,
chartlib: 'pennant' as const,
interval: Interval.INTERVAL_I15M,
type: ChartType.CANDLE,
@@ -47,7 +45,6 @@ export const useChartSettingsStore = create<
setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void;
setChartlib: (lib: Chartlib) => void;
setState: (state: object) => void;
}
>()(
persist(
@@ -95,9 +92,6 @@ export const useChartSettingsStore = create<
state.chartlib = lib;
});
},
setState: (state) => {
set({ state });
},
})),
{
name: 'vega_candles_chart_store',
@@ -151,7 +145,5 @@ export const useChartSettings = () => {
setOverlays: settings.setOverlays,
setStudySizes: settings.setStudySizes,
setChartlib: settings.setChartlib,
state: settings.state,
setState: settings.setState,
};
};
@@ -0,0 +1 @@
export { ProfileDialog } from './profile-dialog';
@@ -0,0 +1,150 @@
import {
Dialog,
FormGroup,
Input,
InputError,
Intent,
TradingButton,
} from '@vegaprotocol/ui-toolkit';
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
import { useForm } from 'react-hook-form';
import { useT } from '../../lib/use-t';
import { useRequired } from '@vegaprotocol/utils';
import {
useSimpleTransaction,
type Status,
useVegaWallet,
} from '@vegaprotocol/wallet-react';
import {
usePartyProfilesQuery,
type PartyProfilesQuery,
} from '../vega-wallet-connect-button/__generated__/PartyProfiles';
export const ProfileDialog = () => {
const t = useT();
const { pubKeys } = useVegaWallet();
const { data, refetch } = usePartyProfilesQuery({
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
skip: pubKeys.length <= 0,
});
const open = useProfileDialogStore((store) => store.open);
const pubKey = useProfileDialogStore((store) => store.pubKey);
const setOpen = useProfileDialogStore((store) => store.setOpen);
const { send, status, error, reset } = useSimpleTransaction({
onSuccess: () => {
refetch();
},
});
const profileEdge = data?.partiesProfilesConnection?.edges.find(
(e) => e.node.partyId === pubKey
);
const sendTx = (field: FormFields) => {
send({
updatePartyProfile: {
alias: field.alias,
metadata: [],
},
});
};
return (
<Dialog
open={open}
onChange={() => {
setOpen(undefined);
reset();
}}
title={t('Edit profile')}
>
<ProfileForm
profile={profileEdge?.node}
status={status}
error={error}
onSubmit={sendTx}
/>
</Dialog>
);
};
interface FormFields {
alias: string;
}
type Profile = NonNullable<
PartyProfilesQuery['partiesProfilesConnection']
>['edges'][number]['node'];
const ProfileForm = ({
profile,
onSubmit,
status,
error,
}: {
profile: Profile | undefined;
onSubmit: (fields: FormFields) => void;
status: Status;
error: string | undefined;
}) => {
const t = useT();
const required = useRequired();
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormFields>({
defaultValues: {
alias: profile?.alias,
},
});
const renderButtonText = () => {
if (status === 'requested') {
return t('Confirm in wallet...');
}
if (status === 'pending') {
return t('Confirming transaction...');
}
return t('Submit');
};
const errorMessage = errors.alias?.message || error;
return (
<form onSubmit={handleSubmit(onSubmit)} className="mt-3">
<FormGroup label="Alias" labelFor="alias">
<Input
{...register('alias', {
validate: {
required,
},
})}
/>
{errorMessage && (
<InputError>
<p className="break-words max-w-full first-letter:uppercase">
{errorMessage}
</p>
</InputError>
)}
{status === 'confirmed' && (
<p className="mt-2 mb-4 text-sm text-success">
{t('Profile updated')}
</p>
)}
</FormGroup>
<TradingButton
type="submit"
intent={Intent.Info}
disabled={status === 'requested' || status === 'pending'}
>
{renderButtonText()}
</TradingButton>
</form>
);
};
@@ -0,0 +1,14 @@
query PartyProfiles($partyIds: [ID!]) {
partiesProfilesConnection(ids: $partyIds) {
edges {
node {
partyId
alias
metadata {
key
value
}
}
}
}
}
@@ -0,0 +1,57 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PartyProfilesQueryVariables = Types.Exact<{
partyIds?: Types.InputMaybe<Array<Types.Scalars['ID']> | Types.Scalars['ID']>;
}>;
export type PartyProfilesQuery = { __typename?: 'Query', partiesProfilesConnection?: { __typename?: 'PartiesProfilesConnection', edges: Array<{ __typename?: 'PartyProfileEdge', node: { __typename?: 'PartyProfile', partyId: string, alias: string, metadata: Array<{ __typename?: 'Metadata', key: string, value: string }> } }> } | null };
export const PartyProfilesDocument = gql`
query PartyProfiles($partyIds: [ID!]) {
partiesProfilesConnection(ids: $partyIds) {
edges {
node {
partyId
alias
metadata {
key
value
}
}
}
}
}
`;
/**
* __usePartyProfilesQuery__
*
* To run a query within a React component, call `usePartyProfilesQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyProfilesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePartyProfilesQuery({
* variables: {
* partyIds: // value for 'partyIds'
* },
* });
*/
export function usePartyProfilesQuery(baseOptions?: Apollo.QueryHookOptions<PartyProfilesQuery, PartyProfilesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<PartyProfilesQuery, PartyProfilesQueryVariables>(PartyProfilesDocument, options);
}
export function usePartyProfilesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<PartyProfilesQuery, PartyProfilesQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<PartyProfilesQuery, PartyProfilesQueryVariables>(PartyProfilesDocument, options);
}
export type PartyProfilesQueryHookResult = ReturnType<typeof usePartyProfilesQuery>;
export type PartyProfilesLazyQueryHookResult = ReturnType<typeof usePartyProfilesLazyQuery>;
export type PartyProfilesQueryResult = Apollo.QueryResult<PartyProfilesQuery, PartyProfilesQueryVariables>;
@@ -1,21 +1,57 @@
import { act, fireEvent, render, screen } from '@testing-library/react';
import { act, fireEvent, render, screen, within } from '@testing-library/react';
import { VegaWalletConnectButton } from './vega-wallet-connect-button';
import { truncateByChars } from '@vegaprotocol/utils';
import userEvent from '@testing-library/user-event';
import {
mockConfig,
MockedWalletProvider,
} from '@vegaprotocol/wallet-react/testing';
import { MockedProvider, type MockedResponse } from '@apollo/react-testing';
import {
PartyProfilesDocument,
type PartyProfilesQuery,
} from './__generated__/PartyProfiles';
jest.mock('../../lib/hooks/use-get-current-route-id', () => ({
useGetCurrentRouteId: jest.fn().mockReturnValue('current-route-id'),
}));
const key = { publicKey: '123456__123456', name: 'test' };
const key2 = { publicKey: 'abcdef__abcdef', name: 'test2' };
const keys = [key, key2];
const keyProfile = {
__typename: 'PartyProfile' as const,
partyId: key.publicKey,
alias: `${key.name} alias`,
metadata: [],
};
const renderComponent = (mockOnClick = jest.fn()) => {
const partyProfilesMock: MockedResponse<PartyProfilesQuery> = {
request: {
query: PartyProfilesDocument,
variables: { partyIds: keys.map((k) => k.publicKey) },
},
result: {
data: {
partiesProfilesConnection: {
__typename: 'PartiesProfilesConnection',
edges: [
{
__typename: 'PartyProfileEdge',
node: keyProfile,
},
],
},
},
},
};
return (
<MockedWalletProvider>
<VegaWalletConnectButton onClick={mockOnClick} />
</MockedWalletProvider>
<MockedProvider mocks={[partyProfilesMock]}>
<MockedWalletProvider>
<VegaWalletConnectButton onClick={mockOnClick} />
</MockedWalletProvider>
</MockedProvider>
);
};
@@ -43,10 +79,6 @@ describe('VegaWalletConnectButton', () => {
});
it('should open dropdown and refresh keys when connected', async () => {
const key = { publicKey: '123456__123456', name: 'test' };
const key2 = { publicKey: 'abcdef__abcdef', name: 'test2' };
const keys = [key, key2];
mockConfig.store.setState({
status: 'connected',
keys,
@@ -61,14 +93,22 @@ describe('VegaWalletConnectButton', () => {
expect(screen.queryByTestId('connect-vega-wallet')).not.toBeInTheDocument();
const button = screen.getByTestId('manage-vega-wallet');
expect(button).toHaveTextContent(truncateByChars(key.publicKey));
expect(button).toHaveTextContent(key.name);
fireEvent.click(button);
expect(await screen.findByRole('menu')).toBeInTheDocument();
expect(await screen.findAllByRole('menuitemradio')).toHaveLength(
keys.length
const menuItems = await screen.findAllByRole('menuitemradio');
expect(menuItems).toHaveLength(keys.length);
expect(within(menuItems[0]).getByTestId('alias')).toHaveTextContent(
keyProfile.alias
);
expect(within(menuItems[1]).getByTestId('alias')).toHaveTextContent(
'No alias'
);
expect(refreshKeys).toHaveBeenCalled();
fireEvent.click(screen.getByTestId(`key-${key2.publicKey}`));
@@ -14,6 +14,7 @@ import {
TradingDropdownItem,
TradingDropdownRadioItem,
TradingDropdownItemIndicator,
Tooltip,
} from '@vegaprotocol/ui-toolkit';
import { isBrowserWalletInstalled, type Key } from '@vegaprotocol/wallet';
import { useDialogStore, useVegaWallet } from '@vegaprotocol/wallet-react';
@@ -22,6 +23,8 @@ import classNames from 'classnames';
import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { useT } from '../../lib/use-t';
import { usePartyProfilesQuery } from './__generated__/PartyProfiles';
import { useProfileDialogStore } from '../../stores/profile-dialog-store';
export const VegaWalletConnectButton = ({
intent = Intent.None,
@@ -68,10 +71,10 @@ export const VegaWalletConnectButton = ({
{activeKey ? (
<>
{activeKey && (
<span className="uppercase">{activeKey.name}</span>
<span className="uppercase">
{activeKey.name ? activeKey.name : t('Unnamed key')}
</span>
)}
{' | '}
{truncateByChars(activeKey.publicKey)}
</>
) : (
<>{'Select key'}</>
@@ -88,20 +91,11 @@ export const VegaWalletConnectButton = ({
onEscapeKeyDown={() => setDropdownOpen(false)}
>
<div className="min-w-[340px]" data-testid="keypair-list">
<TradingDropdownRadioGroup
value={pubKey || undefined}
onValueChange={(value) => {
selectPubKey(value);
}}
>
{pubKeys.map((pk) => (
<KeypairItem
key={pk.publicKey}
pk={pk}
active={pk.publicKey === pubKey}
/>
))}
</TradingDropdownRadioGroup>
<KeypairRadioGroup
pubKey={pubKey}
pubKeys={pubKeys}
onSelect={selectPubKey}
/>
<TradingDropdownSeparator />
{!isReadOnly && (
<TradingDropdownItem
@@ -141,28 +135,52 @@ export const VegaWalletConnectButton = ({
);
};
const KeypairItem = ({ pk, active }: { pk: Key; active: boolean }) => {
const KeypairRadioGroup = ({
pubKey,
pubKeys,
onSelect,
}: {
pubKey: string | undefined;
pubKeys: Key[];
onSelect: (pubKey: string) => void;
}) => {
const { data } = usePartyProfilesQuery({
variables: { partyIds: pubKeys.map((pk) => pk.publicKey) },
skip: pubKeys.length <= 0,
});
return (
<TradingDropdownRadioGroup value={pubKey} onValueChange={onSelect}>
{pubKeys.map((pk) => {
const profile = data?.partiesProfilesConnection?.edges.find(
(e) => e.node.partyId === pk.publicKey
);
return (
<KeypairItem key={pk.publicKey} pk={pk} alias={profile?.node.alias} />
);
})}
</TradingDropdownRadioGroup>
);
};
const KeypairItem = ({ pk, alias }: { pk: Key; alias: string | undefined }) => {
const t = useT();
const [copied, setCopied] = useCopyTimeout();
const setOpen = useProfileDialogStore((store) => store.setOpen);
return (
<TradingDropdownRadioItem value={pk.publicKey}>
<div
className={classNames('flex-1 mr-2', {
'text-default': active,
'text-muted': !active,
})}
data-testid={`key-${pk.publicKey}`}
>
<span className={classNames('mr-2 uppercase')}>
{pk.name}
<div>
<div className="flex items-center gap-2">
<span>{pk.name ? pk.name : t('Unnamed key')}</span>
{' | '}
{truncateByChars(pk.publicKey)}
</span>
<span className="inline-flex items-center gap-1">
<span className="font-mono">
{truncateByChars(pk.publicKey, 3, 3)}
</span>
<CopyToClipboard text={pk.publicKey} onCopy={() => setCopied(true)}>
<button
data-testid="copy-vega-public-key"
className="relative -top-px"
onClick={(e) => e.stopPropagation()}
>
<span className="sr-only">{t('Copy')}</span>
@@ -170,7 +188,17 @@ const KeypairItem = ({ pk, active }: { pk: Key; active: boolean }) => {
</button>
</CopyToClipboard>
{copied && <span className="text-xs">{t('Copied')}</span>}
</span>
</div>
<div
className={classNames('flex-1 mr-2 text-secondary text-sm')}
data-testid={`key-${pk.publicKey}`}
>
<Tooltip description={t('Public facing key alias. Click to edit')}>
<button data-testid="alias" onClick={() => setOpen(pk.publicKey)}>
{alias ? alias : t('No alias')}
</button>
</Tooltip>
</div>
</div>
<TradingDropdownItemIndicator />
</TradingDropdownRadioItem>
+1 -1
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
VEGA_VERSION=v0.74.6
VEGA_VERSION=v0.75.0-preview.2
LOCAL_SERVER=false
-2
View File
@@ -50,8 +50,6 @@ def truncate_middle(market_id, start=6, end=4):
def change_keys(page: Page, vega: VegaServiceNull, key_name):
page.get_by_test_id("manage-vega-wallet").click()
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
page.click(
f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
page.reload()
-1
View File
@@ -339,4 +339,3 @@ def retry_on_http_error(request):
print(f"Retrying due to HTTPError (attempt {i+1}/{retry_count})")
else:
raise
+4 -4
View File
@@ -877,13 +877,13 @@ testing = ["filelock"]
[[package]]
name = "python-dateutil"
version = "2.8.2"
version = "2.9.0.post0"
description = "Extensions to the standard Python datetime module"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7"
files = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},
{file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"},
{file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"},
]
[package.dependencies]
@@ -1166,7 +1166,7 @@ profile = ["pytest-profiling", "snakeviz"]
type = "git"
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
reference = "HEAD"
resolved_reference = "33fec45ce8044ef7f53b625584ce590d174f9057"
resolved_reference = "53eed8942acb670783105cb1115bab76710a46dc"
[[package]]
name = "websocket-client"
@@ -18,7 +18,7 @@ expire = "expire"
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@@ -261,7 +261,7 @@ class TestStopOcoValidation:
@pytest.fixture(scope="class")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="class")
@@ -1,6 +1,7 @@
import pytest
from typing import Tuple, Generator
from fees_test_ids import *
from playwright.sync_api import Page, expect
from playwright.sync_api import expect, Page
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
from wallet_config import MM_WALLET
@@ -16,77 +17,70 @@ from fixtures.market import setup_continuous_market
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(
request, browser
) -> Generator[Tuple[VegaServiceNull, str, Page], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(
lambda: cleanup_container(vega_instance)
request.addfinalizer(lambda: cleanup_container(vega_instance))
market = setup_continuous_market(vega_instance, custom_quantum=100000)
vega_instance.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
yield vega_instance
next_epoch(vega_instance)
vega_instance.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega_instance.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega_instance)
referral_set_id = list(vega_instance.list_referral_sets().keys())[0]
vega_instance.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega_instance)
for _ in range(2):
submit_order(vega_instance, "Key 1", market, "SIDE_BUY", 2, 110)
forward_time(vega_instance, True if _ < 2 - 1 else False)
with init_page(vega_instance, browser, request) as page_instance:
risk_accepted_setup(page_instance)
auth_setup(vega_instance, page_instance)
yield vega_instance, market, page_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_combined_market(vega: VegaServiceNull):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
next_epoch(vega=vega)
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega=vega)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega=vega)
for _ in range(2):
submit_order(vega, "Key 1", market, "SIDE_BUY", 2, 110)
forward_time(vega, True if _ < 2 - 1 else False)
return market
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fees_page_discount_program_my_trading_fees(page: Page):
def test_fees_page_discount_program_my_trading_fees(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text("6.432%-6.432%")
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
@@ -97,10 +91,10 @@ def test_fees_page_discount_program_my_trading_fees(page: Page):
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fees_page_discount_program_total_discount(
page: Page,
):
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text("36%")
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text("Volume discount20%")
@@ -113,8 +107,10 @@ def test_fees_page_discount_program_total_discount(
)
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fees_page_discount_program_fees_by_market(page: Page):
def test_fees_page_discount_program_fees_by_market(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
pinned = page.locator(PINNED_ROW_LOCATOR)
row = page.locator(ROW_LOCATOR)
@@ -126,12 +122,11 @@ def test_fees_page_discount_program_fees_by_market(page: Page):
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_deal_ticket_discount_program(
page: Page,
setup_combined_market,
):
page.goto(f"/#/markets/{setup_combined_market}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text("-36%")
@@ -150,12 +145,9 @@ def test_deal_ticket_discount_program(
expect(tooltip.get_by_test_id(TOTAL_FEE_VALUE)).to_have_text("0.06432 tDAI")
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fills_taker_discount_program(
page: Page,
setup_combined_market,
):
page.goto(f"/#/markets/{setup_combined_market}")
def test_fills_taker_discount_program(setup_environment):
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_SIZE)).to_have_text("+2")
@@ -166,13 +158,11 @@ def test_fills_taker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("7.48926 tDAI")
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fills_maker_discount_program(
vega: VegaServiceNull,
page: Page,
setup_combined_market,
):
page.goto(f"/#/markets/{setup_combined_market}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -184,11 +174,11 @@ def test_fills_maker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("7.452 tDAI")
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fills_maker_fee_tooltip_discount_program(
vega: VegaServiceNull, page: Page, setup_combined_market
):
page.goto(f"/#/markets/{setup_combined_market}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -200,12 +190,11 @@ def test_fills_maker_fee_tooltip_discount_program(
)
@pytest.mark.xdist_group(name="test_fees_combo_tier_2")
def test_fills_taker_fee_tooltip_discount_program(
page: Page,
setup_combined_market,
):
page.goto(f"/#/markets/{setup_combined_market}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
# tbd - tooltip is not visible without this wait
@@ -1,4 +1,5 @@
import pytest
from typing import Tuple, Generator
from fees_test_ids import *
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
@@ -14,63 +15,58 @@ from conftest import (
from actions.utils import next_epoch, change_keys, forward_time
from fixtures.market import setup_continuous_market
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(
request, browser
) -> Generator[Tuple[VegaServiceNull, str, Page], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(
lambda: cleanup_container(vega_instance)
request.addfinalizer(lambda: cleanup_container(vega_instance))
# Setup the market with the referral discount program
market = setup_continuous_market(vega_instance, custom_quantum=100000)
vega_instance.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
yield vega_instance
vega_instance.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega_instance)
referral_set_id = list(vega_instance.list_referral_sets().keys())[0]
vega_instance.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega_instance)
for _ in range(2):
submit_order(vega_instance, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega_instance, True if _ < 2 - 1 else False)
with init_page(vega_instance, browser, request) as page_instance:
risk_accepted_setup(page_instance)
auth_setup(vega_instance, page_instance)
yield vega_instance, market, page_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_referral_discount_program(vega: VegaServiceNull):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega=vega)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega=vega)
for _ in range(2):
submit_order(vega, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega, True if _ < 2 - 1 else False)
return market
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fees_page_discount_program_my_trading_fees(page: Page):
def test_fees_page_discount_program_my_trading_fees(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text("9.045%-9.045%")
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
@@ -81,8 +77,10 @@ def test_fees_page_discount_program_my_trading_fees(page: Page):
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fees_page_discount_program_total_discount(page: Page):
def test_fees_page_discount_program_total_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text("10%")
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text("Volume discount0%")
@@ -95,15 +93,19 @@ def test_fees_page_discount_program_total_discount(page: Page):
)
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fees_page_referral_discount_program_referral_benefits(page: Page):
def test_fees_page_referral_discount_program_referral_benefits(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(RUNNING_NOTIONAL_TAKER_VOLUME)).to_have_text("103")
expect(page.get_by_test_id(EPOCHS_IN_REFERRAL_SET)).to_have_text("1")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fees_page_discount_program_discount(page: Page):
def test_fees_page_discount_program_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TIER_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(TIER_VALUE_1)).to_have_text("2")
@@ -119,8 +121,10 @@ def test_fees_page_discount_program_discount(page: Page):
expect(page.get_by_test_id("your-referral-tier-0").nth(1)).to_have_text("Your tier")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fees_page_discount_program_fees_by_market(page: Page):
def test_fees_page_discount_program_fees_by_market(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
pinned = page.locator(PINNED_ROW_LOCATOR)
row = page.locator(ROW_LOCATOR)
@@ -132,11 +136,11 @@ def test_fees_page_discount_program_fees_by_market(page: Page):
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_deal_ticket_discount_program(
page: Page, setup_market_with_referral_discount_program
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text("-10%")
@@ -155,12 +159,11 @@ def test_deal_ticket_discount_program(
expect(tooltip.get_by_test_id(TOTAL_FEE_VALUE)).to_have_text("0.09045 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fills_taker_discount_program(
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_SIZE)).to_have_text("+1")
@@ -171,13 +174,11 @@ def test_fills_taker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("1.04017 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fills_maker_discount_program(
vega: VegaServiceNull,
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -189,11 +190,11 @@ def test_fills_maker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("1.035 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fills_maker_fee_tooltip_discount_program(
vega: VegaServiceNull, page: Page, setup_market_with_referral_discount_program
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -205,12 +206,11 @@ def test_fills_maker_fee_tooltip_discount_program(
)
@pytest.mark.xdist_group(name="test_fees_referral_tier_1")
def test_fills_taker_fee_tooltip_discount_program(
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
# tbd - tooltip is not visible without this wait
@@ -1,5 +1,6 @@
import pytest
from fees_test_ids import *
from typing import Tuple, Generator
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -16,62 +17,56 @@ from fixtures.market import setup_continuous_market
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(
request, browser
) -> Generator[Tuple[VegaServiceNull, str, Page], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(
lambda: cleanup_container(vega_instance)
request.addfinalizer(lambda: cleanup_container(vega_instance))
# Setup market with referral discount program
market = setup_continuous_market(vega_instance, custom_quantum=100000)
vega_instance.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
yield vega_instance
vega_instance.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega_instance)
referral_set_id = list(vega_instance.list_referral_sets().keys())[0]
vega_instance.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega_instance)
for _ in range(2):
submit_order(vega_instance, "Key 1", market, "SIDE_BUY", 2, 110)
forward_time(vega_instance, True if _ < 2 - 1 else False)
with init_page(vega_instance, browser, request) as page_instance:
risk_accepted_setup(page_instance)
auth_setup(vega_instance, page_instance)
yield vega_instance, market, page_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_referral_discount_program(vega: VegaServiceNull):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_referral_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"minimum_epochs": 1,
"referral_reward_factor": 0.1,
"referral_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"minimum_epochs": 2,
"referral_reward_factor": 0.2,
"referral_discount_factor": 0.2,
},
],
staking_tiers=[
{"minimum_staked_tokens": 100, "referral_reward_multiplier": 1.1},
{"minimum_staked_tokens": 200, "referral_reward_multiplier": 1.2},
],
window_length=1,
)
vega.create_referral_set(key_name=MM_WALLET.name)
next_epoch(vega=vega)
referral_set_id = list(vega.list_referral_sets().keys())[0]
vega.apply_referral_code(key_name="Key 1", id=referral_set_id)
next_epoch(vega=vega)
for _ in range(2):
submit_order(vega, "Key 1", market, "SIDE_BUY", 2, 110)
forward_time(vega, True if _ < 2 - 1 else False)
return market
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fees_page_discount_program_my_trading_fees(page: Page):
def test_fees_page_discount_program_my_trading_fees(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text("8.04%-8.04%")
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
@@ -82,8 +77,10 @@ def test_fees_page_discount_program_my_trading_fees(page: Page):
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fees_page_discount_program_total_discount(page: Page):
def test_fees_page_discount_program_total_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text("20%")
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text("Volume discount0%")
@@ -96,15 +93,19 @@ def test_fees_page_discount_program_total_discount(page: Page):
)
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fees_page_referral_discount_program_referral_benefits(page: Page):
def test_fees_page_referral_discount_program_referral_benefits(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(RUNNING_NOTIONAL_TAKER_VOLUME)).to_have_text("207")
expect(page.get_by_test_id(REQUIRED_FOR_NEXT_TIER)).not_to_be_visible()
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fees_page_discount_program_discount(page: Page):
def test_fees_page_discount_program_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TIER_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(TIER_VALUE_1)).to_have_text("2")
@@ -120,8 +121,10 @@ def test_fees_page_discount_program_discount(page: Page):
expect(page.get_by_test_id("your-referral-tier-1").nth(1)).to_have_text("Your tier")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fees_page_discount_program_fees_by_market(page: Page):
def test_fees_page_discount_program_fees_by_market(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
pinned = page.locator(PINNED_ROW_LOCATOR)
row = page.locator(ROW_LOCATOR)
@@ -133,11 +136,11 @@ def test_fees_page_discount_program_fees_by_market(page: Page):
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_deal_ticket_discount_program(
page: Page, setup_market_with_referral_discount_program
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text("-20%")
@@ -156,12 +159,11 @@ def test_deal_ticket_discount_program(
expect(tooltip.get_by_test_id(TOTAL_FEE_VALUE)).to_have_text("0.0804 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fills_taker_discount_program(
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_SIZE)).to_have_text("+2")
@@ -172,13 +174,11 @@ def test_fills_taker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("4.1607 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fills_maker_discount_program(
vega: VegaServiceNull,
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -190,11 +190,11 @@ def test_fills_maker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("4.14 tDAI")
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fills_maker_fee_tooltip_discount_program(
vega: VegaServiceNull, page: Page, setup_market_with_referral_discount_program
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -206,12 +206,11 @@ def test_fills_maker_fee_tooltip_discount_program(
)
@pytest.mark.xdist_group(name="test_fees_referral_tier_2")
def test_fills_taker_fee_tooltip_discount_program(
page: Page,
setup_market_with_referral_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_referral_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
# tbd - tooltip is not visible without this wait
@@ -1,5 +1,6 @@
import pytest
from fees_test_ids import *
from typing import Tuple, Generator
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -16,49 +17,45 @@ from fixtures.market import setup_continuous_market
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(
request, browser
) -> Generator[Tuple[VegaServiceNull, str, Page], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(
lambda: cleanup_container(vega_instance)
request.addfinalizer(lambda: cleanup_container(vega_instance))
# Setup the market with volume discount program
market = setup_continuous_market(vega_instance, custom_quantum=100000)
vega_instance.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
yield vega_instance
next_epoch(vega=vega_instance)
for _ in range(2):
submit_order(vega_instance, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega_instance, True if _ < 2 - 1 else False)
# Initialize and setup page
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
yield vega_instance, market, page
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_volume_discount_program(vega: VegaServiceNull):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
next_epoch(vega=vega)
for _ in range(2):
submit_order(vega, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega, True if _ < 2 - 1 else False)
return market
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fees_page_discount_program_my_trading_fees(page: Page):
def test_fees_page_discount_program_my_trading_fees(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text("9.045%-9.045%")
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
@@ -69,10 +66,10 @@ def test_fees_page_discount_program_my_trading_fees(page: Page):
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fees_page_discount_program_total_discount(
page: Page,
):
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text("10%")
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text("Volume discount10%")
@@ -85,20 +82,20 @@ def test_fees_page_discount_program_total_discount(
)
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fees_page_volume_discount_program_my_current_volume(
page: Page,
):
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(PAST_EPOCHS_VOLUME)).to_have_text("103")
expect(page.get_by_test_id(REQUIRED_FOR_NEXT_TIER)).to_have_text("97")
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fees_page_discount_program_discount(
page: Page,
):
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TIER_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(TIER_VALUE_1)).to_have_text("2")
@@ -111,8 +108,10 @@ def test_fees_page_discount_program_discount(
expect(page.get_by_test_id("your-volume-tier-0").nth(1)).to_have_text("Your tier")
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fees_page_discount_program_fees_by_market(page: Page):
def test_fees_page_discount_program_fees_by_market(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
pinned = page.locator(PINNED_ROW_LOCATOR)
row = page.locator(ROW_LOCATOR)
@@ -124,11 +123,11 @@ def test_fees_page_discount_program_fees_by_market(page: Page):
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_deal_ticket_discount_program_testing(
page: Page, setup_market_with_volume_discount_program
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text("-10%")
@@ -149,9 +148,10 @@ def test_deal_ticket_discount_program_testing(
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fills_taker_discount_program(
page: Page, setup_market_with_volume_discount_program
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_SIZE)).to_have_text("+1")
@@ -164,9 +164,10 @@ def test_fills_taker_discount_program(
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fills_maker_discount_program(
page: Page, setup_market_with_volume_discount_program, vega: VegaServiceNull
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -178,11 +179,11 @@ def test_fills_maker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("1.035 tDAI")
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fills_maker_fee_tooltip_discount_program(
vega: VegaServiceNull, page: Page, setup_market_with_volume_discount_program
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -194,12 +195,11 @@ def test_fills_maker_fee_tooltip_discount_program(
)
@pytest.mark.xdist_group(name="test_fees_volume_tier_1")
def test_fills_taker_fee_tooltip_discount_program(
page: Page,
setup_market_with_volume_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
# tbd - tooltip is not visible without this wait
@@ -1,5 +1,6 @@
import pytest
from fees_test_ids import *
from typing import Tuple, Generator
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -14,51 +15,47 @@ from conftest import (
from actions.utils import next_epoch, change_keys, forward_time
from fixtures.market import setup_continuous_market
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(
request, browser
) -> Generator[Tuple[VegaServiceNull, str, Page], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(
lambda: cleanup_container(vega_instance)
request.addfinalizer(lambda: cleanup_container(vega_instance))
# Setup the market with volume discount program
market = setup_continuous_market(vega_instance, custom_quantum=100000)
vega_instance.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
yield vega_instance
next_epoch(vega=vega_instance)
for _ in range(3):
submit_order(vega_instance, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega_instance, True if _ < 3 - 1 else False)
# Initialize page and apply setups
with init_page(vega_instance, browser, request) as page_instance:
risk_accepted_setup(page_instance)
auth_setup(vega_instance, page_instance)
yield vega_instance, market, page_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_volume_discount_program(vega: VegaServiceNull):
market = setup_continuous_market(vega, custom_quantum=100000)
vega.update_volume_discount_program(
proposal_key=MM_WALLET.name,
benefit_tiers=[
{
"minimum_running_notional_taker_volume": 100,
"volume_discount_factor": 0.1,
},
{
"minimum_running_notional_taker_volume": 200,
"volume_discount_factor": 0.2,
},
],
window_length=7,
)
next_epoch(vega=vega)
for _ in range(3):
submit_order(vega, "Key 1", market, "SIDE_BUY", 1, 110)
forward_time(vega, True if _ < 3 - 1 else False)
return market
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fees_page_discount_program_my_trading_fees(page: Page):
def test_fees_page_discount_program_my_trading_fees(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(ADJUSTED_FEES)).to_have_text("8.04%-8.04%")
expect(page.get_by_test_id(TOTAL_FEE_BEFORE_DISCOUNT)).to_have_text(
@@ -69,8 +66,10 @@ def test_fees_page_discount_program_my_trading_fees(page: Page):
expect(page.get_by_test_id(LIQUIDITY_FEES)).to_have_text("Liquidity0%-0%")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fees_page_discount_program_total_discount(page: Page):
def test_fees_page_discount_program_total_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TOTAL_DISCOUNT)).to_have_text("20%")
expect(page.get_by_test_id(VOLUME_DISCOUNT_ROW)).to_have_text("Volume discount20%")
@@ -83,15 +82,19 @@ def test_fees_page_discount_program_total_discount(page: Page):
)
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fees_page_volume_discount_program_my_current_volume(page: Page):
def test_fees_page_volume_discount_program_my_current_volume(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(PAST_EPOCHS_VOLUME)).to_have_text("206")
expect(page.get_by_test_id(REQUIRED_FOR_NEXT_TIER)).not_to_be_visible()
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fees_page_discount_program_discount(page: Page):
def test_fees_page_discount_program_discount(
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto("/#/fees")
expect(page.get_by_test_id(TIER_VALUE_0)).to_have_text("1")
expect(page.get_by_test_id(TIER_VALUE_1)).to_have_text("2")
@@ -104,8 +107,8 @@ def test_fees_page_discount_program_discount(page: Page):
expect(page.get_by_test_id("your-volume-tier-1").nth(1)).to_have_text("Your tier")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fees_page_discount_program_fees_by_market(page: Page):
def test_fees_page_discount_program_fees_by_market(setup_environment):
vega, market, page = setup_environment
page.goto("/#/fees")
pinned = page.locator(PINNED_ROW_LOCATOR)
row = page.locator(ROW_LOCATOR)
@@ -117,12 +120,11 @@ def test_fees_page_discount_program_fees_by_market(page: Page):
expect(row.locator(COL_TOTAL_FEE)).to_have_text("10.05%")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_deal_ticket_discount_program(
page: Page,
setup_market_with_volume_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(ORDER_SIZE).fill("1")
page.get_by_test_id(ORDER_PRICE).fill("1")
expect(page.get_by_test_id(DISCOUNT_PILL)).to_have_text("-20%")
@@ -141,12 +143,11 @@ def test_deal_ticket_discount_program(
expect(tooltip.get_by_test_id(TOTAL_FEE_VALUE)).to_have_text("0.0804 tDAI")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fills_taker_discount_program(
page: Page,
setup_market_with_volume_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_SIZE)).to_have_text("+1")
@@ -159,13 +160,11 @@ def test_fills_taker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("2.08035 tDAI")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fills_maker_discount_program(
vega: VegaServiceNull,
page: Page,
setup_market_with_volume_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -177,11 +176,11 @@ def test_fills_maker_discount_program(
expect(row.locator(COL_FEE_DISCOUNT)).to_have_text("2.07 tDAI")
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fills_maker_fee_tooltip_discount_program(
vega, page: Page, setup_market_with_volume_discount_program
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
change_keys(page, vega, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
@@ -193,12 +192,11 @@ def test_fills_maker_fee_tooltip_discount_program(
)
@pytest.mark.xdist_group(name="test_fees_volume_tier_2")
def test_fills_taker_fee_tooltip_discount_program(
page: Page,
setup_market_with_volume_discount_program,
):
page.goto(f"/#/markets/{setup_market_with_volume_discount_program}")
setup_environment: Tuple[VegaServiceNull, str, Page],
) -> None:
vega, market, page = setup_environment
page.goto(f"/#/markets/{market}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
# tbd - tooltip is not visible without this wait
@@ -37,19 +37,16 @@ def validate_info_section(page: Page, fields: [[str, str]]):
for rowNumber, field in enumerate(fields):
name, value = field
expect(
page.get_by_test_id(
"key-value-table-row").nth(rowNumber).locator("dt")
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dt")
).to_contain_text(name)
expect(
page.get_by_test_id(
"key-value-table-row").nth(rowNumber).locator("dd")
page.get_by_test_id("key-value-table-row").nth(rowNumber).locator("dd")
).to_contain_text(value)
def test_market_info_current_fees(page: Page):
# 6002-MDET-101
page.get_by_test_id(market_title_test_id).get_by_text(
"Current fees").click()
page.get_by_test_id(market_title_test_id).get_by_text("Current fees").click()
fields = [
["Maker Fee", "10%"],
["Infrastructure Fee", "0.05%"],
@@ -61,8 +58,7 @@ def test_market_info_current_fees(page: Page):
def test_market_info_market_price(page: Page):
# 6002-MDET-102
page.get_by_test_id(market_title_test_id).get_by_text(
"Market price").click()
page.get_by_test_id(market_title_test_id).get_by_text("Market price").click()
fields = [
["Mark Price", "107.50"],
["Best Bid Price", "101.50"],
@@ -71,25 +67,25 @@ def test_market_info_market_price(page: Page):
]
validate_info_section(page, fields)
def test_market_info_market_volume(page: Page):
#TODO: remove skip once volume is fixed
""" def test_market_info_market_volume(page: Page):
# 6002-MDET-103
page.get_by_test_id(market_title_test_id).get_by_text(
"Market volume").click()
page.get_by_test_id(market_title_test_id).get_by_text("Market volume").click()
fields = [
["24 Hour Volume", "-"],
["24 Hour Volume", "0 (0 )"],
["Open Interest", "1"],
["Best Bid Volume", "99"],
["Best Offer Volume", "99"],
["Best Static Bid Volume", "1"],
["Best Static Offer Volume", "1"],
]
validate_info_section(page, fields)
validate_info_section(page, fields) """
def test_market_info_liquidation_strategy(page: Page):
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidation strategy").click()
"Liquidation strategy"
).click()
fields = [
["Disposal Fraction", "1"],
["Disposal Time Step", "1"],
@@ -101,16 +97,14 @@ def test_market_info_liquidation_strategy(page: Page):
def test_market_info_liquidation(page: Page):
# 6002-MDET-104
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidations").click()
page.get_by_test_id(market_title_test_id).get_by_text("Liquidations").click()
fields = [["Insurance Pool Balance", "0.00 tDAI"]]
validate_info_section(page, fields)
def test_market_info_key_details(page: Page, vega: VegaServiceNull):
# 6002-MDET-201
page.get_by_test_id(market_title_test_id).get_by_text(
"Key details").click()
page.get_by_test_id(market_title_test_id).get_by_text("Key details").click()
market_id = vega.find_market_id("BTC:DAI_2023")
short_market_id = market_id[:6] + "" + market_id[-4:]
fields = [
@@ -156,8 +150,7 @@ def test_market_info_oracle(page: Page):
def test_market_info_settlement_asset(page: Page, vega: VegaServiceNull):
# 6002-MDET-206
page.get_by_test_id(market_title_test_id).get_by_text(
"Settlement asset").click()
page.get_by_test_id(market_title_test_id).get_by_text("Settlement asset").click()
tdai_id = vega.find_asset_id("tDAI")
tdai_id_short = tdai_id[:6] + "" + tdai_id[-4:]
fields = [
@@ -211,8 +204,7 @@ def test_market_info_margin_scaling_factors(page: Page):
def test_market_info_risk_factors(page: Page):
# 6002-MDET-210
page.get_by_test_id(market_title_test_id).get_by_text(
"Risk factors").click()
page.get_by_test_id(market_title_test_id).get_by_text("Risk factors").click()
fields = [
["Long", "0.05153"],
["Short", "0.05422"],
@@ -232,8 +224,7 @@ def test_market_info_price_monitoring_bounds(page: Page):
expect(page.locator("p.col-span-1").nth(0)).to_contain_text(
"99.9999% probability price bounds"
)
expect(page.locator("p.col-span-1").nth(1)
).to_contain_text("Within 86,400 seconds")
expect(page.locator("p.col-span-1").nth(1)).to_contain_text("Within 86,400 seconds")
fields = [
["Highest Price", "138.66685 BTC"],
["Lowest Price", "83.11038 BTC"],
@@ -254,7 +245,7 @@ def test_market_info_liquidity_monitoring_parameters(page: Page):
# Liquidity resolves to 3 results
def test_market_info_liquidit(page: Page):
def test_market_info_liquidity(page: Page):
# 6002-MDET-213
page.get_by_test_id(market_title_test_id).get_by_text(
"Liquidity", exact=True
@@ -283,17 +274,14 @@ def test_market_info_proposal(page: Page, vega: VegaServiceNull):
# 6002-MDET-301
page.get_by_test_id(market_title_test_id).get_by_text("Proposal").click()
first_link = (
page.get_by_test_id(
"accordion-content").get_by_test_id("external-link").first
page.get_by_test_id("accordion-content").get_by_test_id("external-link").first
)
second_link = (
page.get_by_test_id(
"accordion-content").get_by_test_id("external-link").nth(1)
page.get_by_test_id("accordion-content").get_by_test_id("external-link").nth(1)
)
expect(first_link).to_have_text("View governance proposal")
expect(first_link).to_have_attribute(
"href", re.compile(
rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
"href", re.compile(rf'(\/proposals\/{vega.find_market_id("BTC:DAI_2023")})')
)
expect(second_link).to_have_text("Propose a change to market")
@@ -304,12 +292,10 @@ def test_market_info_proposal(page: Page, vega: VegaServiceNull):
def test_market_info_succession_line(page: Page, vega: VegaServiceNull):
page.get_by_test_id(market_title_test_id).get_by_text(
"Succession line").click()
page.get_by_test_id(market_title_test_id).get_by_text("Succession line").click()
market_id = vega.find_market_id("BTC:DAI_2023")
succession_line = page.get_by_test_id("succession-line-item")
expect(succession_line.get_by_test_id(
"external-link")).to_have_text("BTC:DAI_2023")
expect(succession_line.get_by_test_id("external-link")).to_have_text("BTC:DAI_2023")
expect(succession_line.get_by_test_id("external-link")).to_have_attribute(
"href", re.compile(rf"(\/proposals\/{market_id})")
)
+184 -192
View File
@@ -10,228 +10,218 @@ order_tab = "tab-orders"
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser):
# Initialize Vega with cleanup
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function
yield vega_instance
request.addfinalizer(lambda: cleanup_container(vega_instance))
# Setup multiple markets
markets = {
"market_1": setup_continuous_market(vega_instance, custom_market_name="market-1"),
"market_2": setup_continuous_market(vega_instance, custom_market_name="market-2"),
"market_3": setup_continuous_market(vega_instance, custom_market_name="market-3"),
"market_4": setup_continuous_market(vega_instance, custom_market_name="market-4"),
"market_5": setup_simple_market(vega_instance, custom_market_name="market-5"),
}
@pytest.fixture(scope="module", autouse=True)
def markets(vega: VegaServiceNull):
market_1 = setup_continuous_market(
vega,
custom_market_name="market-1",
)
market_2 = setup_continuous_market(
vega,
custom_market_name="market-2",
)
market_3 = setup_continuous_market(
vega,
custom_market_name="market-3",
)
market_4 = setup_continuous_market(
vega,
custom_market_name="market-4",
)
market_5 = setup_simple_market(
vega,
custom_market_name="market-5",
)
# Execute a series of orders across different markets
# Example for market_1 orders, adjust as necessary for your test scenario
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_1"],
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=130,
)
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_1,
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=130,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_1"],
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=88,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_1,
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=88,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_1"],
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=88,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_1,
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=100,
price=88,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_1"],
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=1e10,
price=130,
wait=False,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_1,
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=1e10,
price=130,
wait=False,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_2"],
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_BUY",
volume=100,
price=104,
)
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_3"],
time_in_force="TIME_IN_FORCE_GTT",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=120,
expires_at=vega_instance.get_blockchain_time() + 5 * 1e9,
)
vega.submit_order(
trading_key="Key 1",
market_id=market_2,
time_in_force="TIME_IN_FORCE_IOC",
order_type="TYPE_LIMIT",
side="SIDE_BUY",
volume=100,
price=104,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_3,
time_in_force="TIME_IN_FORCE_GTT",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=120,
expires_at=vega.get_blockchain_time() + 5 * 1e9,
)
vega_instance.submit_order(
market_id=markets["market_4"],
trading_key="Key 1",
side="SIDE_BUY",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=5),
time_in_force="TIME_IN_FORCE_GTC",
volume=20,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
market_id=market_4,
trading_key="Key 1",
side="SIDE_BUY",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=5),
time_in_force="TIME_IN_FORCE_GTC",
volume=20,
)
vega_instance.submit_order(
market_id=markets["market_4"],
trading_key="Key 1",
side="SIDE_BUY",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_BID", offset=10),
time_in_force="TIME_IN_FORCE_GTC",
volume=40,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
market_id=market_4,
trading_key="Key 1",
side="SIDE_BUY",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_BID", offset=10),
time_in_force="TIME_IN_FORCE_GTC",
volume=40,
)
vega_instance.submit_order(
market_id=markets["market_4"],
trading_key="Key 1",
side="SIDE_SELL",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15),
time_in_force="TIME_IN_FORCE_GTC",
volume=60,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
market_id=market_4,
trading_key="Key 1",
side="SIDE_SELL",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15),
time_in_force="TIME_IN_FORCE_GTC",
volume=60,
)
vega_instance.submit_order(
market_id=markets["market_5"],
trading_key="Key 1",
side="SIDE_SELL",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=60,
)
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.forward("2s")
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
market_id=market_5,
trading_key="Key 1",
side="SIDE_SELL",
order_type="TYPE_LIMIT",
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_BEST_ASK", offset=15),
wait=False,
time_in_force="TIME_IN_FORCE_GTC",
volume=60,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_2"],
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=150,
)
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.forward("2s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_2"],
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=160,
)
vega.submit_order(
trading_key="Key 1",
market_id=market_2,
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=150,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_2,
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_SELL",
volume=10,
price=160,
)
vega_instance.submit_order(
trading_key="Key 1",
market_id=markets["market_3"],
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_BUY",
volume=10,
price=60,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega.submit_order(
trading_key="Key 1",
market_id=market_3,
time_in_force="TIME_IN_FORCE_GTC",
order_type="TYPE_LIMIT",
side="SIDE_BUY",
volume=10,
price=60,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega_instance.wait_fn(1)
vega_instance.wait_for_total_catchup()
# Initialize page and setup
yield vega_instance, markets
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
def page(setup_environment, browser, request):
vega_instance, markets = setup_environment
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
auth_setup(vega_instance, page)
page.goto("/")
page.get_by_test_id("All").click()
yield page
# close toast that is still opened after test
@pytest.fixture(autouse=True)
def after_each(page: Page):
yield
@@ -381,7 +371,8 @@ def test_order_status_pegged_mid(page: Page):
)
def test_order_amend_order(vega: VegaServiceNull, page: Page):
def test_order_amend_order(setup_environment, page:Page):
vega, markets = setup_environment
# 7002-SORD-053
# 7003-MORD-012
# 7003-MORD-014
@@ -402,13 +393,13 @@ def test_order_amend_order(vega: VegaServiceNull, page: Page):
)
def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
def test_order_cancel_single_order(setup_environment, page:Page):
vega, markets = setup_environment
# 7003-MORD-009
# 7003-MORD-010
# 7003-MORD-011
# 7002-SORD-043
page.get_by_test_id("cancel").first.click()
wait_for_toast_confirmation(page, timeout=5000)
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -420,7 +411,8 @@ def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
)
def test_order_cancel_all_orders(vega: VegaServiceNull, page: Page):
def test_order_cancel_all_orders(setup_environment, page:Page):
vega, markets = setup_environment
# 7003-MORD-009
# 7003-MORD-010
# 7003-MORD-011
@@ -57,7 +57,7 @@ class TestPerpetuals:
page.goto(f"/#/markets/{perps_market}")
page.get_by_test_id("Funding payments").click()
row = page.locator(row_selector)
expect(row.locator(col_amount)).to_have_text("9.00 tDAI")
expect(row.locator(col_amount)).to_have_text("4.45 tDAI")
@pytest.mark.usefixtures("risk_accepted", "auth")
def test_funding_payment_loss(self, perps_market, page: Page, vega):
@@ -65,7 +65,7 @@ class TestPerpetuals:
change_keys(page, vega, "market_maker")
page.get_by_test_id("Funding payments").click()
row = page.locator(row_selector)
expect(row.locator(col_amount)).to_have_text("-27.00 tDAI")
expect(row.locator(col_amount)).to_have_text("-13.35 tDAI")
@pytest.mark.usefixtures("risk_accepted", "auth")
def test_funding_header(self, perps_market, page: Page):
@@ -1,8 +1,15 @@
import pytest
import vega_sim.proto.vega as vega_protos
from typing import Tuple, Generator
from rewards_test_ids import *
from playwright.sync_api import Page, expect
from conftest import init_vega, init_page, auth_setup, risk_accepted_setup, cleanup_container
from conftest import (
init_vega,
init_page,
auth_setup,
risk_accepted_setup,
cleanup_container,
)
from fixtures.market import setup_continuous_market
from actions.utils import next_epoch, change_keys
from wallet_config import MM_WALLET
@@ -10,22 +17,20 @@ from vega_sim.null_service import VegaServiceNull
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser) -> Generator[Tuple[Page, str, str], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
page.goto(REWARDS_URL)
change_keys(page, vega, PARTY_B)
yield page
tDAI_market, tDAI_asset_id = setup_market_with_reward_program(vega_instance)
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B)
yield page, tDAI_market, tDAI_asset_id
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_reward_program(vega: VegaServiceNull):
tDAI_market = setup_continuous_market(vega)
PARTY_A, PARTY_B, PARTY_C, PARTY_D = keys(vega)
@@ -83,39 +88,39 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
return tDAI_market, tDAI_asset_id
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_0")
def test_network_reward_pot(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text("50.00 tDAI")
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_0")
def test_reward_multiplier(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text("1x")
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
expect(page.get_by_test_id(HOARDER_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_0")
def test_hoarder_bonus(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(HOARDER_BONUS_TOTAL_HOARDED)).to_contain_text(
"5,000,000"
)
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_0")
def test_reward_history(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
page.locator('[name="fromEpoch"]').fill("1")
expect((page.get_by_role(ROW).locator(PRICE_TAKING_COL_ID)).nth(1)).to_have_text(
"100.00100.00%"
)
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text("100.00")
page.get_by_test_id(EARNED_BY_ME_BUTTON).click()
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text("50.00")
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text("50.00")
@@ -1,8 +1,15 @@
import pytest
from rewards_test_ids import *
from typing import Tuple, Generator
import vega_sim.proto.vega as vega_protos
from playwright.sync_api import Page, expect
from conftest import init_vega, init_page, auth_setup, risk_accepted_setup, cleanup_container
from conftest import (
init_vega,
init_page,
auth_setup,
risk_accepted_setup,
cleanup_container,
)
from fixtures.market import setup_continuous_market
from actions.utils import next_epoch, change_keys
from wallet_config import MM_WALLET
@@ -10,23 +17,21 @@ from vega_sim.null_service import VegaServiceNull
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser) -> Generator[Tuple[Page, str, str], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
request.addfinalizer(lambda: cleanup_container(vega_instance))
tDAI_market, tDAI_asset_id = setup_market_with_reward_program(vega_instance)
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B)
yield page, tDAI_market, tDAI_asset_id
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
page.goto(REWARDS_URL)
change_keys(page, vega, PARTY_B)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_reward_program(vega: VegaServiceNull):
tDAI_market = setup_continuous_market(vega)
PARTY_A, PARTY_B, PARTY_C, PARTY_D = keys(vega)
@@ -135,31 +140,35 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
return tDAI_market, tDAI_asset_id
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_1")
def test_network_reward_pot(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text("166.66666 tDAI")
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_1")
def test_reward_multiplier(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text("2x")
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
expect(page.get_by_test_id(HOARDER_REWARD_MULTIPLIER_VALUE)).to_have_text("2x")
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_1")
def test_hoarder_bonus(page: Page):
def test_hoarder_bonus(
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(HOARDER_BONUS_TOTAL_HOARDED)).to_contain_text(
"16,666,666"
)
@pytest.mark.xdist_group(name="test_rewards_hoarder_tier_1")
def test_reward_history(page: Page):
def test_reward_history(
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
page.locator('[name="fromEpoch"]').fill("1")
expect((page.get_by_role(ROW).locator(PRICE_TAKING_COL_ID)).nth(1)).to_have_text(
"299.99999100.00%"
@@ -170,4 +179,4 @@ def test_reward_history(page: Page):
page.get_by_test_id(EARNED_BY_ME_BUTTON).click()
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text(
"166.66666"
)
)
@@ -1,5 +1,6 @@
import pytest
from rewards_test_ids import *
from typing import Tuple, Generator
import vega_sim.proto.vega as vega_protos
from playwright.sync_api import Page, expect
from conftest import (
@@ -16,23 +17,23 @@ from vega_sim.null_service import VegaServiceNull
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser) -> Generator[Tuple[Page, str, str], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
tDAI_market, tDAI_asset_id = setup_market_with_reward_program(vega_instance)
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B)
yield page, tDAI_market, tDAI_asset_id
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
page.goto(REWARDS_URL)
change_keys(page, vega, PARTY_B)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_reward_program(vega: VegaServiceNull):
tDAI_market = setup_continuous_market(vega)
PARTY_A, PARTY_B, PARTY_C, PARTY_D = keys(vega)
@@ -90,25 +91,23 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
return tDAI_market, tDAI_asset_id
@pytest.mark.xdist_group(name="test_rewards_activity_tier_0")
def test_network_reward_pot(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text("50.00 tDAI")
page.pause()
@pytest.mark.xdist_group(name="test_rewards_activity_tier_0")
def test_reward_multiplier(
page: Page,
):
page.pause()
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text("1x")
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
expect(page.get_by_test_id(HOARDER_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
""" @pytest.mark.xdist_group(name="test_rewards_activity_tier_0")
"""
def test_activity_streak(
page: Page,
):
@@ -117,7 +116,6 @@ def test_activity_streak(
)
@pytest.mark.xdist_group(name="test_rewards_activity_tier_0")
def test_reward_history(
page: Page,
):
@@ -1,8 +1,15 @@
import pytest
from rewards_test_ids import *
from typing import Tuple, Generator
import vega_sim.proto.vega as vega_protos
from playwright.sync_api import Page, expect
from conftest import init_vega, init_page, auth_setup, risk_accepted_setup, cleanup_container
from conftest import (
init_vega,
init_page,
auth_setup,
risk_accepted_setup,
cleanup_container,
)
from fixtures.market import setup_continuous_market
from actions.utils import next_epoch, change_keys
from wallet_config import MM_WALLET
@@ -10,22 +17,21 @@ from vega_sim.null_service import VegaServiceNull
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser) -> Generator[Tuple[Page, str, str], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
tDAI_market, tDAI_asset_id = setup_market_with_reward_program(vega_instance)
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B)
yield page, tDAI_market, tDAI_asset_id
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
page.goto(REWARDS_URL)
change_keys(page, vega, PARTY_B)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_reward_program(vega: VegaServiceNull):
tDAI_market = setup_continuous_market(vega)
PARTY_A, PARTY_B, PARTY_C, PARTY_D = keys(vega)
@@ -128,34 +134,35 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
return tDAI_market, tDAI_asset_id
@pytest.mark.xdist_group(name="test_rewards_activity_tier_1")
def test_network_reward_pot(page: Page):
def test_network_reward_pot(
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text("116.66666 tDAI")
@pytest.mark.xdist_group(name="test_rewards_activity_tier_1")
def test_reward_multiplier(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text("2x")
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text("2x")
expect(page.get_by_test_id(HOARDER_REWARD_MULTIPLIER_VALUE)).to_have_text("1x")
@pytest.mark.xdist_group(name="test_rewards_activity_tier_1")
def test_activity_streak(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(EPOCH_STREAK)).to_have_text(
"Active trader: 7 epochs so far (Tier 1 as of last epoch)"
)
@pytest.mark.xdist_group(name="test_rewards_activity_tier_1")
def test_reward_history(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
page.locator('[name="fromEpoch"]').fill("1")
expect((page.get_by_role(ROW).locator(PRICE_TAKING_COL_ID)).nth(1)).to_have_text(
"300.00100.00%"
@@ -167,10 +174,10 @@ def test_reward_history(
)
@pytest.mark.xdist_group(name="test_rewards_activity_tier_1")
def test_redeem(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
page.get_by_test_id("redeem-rewards-button").click()
available_to_withdraw = page.get_by_test_id(
"available-to-withdraw-value"
@@ -184,4 +191,4 @@ def test_redeem(
)
page.get_by_test_id("use-max-button").first.click()
expect(page.get_by_test_id(TRANSFER_AMOUNT)).to_have_text(available_to_withdraw)
expect(page.get_by_test_id(TRANSFER_AMOUNT)).to_have_text(available_to_withdraw)
@@ -1,5 +1,6 @@
import pytest
from rewards_test_ids import *
from typing import Tuple, Generator
import vega_sim.proto.vega as vega_protos
from playwright.sync_api import Page, expect
from conftest import init_vega, init_page, auth_setup, risk_accepted_setup, cleanup_container
@@ -9,24 +10,20 @@ from wallet_config import MM_WALLET
from vega_sim.null_service import VegaServiceNull
@pytest.fixture(scope="module")
def vega(request):
def setup_environment(request, browser) -> Generator[Tuple[Page, str, str], None, None]:
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
tDAI_market, tDAI_asset_id = setup_market_with_reward_program(vega_instance)
with init_page(vega_instance, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega_instance, page)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B)
yield page, tDAI_market, tDAI_asset_id
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(vega, page)
page.goto(REWARDS_URL)
change_keys(page, vega, PARTY_B)
yield page
@pytest.fixture(scope="module", autouse=True)
def setup_market_with_reward_program(vega: VegaServiceNull):
tDAI_market = setup_continuous_market(vega)
PARTY_A, PARTY_B, PARTY_C, PARTY_D = keys(vega)
@@ -57,6 +54,16 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
)
next_epoch(vega=vega)
vega.recurring_transfer(
from_key_name=PARTY_A.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
to_account_type=vega_protos.vega.ACCOUNT_TYPE_GLOBAL_REWARD,
asset=tDAI_asset_id,
amount=100,
factor=1.0,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
vega.recurring_transfer(
from_key_name=PARTY_A.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
@@ -140,16 +147,18 @@ def setup_market_with_reward_program(vega: VegaServiceNull):
return tDAI_market, tDAI_asset_id
@pytest.mark.xdist_group(name="test_rewards_combo_tier_1")
def test_network_reward_pot( page: Page
):
def test_network_reward_pot( setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text("183.33333 tDAI")
@pytest.mark.xdist_group(name="test_rewards_combo_tier_1")
def test_reward_multiplier(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text("4x")
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text(
"2x"
@@ -159,10 +168,11 @@ def test_reward_multiplier(
)
@pytest.mark.xdist_group(name="test_rewards_combo_tier_1")
def test_reward_history(
page: Page,
):
setup_environment: Tuple[Page, str, str],
) -> None:
page, tDAI_market, tDAI_asset_id = setup_environment
page.locator('[name="fromEpoch"]').fill("1")
expect((page.get_by_role(ROW).locator(PRICE_TAKING_COL_ID)).nth(1)).to_have_text(
"299.99999100.00%"
@@ -171,4 +181,22 @@ def test_reward_history(
page.get_by_test_id(EARNED_BY_ME_BUTTON).click()
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text(
"183.33333"
)
)
def test_staking_reward(
page: Page,
):
expect(page.get_by_test_id("active-rewards-card")).to_have_count(2)
staking_reward_card = page.get_by_test_id("active-rewards-card").nth(1)
expect(staking_reward_card).to_be_visible()
expect(staking_reward_card.get_by_test_id("entity-scope")).to_have_text("Individual")
expect(staking_reward_card.get_by_test_id("locked-for")).to_have_text("0 epochs")
expect(staking_reward_card.get_by_test_id("reward-value")).to_have_text("100.00")
expect(staking_reward_card.get_by_test_id("distribution-strategy")).to_have_text("Pro rata")
expect(staking_reward_card.get_by_test_id("dispatch-metric-info")).to_have_text(
"Staking rewards"
)
expect(staking_reward_card.get_by_test_id("assessed-over")).to_have_text("1 epoch")
expect(staking_reward_card.get_by_test_id("scope")).to_have_text("Individual")
expect(staking_reward_card.get_by_test_id("staking-requirement")).to_have_text("1.00")
expect(staking_reward_card.get_by_test_id("average-position")).to_have_text("0.00")
@@ -97,10 +97,13 @@ def test_banners(vega: VegaServiceNull, page: Page):
settlement_price=100,
market_id=parent_market_id,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
successor_name = "successor market name"
propose_successor(vega, parent_market_id, tdai_id, successor_name)
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.reload()
# Check that the banners notifying about the successor proposal and market has been settled are shown
banner = page.get_by_test_id(market_banner)
expect(banner).to_be_attached()
+3 -3
View File
@@ -299,10 +299,10 @@ def test_leaderboard(competitions_page: Page, setup_teams_and_games):
def test_game_card(competitions_page: Page):
expect(competitions_page.get_by_test_id("active-rewards-card")).to_have_count(2)
expect(competitions_page.get_by_test_id("active-rewards-card")).to_have_count(1)
game_1 = competitions_page.get_by_test_id("active-rewards-card").first
expect(game_1).to_be_visible()
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual")
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Team")
expect(game_1.get_by_test_id("locked-for")).to_have_text("1 epoch")
expect(game_1.get_by_test_id("reward-value")).to_have_text("100.00")
expect(game_1.get_by_test_id("reward-asset")).to_have_text("VEGA")
@@ -311,7 +311,7 @@ def test_game_card(competitions_page: Page):
"Price maker fees paid • tDAI"
)
expect(game_1.get_by_test_id("assessed-over")).to_have_text("15 epochs")
expect(game_1.get_by_test_id("scope")).to_have_text("In team")
expect(game_1.get_by_test_id("scope")).to_have_text("All teams")
expect(game_1.get_by_test_id("staking-requirement")).to_have_text("0.00")
expect(game_1.get_by_test_id("average-position")).to_have_text("0.00")
+6 -2
View File
@@ -25,8 +25,12 @@ fragment GameFields on Game {
}
}
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
query Games($epochFrom: Int, $teamId: ID) {
games(
epochFrom: $epochFrom
teamId: $teamId
entityScope: ENTITY_SCOPE_TEAMS
) {
edges {
node {
...GameFields
+4 -2
View File
@@ -9,6 +9,7 @@ export type GameFieldsFragment = { __typename?: 'Game', id: string, epoch: numbe
export type GamesQueryVariables = Types.Exact<{
epochFrom?: Types.InputMaybe<Types.Scalars['Int']>;
teamId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
@@ -44,8 +45,8 @@ export const GameFieldsFragmentDoc = gql`
}
${TeamEntityFragmentDoc}`;
export const GamesDocument = gql`
query Games($epochFrom: Int) {
games(epochFrom: $epochFrom, entityScope: ENTITY_SCOPE_TEAMS) {
query Games($epochFrom: Int, $teamId: ID) {
games(epochFrom: $epochFrom, teamId: $teamId, entityScope: ENTITY_SCOPE_TEAMS) {
edges {
node {
...GameFields
@@ -68,6 +69,7 @@ export const GamesDocument = gql`
* const { data, loading, error } = useGamesQuery({
* variables: {
* epochFrom: // value for 'epochFrom'
* teamId: // value for 'teamId'
* },
* });
*/
+1
View File
@@ -51,6 +51,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
const { data, loading, error } = useGamesQuery({
variables: {
epochFrom: from,
teamId: teamId,
},
skip: !from,
fetchPolicy: 'cache-and-network',
+2 -2
View File
@@ -194,11 +194,11 @@ describe('isScopedToTeams', () => {
undefined,
makeDispatchStrategy(
EntityScope.ENTITY_SCOPE_INDIVIDUALS,
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM // individual in teams
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM // individual in teams but not a team game
),
'RecurringTransfer'
),
true,
false,
],
[
makeReward(
+1 -11
View File
@@ -14,7 +14,6 @@ import {
TransferStatus,
type DispatchStrategy,
EntityScope,
IndividualScope,
MarketState,
AccountType,
} from '@vegaprotocol/types';
@@ -75,20 +74,11 @@ export const isActiveReward = (node: RewardTransfer, currentEpoch: number) => {
/**
* Checks if given reward (transfer) is scoped to teams.
*
* A reward is scoped to teams if it's entity scope is set to teams or
* if the scope is set to individuals but the individuals are in a team.
*/
export const isScopedToTeams = (node: EnrichedRewardTransfer) =>
// scoped to teams
node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_TEAMS ||
// or to individuals
(node.transfer.kind.dispatchStrategy?.entityScope ===
EntityScope.ENTITY_SCOPE_INDIVIDUALS &&
// but they have to be in a team
node.transfer.kind.dispatchStrategy?.individualScope ===
IndividualScope.INDIVIDUAL_SCOPE_IN_TEAM);
EntityScope.ENTITY_SCOPE_TEAMS;
/** Retrieves rewards (transfers) */
export const useRewards = ({
-2
View File
@@ -24,7 +24,6 @@ import {
ProtocolUpgradeInProgressNotification,
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingBanner } from '../components/viewing-banner';
import { Telemetry } from '../components/telemetry';
import { SSRLoader } from './ssr-loader';
import { PartyActiveOrdersHandler } from './party-active-orders-handler';
@@ -77,7 +76,6 @@ function AppBody({ Component }: AppProps) {
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
/>
<ProtocolUpgradeInProgressNotification />
<ViewingBanner />
</div>
<div data-testid={`pathname-${location.pathname}`}>
<Component />
+2
View File
@@ -8,6 +8,7 @@ import {
} from '@vegaprotocol/web3';
import { WelcomeDialog } from '../components/welcome-dialog';
import { VegaWalletConnectDialog } from '../components/vega-wallet-connect-dialog';
import { ProfileDialog } from '../components/profile-dialog';
const DialogsContainer = () => {
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
@@ -24,6 +25,7 @@ const DialogsContainer = () => {
<WelcomeDialog />
<Web3ConnectUncontrolledDialog />
<WithdrawalApprovalDialogContainer />
<ProfileDialog />
</>
);
};
@@ -0,0 +1,19 @@
import { create } from 'zustand';
interface ProfileDialogStore {
open: boolean;
pubKey: string | undefined;
setOpen: (pubKey: string | undefined) => void;
}
export const useProfileDialogStore = create<ProfileDialogStore>((set) => ({
open: false,
pubKey: undefined,
setOpen: (pubKey) => {
if (pubKey) {
set({ open: true, pubKey });
} else {
set({ open: false, pubKey: undefined });
}
},
}));
+7
View File
@@ -62,3 +62,10 @@ export const DENY_LIST: Record<string, string[]> = {
'fdf0ec118d98393a7702cf72e46fc87ad680b152f64b2aac59e093ac2d688fbb',
],
};
// We need a record of USDT on mainnet as it needs special handling for
// deposits and approvals due to the contract not conforming exactly
// to ERC20
export const USDT_ID = {
MAINNET: 'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba',
} as const;
+5
View File
@@ -6,7 +6,12 @@ export const PENNANT_INTERVAL_MAP = {
[Interval.INTERVAL_I1M]: PennantInterval.I1M,
[Interval.INTERVAL_I5M]: PennantInterval.I5M,
[Interval.INTERVAL_I15M]: PennantInterval.I15M,
[Interval.INTERVAL_I30M]: PennantInterval.I30M,
[Interval.INTERVAL_I1H]: PennantInterval.I1H,
[Interval.INTERVAL_I4H]: PennantInterval.I4H,
[Interval.INTERVAL_I6H]: PennantInterval.I6H,
[Interval.INTERVAL_I8H]: PennantInterval.I8H,
[Interval.INTERVAL_I12H]: PennantInterval.I12H,
[Interval.INTERVAL_I1D]: PennantInterval.I1D,
[Interval.INTERVAL_I7D]: PennantInterval.I7D,
} as const;
+45
View File
@@ -34,18 +34,28 @@ const INTERVAL_TO_PENNANT_MAP = {
[PennantInterval.I1M]: Schema.Interval.INTERVAL_I1M,
[PennantInterval.I5M]: Schema.Interval.INTERVAL_I5M,
[PennantInterval.I15M]: Schema.Interval.INTERVAL_I15M,
[PennantInterval.I30M]: Schema.Interval.INTERVAL_I30M,
[PennantInterval.I1H]: Schema.Interval.INTERVAL_I1H,
[PennantInterval.I4H]: Schema.Interval.INTERVAL_I4H,
[PennantInterval.I6H]: Schema.Interval.INTERVAL_I6H,
[PennantInterval.I8H]: Schema.Interval.INTERVAL_I8H,
[PennantInterval.I12H]: Schema.Interval.INTERVAL_I12H,
[PennantInterval.I1D]: Schema.Interval.INTERVAL_I1D,
[PennantInterval.I7D]: Schema.Interval.INTERVAL_I7D,
};
const defaultConfig = {
decimalPlaces: 5,
supportedIntervals: [
PennantInterval.I7D,
PennantInterval.I1D,
PennantInterval.I12H,
PennantInterval.I8H,
PennantInterval.I6H,
PennantInterval.I4H,
PennantInterval.I1H,
PennantInterval.I15M,
PennantInterval.I30M,
PennantInterval.I5M,
PennantInterval.I1M,
],
@@ -137,10 +147,15 @@ export class VegaDataSource implements DataSource {
decimalPlaces: this._decimalPlaces,
positionDecimalPlaces: this._positionDecimalPlaces,
supportedIntervals: [
PennantInterval.I7D,
PennantInterval.I1D,
PennantInterval.I12H,
PennantInterval.I8H,
PennantInterval.I6H,
PennantInterval.I4H,
PennantInterval.I1H,
PennantInterval.I15M,
PennantInterval.I30M,
PennantInterval.I5M,
PennantInterval.I1M,
],
@@ -255,6 +270,10 @@ const getDuration = (
multiplier: number
): Duration => {
switch (interval) {
case 'I7D':
return {
days: 7 * multiplier,
};
case 'I1D':
return {
days: 1 * multiplier,
@@ -271,14 +290,30 @@ const getDuration = (
return {
minutes: 5 * multiplier,
};
case 'I4H':
return {
hours: 4 * multiplier,
};
case 'I6H':
return {
hours: 6 * multiplier,
};
case 'I8H':
return {
hours: 8 * multiplier,
};
case 'I12H':
return {
hours: 12 * multiplier,
};
case 'I15M':
return {
minutes: 15 * multiplier,
};
case 'I30M':
return {
minutes: 30 * multiplier,
};
}
};
@@ -288,14 +323,24 @@ const getDifference = (
dateRight: Date
): number => {
switch (interval) {
case 'I7D':
return differenceInDays(dateRight, dateLeft) / 7;
case 'I1D':
return differenceInDays(dateRight, dateLeft);
case 'I12H':
return differenceInHours(dateRight, dateLeft) / 12;
case 'I8H':
return differenceInHours(dateRight, dateLeft) / 8;
case 'I6H':
return differenceInHours(dateRight, dateLeft) / 6;
case 'I4H':
return differenceInHours(dateRight, dateLeft) / 4;
case 'I1H':
return differenceInHours(dateRight, dateLeft);
case 'I15M':
return differenceInMinutes(dateRight, dateLeft) / 15;
case 'I30M':
return differenceInMinutes(dateRight, dateLeft) / 30;
case 'I5M':
return differenceInMinutes(dateRight, dateLeft) / 5;
case 'I1M':
@@ -12,7 +12,7 @@ declare global {
}
}
const chainId = 'test-id';
const chainId = 'vega-stagnet1-202307191148';
export function addMockChainId() {
Cypress.Commands.add('mockChainId', () => {
@@ -37,10 +37,12 @@ export function addVegaWalletConnect() {
Cypress.Commands.add('connectVegaWallet', (isMobile) => {
mockConnectWallet();
cy.highlight(`Connecting Vega Wallet`);
cy.get('[data-testid="splash-loader"]', { timeout: 20000 }).should(
'not.exist'
);
const connectVegaWalletButton = `[data-testid=connect-vega-wallet${
isMobile ? '-mobile' : ''
}]:visible`;
cy.get(connectVegaWalletButton).then((btn) => {
if (btn.length === 0) {
cy.log('could not find the button, perhaps already connected');
+3 -2
View File
@@ -13,7 +13,8 @@ declare global {
const hasMethod = (req: CyHttpMessages.IncomingHttpRequest, method: string) => {
const { body } = req;
return 'method' in body && body.method === method;
const b = JSON.parse(body);
return 'method' in b && b.method === method;
};
export function addMockWalletCommand() {
@@ -72,7 +73,7 @@ export const aliasWalletConnectQuery = (
body: {
jsonrpc: '2.0',
result: {
chainID: 'test-id',
chainID: 'vega-fairground-202305051805',
},
id: '1',
},
@@ -73,7 +73,7 @@ export const DealTicketContainer = ({
market={market}
marketPrice={marketPrice}
marketData={marketData}
submit={(orderSubmission) => create({ orderSubmission })}
submit={(transaction) => create(transaction)}
/>
)}
</>
@@ -0,0 +1,172 @@
import { Controller, type Control } from 'react-hook-form';
import type { Market } from '@vegaprotocol/markets';
import type { OrderFormValues } from '../../hooks/use-form-values';
import { toDecimal, useValidateAmount } from '@vegaprotocol/utils';
import {
TradingFormGroup,
TradingInputError,
Tooltip,
FormGroup,
Input,
InputError,
Pill,
} from '@vegaprotocol/ui-toolkit';
import { useT } from '../../use-t';
export interface DealTicketPriceTakeProfitStopLossProps {
control: Control<OrderFormValues>;
market: Market;
takeProfitError?: string;
stopLossError?: string;
quoteName?: string;
}
export const DealTicketPriceTakeProfitStopLoss = ({
control,
market,
takeProfitError,
stopLossError,
quoteName,
}: DealTicketPriceTakeProfitStopLossProps) => {
const t = useT();
const validateAmount = useValidateAmount();
const priceStep = toDecimal(market?.decimalPlaces);
const renderTakeProfitError = () => {
if (takeProfitError) {
return (
<TradingInputError testId="deal-ticket-take-profit-error-message">
{takeProfitError}
</TradingInputError>
);
}
return null;
};
const renderStopLossError = () => {
if (stopLossError) {
return (
<TradingInputError testId="deal-stop-loss-error-message">
{stopLossError}
</TradingInputError>
);
}
return null;
};
return (
<div className="mb-2">
<div className="flex flex-col gap-2">
<div className="flex-1">
<TradingFormGroup
label={
<Tooltip
description={<div>{t('The price for take profit.')}</div>}
>
<span className="text-xs">{t('Take profit')}</span>
</Tooltip>
}
labelFor="input-order-take-profit"
className="!mb-1"
>
<Controller
name="takeProfit"
control={control}
rules={{
min: {
value: priceStep,
message: t(
'Take profit price cannot be lower than {{priceStep}}',
{
priceStep,
}
),
},
validate: validateAmount(priceStep, 'takeProfit'),
}}
render={({ field, fieldState }) => (
<div className="mb-2">
<FormGroup
labelFor="input-price-take-profit"
label={''}
compact
>
<Input
id="input-price-take-profit"
appendElement={<Pill size="xs">{quoteName}</Pill>}
className="w-full"
type="number"
step={priceStep}
data-testid="order-price-take-profit"
onWheel={(e) => e.currentTarget.blur()}
{...field}
/>
</FormGroup>
{fieldState.error && (
<InputError testId="deal-ticket-error-message-price-take-profit">
{fieldState.error.message}
</InputError>
)}
</div>
)}
/>
</TradingFormGroup>
</div>
<div className="flex-1">
<TradingFormGroup
label={
<Tooltip description={<div>{t('The price for stop loss.')}</div>}>
<span className="text-xs">{t('Stop loss')}</span>
</Tooltip>
}
labelFor="input-order-stop-loss"
className="!mb-1"
>
<Controller
name="stopLoss"
control={control}
rules={{
min: {
value: priceStep,
message: t('Price cannot be lower than {{priceStep}}', {
priceStep,
}),
},
validate: validateAmount(priceStep, 'stopLoss'),
}}
render={({ field, fieldState }) => (
<div className="mb-2">
<FormGroup
labelFor="input-price-stop-loss"
label={''}
compact
>
<Input
id="input-price-stop-loss"
appendElement={<Pill size="xs">{quoteName}</Pill>}
className="w-full"
type="number"
step={priceStep}
data-testid="order-price-stop-loss"
onWheel={(e) => e.currentTarget.blur()}
{...field}
/>
</FormGroup>
{fieldState.error && (
<InputError testId="deal-ticket-error-message-price-stop-loss">
{fieldState.error.message}
</InputError>
)}
</div>
)}
/>
</TradingFormGroup>
</div>
</div>
{renderTakeProfitError()}
{renderStopLossError()}
</div>
);
};
@@ -1003,7 +1003,7 @@ export const StopOrder = ({ market, marketPrice, submit }: StopOrderProps) => {
name="oco"
label={
<Tooltip
description={<span>{t('One cancels another')}</span>}
description={<span>{t('One cancels the other')}</span>}
>
<>{t('OCO')}</>
</Tooltip>
@@ -8,9 +8,12 @@ import { ExpirySelector } from './expiry-selector';
import { SideSelector } from './side-selector';
import { TimeInForceSelector } from './time-in-force-selector';
import { TypeSelector } from './type-selector';
import { type OrderSubmission } from '@vegaprotocol/wallet';
import { useVegaWallet } from '@vegaprotocol/wallet-react';
import { mapFormValuesToOrderSubmission } from '../../utils/map-form-values-to-submission';
import { type Transaction } from '@vegaprotocol/wallet';
import {
mapFormValuesToOrderSubmission,
mapFormValuesToTakeProfitAndStopLoss,
} from '../../utils/map-form-values-to-submission';
import {
TradingInput as Input,
TradingCheckbox as Checkbox,
@@ -77,6 +80,8 @@ import { isNonPersistentOrder } from '../../utils/time-in-force-persistence';
import { KeyValue } from './key-value';
import { DocsLinks } from '@vegaprotocol/environment';
import { useT } from '../../use-t';
import { DealTicketPriceTakeProfitStopLoss } from './deal-ticket-price-tp-sl';
import uniqueId from 'lodash/uniqueId';
export const REDUCE_ONLY_TOOLTIP =
'"Reduce only" will ensure that this order will not increase the size of an open position. When the order is matched, it will only trade enough volume to bring your open volume towards 0 but never change the direction of your position. If applied to a limit order that is not instantly filled, the order will be stopped.';
@@ -86,7 +91,7 @@ export interface DealTicketProps {
marketData: StaticMarketData;
marketPrice?: string | null;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
submit: (order: OrderSubmission) => void;
submit: (order: Transaction) => void;
onDeposit: (assetId: string) => void;
}
@@ -184,6 +189,7 @@ export const DealTicket = ({
const rawSize = watch('size');
const rawPrice = watch('price');
const iceberg = watch('iceberg');
const tpSl = watch('tpSl');
const peakSize = watch('peakSize');
const expiresAt = watch('expiresAt');
const postOnly = watch('postOnly');
@@ -382,17 +388,28 @@ export const DealTicket = ({
if (lastSubmitTime.current && now - lastSubmitTime.current < 1000) {
return;
}
submit(
mapFormValuesToOrderSubmission(
if (formValues.tpSl) {
const reference = `${pubKey}-${now}-${uniqueId()}`;
const batchMarketInstructions = mapFormValuesToTakeProfitAndStopLoss(
formValues,
market,
reference
);
submit({
batchMarketInstructions,
});
} else {
const orderSubmission = mapFormValuesToOrderSubmission(
formValues,
market.id,
market.decimalPlaces,
market.positionDecimalPlaces
)
);
);
submit({ orderSubmission });
}
lastSubmitTime.current = now;
},
[submit, market.decimalPlaces, market.positionDecimalPlaces, market.id]
[market, pubKey, submit]
);
useController({
name: 'type',
@@ -674,40 +691,63 @@ export const DealTicket = ({
)}
/>
</div>
{isLimitType && (
{
<>
<div className="flex justify-between gap-2 pb-2">
{isLimitType && (
<Controller
name="iceberg"
control={control}
render={({ field }) => (
<Tooltip
description={
<p>
{t(
'ICEBERG_TOOLTIP',
'Trade only a fraction of the order size at once. After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. Note that the full volume of the order is not hidden and is still reflected in the order book.'
)}{' '}
<ExternalLink href={DocsLinks?.ICEBERG_ORDERS}>
{t('Find out more')}
</ExternalLink>{' '}
</p>
}
>
<div>
<Checkbox
name="iceberg"
checked={field.value}
onCheckedChange={field.onChange}
disabled={disableIcebergCheckbox}
label={t('Iceberg')}
/>
</div>
</Tooltip>
)}
/>
)}
<Controller
name="iceberg"
name="tpSl"
control={control}
render={({ field }) => (
<Tooltip
description={
<p>
{t(
'ICEBERG_TOOLTIP',
'Trade only a fraction of the order size at once. After the peak size of the order has traded, the size is reset. This is repeated until the order is cancelled, expires, or its full volume trades away. For example, an iceberg order with a size of 1000 and a peak size of 100 will effectively be split into 10 orders with a size of 100 each. Note that the full volume of the order is not hidden and is still reflected in the order book.'
)}{' '}
<ExternalLink href={DocsLinks?.ICEBERG_ORDERS}>
{t('Find out more')}
</ExternalLink>{' '}
</p>
<p>{t('TP_SL_TOOLTIP', 'Take profit / Stop loss')}</p>
}
>
<div>
<Checkbox
name="iceberg"
name="tpSl"
checked={field.value}
onCheckedChange={field.onChange}
disabled={disableIcebergCheckbox}
label={t('Iceberg')}
disabled={false}
label={t('TP / SL')}
/>
</div>
</Tooltip>
)}
/>
</div>
{iceberg && (
{isLimitType && iceberg && (
<DealTicketSizeIceberg
market={market}
peakSizeError={errors.peakSize?.message}
@@ -717,8 +757,18 @@ export const DealTicket = ({
peakSize={peakSize}
/>
)}
{tpSl && (
<DealTicketPriceTakeProfitStopLoss
market={market}
takeProfitError={errors.takeProfit?.message}
stopLossError={errors.stopLoss?.message}
control={control}
quoteName={quoteName}
/>
)}
</>
)}
}
<SummaryMessage
error={summaryError}
asset={asset}
@@ -53,6 +53,9 @@ export type OrderFormValues = {
iceberg?: boolean;
peakSize?: string;
minimumVisibleSize?: string;
tpSl?: boolean;
takeProfit?: string;
stopLoss?: string;
};
type UpdateOrder = (marketId: string, values: Partial<OrderFormValues>) => void;
@@ -10,13 +10,16 @@ import type {
import * as Schema from '@vegaprotocol/types';
import { removeDecimal, toNanoSeconds } from '@vegaprotocol/utils';
import { isPersistentOrder } from './time-in-force-persistence';
import { type MarketFieldsFragment } from '@vegaprotocol/markets';
export const mapFormValuesToOrderSubmission = (
order: OrderFormValues,
marketId: string,
decimalPlaces: number,
positionDecimalPlaces: number
positionDecimalPlaces: number,
reference?: string
): OrderSubmission => ({
reference,
marketId: marketId,
type: order.type,
side: order.side,
@@ -81,7 +84,8 @@ export const mapFormValuesToStopOrdersSubmission = (
data: StopOrderFormValues,
marketId: string,
decimalPlaces: number,
positionDecimalPlaces: number
positionDecimalPlaces: number,
reference?: string
): StopOrdersSubmission => {
const submission: StopOrdersSubmission = {};
const stopOrderSetup: StopOrderSetup = {
@@ -96,7 +100,8 @@ export const mapFormValuesToStopOrdersSubmission = (
},
marketId,
decimalPlaces,
positionDecimalPlaces
positionDecimalPlaces,
reference
),
};
setTrigger(
@@ -120,7 +125,8 @@ export const mapFormValuesToStopOrdersSubmission = (
},
marketId,
decimalPlaces,
positionDecimalPlaces
positionDecimalPlaces,
reference
),
};
setTrigger(
@@ -159,3 +165,125 @@ export const mapFormValuesToStopOrdersSubmission = (
return submission;
};
export const mapFormValuesToTakeProfitAndStopLoss = (
formValues: OrderFormValues,
market: MarketFieldsFragment,
reference: string
) => {
const orderSubmission = mapFormValuesToOrderSubmission(
formValues,
market.id,
market.decimalPlaces,
market.positionDecimalPlaces,
reference
);
const oppositeSide =
formValues.side === Schema.Side.SIDE_BUY
? Schema.Side.SIDE_SELL
: Schema.Side.SIDE_BUY;
// For direction it needs to be implied
// If position is LONG (BUY)
// TP is SHORT and trigger is RISES ABOVE
// If position is SHORT
// TP is LONG and trigger is FALLS BELOW
const takeProfitTriggerDirection =
formValues.side === Schema.Side.SIDE_BUY
? Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE
: Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW;
// For direction it needs to be implied
// If position is LONG (BUY)
// SL is SHORT and trigger is FALLS BELOW
// If position is SHORT
// SL is LONG and trigger is RISES ABOVE
const stopLossTriggerDirection =
formValues.side === Schema.Side.SIDE_BUY
? Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_FALLS_BELOW
: Schema.StopOrderTriggerDirection.TRIGGER_DIRECTION_RISES_ABOVE;
const stopOrdersSubmission = [];
// if there are both take profit and stop loss then the stop order needs to be OCO
if (formValues.takeProfit && formValues.stopLoss) {
const ocoStopOrderSubmission = mapFormValuesToStopOrdersSubmission(
{
...formValues,
triggerPrice: formValues.stopLoss,
ocoTriggerPrice: formValues.takeProfit,
price: formValues.stopLoss,
triggerDirection: stopLossTriggerDirection,
triggerType: 'price',
side: oppositeSide,
expire: false,
type: Schema.OrderType.TYPE_MARKET,
oco: true,
ocoPrice: formValues.takeProfit,
ocoTriggerType: 'price',
ocoType: Schema.OrderType.TYPE_MARKET,
ocoSize: formValues.size,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
ocoTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
},
market.id,
market.decimalPlaces,
market.positionDecimalPlaces,
reference
);
stopOrdersSubmission.push(ocoStopOrderSubmission);
} else if (formValues.takeProfit) {
const takeProfitStopOrderSubmission = mapFormValuesToStopOrdersSubmission(
{
...formValues,
price: formValues.takeProfit,
triggerDirection: takeProfitTriggerDirection,
triggerType: 'price',
triggerPrice: formValues.takeProfit,
side: oppositeSide,
expire: false,
ocoTriggerType: 'price',
type: Schema.OrderType.TYPE_MARKET,
oco: false,
ocoType: Schema.OrderType.TYPE_MARKET,
ocoSize: formValues.size,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
ocoTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
},
market.id,
market.decimalPlaces,
market.positionDecimalPlaces,
reference
);
stopOrdersSubmission.push(takeProfitStopOrderSubmission);
} else if (formValues.stopLoss) {
const stopLossStopOrderSubmission = mapFormValuesToStopOrdersSubmission(
{
...formValues,
triggerPrice: formValues.stopLoss,
price: formValues.stopLoss,
triggerDirection: stopLossTriggerDirection,
triggerType: 'price',
side: oppositeSide,
expire: false,
type: Schema.OrderType.TYPE_MARKET,
oco: false,
ocoTriggerType: 'price',
ocoType: Schema.OrderType.TYPE_MARKET,
ocoSize: formValues.size,
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
ocoTimeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
},
market.id,
market.decimalPlaces,
market.positionDecimalPlaces,
reference
);
stopOrdersSubmission.push(stopLossStopOrderSubmission);
}
const batchMarketInstructions = {
submissions: [orderSubmission],
stopOrdersSubmission,
};
return batchMarketInstructions;
};
@@ -1,8 +1,15 @@
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
import { mapFormValuesToOrderSubmission } from './map-form-values-to-submission';
import type {
OrderSubmissionBody,
StopOrdersSubmission,
} from '@vegaprotocol/wallet';
import {
mapFormValuesToOrderSubmission,
mapFormValuesToTakeProfitAndStopLoss,
} from './map-form-values-to-submission';
import * as Schema from '@vegaprotocol/types';
import { OrderTimeInForce, OrderType } from '@vegaprotocol/types';
import type { OrderFormValues } from '../hooks';
import { type MarketFieldsFragment } from '@vegaprotocol/markets';
describe('mapFormValuesToOrderSubmission', () => {
it('sets and formats price only for limit orders', () => {
@@ -186,3 +193,232 @@ describe('mapFormValuesToOrderSubmission', () => {
}
);
});
const mockMarket: MarketFieldsFragment = {
__typename: 'Market',
id: 'marketId',
decimalPlaces: 1,
positionDecimalPlaces: 4,
state: Schema.MarketState.STATE_ACTIVE,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
} as MarketFieldsFragment;
const orderFormValues: OrderFormValues = {
type: OrderType.TYPE_LIMIT,
side: Schema.Side.SIDE_BUY,
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '1',
price: '66300',
postOnly: false,
reduceOnly: false,
tpSl: true,
takeProfit: '70000',
stopLoss: '60000',
};
describe('mapFormValuesToTakeProfitAndStopLoss', () => {
it('creates batch market instructions for a normal order created with TP and SL', () => {
const result = mapFormValuesToTakeProfitAndStopLoss(
orderFormValues,
mockMarket,
'reference'
);
const expected: {
submissions: Schema.OrderSubmission[];
stopOrdersSubmission: StopOrdersSubmission[];
} = {
stopOrdersSubmission: [
{
fallsBelow: {
orderSubmission: {
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: undefined,
reduceOnly: true,
reference: 'reference',
side: Schema.Side.SIDE_SELL,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_MARKET,
},
price: '600000',
},
risesAbove: {
orderSubmission: {
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: undefined,
reduceOnly: true,
reference: 'reference',
side: Schema.Side.SIDE_SELL,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_MARKET,
},
price: '700000',
},
},
],
submissions: [
{
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: '663000',
reduceOnly: false,
reference: 'reference',
side: Schema.Side.SIDE_BUY,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
type: OrderType.TYPE_LIMIT,
},
],
};
expect(result).toEqual(expected);
});
it('creates batch market instructions for a normal order created without TP and SL', () => {
// Create order form values without TP and SL
const orderFormValuesWithoutTPSL = { ...orderFormValues };
delete orderFormValuesWithoutTPSL.takeProfit;
delete orderFormValuesWithoutTPSL.stopLoss;
const result = mapFormValuesToTakeProfitAndStopLoss(
orderFormValuesWithoutTPSL,
mockMarket,
'reference'
);
// Expected result when TP and SL are not provided
const expected: {
submissions: Schema.OrderSubmission[];
stopOrdersSubmission: StopOrdersSubmission[];
} = {
stopOrdersSubmission: [],
submissions: [
{
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: '663000',
reduceOnly: false,
reference: 'reference',
side: Schema.Side.SIDE_BUY,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
type: OrderType.TYPE_LIMIT,
},
],
};
expect(result).toEqual(expected);
});
it('creates batch market instructions for a normal order created with TP only', () => {
// Create order form values with TP only
const orderFormValuesWithTP = { ...orderFormValues };
orderFormValuesWithTP.stopLoss = undefined;
const result = mapFormValuesToTakeProfitAndStopLoss(
orderFormValuesWithTP,
mockMarket,
'reference'
);
// Expected result when only TP is provided
const expected: {
submissions: Schema.OrderSubmission[];
stopOrdersSubmission: StopOrdersSubmission[];
} = {
stopOrdersSubmission: [
{
risesAbove: {
orderSubmission: {
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: undefined,
reduceOnly: true,
reference: 'reference',
side: Schema.Side.SIDE_SELL,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_MARKET,
},
price: '700000',
},
},
],
submissions: [
{
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: '663000',
reduceOnly: false,
reference: 'reference',
side: Schema.Side.SIDE_BUY,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
type: OrderType.TYPE_LIMIT,
},
],
};
expect(result).toEqual(expected);
});
it('creates batch market instructions for a normal order created with SL only', () => {
// Create order form values with SL only
const orderFormValuesWithSL = { ...orderFormValues };
orderFormValuesWithSL.takeProfit = undefined;
const result = mapFormValuesToTakeProfitAndStopLoss(
orderFormValuesWithSL,
mockMarket,
'reference'
);
// Expected result when only SL is provided
const expected: {
submissions: Schema.OrderSubmission[];
stopOrdersSubmission: StopOrdersSubmission[];
} = {
stopOrdersSubmission: [
{
fallsBelow: {
orderSubmission: {
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: undefined,
reduceOnly: true,
reference: 'reference',
side: Schema.Side.SIDE_SELL,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
type: OrderType.TYPE_MARKET,
},
price: '600000',
},
},
],
submissions: [
{
expiresAt: undefined,
marketId: 'marketId',
postOnly: false,
price: '663000',
reduceOnly: false,
reference: 'reference',
side: Schema.Side.SIDE_BUY,
size: '10000',
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
type: OrderType.TYPE_LIMIT,
},
],
};
expect(result).toEqual(expected);
});
});
+37 -15
View File
@@ -1,5 +1,9 @@
import type { Asset } from '@vegaprotocol/assets';
import { EtherscanLink } from '@vegaprotocol/environment';
import { USDT_ID, type Asset } from '@vegaprotocol/assets';
import {
EtherscanLink,
Networks,
useEnvironment,
} from '@vegaprotocol/environment';
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
import {
formatNumber,
@@ -15,7 +19,7 @@ import { useT } from './use-t';
interface ApproveNotificationProps {
isActive: boolean;
selectedAsset?: Asset;
onApprove: () => void;
onApprove: (amount?: string) => void;
approved: boolean;
balances: DepositBalances | null;
amount: string;
@@ -33,6 +37,7 @@ export const ApproveNotification = ({
approveTxId,
intent = Intent.Warning,
}: ApproveNotificationProps) => {
const { VEGA_ENV } = useEnvironment();
const t = useT();
const tx = useEthTransactionStore((state) => {
return state.transactions.find((t) => t?.id === approveTxId);
@@ -64,28 +69,45 @@ export const ApproveNotification = ({
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: onApprove,
action: () => onApprove(),
dataTestId: 'approve-submit',
}}
/>
</div>
);
let message = t('Approve again to deposit more than {{allowance}}', {
allowance: formatNumber(balances.allowance.toString()),
});
const buttonProps = {
size: 'small' as const,
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: () => onApprove(),
dataTestId: 'reapprove-submit',
};
if (VEGA_ENV === Networks.MAINNET && selectedAsset.id === USDT_ID[VEGA_ENV]) {
message = t(
'USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.',
{
allowance: formatNumber(balances.allowance.toString()),
}
);
buttonProps.text = t('Revoke {{assetSymbol}} approval', {
assetSymbol: selectedAsset?.symbol,
});
buttonProps.action = () => onApprove('0');
}
const reApprovePrompt = (
<div className="mb-4">
<Notification
intent={intent}
testId="reapprove-default"
message={t('Approve again to deposit more than {{allowance}}', {
allowance: formatNumber(balances.allowance.toString()),
})}
buttonProps={{
size: 'small',
text: t('Approve {{assetSymbol}}', {
assetSymbol: selectedAsset?.symbol,
}),
action: onApprove,
dataTestId: 'reapprove-submit',
}}
message={message}
buttonProps={buttonProps}
/>
</div>
);
+3 -3
View File
@@ -58,7 +58,7 @@ export interface DepositFormProps {
onSelectAsset: (assetId: string) => void;
handleAmountChange: (amount: string) => void;
onDisconnect: () => void;
submitApprove: () => void;
submitApprove: (amount?: string) => void;
approveTxId: number | null;
submitFaucet: () => void;
faucetTxId: number | null;
@@ -423,8 +423,8 @@ export const DepositForm = ({
isActive={isActive}
approveTxId={approveTxId}
selectedAsset={selectedAsset}
onApprove={() => {
submitApprove();
onApprove={(amount) => {
submitApprove(amount);
setApproveNotificationIntent(Intent.Warning);
}}
balances={balances}
+2 -2
View File
@@ -35,11 +35,11 @@ export const useSubmitApproval = (
reset: () => {
setId(null);
},
perform: () => {
perform: (amount?: string) => {
if (!asset || !config) return;
const id = createEthTransaction(contract, 'approve', [
config?.collateral_bridge_contract.address,
MaxUint256.toString(),
amount ? amount : MaxUint256.toString(),
]);
setId(id);
},
@@ -1,16 +0,0 @@
import { Splash } from '@vegaprotocol/ui-toolkit';
export const AppFailure = ({
title,
error,
}: {
title: string;
error?: string | null;
}) => {
return (
<Splash>
<p className="mb-4 text-xl">{title}</p>
{error && <p className="mb-8 text-sm">{error}</p>}
</Splash>
);
};
@@ -1 +0,0 @@
export * from './app-failure';
-1
View File
@@ -1,4 +1,3 @@
export * from './app-failure';
export * from './app-loader';
export * from './network-loader';
export * from './network-switcher';
@@ -40,6 +40,16 @@ const mockStatsQuery = (
vegaTime: new Date().toISOString(),
chainId: 'test-chain-id',
},
networkParametersConnection: {
edges: [
{
node: {
key: 'a',
value: '1',
},
},
],
},
},
},
});
@@ -335,6 +345,16 @@ describe('RowData', () => {
vegaTime: new Date().toISOString(),
chainId: 'test-chain-id',
},
networkParametersConnection: {
edges: [
{
node: {
key: 'a',
value: '1',
},
},
],
},
},
},
};
@@ -1,4 +1,9 @@
import { TradingRadio } from '@vegaprotocol/ui-toolkit';
import {
CopyWithTooltip,
TradingRadio,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { useEffect, useState } from 'react';
import { CUSTOM_NODE_KEY } from '../../types';
import {
@@ -127,8 +132,17 @@ export const RowData = ({
return (
<>
{id !== CUSTOM_NODE_KEY && (
<div className="break-all" data-testid="node">
<div className="break-all flex gap-2" data-testid="node">
<TradingRadio id={`node-url-${id}`} value={url} label={url} />
{url.length > 0 && url !== 'custom' && (
<span className="text-muted">
<CopyWithTooltip text={url}>
<button>
<VegaIcon name={VegaIconNames.COPY} />
</button>
</CopyWithTooltip>
</span>
)}
</div>
)}
<LayoutCell
@@ -39,6 +39,19 @@ export const getMockStatisticsResult = (
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
networkParametersConnection: {
__typename: 'NetworkParametersConnection',
edges: [
{
__typename: 'NetworkParameterEdge',
node: {
__typename: 'NetworkParameter',
key: 'a',
value: '1',
},
},
],
},
});
export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({
@@ -48,6 +61,19 @@ export const getMockQueryResult = (env: Networks): NodeCheckQuery => ({
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
networkParametersConnection: {
__typename: 'NetworkParametersConnection',
edges: [
{
__typename: 'NetworkParameterEdge',
node: {
__typename: 'NetworkParameter',
key: 'a',
value: '1',
},
},
],
},
});
function getHandler<T>(
@@ -34,6 +34,16 @@ const createDefaultMockClient = () => {
blockHeight: '100',
vegaTime: new Date().toISOString(),
},
networkParametersConnection: {
edges: [
{
node: {
key: 'something',
value: 123,
},
},
],
},
},
}),
subscribe: () => ({
@@ -183,6 +193,16 @@ describe('useEnvironment', () => {
blockHeight: '100',
vegaTime: new Date(1).toISOString(),
},
networkParametersConnection: {
edges: [
{
node: {
key: 'something',
value: 123,
},
},
],
},
},
});
}, wait);
@@ -244,6 +264,16 @@ describe('useEnvironment', () => {
blockHeight: '100',
vegaTime: new Date().toISOString(),
},
networkParametersConnection: {
edges: [
{
node: {
key: 'something',
value: 123,
},
},
],
},
},
}),
subscribe: () => ({
+72 -54
View File
@@ -1,5 +1,5 @@
import { parse as tomlParse } from 'toml';
import { isValidUrl, LocalStorage } from '@vegaprotocol/utils';
import { LocalStorage, isValidUrl } from '@vegaprotocol/utils';
import { useEffect } from 'react';
import { create } from 'zustand';
import { createClient } from '@vegaprotocol/apollo-client';
@@ -22,6 +22,7 @@ import uniq from 'lodash/uniq';
import orderBy from 'lodash/orderBy';
import first from 'lodash/first';
import { canMeasureResponseTime, measureResponseTime } from '../utils/time';
import compact from 'lodash/compact';
type Client = ReturnType<typeof createClient>;
type ClientCollection = {
@@ -70,10 +71,19 @@ const fetchConfig = async (url?: string) => {
};
/**
* Find a suitable node by running a test query and test
* Find a suitable nodes by running a test query and test
* subscription, against a list of clients, first to resolve wins
*/
const findNode = async (clients: ClientCollection): Promise<string | null> => {
const findHealthyNodes = async (nodes: string[]) => {
const clients: ClientCollection = {};
nodes.forEach((url) => {
clients[url] = createClient({
url,
cacheConfig: undefined,
retry: false,
connectToDevTools: false,
});
});
const tests = Object.entries(clients).map((args) => testNode(...args));
try {
const nodes = await Promise.all(tests);
@@ -93,11 +103,10 @@ const findNode = async (clients: ClientCollection): Promise<string | null> => {
['desc', 'desc', 'asc']
);
const best = first(ordered);
return best ? best.url : null;
return ordered;
} catch (err) {
// All tests rejected, no suitable node found
return null;
return [];
}
};
@@ -142,6 +151,15 @@ const testQuery = (
})
.then((result) => {
if (result && !result.error) {
const netParams = compact(
result.data.networkParametersConnection.edges?.map((n) => n?.node)
);
if (netParams.length === 0) {
// any node that doesn't return the network parameters is considered
// failed
resolve(false);
return;
}
const res = {
blockHeight: Number(result.data.statistics.blockHeight),
vegaTime: new Date(result.data.statistics.vegaTime),
@@ -600,32 +618,34 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
}
const state = get();
const storedUrl = LocalStorage.getItem(STORAGE_KEY);
let storedUrl = LocalStorage.getItem(STORAGE_KEY);
if (!isValidUrl(storedUrl)) {
// remove invalid data from local storage
LocalStorage.removeItem(STORAGE_KEY);
storedUrl = null;
}
let nodes: string[] | undefined;
try {
nodes = await fetchConfig(state.VEGA_CONFIG_URL);
const enrichedNodes = uniq(
[...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[]
nodes = uniq(
compact([
// url from local storage
storedUrl,
// url from state (if set via env var)
state.VEGA_URL,
// urls from network configuration
...(await fetchConfig(state.VEGA_CONFIG_URL)),
])
);
set({ nodes: enrichedNodes });
set({ nodes });
} catch (err) {
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
}
// Node url found in localStorage, if its valid attempt to connect
if (storedUrl) {
if (isValidUrl(storedUrl)) {
set({ VEGA_URL: storedUrl, status: 'success' });
return;
} else {
LocalStorage.removeItem(STORAGE_KEY);
}
}
// VEGA_URL env var is set and is a valid url no need to proceed
if (state.VEGA_URL) {
set({ status: 'success' });
// skip picking up the best node if VEGA_URL env variable is set
if (state.VEGA_URL && isValidUrl(state.VEGA_URL)) {
state.setUrl(state.VEGA_URL);
return;
}
@@ -639,37 +659,35 @@ export const useEnvironment = create<EnvStore>()((set, get) => ({
return;
}
// Create a map of node urls to client instances
const clients: ClientCollection = {};
nodes.forEach((url) => {
clients[url] = createClient({
url,
cacheConfig: undefined,
retry: false,
connectToDevTools: false,
});
const healthyNodes = await findHealthyNodes(nodes);
// A requested node is a node to which the app was previously connected
// or the one set via env variable.
const requestedNodeUrl = storedUrl || state.VEGA_URL;
const bestNode = first(healthyNodes);
const requestedNode = healthyNodes.find(
(n) => requestedNodeUrl && n.url === requestedNodeUrl
);
if (!requestedNode) {
// remove unhealthy node url from local storage
LocalStorage.removeItem(STORAGE_KEY);
}
// A node's url (VEGA_URL) is either the requested node (previously
// connected or taken form env variable) or the currently best available
// node.
const url = requestedNode?.url || bestNode?.url;
if (url != null) {
state.setUrl(url);
return;
}
set({
status: 'failed',
error: 'No suitable node found',
});
// Find a suitable node to connect to by attempting a query and a
// subscription, first to fulfill both will be the resulting url.
const url = await findNode(clients);
if (url !== null) {
set({
status: 'success',
VEGA_URL: url,
});
LocalStorage.setItem(STORAGE_KEY, url);
}
// Every node failed either to make a query or retrieve data from
// a subscription
else {
set({
status: 'failed',
error: 'No node found',
});
console.warn('No suitable vega node was found');
}
console.warn('No suitable node was found');
},
}));
+1
View File
@@ -134,6 +134,7 @@ export const CONSOLE_TRANSFER = '#/portfolio/assets/transfer';
export const CONSOLE_TRANSFER_ASSET =
'#/portfolio/assets/transfer?assetId=:assetId';
export const CONSOLE_MARKET_PAGE = '#/markets/:marketId';
export const CONSOLE_REWARDS_PAGE = '#/rewards';
// Governance pages
export const TOKEN_NEW_MARKET_PROPOSAL = '/proposals/propose/new-market';
@@ -28,6 +28,16 @@ const createStatsMock = (
blockHeight: blockHeight.toString(),
vegaTime: '12345',
},
networkParametersConnection: {
edges: [
{
node: {
key: 'a',
value: '1',
},
},
],
},
},
},
};
@@ -1,9 +1,19 @@
query NodeCheck {
# statistics needed to get the most recent node in terms of block height
statistics {
chainId
blockHeight
vegaTime
}
# net params needed to filter out the nodes that are not suitable
networkParametersConnection {
edges {
node {
key
value
}
}
}
}
subscription NodeCheckTimeUpdate {
+9 -1
View File
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
export type NodeCheckQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any } };
export type NodeCheckQuery = { __typename?: 'Query', statistics: { __typename?: 'Statistics', chainId: string, blockHeight: string, vegaTime: any }, networkParametersConnection: { __typename?: 'NetworkParametersConnection', edges?: Array<{ __typename?: 'NetworkParameterEdge', node: { __typename?: 'NetworkParameter', key: string, value: string } } | null> | null } };
export type NodeCheckTimeUpdateSubscriptionVariables = Types.Exact<{ [key: string]: never; }>;
@@ -21,6 +21,14 @@ export const NodeCheckDocument = gql`
blockHeight
vegaTime
}
networkParametersConnection {
edges {
node {
key
value
}
}
}
}
`;
+13
View File
@@ -12,6 +12,19 @@ export const statisticsQuery = (
blockHeight: '11',
vegaTime: new Date().toISOString(),
},
networkParametersConnection: {
__typename: 'NetworkParametersConnection',
edges: [
{
__typename: 'NetworkParameterEdge',
node: {
__typename: 'NetworkParameter',
key: 'a',
value: '1',
},
},
],
},
};
return merge(defaultResult, override);
+7 -1
View File
@@ -66,7 +66,7 @@
"Notional": "Notional",
"NOTIONAL_SIZE_TOOLTIP_TEXT": "The notional size represents the position size in the settlement asset {{quoteName}} of the futures contract. This is calculated by multiplying the number of contracts by the prices of the contract. For example 10 contracts traded at a price of $50 has a notional size of $500.",
"OCO": "OCO",
"One cancels another": "One cancels another",
"One cancels the other": "One cancels the other",
"Only limit orders are permitted when market is in auction": "Only limit orders are permitted when market is in auction",
"Only your allocated margin will be used to fund this position, and if the maintenance margin is breached you will be closed out.": "Only your allocated margin will be used to fund this position, and if the maintenance margin is breached you will be closed out.",
"You have an existing position on this market.": "You have an existing position on this market.",
@@ -135,6 +135,12 @@
"Total margin available": "Total margin available",
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) + order margin balance ({{orderMarginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
"No trading": "No trading",
"TP / SL": "TP / SL",
"TP_SL_TOOLTIP": "Take profit / Stop loss",
"Take profit": "Take profit",
"Stop loss": "Stop loss",
"The price for take profit.": "The price for take profit.",
"The price for stop loss.": "The price for stop loss.",
"Trailing percent offset cannot be higher than 99.9": "Trailing percent offset cannot be higher than 99.9",
"Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}": "Trailing percent offset cannot be lower than {{trailingPercentOffsetStep}}",
"Trailing percentage offset": "Trailing percentage offset",
+2
View File
@@ -4,6 +4,7 @@
"Approval failed": "Approval failed",
"Approve {{assetSymbol}}": "Approve {{assetSymbol}}",
"Approve again to deposit more than {{allowance}}": "Approve again to deposit more than {{allowance}}",
"USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.": "USDT approved amount cannot be changed, only revoked. Revoke and reapprove to deposit more than {{allowance}}.",
"Asset": "Asset",
"Balance available": "Balance available",
"Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet": "Before you can make a deposit of your chosen asset, {{assetSymbol}}, you need to approve its use in your Ethereum wallet",
@@ -23,6 +24,7 @@
"Please select": "Please select",
"Please select an asset": "Please select an asset",
"Remaining deposit allowance": "Remaining deposit allowance",
"Revoke {{assetSymbol}} approval": "Revoke {{assetSymbol}} approval",
"Select from wallet": "Select from wallet",
"The {{symbol}} faucet is not available at this time": "The {{symbol}} faucet is not available at this time",
"The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.": "The deposit cap is set when you approve an asset for use with this app. To increase this cap, approve {{assetSymbol}} again and choose a higher cap. Check the documentation for your Ethereum wallet app for details.",
+2 -1
View File
@@ -971,5 +971,6 @@
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
"yourStake": "Your stake",
"yourVote": "Your vote",
"youVoted": "You voted"
"youVoted": "You voted",
"rewardsMovedNotification": "Trading and liquidity rewards have moved. Visit <0>Console</0> to view your rewards."
}
-5
View File
@@ -1,8 +1,6 @@
{
"{{liquidityPriceRange}} of mid price": "{{liquidityPriceRange}} of mid price",
"{{probability}} probability price bounds": "{{probability}} probability price bounds",
"24 hour change is unavailable at this time. The price change in the last 120 hours is:": "24 hour change is unavailable at this time. The price change in the last 120 hours is:",
"24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"A concept derived from traditional markets. It is a calculated value for the current market price on a market.": "A concept derived from traditional markets. It is a calculated value for the current market price on a market.",
"A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.": "A number that will be calculated by an appropriate stochastic risk model, dependent on the type of risk model used and its parameters.",
"A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.": "A sliding penalty for how much an LP bond is slashed if an LP fails to reach the minimum SLA. This is a network parameter.",
@@ -51,9 +49,6 @@
"Market": "Market",
"Market data": "Market data",
"Market governance": "Market governance",
"Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is:",
"Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}": "Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}",
"Market ID": "Market ID",
"Market price": "Market price",
"Market specification": "Market specification",
+21 -4
View File
@@ -86,6 +86,7 @@
"Docs": "Docs",
"Earn commission & stake rewards": "Earn commission & stake rewards",
"Earned by me": "Earned by me",
"Edit alias": "Edit alias",
"Eligible teams": "Eligible teams",
"Enactment date reached and usual auction exit checks pass": "Enactment date reached and usual auction exit checks pass",
"[empty]": "[empty]",
@@ -142,12 +143,17 @@
"Hoarder reward multiplier": "Hoarder reward multiplier",
"How it works": "How it works",
"I want a code": "I want a code",
"INTERVAL_I12H": "12H",
"INTERVAL_I15M": "15m",
"INTERVAL_I1D": "1D",
"INTERVAL_I1H": "1H",
"INTERVAL_I1D": "D",
"INTERVAL_I1H": "1h",
"INTERVAL_I1M": "1m",
"INTERVAL_I30M": "30m",
"INTERVAL_I4H": "4H",
"INTERVAL_I5M": "5m",
"INTERVAL_I6H": "6H",
"INTERVAL_I6H": "6h",
"INTERVAL_I8H": "8h",
"INTERVAL_I7D": "W",
"Improve vega console": "Improve vega console",
"Inactive": "Inactive",
"Index Price": "Index Price",
@@ -162,6 +168,7 @@
"Joined": "Joined",
"Joined at": "Joined at",
"Joined epoch": "Joined epoch",
"Key name": "Key name",
"gameCount_one": "Last game result",
"gameCount_other": "Last {{count}} game results",
"Learn about providing liquidity": "Learn about providing liquidity",
@@ -194,6 +201,7 @@
"My liquidity provision": "My liquidity provision",
"My trading fees": "My trading fees",
"Name": "Name",
"No alias": "No alias",
"No closed orders": "No closed orders",
"No data": "No data",
"No deposits": "No deposits",
@@ -227,6 +235,7 @@
"Not connected": "Not connected",
"Number of epochs after distribution to delay vesting of rewards by": "Number of epochs after distribution to delay vesting of rewards by",
"Number of traders": "Number of traders",
"On-change alias": "On-change alias",
"Open": "Open",
"Open a position": "Open a position",
"Open markets": "Open markets",
@@ -249,6 +258,7 @@
"Portfolio": "Portfolio",
"Positions": "Positions",
"Price": "Price",
"Profile updated": "Profile updated",
"Program ends:": "Program ends:",
"Propose a new market": "Propose a new market",
"Proposed final price is {{price}} {{assetSymbol}}.": "Proposed final price is {{price}} {{assetSymbol}}.",
@@ -289,6 +299,7 @@
"Search": "Search",
"See all markets": "See all markets",
"Select market": "Select market",
"Set party alias": "Set party alias",
"Settings": "Settings",
"Settlement asset": "Settlement asset",
"Settlement date": "Settlement date",
@@ -311,6 +322,7 @@
"Stop": "Stop",
"Stop orders": "Stop orders",
"Streak reward multiplier": "Streak reward multiplier",
"Submit": "Submit",
"Successor of a market": "Successor of a market",
"Successors to this market have been proposed": "Successors to this market have been proposed",
"Supplied stake": "Supplied stake",
@@ -371,6 +383,7 @@
"Staking rewards": "Staking rewards",
"Unknown": "Unknown",
"Unknown settlement date": "Unknown settlement date",
"Unnamed key": "Unnamed key",
"Update team": "Update team",
"URL": "URL",
"Use a comma separated list to allow only specific public keys to join the team": "Use a comma separated list to allow only specific public keys to join the team",
@@ -407,8 +420,10 @@
"You will no longer be able to hold a position on this market when it closes in {{duration}}.": "You will no longer be able to hold a position on this market when it closes in {{duration}}.",
"Your code has been rejected": "Your code has been rejected",
"Your identity is always anonymous on Vega": "Your identity is always anonymous on Vega",
"Your key's private name, can be changed in your wallet": "Your key's private name, can be changed in your wallet",
"Your referral code": "Your referral code",
"Your tier": "Your tier",
"Your public alias, stored on chain": "Your public alias, stored on chain",
"checkOutProposalsAndVote": "Check out the terms of the proposals and vote:",
"checkOutProposalsAndVote_one": "Check out the terms of the proposal and vote:",
"checkOutProposalsAndVote_other": "Check out the terms of the proposals and vote:",
@@ -470,5 +485,7 @@
"See all the live games on the cards below. Every on-chain game is community funded and designed. <0>Find out how to create one</0>.": "See all the live games on the cards below. Every on-chain game is community funded and designed. <0>Find out how to create one</0>.",
"Teams can earn rewards if they meet the goals set in the on-chain trading competitions. Track your earned rewards here, and see which teams are top of the leaderboard this month.": "Teams can earn rewards if they meet the goals set in the on-chain trading competitions. Track your earned rewards here, and see which teams are top of the leaderboard this month.",
"Currently no active games on the network.": "Currently no active games on the network.",
"Currently no active teams on the network.": "Currently no active teams on the network."
"Currently no active teams on the network.": "Currently no active teams on the network.",
"It looks like you're connection is slow, try switching to another node.": "It looks like you're connection is slow, try switching to another node.",
"It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node.": "It appears that the connection to the node <0>{{VEGA_URL}}</0> does not return necessary data, try switching to another node."
}
@@ -7,6 +7,7 @@
"Get MetaMask": "Get MetaMask",
"Get the Vega Wallet": "Get the Vega Wallet",
"I agree": "I agree",
"Once you have added the extension, <0>refresh</0> your browser.": "Once you have added the extension, <0>refresh</0> your browser.",
"Successfully connected": "Successfully connected",
"Transaction was not successful": "Transaction was not successful",
"Wallet rejected transaction": "Wallet rejected transaction"
@@ -2,16 +2,14 @@ import { type ReactNode } from 'react';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
getDateTimeFormat,
priceChange,
priceChangePercentage,
} from '@vegaprotocol/utils';
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid';
import { Tooltip, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { signedNumberCssClass } from '@vegaprotocol/datagrid';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { useCandles } from '../../hooks/use-candles';
import BigNumber from 'bignumber.js';
import classNames from 'classnames';
import { useT } from '../../use-t';
interface Props {
marketId?: string;
@@ -24,7 +22,6 @@ export const Last24hPriceChange = ({
decimalPlaces,
fallback,
}: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId,
});
@@ -35,56 +32,6 @@ export const Last24hPriceChange = ({
return nonIdeal;
}
if (fiveDaysCandles.length < 24) {
return (
<Tooltip
description={
<span className="justify-start">
{t(
'Market has not been active for 24 hours. The price change between {{start}} and {{end}} is:',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
}
)}
<PriceChangeCell
candles={fiveDaysCandles.map((c) => c.close) || []}
decimalPlaces={decimalPlaces}
/>
</span>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
return (
<Tooltip
description={
<span className="justify-start">
{t(
'24 hour change is unavailable at this time. The price change in the last 120 hours is:'
)}{' '}
<PriceChangeCell
candles={fiveDaysCandles.map((c) => c.close) || []}
decimalPlaces={decimalPlaces}
/>
</span>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
const candles = oneDayCandles?.map((c) => c.close) || [];
const change = priceChange(candles);
const changePercentage = priceChangePercentage(candles);
@@ -2,7 +2,6 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
import {
addDecimalsFormatNumber,
formatNumber,
getDateTimeFormat,
isNumeric,
} from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
@@ -37,83 +36,6 @@ export const Last24hVolume = ({
return nonIdeal;
}
if (fiveDaysCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
marketDecimals,
positionDecimalPlaces
);
const candleVolumeValue =
candleVolume && isNumeric(positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
positionDecimalPlaces,
formatDecimals
)
: '-';
return (
<Tooltip
description={
<div>
<span className="flex flex-col">
{t(
'Market has not been active for 24 hours. The volume traded between {{start}} and {{end}} is {{candleVolumeValue}} for a total of {{candleVolumePrice}} {{quoteUnit}}',
{
start: getDateTimeFormat().format(
new Date(fiveDaysCandles[0].periodStart)
),
end: getDateTimeFormat().format(
new Date(
fiveDaysCandles[fiveDaysCandles.length - 1].periodStart
)
),
candleVolumeValue,
candleVolumePrice,
quoteUnit,
}
)}
</span>
</div>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
if (oneDayCandles.length < 24) {
const candleVolume = calcCandleVolume(fiveDaysCandles);
const candleVolumePrice = calcCandleVolumePrice(
fiveDaysCandles,
marketDecimals,
positionDecimalPlaces
);
const candleVolumeValue =
candleVolume && isNumeric(positionDecimalPlaces)
? addDecimalsFormatNumber(
candleVolume,
positionDecimalPlaces,
formatDecimals
)
: '-';
return (
<Tooltip
description={
<div>
<span className="flex flex-col">
{t(
'24 hour change is unavailable at this time. The volume change in the last 120 hours is {{candleVolumeValue}} for a total of ({{candleVolumePrice}} {{quoteUnit}})',
{ candleVolumeValue, candleVolumePrice, quoteUnit }
)}
</span>
</div>
}
>
<span>{nonIdeal}</span>
</Tooltip>
);
}
const candleVolume = oneDayCandles
? calcCandleVolume(oneDayCandles)
: initialValue;
+1 -8
View File
@@ -8,7 +8,7 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
const fiveDaysAgo = useFiveDaysAgo();
const yesterday = useYesterday();
const since = new Date(fiveDaysAgo).toISOString();
const { data, error } = useThrottledDataProvider({
const { data: fiveDaysCandles, error } = useThrottledDataProvider({
dataProvider: marketCandlesProvider,
variables: {
marketId: marketId || '',
@@ -18,13 +18,6 @@ export const useCandles = ({ marketId }: { marketId?: string }) => {
skip: !marketId,
});
const fiveDaysCandles = data?.filter((c) => {
if (c.open === '' || c.close === '' || c.high === '' || c.close === '') {
return false;
}
return true;
});
const oneDayCandles = fiveDaysCandles?.filter((candle) =>
isCandleLessThan24hOld(candle, yesterday)
);
+5
View File
@@ -15,9 +15,14 @@ export const TRADINGVIEW_INTERVAL_MAP = {
[Interval.INTERVAL_I1M]: '1',
[Interval.INTERVAL_I5M]: '5',
[Interval.INTERVAL_I15M]: '15',
[Interval.INTERVAL_I30M]: '30',
[Interval.INTERVAL_I1H]: '60',
[Interval.INTERVAL_I4H]: '240',
[Interval.INTERVAL_I6H]: '360',
[Interval.INTERVAL_I8H]: '480',
[Interval.INTERVAL_I12H]: '720',
[Interval.INTERVAL_I1D]: '1D',
[Interval.INTERVAL_I7D]: '1W',
} as const;
export type ResolutionRecord = typeof TRADINGVIEW_INTERVAL_MAP;
@@ -1,7 +1,7 @@
import { useScript } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from './use-t';
import { TradingView, type OnAutoSaveNeededCallback } from './trading-view';
import { TradingView } from './trading-view';
import { CHARTING_LIBRARY_FILE, type ResolutionString } from './constants';
export const TradingViewContainer = ({
@@ -10,16 +10,12 @@ export const TradingViewContainer = ({
marketId,
interval,
onIntervalChange,
onAutoSaveNeeded,
state,
}: {
libraryPath: string;
libraryHash: string;
marketId: string;
interval: ResolutionString;
onIntervalChange: (interval: string) => void;
onAutoSaveNeeded: OnAutoSaveNeededCallback;
state: object | undefined;
}) => {
const t = useT();
const scriptState = useScript(
@@ -49,8 +45,6 @@ export const TradingViewContainer = ({
marketId={marketId}
interval={interval}
onIntervalChange={onIntervalChange}
onAutoSaveNeeded={onAutoSaveNeeded}
state={state}
/>
);
};
+1 -20
View File
@@ -25,15 +25,11 @@ export const TradingView = ({
libraryPath,
interval,
onIntervalChange,
onAutoSaveNeeded,
state,
}: {
marketId: string;
libraryPath: string;
interval: ResolutionString;
onIntervalChange: (interval: string) => void;
onAutoSaveNeeded: OnAutoSaveNeededCallback;
state: object | undefined;
}) => {
const { isMobile } = useScreenDimensions();
const { theme } = useThemeSwitcher();
@@ -108,7 +104,6 @@ export const TradingView = ({
backgroundColor: overrides['paneProperties.background'],
},
auto_save_delay: 1,
saved_data: state,
};
widgetRef.current = new window.TradingView.widget(widgetOptions);
@@ -117,25 +112,12 @@ export const TradingView = ({
if (!widgetRef.current) return;
const activeChart = widgetRef.current.activeChart();
if (!state) {
// If chart has loaded with no state, create a volume study
activeChart.createStudy('Volume');
}
activeChart.createStudy('Volume');
// Subscribe to interval changes so it can be persisted in chart settings
activeChart.onIntervalChanged().subscribe(null, onIntervalChange);
});
widgetRef.current.subscribe('onAutoSaveNeeded', () => {
if (!widgetRef.current) return;
widgetRef.current.save((newState) => {
onAutoSaveNeeded(newState);
});
});
}, [
state,
datafeed,
interval,
prevTheme,
@@ -145,7 +127,6 @@ export const TradingView = ({
language,
libraryPath,
isMobile,
onAutoSaveNeeded,
onIntervalChange,
]);
@@ -32,9 +32,14 @@ const resolutionMap: Record<string, Interval> = {
'1': Interval.INTERVAL_I1M,
'5': Interval.INTERVAL_I5M,
'15': Interval.INTERVAL_I15M,
'30': Interval.INTERVAL_I30M,
'60': Interval.INTERVAL_I1H,
'240': Interval.INTERVAL_I4H,
'360': Interval.INTERVAL_I6H,
'480': Interval.INTERVAL_I8H,
'720': Interval.INTERVAL_I12H,
'1D': Interval.INTERVAL_I1D,
'1W': Interval.INTERVAL_I7D,
} as const;
const supportedResolutions = Object.keys(resolutionMap);
+12
View File
@@ -936,6 +936,8 @@ export enum DispatchMetric {
/** Dispatch strategy for a recurring transfer */
export type DispatchStrategy = {
__typename?: 'DispatchStrategy';
/** Optional multiplier on taker fees used to cap the rewards a party may receive in an epoch */
capRewardFeeMultiple?: Maybe<Scalars['String']>;
/** Defines the data that will be used to compare markets so as to distribute rewards appropriately */
dispatchMetric: DispatchMetric;
/** The asset to use for measuring contribution to the metric */
@@ -2391,6 +2393,8 @@ export type Market = {
state: MarketState;
/** Optional: Market ID of the successor to this market if one exists */
successorMarketID?: Maybe<Scalars['ID']>;
/** The market minimum tick size */
tickSize: Scalars['String'];
/** An instance of, or reference to, a tradable instrument. */
tradableInstrument: TradableInstrument;
/** @deprecated Simplify and consolidate trades query and remove nesting. Use trades query instead */
@@ -2816,6 +2820,8 @@ export type NewMarket = {
riskParameters: RiskModel;
/** Successor market configuration. If this proposed market is meant to succeed a given market, then this needs to be set. */
successorConfiguration?: Maybe<SuccessorConfiguration>;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
/** Configuration for a new spot market on Vega */
@@ -2839,6 +2845,8 @@ export type NewSpotMarket = {
riskParameters?: Maybe<RiskModel>;
/** Specifies parameters related to liquidity target stake calculation */
targetStakeParameters: TargetStakeParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type NewTransfer = {
@@ -7074,6 +7082,8 @@ export type UpdateMarketConfiguration = {
quadraticSlippageFactor: Scalars['String'];
/** Updated futures market risk model parameters. */
riskParameters: UpdateMarketRiskParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type UpdateMarketLogNormalRiskModel = {
@@ -7171,6 +7181,8 @@ export type UpdateSpotMarketConfiguration = {
riskParameters: RiskModel;
/** Specifies parameters related to target stake calculation */
targetStakeParameters: TargetStakeParameters;
/** The market minimum tick size */
tickSize: Scalars['String'];
};
export type UpdateVolumeDiscountProgram = {
@@ -17,8 +17,8 @@ export const InputError = ({
...props
}: InputErrorProps) => {
const effectiveClassName = classNames(
'text-sm flex items-center first-letter:uppercase',
'mt-2',
'text-sm block items-center first-letter:capitalize',
'mt-2 min-w-0 break-words',
{
'border-danger': intent === 'danger',
'border-warning': intent === 'warning',
+1 -1
View File
@@ -1,4 +1,4 @@
export const isValidUrl = (url?: string) => {
export const isValidUrl = (url?: string | null) => {
if (!url) return false;
try {
new URL(url);
@@ -1,4 +1,9 @@
import { type ReactNode, type FunctionComponent, forwardRef } from 'react';
import {
type ReactNode,
type FunctionComponent,
forwardRef,
useState,
} from 'react';
import {
ConnectorErrors,
isBrowserWalletInstalled,
@@ -12,6 +17,7 @@ import { useConnect } from '../../hooks/use-connect';
import { Links } from '../../constants';
import { ConnectorIcon } from './connector-icon';
import { useUserAgent } from '@vegaprotocol/react-helpers';
import { Trans } from 'react-i18next';
const vegaExtensionsLinks = {
chrome: Links.chromeExtension,
@@ -29,48 +35,67 @@ export const ConnectionOptions = ({
onConnect: (id: ConnectorType) => void;
}) => {
const t = useT();
const error = useWallet((store) => store.error);
const { connectors } = useConnect();
const error = useWallet((store) => store.error);
const [isInstalling, setIsInstalling] = useState(false);
return (
<div className="flex flex-col items-start gap-4">
<h2 className="text-xl">{t('Connect to Vega')}</h2>
<ul
className="grid grid-cols-1 sm:grid-cols-2 gap-1 -mx-2"
data-testid="connectors-list"
>
{connectors.map((c) => {
const ConnectionOption = ConnectionOptionRecord[c.id];
const props = {
id: c.id,
name: c.name,
description: c.description,
showDescription: false,
onClick: () => onConnect(c.id),
};
if (ConnectionOption) {
return (
<li key={c.id}>
<ConnectionOption {...props} />
</li>
);
}
return (
<li key={c.id}>
<ConnectionOptionDefault {...props} />
</li>
);
})}
</ul>
{error && error.code !== ConnectorErrors.userRejected.code && (
<p
className="text-danger text-sm first-letter:uppercase"
data-testid="connection-error"
>
{error.message}
{isInstalling ? (
<p className="text-warning">
<Trans
i18nKey="Once you have added the extension, <0>refresh</0> your browser."
components={[
<button
onClick={() => window.location.reload()}
className="underline underline-offset-4"
/>,
]}
/>
</p>
) : (
<>
<ul
className="grid grid-cols-1 sm:grid-cols-2 gap-1 -mx-2"
data-testid="connectors-list"
>
{connectors.map((c) => {
const ConnectionOption = ConnectionOptionRecord[c.id];
const props = {
id: c.id,
name: c.name,
description: c.description,
showDescription: false,
onClick: () => onConnect(c.id),
onInstall: () => setIsInstalling(true),
};
if (ConnectionOption) {
return (
<li key={c.id}>
<ConnectionOption {...props} />
</li>
);
}
return (
<li key={c.id}>
<ConnectionOptionDefault {...props} />
</li>
);
})}
</ul>
{error && error.code !== ConnectorErrors.userRejected.code && (
<p
className="text-danger text-sm first-letter:uppercase"
data-testid="connection-error"
>
{error.message}
{error.data ? `: ${error.data}` : ''}
</p>
)}
</>
)}
<a
href={Links.walletOverview}
@@ -90,6 +115,7 @@ interface ConnectionOptionProps {
description: string;
showDescription?: boolean;
onClick: () => void;
onInstall?: () => void;
}
const CONNECTION_OPTION_CLASSES =
@@ -142,6 +168,7 @@ export const ConnectionOptionInjected = ({
description,
showDescription = false,
onClick,
onInstall,
}: ConnectionOptionProps) => {
const t = useT();
const userAgent = useUserAgent();
@@ -158,7 +185,11 @@ export const ConnectionOptionInjected = ({
</span>
</ConnectionOptionButtonWithDescription>
) : (
<ConnectionOptionLinkWithDescription id={id} href={link}>
<ConnectionOptionLinkWithDescription
id={id}
href={link}
onClick={onInstall}
>
<span className="flex flex-col justify-start text-left">
<span className="capitalize leading-5">
{t('Get the Vega Wallet')}
@@ -183,7 +214,7 @@ export const ConnectionOptionInjected = ({
{name}
</ConnectionOptionButton>
) : (
<ConnectionOptionLink id={id} href={link}>
<ConnectionOptionLink id={id} href={link} onClick={onInstall}>
{t('Get the Vega Wallet')}
</ConnectionOptionLink>
)}
@@ -275,8 +306,9 @@ const ConnectionOptionLink = forwardRef<
children: ReactNode;
id: ConnectorType;
href: string;
onClick?: () => void;
}
>(({ children, id, href }, ref) => {
>(({ children, id, href, onClick }, ref) => {
return (
<a
href={href}
@@ -285,6 +317,7 @@ const ConnectionOptionLink = forwardRef<
className={CONNECTION_OPTION_CLASSES}
data-testid={`connector-${id}`}
ref={ref}
onClick={onClick}
>
<ConnectorIcon id={id} />
{children}
@@ -320,8 +353,10 @@ const ConnectionOptionLinkWithDescription = forwardRef<
children: ReactNode;
id: ConnectorType;
href: string;
onClick?: () => void;
}
>(({ children, id, href }, ref) => {
>(({ children, id, href, onClick }, ref) => {
return (
<a
ref={ref}
@@ -329,6 +364,7 @@ const ConnectionOptionLinkWithDescription = forwardRef<
href={href}
target="_blank"
rel="noreferrer"
onClick={onClick}
>
<span>
<ConnectorIcon id={id} />

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