Compare commits

..
28 changed files with 373 additions and 228 deletions
@@ -1,44 +0,0 @@
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,7 +10,6 @@ import {
ProtocolUpgradeProposalNotification,
} from '@vegaprotocol/proposals';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { RewardsMovedNotification } from '../notifications/rewards-moved-notification';
interface AppLayoutProps {
children: ReactNode;
@@ -46,10 +45,8 @@ 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}
/>
@@ -288,7 +288,7 @@ describe('Consensus validators table', () => {
expect(
grid.querySelector('[role="gridcell"][col-id="totalPenalties"]')
).toHaveTextContent('10.07%');
).toHaveTextContent('13.16%');
expect(
grid.querySelector('[role="gridcell"][col-id="normalisedVotingPower"]')
@@ -185,19 +185,15 @@ export const ConsensusValidatorsTable = ({
const { rawValidatorScore: previousEpochValidatorScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
const overstakingPenalty = calculateOverstakedPenalty(
const overstakingPenalty = calculateOverallPenalty(
id,
allNodesInPreviousEpoch
);
const totalPenalty = calculateOverallPenalty(
const totalPenalty = calculateOverstakedPenalty(
id,
allNodesInPreviousEpoch
);
const lastEpochDataForNode = allNodesInPreviousEpoch.find(
(node) => node.id === id
);
return {
id,
[ValidatorFields.RANKING_INDEX]: stakedTotalRanking,
@@ -243,12 +239,6 @@ export const ConsensusValidatorsTable = ({
: undefined,
[ValidatorFields.MULTISIG_ERROR]:
multisigStatus?.showMultisigStatusError,
[ValidatorFields.MULTISIG_PENALTY]: formatNumberPercentage(
new BigNumber(1)
.minus(lastEpochDataForNode?.rewardScore?.multisigScore ?? 1)
.times(100),
2
),
};
}
);
@@ -388,6 +378,7 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('StakeDescription').toString(),
cellRenderer: TotalStakeRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.PENDING_STAKE,
@@ -409,7 +400,6 @@ export const ConsensusValidatorsTable = ({
headerTooltip: t('NormalisedVotingPowerDescription').toString(),
cellRenderer: VotingPowerRenderer,
width: 120,
sort: 'desc',
},
{
field: ValidatorFields.TOTAL_PENALTIES,
@@ -40,7 +40,6 @@ export enum ValidatorFields {
PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare',
MULTISIG_ERROR = 'multisigError',
MULTISIG_PENALTY = 'multisigPenalty',
}
export const addUserDataToValidator = (
@@ -328,7 +327,7 @@ interface TotalPenaltiesRendererProps {
overstakedAmount: string;
overstakingPenalty: string;
totalPenalties: string;
multisigPenalty: string;
multisigError?: boolean;
};
}
@@ -347,9 +346,11 @@ export const TotalPenaltiesRenderer = ({
<div data-testid="overstaked-penalty-tooltip">
{t('overstakedPenalty')}: {data.overstakingPenalty}
</div>
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: {data.multisigPenalty}
</div>
{data.multisigError && (
<div data-testid="multisig-error-tooltip">
{t('multisigPenalty')}: 100%
</div>
)}
</>
}
>
@@ -37,6 +37,7 @@ 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',
@@ -104,10 +105,9 @@ export const ValidatorTable = ({
};
}, [node, previousEpochData?.epoch.validatorsConnection?.edges]);
const previousNodeData =
previousEpochData?.epoch.validatorsConnection?.edges?.find(
(e) => e?.node.id === node.id
);
const multisigStatus = previousEpochData
? getMultisigStatusInfo(previousEpochData)
: undefined;
return (
<>
@@ -293,15 +293,21 @@ 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(
new BigNumber(1)
.minus(
previousNodeData?.node.rewardScore?.multisigScore ??
1
)
.times(100),
BigNumber(
multisigStatus?.showMultisigStatusError ? 100 : 0
),
2
)}
</span>
@@ -30,6 +30,8 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
setStudies,
setStudySizes,
setOverlays,
state,
setState,
} = useChartSettings();
const pennantChart = (
@@ -66,6 +68,10 @@ export const ChartContainer = ({ marketId }: { marketId: string }) => {
onIntervalChange={(newInterval) => {
setInterval(fromTradingViewResolution(newInterval));
}}
onAutoSaveNeeded={(data) => {
setState(data);
}}
state={state}
/>
);
}
@@ -9,6 +9,7 @@ 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
@@ -29,6 +30,7 @@ const STUDY_ORDER: Study[] = [
];
export const DEFAULT_CHART_SETTINGS = {
state: undefined,
chartlib: 'pennant' as const,
interval: Interval.INTERVAL_I15M,
type: ChartType.CANDLE,
@@ -45,6 +47,7 @@ export const useChartSettingsStore = create<
setStudies: (studies?: Study[]) => void;
setStudySizes: (sizes: number[]) => void;
setChartlib: (lib: Chartlib) => void;
setState: (state: object) => void;
}
>()(
persist(
@@ -92,6 +95,9 @@ export const useChartSettingsStore = create<
state.chartlib = lib;
});
},
setState: (state) => {
set({ state });
},
})),
{
name: 'vega_candles_chart_store',
@@ -145,5 +151,7 @@ export const useChartSettings = () => {
setOverlays: settings.setOverlays,
setStudySizes: settings.setStudySizes,
setChartlib: settings.setChartlib,
state: settings.state,
setState: settings.setState,
};
};
@@ -21,13 +21,18 @@ const useFeesTableColumnDefs = (): ColDef[] => {
pinned: 'left',
width: 150,
},
{
field: 'liquidityFee',
valueFormatter: ({ value }: { value: number }) => value + '%',
},
{
field: 'feeAfterDiscount',
headerName: t('Total fee after discount'),
valueFormatter: ({ value }: { value: number }) => value + '%',
},
{
field: 'liquidityFee',
field: 'totalFee',
headerName: t('Total fee before discount'),
valueFormatter: ({ value }: { value: number }) => value + '%',
},
{
@@ -38,11 +43,6 @@ const useFeesTableColumnDefs = (): ColDef[] => {
field: 'makerFee',
valueFormatter: ({ value }: { value: number }) => value + '%',
},
{
field: 'totalFee',
headerName: t('Total fee before discount'),
valueFormatter: ({ value }: { value: number }) => value + '%',
},
] as ColDef[],
[t]
);
@@ -24,7 +24,6 @@ import {
type DispatchStrategy,
IndividualScopeMapping,
IndividualScopeDescriptionMapping,
type Asset,
} from '@vegaprotocol/types';
import { Card } from '../card/card';
import { type ReactNode, useState } from 'react';
@@ -201,8 +200,7 @@ export const ActiveRewardCard = ({
transferNode.transfer.asset?.decimals || 0,
6
)}
rewardAsset={transferNode.dispatchAsset}
transferAsset={transferNode.transfer.asset || undefined}
rewardAsset={transferNode.asset}
endsIn={
transferNode.transfer.kind.endEpoch != null
? transferNode.transfer.kind.endEpoch - currentEpoch
@@ -218,8 +216,6 @@ const RewardCard = ({
colour,
rewardAmount,
rewardAsset,
transferAsset,
vegaAsset,
dispatchStrategy,
endsIn,
dispatchMetricInfo,
@@ -228,14 +224,12 @@ const RewardCard = ({
rewardAmount: string;
/** The asset linked to the dispatch strategy via `dispatchMetricAssetId` property. */
rewardAsset?: BasicAssetDetails;
/** The VEGA asset details, required to format the min staking amount. */
transferAsset?: Asset | undefined;
/** The VEGA asset details, required to format the min staking amount. */
vegaAsset?: BasicAssetDetails;
/** The transfer's dispatch strategy. */
dispatchStrategy: DispatchStrategy;
/** The number of epochs until the transfer stops. */
endsIn?: number;
/** The VEGA asset details, required to format the min staking amount. */
vegaAsset?: BasicAssetDetails;
dispatchMetricInfo?: ReactNode;
}) => {
const t = useT();
@@ -275,9 +269,7 @@ const RewardCard = ({
{rewardAmount}
</span>
<span className="font-alpha" data-testid="reward-asset">
{transferAsset?.symbol || ''}
</span>
<span className="font-alpha">{rewardAsset?.symbol || ''}</span>
</h3>
{/** DISTRIBUTION STRATEGY */}
@@ -365,7 +357,6 @@ const RewardCard = ({
<RewardRequirements
dispatchStrategy={dispatchStrategy}
rewardAsset={rewardAsset}
vegaAsset={vegaAsset}
/>
)}
</div>
@@ -388,8 +379,8 @@ export const DispatchMetricInfo = ({
let additionalDispatchMetricInfo = null;
// if asset found then display asset symbol
if (reward.dispatchAsset) {
additionalDispatchMetricInfo = <span>{reward.dispatchAsset.symbol}</span>;
if (reward.asset) {
additionalDispatchMetricInfo = <span>{reward.asset.symbol}</span>;
}
// but if scoped to only one market then display market name
if (marketNames.length === 1) {
+24
View File
@@ -182,7 +182,27 @@ def vega(request):
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="session", autouse=True)
def shared_vega(request):
with init_vega(request) as vega_instance:
try:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
finally:
cleanup_container(vega_instance)
@pytest.fixture
def page_shared_vega(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page_instance:
yield page_instance
@pytest.fixture
def auth_shared_vega(shared_vega: VegaServiceNull, page_shared_vega: Page):
return auth_setup(shared_vega, page_shared_vega)
@pytest.fixture
def risk_accepted_shared_vega(page_shared_vega: Page):
risk_accepted_setup(page_shared_vega)
def cleanup_container(vega_instance):
try:
@@ -283,6 +303,10 @@ def opening_auction_market(vega):
return setup_opening_auction_market(vega)
@pytest.fixture(scope="function")
def shared_continuous_market(shared_vega:VegaServiceNull):
return setup_continuous_market(shared_vega)
@pytest.fixture(scope="function")
def continuous_market(vega):
return setup_continuous_market(vega)
@@ -2,35 +2,62 @@ import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from datetime import datetime, timedelta
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup, auth_setup
from fixtures.market import setup_continuous_market
from actions.utils import wait_for_toast_confirmation
from wallet_config import WalletConfig, MM_WALLET2
from actions.utils import (
change_keys,
create_and_faucet_wallet,
)
order_size = "order-size"
order_price = "order-price"
place_order = "place-order"
order_side_sell = "order-side-SIDE_SELL"
order_side_buy = "order-side-SIDE_BUY"
market_order = "order-type-Market"
limit_order = "order-type-Limit"
tif = "order-tif"
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
yield vega_instance
@pytest.fixture(scope="module")
def page(shared_vega, browser, request, continuous_market):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(shared_vega, page)
basic_key = WalletConfig("basic_key", "basic_key")
create_and_faucet_wallet(vega=shared_vega, wallet=basic_key)
page.goto(f"/#/markets/{continuous_market}")
change_keys(page, shared_vega, "basic_key")
yield page
@pytest.fixture(scope="module")
def continuous_market(vega):
return setup_continuous_market(vega)
def continuous_market(shared_vega:VegaServiceNull):
keypairs = shared_vega.wallet.get_keypairs("MarketSim")
proposal_key = keypairs.get('market_maker')
termination_key=keypairs.get('FJMKnwfZdd48C8NqvYrG')
mm_2_key=keypairs.get('market_maker_2')
kwargs = {}
if proposal_key is not None:
kwargs['proposal_key'] = proposal_key
if termination_key is not None:
kwargs['termination_key'] = termination_key
if mm_2_key is not None:
kwargs['mm_2_key'] = mm_2_key
return setup_continuous_market(shared_vega, **kwargs)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order_GTT( shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(tif).select_option("Good 'til Time (GTT)")
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
@@ -47,30 +74,31 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Pag
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT:")
expect(page.get_by_role("row").nth(4)).to_contain_text("10+10LimitFilled120.00GTT:")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(2)
vega.wait_for_total_catchup()
shared_vega.wait_fn(2)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(6)).to_contain_text("10+10LimitFilled120.00GTC")
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("100")
page.get_by_test_id(order_side_sell).click()
@@ -84,15 +112,15 @@ def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10LimitFilled100.00GFN")
page.reload()
expect(page.get_by_role("row").nth(6)).to_contain_text("10-10LimitFilled100.00GFN")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_side_sell).click()
@@ -105,32 +133,32 @@ def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page)
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(8)).to_contain_text("10-10MarketFilled-IOC")
page.reload()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10MarketFilled-IOC")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(tif).select_option("Fill or Kill (FOK)")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-010
# 0003-WTXN-012
# 0003-WTXN-003
expect(page.get_by_role("row").nth(9)).to_contain_text("10+10MarketFilled-FOK")
expect(page.get_by_role("row").nth(8)).to_contain_text("10+10MarketFilled-FOK")
@pytest.mark.usefixtures("risk_accepted")
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_sidebar_should_be_open_after_reload(page: Page):
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
page.get_by_test_id("Order").click()
expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible()
@@ -1,4 +1,4 @@
import pytest
""" import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -87,3 +87,4 @@ def test_margin_and_fees_estimations(continuous_market, vega: VegaServiceNull, p
# expect(page.get_by_test_id("toast-content")).to_contain_text(
# "Your transaction has been confirmed"
# )
"""
@@ -44,25 +44,25 @@ def create_position(vega: VegaServiceNull, market_id):
vega.wait_fn(1)
vega.wait_for_total_catchup
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_stop_order_form_error_validation(continuous_market, page: Page):
@pytest.mark.usefixtures("auth_shared_vega", "risk_accepted_shared_vega")
def test_stop_order_form_error_validation(shared_continuous_market, page_shared_vega: Page):
# 7002-SORD-032
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(stop_order_btn).click()
page.get_by_test_id(stop_limit_order_btn).is_visible()
page.get_by_test_id(stop_limit_order_btn).click()
page.get_by_test_id(order_side_sell).click()
page.get_by_test_id(submit_stop_order).click()
expect(page.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
page_shared_vega.goto(f"/#/markets/{shared_continuous_market}")
page_shared_vega.get_by_test_id(stop_order_btn).click()
page_shared_vega.get_by_test_id(stop_limit_order_btn).is_visible()
page_shared_vega.get_by_test_id(stop_limit_order_btn).click()
page_shared_vega.get_by_test_id(order_side_sell).click()
page_shared_vega.get_by_test_id(submit_stop_order).click()
expect(page_shared_vega.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
"You need provide a price"
)
expect(page.get_by_test_id("stop-order-error-message-size")).to_have_text(
expect(page_shared_vega.get_by_test_id("stop-order-error-message-size")).to_have_text(
"Size cannot be lower than 1"
)
page.get_by_test_id(order_size).fill("1")
page.get_by_test_id(order_price).fill("0.0000001")
expect(page.get_by_test_id("stop-order-error-message-price")).to_have_text(
page_shared_vega.get_by_test_id(order_size).fill("1")
page_shared_vega.get_by_test_id(order_price).fill("0.0000001")
expect(page_shared_vega.get_by_test_id("stop-order-error-message-price")).to_have_text(
"Price cannot be lower than 0.00001"
)
@@ -257,7 +257,7 @@ def test_submit_stop_limit_order_cancel(
).to_have_text("Cancelled")
class TestStopOcoValidation:
""" class TestStopOcoValidation:
@pytest.fixture(scope="class")
def vega(request):
with init_vega(request) as vega_instance:
@@ -296,3 +296,4 @@ class TestStopOcoValidation:
expect(page.get_by_test_id("stop-order-warning-limit")).to_have_text(
"There is a limit of 4 active stop orders per market. Orders submitted above the limit will be immediately rejected."
)
"""
@@ -77,7 +77,7 @@ def test_market_info_market_volume(page: Page):
page.get_by_test_id(market_title_test_id).get_by_text(
"Market volume").click()
fields = [
["24 Hour Volume", "0 (0 )"],
["24 Hour Volume", "-"],
["Open Interest", "1"],
["Best Bid Volume", "99"],
["Best Offer Volume", "99"],
@@ -1,39 +1,30 @@
import pytest
from playwright.sync_api import Page, expect, Locator
from conftest import init_page, init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
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:
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
page.goto("/#/disclaimer")
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_network_switcher(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_network_switcher(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navbar_pages(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_links(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navigation_mobile(page: Page):
page.goto("/#/disclaimer")
page.set_viewport_size({"width": 800, "height": 1040})
navbar = page.locator('nav[aria-label="Main"]')
@@ -108,4 +99,4 @@ def assert_network_switcher(container: Locator):
expect(mainnet_link).to_be_visible()
# 0006-NETW-003
expect(mainnet_link).to_have_attribute("href", "https://console.vega.xyz")
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
@@ -49,7 +49,7 @@ def markets(vega: VegaServiceNull):
price=130,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -63,7 +63,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -77,7 +77,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -92,7 +92,7 @@ def markets(vega: VegaServiceNull):
wait=False,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -105,7 +105,7 @@ def markets(vega: VegaServiceNull):
volume=100,
price=104,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -120,7 +120,7 @@ def markets(vega: VegaServiceNull):
expires_at=vega.get_blockchain_time() + 5 * 1e9,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -134,7 +134,7 @@ def markets(vega: VegaServiceNull):
volume=20,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -148,7 +148,7 @@ def markets(vega: VegaServiceNull):
volume=40,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -162,7 +162,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -177,7 +177,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -190,7 +190,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=150,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -203,7 +203,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=160,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -216,7 +216,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=60,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -1,16 +1,15 @@
import pytest
from playwright.sync_api import expect, Page
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_share_usage_data(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -41,7 +40,6 @@ ICON_TO_TOAST = {
}
@pytest.mark.usefixtures("risk_accepted")
def test_toast_positions(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -52,10 +50,10 @@ def test_toast_positions(page: Page):
expect(page.locator(f"[{toast_selector}]")).to_be_visible()
@pytest.mark.usefixtures("risk_accepted")
def test_dark_mode(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
expect(page.locator("html")).not_to_have_attribute("class", "dark")
page.locator("#switch-settings-theme-switch").click()
expect(page.locator("html")).to_have_attribute("class", "dark")
expect(page.locator("html")).to_have_attribute("class", "dark")
+2 -20
View File
@@ -52,25 +52,8 @@ def setup_teams_and_games(vega: VegaServiceNull):
vega.update_network_parameter(
MM_WALLET.name, parameter="reward.asset", new_value=tDAI_asset_id
)
next_epoch(vega=vega)
vega.create_asset(
MM_WALLET.name,
name="VEGA",
symbol="VEGA",
decimals=5,
max_faucet_amount=1e10,
quantum=100000,
)
vega.wait_fn(1)
vega.wait_for_total_catchup()
VEGA_asset_id = vega.find_asset_id(symbol="VEGA")
vega.mint(PARTY_A.name, VEGA_asset_id, 1e5)
vega.mint(PARTY_B.name, VEGA_asset_id, 1e5)
vega.wait_fn(1)
vega.wait_for_total_catchup()
team_name = create_team(vega)
next_epoch(vega)
@@ -118,7 +101,7 @@ def setup_teams_and_games(vega: VegaServiceNull):
from_key_name=PARTY_A.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
asset=VEGA_asset_id,
asset=tDAI_asset_id,
reference="reward",
asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
@@ -136,7 +119,7 @@ def setup_teams_and_games(vega: VegaServiceNull):
from_key_name=PARTY_B.name,
from_account_type=vega_protos.vega.ACCOUNT_TYPE_GENERAL,
to_account_type=vega_protos.vega.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES,
asset=VEGA_asset_id,
asset=tDAI_asset_id,
reference="reward",
asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
@@ -305,7 +288,6 @@ def test_game_card(competitions_page: Page):
expect(game_1.get_by_test_id("entity-scope")).to_have_text("Individual")
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")
expect(game_1.get_by_test_id("distribution-strategy")).to_have_text("Pro rata")
expect(game_1.get_by_test_id("dispatch-metric-info")).to_have_text(
"Price maker fees paid • tDAI"
+5 -6
View File
@@ -31,7 +31,7 @@ export type RewardTransfer = TransferNode & {
export type EnrichedRewardTransfer = RewardTransfer & {
/** Dispatch metric asset (reward asset) */
dispatchAsset?: AssetFieldsFragment;
asset?: AssetFieldsFragment;
/** A flag determining whether a reward asset is being traded on any of the active markets */
isAssetTraded?: boolean;
/** A list of markets in scope */
@@ -142,10 +142,9 @@ export const useRewards = ({
.filter((node) => (scopeToTeams ? isScopedToTeams(node) : true))
// enrich with dispatch asset and markets in scope details
.map((node) => {
const dispatchAsset =
(assets &&
assets[node.transfer.kind.dispatchStrategy.dispatchMetricAssetId]) ||
undefined;
const asset =
assets &&
assets[node.transfer.kind.dispatchStrategy.dispatchMetricAssetId];
const marketsInScope = compact(
node.transfer.kind.dispatchStrategy.marketIdsInScope?.map(
(id) => markets && markets[id]
@@ -168,7 +167,7 @@ export const useRewards = ({
});
return {
...node,
dispatchAsset,
asset: asset ? asset : undefined,
isAssetTraded: isAssetTraded != null ? isAssetTraded : undefined,
markets: marketsInScope.length > 0 ? marketsInScope : undefined,
};
-1
View File
@@ -134,7 +134,6 @@ 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';
+1 -2
View File
@@ -969,6 +969,5 @@
"YourIdentityAnonymous": "Your identity is always anonymous on Vega",
"yourStake": "Your stake",
"yourVote": "Your vote",
"youVoted": "You voted",
"rewardsMovedNotification": "Trading and liquidity rewards have moved. Visit <0>Console</0> to view your rewards."
"youVoted": "You voted"
}
+5
View File
@@ -1,6 +1,8 @@
{
"{{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.",
@@ -49,6 +51,9 @@
"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",
@@ -2,14 +2,16 @@ import { type ReactNode } from 'react';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
getDateTimeFormat,
priceChange,
priceChangePercentage,
} from '@vegaprotocol/utils';
import { signedNumberCssClass } from '@vegaprotocol/datagrid';
import { VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { PriceChangeCell, signedNumberCssClass } from '@vegaprotocol/datagrid';
import { Tooltip, 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;
@@ -22,6 +24,7 @@ export const Last24hPriceChange = ({
decimalPlaces,
fallback,
}: Props) => {
const t = useT();
const { oneDayCandles, fiveDaysCandles, error } = useCandles({
marketId,
});
@@ -32,6 +35,56 @@ 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,6 +2,7 @@ import { calcCandleVolume, calcCandleVolumePrice } from '../../market-utils';
import {
addDecimalsFormatNumber,
formatNumber,
getDateTimeFormat,
isNumeric,
} from '@vegaprotocol/utils';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
@@ -36,6 +37,83 @@ 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;
+8 -1
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: fiveDaysCandles, error } = useThrottledDataProvider({
const { data, error } = useThrottledDataProvider({
dataProvider: marketCandlesProvider,
variables: {
marketId: marketId || '',
@@ -18,6 +18,13 @@ 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)
);
@@ -1,7 +1,7 @@
import { useScript } from '@vegaprotocol/react-helpers';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useT } from './use-t';
import { TradingView } from './trading-view';
import { TradingView, type OnAutoSaveNeededCallback } from './trading-view';
import { CHARTING_LIBRARY_FILE, type ResolutionString } from './constants';
export const TradingViewContainer = ({
@@ -10,12 +10,16 @@ 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(
@@ -45,6 +49,8 @@ export const TradingViewContainer = ({
marketId={marketId}
interval={interval}
onIntervalChange={onIntervalChange}
onAutoSaveNeeded={onAutoSaveNeeded}
state={state}
/>
);
};
+20 -1
View File
@@ -25,11 +25,15 @@ 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();
@@ -104,6 +108,7 @@ export const TradingView = ({
backgroundColor: overrides['paneProperties.background'],
},
auto_save_delay: 1,
saved_data: state,
};
widgetRef.current = new window.TradingView.widget(widgetOptions);
@@ -112,12 +117,25 @@ export const TradingView = ({
if (!widgetRef.current) return;
const activeChart = widgetRef.current.activeChart();
activeChart.createStudy('Volume');
if (!state) {
// If chart has loaded with no state, create a volume study
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,
@@ -127,6 +145,7 @@ export const TradingView = ({
language,
libraryPath,
isMobile,
onAutoSaveNeeded,
onIntervalChange,
]);