Compare commits

...
Author SHA1 Message Date
Madalina Raicu da8a6b8e8d fix: revert dark theme default 2024-01-18 09:46:05 +00:00
Madalina Raicu a203d2e8b1 fix: remove redunant unit test 2024-01-17 18:01:32 +00:00
Madalina Raicu 450795535c fix: navbar and chart-menu tests 2024-01-17 17:10:03 +00:00
Madalina Raicu 2ae8886f9a feat: add explorer link 2024-01-17 16:54:17 +00:00
Madalina Raicu b692634cd2 fix(trading): set dark theme and trading view as default 2024-01-17 16:19:47 +00:00
Ben 9253e8067a chore(trading): market-sim branch parralel fix (#5630) 2024-01-17 14:44:11 +00:00
Bartłomiej Głownia 58972f0a11 feat(ui-toolkit): add leverage slider component (#5594) 2024-01-17 13:46:11 +00:00
Ben a3c55fd7c3 chore(trading): update vega-market-sim to use to 0.74.0-preview.2 (#5603) 2024-01-17 12:19:52 +00:00
Matthew Russell c63cba1071 chore(trading): enable trading view on mainnet (#5628) 2024-01-16 18:16:58 +00:00
m.ray bc9d87fe30 chore(trading): clean up triggering ratio (#5604) 2024-01-16 18:10:32 +01:00
m.ray 6a21862378 fix(trading): use No trading instead of Trading terminated (#5624) 2024-01-16 18:30:42 +02:00
Ben 0fb4348e9c chore(trading): upload market-sim logs (#5622) 2024-01-15 20:56:52 +00:00
m.ray b751bcf17d feat(trading): mobile responsiveness tweaks (#5613) 2024-01-15 15:26:13 +00:00
m.ray d32f27fcb1 feat(trading): mobile responsiveness - market selector (#5582) 2024-01-11 11:23:48 +00:00
m.ray d05cd6a2ed fix(trading): tiny scroll for rewards (#5586) 2024-01-10 15:49:58 +00:00
m.ray c003e5fa30 fix(trading): liquidity table improve readability and remove grouping (#5598) 2024-01-10 15:49:35 +00:00
Ben f62d3289ab chore(trading): retry on http error (#5601) 2024-01-10 15:44:01 +00:00
Ben a3d3d18c5c chore(trading): fix price monitoring test (#5600) 2024-01-10 14:56:17 +00:00
77ca101781 chore(trading): merge main back in develop (#5597)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
Co-authored-by: Edd <edd@vega.xyz>
2024-01-10 10:31:15 +00:00
Ben d238c37d0c chore(trading): vesting test (#5595) 2024-01-10 10:07:59 +00:00
Edd 79feb485f6 fix(governance): improve error message for configuration error (#5589) 2024-01-09 17:21:56 +00:00
Ben 6aa5c3b6e3 chore(trading): rewards page e2e (#5578) 2024-01-09 15:54:40 +00:00
Ben 82abc13fda chore(trading): skip test due to issue (#5583) 2024-01-08 16:16:03 +00:00
97 changed files with 1461 additions and 1192 deletions
+46 -3
View File
@@ -19,7 +19,7 @@ jobs:
create-docker-image:
name: Create docker image for console-test
runs-on: ubuntu-22.04
timeout-minutes: 20
timeout-minutes: 90
steps:
#----------------------------------------------
# check-out frontend-monorepo
@@ -138,7 +138,7 @@ jobs:
name: run-tests
runs-on: 8-cores
needs: [create-docker-image, console-test-branch]
timeout-minutes: 20
timeout-minutes: 90
steps:
#----------------------------------------------
# load docker image
@@ -205,7 +205,7 @@ jobs:
# run tests
#----------------------------------------------
- name: Run tests
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 4 --dist loadfile --durations=15
run: CONSOLE_IMAGE_NAME=ci/trading:local poetry run pytest -v -s --numprocesses 2 --dist loadfile --durations=90
working-directory: apps/trading/e2e
#----------------------------------------------
# upload traces
@@ -227,3 +227,46 @@ jobs:
name: worker-logs
path: ./logs/
retention-days: 15
#----------------------------------------------
# ----- upload market-sim logs -----
#----------------------------------------------
- name: Prepare and Zip market-sim-logs
if: always()
run: |
parent_dir="/tmp/market-sim-logs"
echo "Creating parent directory at $parent_dir"
mkdir -p "$parent_dir"
echo "Waiting for vega-sim-* folders to be created..."
sleep 10 # Waits 10 seconds to ensure all folders are created
echo "Before searching for vega-sim-* folders in /tmp..."
folders=$(find /tmp -mindepth 1 -type d -name 'vega-sim-*' -print) || echo "Find command failed with exit code $?"
echo "After searching for vega-sim-* folders in /tmp..."
if [ -z "$folders" ]; then
echo "No vega-sim-* folders found."
exit 0
fi
echo "Moving vega-sim-* folders to $parent_dir"
echo "$folders" | xargs -I {} mv {} "$parent_dir/"
echo "Checking if $parent_dir is not empty..."
if [ "$(ls -A $parent_dir)" ]; then
echo "Zipping the parent directory..."
zip -r market-sim-logs.zip "$parent_dir" && echo "Zip file created successfully."
else
echo "$parent_dir is empty. No zip file created."
exit 0
fi
shell: /usr/bin/bash -e {0}
- name: Upload market-sim-logs
uses: actions/upload-artifact@v3
if: always()
with:
name: market-sim-logs
path: market-sim-logs.zip
retention-days: 15
@@ -44,7 +44,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
cy.getByTestId('icon-cross').click();
});
it.skip('Proposal page displayed on mobile', function () {
it('Proposal page displayed on mobile', function () {
const proposalTitle = 'Add Lorem Ipsum market';
cy.common_switch_to_mobile_and_click_toggle();
@@ -55,7 +55,7 @@ context('Proposal page', { tags: '@smoke' }, function () {
});
});
it('Able to view new asset proposal', function () {
it.skip('Able to view new asset proposal', function () {
const proposalTitle = 'Test new asset proposal';
const newAssetProposalBody = getNewAssetTxBody();
cy.VegaWalletSubmitProposal(newAssetProposalBody);
@@ -1,9 +1,5 @@
import { render, waitFor } from '@testing-library/react';
import {
BORDER_COLOURS,
NestedDataList,
sortNestedDataByChildren,
} from './nested-data-list';
import { NestedDataList, sortNestedDataByChildren } from './nested-data-list';
import userEvent from '@testing-library/user-event';
const mockData = {
@@ -61,38 +57,6 @@ describe('NestedDataList', () => {
expect(parent[0].querySelector('li')).toHaveClass('pl-4 border-l-4 pt-2');
});
it('should repeat the border colours in the correct order', () => {
const colourMockData = {
t0: {
t1: {
t2: {
t3: {
t4: {
t5: {
t6: {
t7: {
t8: {
hello: 'world',
},
},
},
},
},
},
},
},
},
};
const tree = render(<NestedDataList data={colourMockData} />);
const { getByTestId } = tree;
for (let i = 0; i < 8; i++) {
const item = getByTestId(`T${i}`);
const expected = BORDER_COLOURS.light[i % 5];
expect(item.style.borderColor.toUpperCase()).toBe(expected);
}
});
it('should sort the data by values with children', () => {
const mockData = {
nonce: '5980890939790185837',
@@ -196,6 +196,7 @@ export function createNewMarketProposalTxBody(): ProposalSubmissionBody {
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -334,6 +335,7 @@ export function createSuccessorMarketProposalTxBody(
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -205,7 +205,6 @@ query Proposal(
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
@@ -366,7 +365,6 @@ query Proposal(
}
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
File diff suppressed because one or more lines are too long
+7 -2
View File
@@ -22,7 +22,12 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
NX_SUCCESSOR_MARKETS=true
NX_STOP_ORDERS=true
NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS
NX_METAMASK_SNAPS=true
NX_REFERRALS=true
NX_TEAM_COMPETITION=true
# NX_DISABLE_CLOSE_POSITION=false
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
+3
View File
@@ -28,3 +28,6 @@ NX_REFERRALS=true
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
+2
View File
@@ -54,3 +54,5 @@ To run the UI automation tests with a mocked API, run:
```bash
yarn nx run trading-e2e:e2e
```
To run tests with market sim please read [the readme](e2e/README.md).
@@ -314,7 +314,7 @@ describe('Closed', () => {
});
it('display market actions', async () => {
// Use market with a succcessor Id as the actions dropdown will optionally
// Use market with a successor Id as the actions dropdown will optionally
// show a link to the successor market
const marketsWithSuccessorAndParent = [
{
@@ -137,6 +137,8 @@ const ClosedMarketsDataGrid = ({
headerName: t('Market'),
field: 'code',
cellRenderer: 'MarketCodeCell',
width: 150,
resizable: true,
},
{
headerName: t('Status'),
@@ -280,6 +282,7 @@ const ClosedMarketsDataGrid = ({
return (
<AgGrid
rowData={rowData}
defaultColDef={COL_DEFS.default}
columnDefs={colDefs}
getRowId={({ data }) => data.id}
overlayNoRowsTemplate={error ? error.message : t('No markets')}
@@ -17,6 +17,7 @@ const defaultColDef = {
filter: true,
resizable: true,
filterParams: { buttons: ['reset'] },
minWidth: 120,
};
const components = {
@@ -53,6 +53,7 @@ export const MarketsPage = () => {
size="extra-small"
data-testid="propose-new-market"
href={externalLink}
target="_blank"
>
{t('Propose a new market')}
</TradingAnchorButton>
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
{
headerName: t('Market'),
field: 'tradableInstrument.instrument.code',
pinned: true,
cellRenderer: ({
value,
data,
@@ -4,6 +4,7 @@ import { useT } from '../../lib/use-t';
import { RewardsContainer } from '../../components/rewards-container';
import { usePageTitleStore } from '../../stores';
import { ErrorBoundary } from '../../components/error-boundary';
import { TinyScroll } from '@vegaprotocol/ui-toolkit';
export const Rewards = () => {
const t = useT();
@@ -16,10 +17,10 @@ export const Rewards = () => {
}, [updateTitle, title]);
return (
<ErrorBoundary feature="rewards">
<div className="container mx-auto p-4">
<TinyScroll className="p-4 max-h-full overflow-auto">
<h1 className="px-4 pb-4 text-2xl">{title}</h1>
<RewardsContainer />
</div>
</TinyScroll>
</ErrorBoundary>
);
};
@@ -27,11 +27,11 @@ describe('ChartMenu', () => {
render(<ChartMenu />);
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
await userEvent.click(screen.getByRole('button', { name: 'Vega chart' }));
expect(useChartSettingsStore.getState().chartlib).toEqual('pennant');
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
});
describe('tradingview', () => {
@@ -30,7 +30,7 @@ const STUDY_ORDER: Study[] = [
];
export const DEFAULT_CHART_SETTINGS = {
chartlib: 'pennant' as const,
chartlib: 'tradingview' as const,
interval: Interval.INTERVAL_I15M,
type: ChartType.CANDLE,
overlays: [Overlay.MOVING_AVERAGE],
@@ -42,13 +42,9 @@ export const LiquidityHeader = () => {
const assetDecimalPlaces = asset?.decimals || 0;
const symbol = asset?.symbol;
const triggeringRatio =
market?.liquidityMonitoringParameters.triggeringRatio || '1';
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: suppliedStake || 0,
targetStake: targetStake || 0,
triggeringRatio,
});
const feesObject = feesPaidRes?.paidLiquidityFees?.edges?.find(
@@ -47,9 +47,6 @@ export const MarketLiquiditySupplied = ({
]);
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
const triggeringRatio = Number(
params.market_liquidity_targetstake_triggering_ratio
);
const variables = useMemo(
() => ({
@@ -94,7 +91,6 @@ export const MarketLiquiditySupplied = ({
const { percentage, status } = useCheckLiquidityStatus({
suppliedStake: market?.suppliedStake || 0,
targetStake: market?.targetStake || 0,
triggeringRatio,
});
const showMessage =
@@ -97,9 +97,9 @@ const MarketData = ({
return (
<>
<div className="w-2/5" role="gridcell">
<div className="w-2/6" role="gridcell">
<h3 className="flex items-baseline">
<span className="overflow-hidden text-sm lg:text-base text-ellipsis whitespace-nowrap">
<span className="overflow-hidden text-xs md:text-sm lg:text-base text-ellipsis whitespace-nowrap">
{market.tradableInstrument.instrument.code}
</span>
{allProducts && productType && (
@@ -113,7 +113,7 @@ const MarketData = ({
)}
</div>
<div
className="w-1/5 overflow-hidden text-xs lg:text-sm whitespace-nowrap text-ellipsis"
className="w-2/6 overflow-hidden text-xs lg:text-sm whitespace-nowrap text-ellipsis text-right"
title={symbol}
data-testid="market-selector-price"
role="gridcell"
@@ -121,14 +121,14 @@ const MarketData = ({
{price} {symbol}
</div>
<div
className="w-1/5 overflow-hidden text-xs text-right lg:text-sm whitespace-nowrap text-ellipsis"
className="w-2/6 sm:w-1/6 overflow-hidden text-xs lg:text-sm whitespace-nowrap text-ellipsis text-right"
title={t('24h vol')}
data-testid="market-selector-volume"
role="gridcell"
>
{volume}
</div>
<div className="flex justify-end w-1/5" role="gridcell">
<div className="hidden sm:w-1/6 sm:flex justify-end" role="gridcell">
{oneDayCandles && (
<Sparkline
width={64}
@@ -64,7 +64,7 @@ export const MarketSelector = ({
setFilter((curr) => ({ ...curr, product }));
}}
/>
<div className="text-sm grid grid-cols-[2fr_1fr_1fr] gap-1 ">
<div className="text-sm flex sm:grid grid-cols-[2fr_1fr_1fr] gap-1 ">
<div className="flex-1">
<TradingInput
onChange={(e) =>
@@ -182,16 +182,16 @@ const MarketList = ({
'p-2 mx-2 border-b border-default text-xs text-secondary'
)}
>
<div className="w-2/5" role="columnheader">
<div className="w-2/6" role="columnheader">
{t('Name')}
</div>
<div className="w-1/5" role="columnheader">
<div className="w-2/6 text-right pr-4" role="columnheader">
{t('Price')}
</div>
<div className="w-1/5 text-right" role="columnheader">
<div className="w-2/6 sm:w-1/6 text-right" role="columnheader">
{t('24h volume')}
</div>
<div className="w-1/5" role="columnheader" />
<div className="hidden sm:w-1/6" role="columnheader" />
</div>
<div ref={listRef}>
<List
+11 -1
View File
@@ -5,6 +5,7 @@ import { useParams } from 'react-router-dom';
import * as PopoverPrimitive from '@radix-ui/react-popover';
import { useState } from 'react';
import { useT } from '../../lib/use-t';
import classNames from 'classnames';
/**
* This is only rendered for the mobile navigation
@@ -30,7 +31,16 @@ export const NavHeader = () => {
trigger={
<h1 className="flex gap-1 sm:gap-2 md:gap-4 items-center text-default text-lg whitespace-nowrap xl:pr-4 xl:border-r border-default">
{data ? data.tradableInstrument.instrument.code : t('Select market')}
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
<span
className={classNames(
'transition-transform ease-in-out duration-300',
{
'rotate-180': open,
}
)}
>
<VegaIcon name={VegaIconNames.CHEVRON_DOWN} size={20} />
</span>
</h1>
}
>
+10 -1
View File
@@ -162,6 +162,8 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
const envNameMapping = useEnvNameMapping();
const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment();
const marketId = useGlobalStore((store) => store.marketId);
const GOVERNANCE_LINK = useLinks(DApp.Governance)();
const EXPLORER_LINK = useLinks(DApp.Explorer)();
return (
<div className="gap-3 lg:flex lg:h-full">
@@ -220,7 +222,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
</NavbarLink>
</NavbarItem>
<NavbarItem>
<NavbarLinkExternal to={useLinks(DApp.Governance)()}>
<NavbarLinkExternal to={GOVERNANCE_LINK}>
{t('Governance')}
</NavbarLinkExternal>
</NavbarItem>
@@ -228,6 +230,13 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
<NavbarTrigger>{t('Resources')}</NavbarTrigger>
<NavbarContent data-testid="navbar-content-resources">
<ul className="lg:p-4">
{EXPLORER_LINK && (
<NavbarSubItem>
<NavbarLinkExternal to={EXPLORER_LINK}>
{t('Explorer')}
</NavbarLinkExternal>
</NavbarSubItem>
)}
{DocsLinks?.NEW_TO_VEGA && (
<NavbarSubItem>
<NavbarLinkExternal to={DocsLinks?.NEW_TO_VEGA}>
@@ -14,6 +14,7 @@ import {
VegaIconNames,
type VegaIconSize,
TradingInput,
TinyScroll,
} from '@vegaprotocol/ui-toolkit';
import { IconNames } from '@blueprintjs/icons';
import {
@@ -149,47 +150,45 @@ export const ActiveRewards = ({ currentEpoch }: { currentEpoch: number }) => {
return (
<Card title={t('Active rewards')} className="lg:col-span-full">
<div className="">
{transfers.length > 1 && (
<TradingInput
onChange={(e) =>
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
{transfers.length > 1 && (
<TradingInput
onChange={(e) =>
setFilter((curr) => ({ ...curr, searchTerm: e.target.value }))
}
value={filter.searchTerm}
type="text"
placeholder={t(
'Search by reward dispatch metric, entity scope or asset name'
)}
data-testid="search-term"
className="mb-4 w-20 mr-2"
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
/>
)}
<TinyScroll className="grid gap-x-8 gap-y-10 h-fit grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] md:grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] lg:grid-cols-[repeat(auto-fill,_minmax(320px,_1fr))] xl:grid-cols-[repeat(auto-fill,_minmax(335px,_1fr))] max-h-[40rem] overflow-auto pr-2">
{transfers
.filter((n) => applyFilter(n, filter))
.map((node, i) => {
const { transfer } = node;
if (
transfer.kind.__typename !== 'RecurringTransfer' ||
!transfer.kind.dispatchStrategy?.dispatchMetric
) {
return null;
}
value={filter.searchTerm}
type="text"
placeholder={t(
'Search by reward dispatch metric, entity scope or asset name'
)}
data-testid="search-term"
className="mb-4 w-20"
prependElement={<VegaIcon name={VegaIconNames.SEARCH} />}
/>
)}
<div className="grid gap-x-8 gap-y-10 h-fit grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] md:grid-cols-[repeat(auto-fill,_minmax(230px,_1fr))] lg:grid-cols-[repeat(auto-fill,_minmax(320px,_1fr))] xl:grid-cols-[repeat(auto-fill,_minmax(343px,_1fr))] max-h-[40rem] overflow-auto">
{transfers
.filter((n) => applyFilter(n, filter))
.map((node, i) => {
const { transfer } = node;
if (
transfer.kind.__typename !== 'RecurringTransfer' ||
!transfer.kind.dispatchStrategy?.dispatchMetric
) {
return null;
}
return (
node && (
<ActiveRewardCard
key={i}
transferNode={node}
kind={transfer.kind}
currentEpoch={currentEpoch}
/>
)
);
})}
</div>
</div>
return (
node && (
<ActiveRewardCard
key={i}
transferNode={node}
kind={transfer.kind}
currentEpoch={currentEpoch}
/>
)
);
})}
</TinyScroll>
</Card>
);
};
+1 -1
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
VEGA_VERSION=v0.73.10
VEGA_VERSION=v0.74.0-preview.2
LOCAL_SERVER=false
+2 -2
View File
@@ -1,3 +1,3 @@
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
VEGA_VERSION=v0.73.10
LOCAL_SERVER=false
VEGA_VERSION=v0.74.0-preview.2
LOCAL_SERVER=false
+16 -1
View File
@@ -8,7 +8,7 @@ import docker
import http.server
import sys
from dotenv import load_dotenv
from playwright.sync_api import Error as PlaywrightError
from docker.models.containers import Container
from docker.errors import APIError
from contextlib import contextmanager
@@ -274,3 +274,18 @@ def perps_market(vega, request):
if hasattr(request, "param"):
kwargs.update(request.param)
return setup_perps_market(vega, **kwargs)
@pytest.fixture(autouse=True)
def retry_on_http_error(request):
retry_count = 3
for i in range(retry_count):
try:
yield
return
except requests.exceptions.HTTPError:
if i < retry_count - 1:
print(f"Retrying due to HTTPError (attempt {i+1}/{retry_count})")
else:
raise
+56 -18
View File
@@ -18,14 +18,14 @@ def setup_simple_market(
custom_market_name=market_name,
custom_asset_name="tDAI",
custom_asset_symbol="tDAI",
custom_quantum=1
custom_quantum=1,
):
for wallet in wallets:
vega.create_key(wallet.name)
vega.mint(
MM_WALLET.name,
asset="VOTE",
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
amount=mint_amount,
)
@@ -117,18 +117,30 @@ def setup_simple_successor_market(
return market_id
def setup_opening_auction_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
def setup_opening_auction_market(
vega: VegaService,
market_id: str = None,
buy_orders=default_buy_orders,
sell_orders=default_sell_orders,
add_liquidity=True,
custom_market_name="BTC:DAI_2023",
custom_asset_name="tDAI",
custom_asset_symbol="tDAI",
**kwargs,
):
if not market_exists(vega, market_id):
market_id = setup_simple_market(vega, **kwargs)
market_id = setup_simple_market(
vega,
custom_market_name=custom_market_name,
custom_asset_name=custom_asset_name,
custom_asset_symbol=custom_asset_symbol,
**kwargs,
)
if add_liquidity:
submit_liquidity(vega, MM_WALLET.name, market_id)
submit_multiple_orders(
vega, MM_WALLET.name, market_id, "SIDE_SELL", sell_orders
)
submit_multiple_orders(
vega, MM_WALLET2.name, market_id, "SIDE_BUY", buy_orders
)
submit_multiple_orders(vega, MM_WALLET.name, market_id, "SIDE_SELL", sell_orders)
submit_multiple_orders(vega, MM_WALLET2.name, market_id, "SIDE_BUY", buy_orders)
vega.forward("10s")
vega.wait_fn(1)
@@ -146,13 +158,37 @@ def market_exists(vega: VegaService, market_id: str):
# Add sell orders and buy orders to put on the book
def setup_continuous_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
if not market_exists(vega, market_id) or buy_orders != default_buy_orders or sell_orders != default_sell_orders:
def setup_continuous_market(
vega: VegaService,
market_id: str = None,
buy_orders=default_buy_orders,
sell_orders=default_sell_orders,
add_liquidity=True,
custom_market_name="BTC:DAI_2023",
custom_asset_name="tDAI",
custom_asset_symbol="tDAI",
**kwargs,
):
if (
not market_exists(vega, market_id)
or buy_orders != default_buy_orders
or sell_orders != default_sell_orders
):
market_id = setup_opening_auction_market(
vega, market_id, buy_orders, sell_orders, add_liquidity, **kwargs)
vega,
market_id,
buy_orders,
sell_orders,
add_liquidity,
custom_market_name=custom_market_name,
custom_asset_name=custom_asset_name,
custom_asset_symbol=custom_asset_symbol,
**kwargs,
)
submit_order(vega, "Key 1", market_id, "SIDE_BUY",
sell_orders[0][0], sell_orders[0][1])
submit_order(
vega, "Key 1", market_id, "SIDE_BUY", sell_orders[0][0], sell_orders[0][1]
)
vega.forward("10s")
vega.wait_fn(1)
@@ -171,7 +207,7 @@ def setup_perps_market(
vega.mint(
MM_WALLET.name,
asset="VOTE",
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
amount=mint_amount,
)
@@ -250,6 +286,8 @@ def setup_perps_market(
def market_exists(vega: VegaService, market_id: str):
if market_id is None:
return False
all_markets = vega.all_markets()
all_markets = vega.all_markets()
market_ids = [market.id for market in all_markets]
return market_id in market_ids
print("Checking for market ID:", market_id)
print("Available market IDs:", market_ids)
return market_id in market_ids
+4 -4
View File
@@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand.
[[package]]
name = "certifi"
@@ -1160,8 +1160,8 @@ profile = ["pytest-profiling", "snakeviz"]
[package.source]
type = "git"
url = "https://github.com/vegaprotocol/vega-market-sim.git/"
reference = "fix/genesis_panic"
resolved_reference = "de30d2d4c7a1b81a830527ca76473e23ef59de12"
reference = "HEAD"
resolved_reference = "2aed8c94b25d8fa2e376d3b63ca1f9193d28cdfd"
[[package]]
name = "websocket-client"
@@ -1342,4 +1342,4 @@ files = [
[metadata]
lock-version = "2.0"
python-versions = ">=3.9,<3.11"
content-hash = "68ed0de55290a3b929d47eb7f7b031fb7e172261c7bbeb4f554b7c27a4462754"
content-hash = "39ce8400de7bf060857447281ef27bd78c9b1d9639da063b051e3ae6e7887a67"
+1 -1
View File
@@ -9,7 +9,7 @@ packages = [{include = "trading market-sim e2e"}]
[tool.poetry.dependencies]
python = ">=3.9,<3.11"
psutil = "^5.9.5"
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/", branch = "fix/genesis_panic"}
vega-sim = {git = "https://github.com/vegaprotocol/vega-market-sim.git/"}
pytest-playwright = "^0.4.2"
docker = "^6.1.3"
pytest-xdist = "^3.3.1"
@@ -47,8 +47,8 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Pag
vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
# 7002-SORD-017
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr10+10LimitFilled120.00GTT:"
expect(page.get_by_role("row").nth(5)).to_contain_text(
"10+10LimitFilled120.00GTT:"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@@ -64,8 +64,8 @@ def test_limit_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
# 7002-SORD-017
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr10+10LimitFilled120.00GTC"
expect(page.get_by_role("row").nth(6)).to_contain_text(
"10+10LimitFilled120.00GTC"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@@ -88,8 +88,8 @@ def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr10-10LimitFilled100.00GFN"
expect(page.get_by_role("row").nth(7)).to_contain_text(
"10-10LimitFilled100.00GFN"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@@ -112,8 +112,8 @@ def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page)
vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr10-10MarketFilled-IOC"
expect(page.get_by_role("row").nth(8)).to_contain_text(
"10-10MarketFilled-IOC"
)
@pytest.mark.usefixtures("auth", "risk_accepted")
@@ -131,6 +131,6 @@ def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
# 7002-SORD-010
# 0003-WTXN-012
# 0003-WTXN-003
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr10+10MarketFilled-FOK"
expect(page.get_by_role("row").nth(9)).to_contain_text(
"10+10MarketFilled-FOK"
)
@@ -27,7 +27,7 @@ submit_stop_order = "place-order"
stop_orders_tab = "Stop orders"
row_table = "row"
cancel = "cancel"
market_name_col = '[col-id="market.tradableInstrument.instrument.code"]'
market_name_col = '[data-testid="market-code"]'
trigger_col = '[col-id="trigger"]'
expiresAt_col = '[col-id="expiresAt"]'
size_col = '[col-id="submission.size"]'
+1 -3
View File
@@ -53,7 +53,7 @@ FEE_BREAKDOWN_TOOLTIP = "fee-breakdown-tooltip"
PINNED_ROW_LOCATOR = ".ag-pinned-left-cols-container .ag-row"
ROW_LOCATOR = ".ag-center-cols-container .ag-row"
# Col-Ids:
COL_INSTRUMENT_CODE = '[col-id="market.tradableInstrument.instrument.code"]'
COL_INSTRUMENT_CODE = '[data-testid="market-code"]'
COL_CODE = '[col-id="code"]'
COL_SIZE = '[col-id="size"]'
COL_PRICE = '[col-id="price"]'
@@ -563,7 +563,6 @@ def test_fills_taker_discount_program(
page.goto(f"/#/markets/{market_id}")
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
expect(row.locator(COL_SIZE)).to_have_text(size)
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
@@ -605,7 +604,6 @@ def test_fills_maker_discount_program(
change_keys(page, vega_instance, MM_WALLET.name)
page.get_by_test_id(FILLS).click()
row = page.get_by_test_id(TAB_FILLS).locator(ROW_LOCATOR).first
expect(row.locator(COL_INSTRUMENT_CODE)).to_have_text("BTC:DAI_2023Futr")
expect(row.locator(COL_SIZE)).to_have_text(size)
expect(row.locator(COL_PRICE)).to_have_text("103.50 tDAI")
expect(row.locator(COL_PRICE_1)).to_have_text(price_1)
@@ -77,7 +77,7 @@ class TestGetStarted:
vega.mint(
MM_WALLET.name,
asset="VOTE",
asset=vega.find_asset_id(symbol="VOTE", enabled=True),
amount=mint_amount,
)
@@ -105,6 +105,8 @@ class TestGetStarted:
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.reload()
# Assert step 2 complete
expect(page.get_by_test_id("icon-tick")).to_have_count(2)
@@ -70,7 +70,6 @@ def test_iceberg_open_order(continuous_market, vega: VegaServiceNull, page: Page
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='size']").first
).to_have_text("-102")
page.pause()
expect(
page.locator(".ag-center-cols-container .ag-row [col-id='type'] ").first
).to_have_text("Limit (Iceberg)")
@@ -16,7 +16,6 @@ def vega(request):
def continuous_market(vega):
return setup_continuous_market(vega)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_liquidity_provision_amendment(continuous_market, vega: VegaServiceNull, page: Page):
# TODO Refactor asserting the grid
@@ -56,9 +56,6 @@ def test_renders_markets_correctly(proposed_market, page: Page):
page.goto(f"/#/markets/all")
page.click('[data-testid="Proposed markets"]')
row = page.locator(row_selector)
# 6001-MARK-049
expect(row.locator(col_market_id)).to_have_text("BTC:DAI_2023")
# 6001-MARK-051
expect(row.locator('[col-id="asset"]')).to_have_text("tDAI")
@@ -64,18 +64,16 @@ def setup_market_monitoring_auction(vega: VegaServiceNull, simple_market):
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 1, 1 + 0.1 / 2)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_SELL", 1, 1)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
# add orders that change the price so that it goes beyond the limits of price monitoring
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 110)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 90)
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 105)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 95)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 105)
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 300)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 290)
submit_order(vega, MM_WALLET.name, simple_market, "SIDE_SELL", 100, 305)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 100, 295)
submit_order(vega, MM_WALLET2.name, simple_market, "SIDE_BUY", 1, 305)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -91,7 +89,6 @@ def test_market_monitoring_auction_price_volatility_limit_order(
page.get_by_test_id("order-price").type("110")
page.get_by_test_id("order-tif").select_option("Fill or Kill (FOK)")
page.get_by_test_id("place-order").click()
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
)
@@ -109,12 +106,11 @@ def test_market_monitoring_auction_price_volatility_limit_order(
page.get_by_test_id("place-order").click()
wait_for_toast_confirmation(page)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(2)).to_contain_text(
"BTC:DAI_2023Futr0+1LimitActive110.00GTC"
expect(page.get_by_role("row").nth(4)).to_contain_text(
"0+1LimitActive110.00GTC"
)
@@ -128,7 +124,6 @@ def test_market_monitoring_auction_price_volatility_market_order(
page.get_by_test_id("order-size").type("1")
# 7002-SORD-060
page.get_by_test_id("place-order").click()
expect(page.get_by_test_id("deal-ticket-error-message-tif")).to_have_text(
"This market is in auction due to high price volatility. Until the auction ends, you can only place GFA, GTT, or GTC limit orders."
)
@@ -174,7 +174,7 @@ def test_market_lifecycle(proposed_market, vega: VegaServiceNull, page: Page):
vega.wait_fn(1)
vega.wait_for_total_catchup()
# market state should be changed to "Trading Terminated" because of the invalid oracle
# market state should be changed to "No trading" because of the invalid oracle
expect(trading_mode).to_have_text("No trading")
expect(market_state).to_have_text("Trading Terminated")
@@ -65,7 +65,6 @@ def test_limit_order_trade_open_order(
expect(orderbook_trade).to_be_visible()
expected_open_order = [
"BTC:DAI_2023",
"+1",
"Limit",
"Active",
@@ -87,9 +86,6 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
secondary_id = "stack-cell-secondary"
position = {
"market_code": "BTC:DAI_2023",
"settlement_asset": "tDAI",
"product_type": "Futr",
"size": "+1",
"notional": "107.50",
"average_entry_price": "107.50",
@@ -107,12 +103,6 @@ def test_limit_order_trade_open_position(continuous_market, page: Page):
# 7004-POSI-001
# 7004-POSI-002
market = table.locator("[col-id='marketCode']")
expect(market.get_by_test_id(primary_id)).to_have_text(position["market_code"])
expect(market.get_by_test_id(secondary_id)).to_have_text(
position["settlement_asset"] + position["product_type"]
)
size_and_notional = table.locator("[col-id='openVolume']")
expect(size_and_notional.get_by_test_id(primary_id)).to_have_text(position["size"])
expect(size_and_notional.get_by_test_id(secondary_id)).to_have_text(
@@ -257,46 +257,63 @@ def test_order_sorted(page: Page):
def test_order_status_active(page: Page):
# 7002-SORD-041
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-2Futr" + "0" + "-10" + "Limit" + "Active" + "150.00" + "GTC"
expect(page.locator('[row-index="2"]').first).to_contain_text(
"market-2Futr"
)
expect(page.locator('[row-index="2"]').nth(1)).to_contain_text(
"0" + "-10" + "Limit" + "Active" + "150.00" + "GTC"
)
def test_status_expired(page: Page):
# 7002-SORD-042
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-3Futr" + "0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:"
expect(page.locator('[row-index="7"]').first).to_contain_text(
"market-3Futr"
)
expect(page.locator('[row-index="7"]').nth(1)).to_contain_text(
"0" + "-10" + "Limit" + "Expired" + "120.00" + "GTT:"
)
def test_order_status_Stopped(page: Page):
# 7002-SORD-044
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-1Futr" + "0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC"
expect(page.locator('[row-index="12"]').first).to_contain_text(
"market-1Futr"
)
expect(page.locator('[row-index="12"]').nth(1)).to_contain_text(
"0" + "-100" + "Limit" + "Stopped" + "130.00" + "IOC"
)
def test_order_status_partially_filled(page: Page):
# 7002-SORD-045
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-2Futr" + "99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC"
expect(page.locator('[row-index="8"]').first).to_contain_text(
"market-2Futr"
)
expect(page.locator('[row-index="8"]').nth(1)).to_contain_text(
"99" + "+100" + "Limit" + "Partially Filled" + "104.00" + "IOC"
)
def test_order_status_filled(page: Page):
# 7002-SORD-046
# 7003-MORD-020
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-1Futr" + "100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC"
expect(page.locator('[row-index="11"]').first).to_contain_text(
"market-1Futr"
)
expect(page.locator('[row-index="11"]').nth(1)).to_contain_text(
"100" + "-100" + "Limit" + "Filled" + "88.00" + "GTC"
)
def test_order_status_rejected(page: Page):
# 7002-SORD-047
# 7003-MORD-018
expect(page.get_by_test_id(order_tab)).to_contain_text(
expect(page.locator('[row-index="9"]').first).to_contain_text(
"market-1Futr"
+ "0"
)
expect(page.locator('[row-index="9"]').nth(1)).to_contain_text(
"0"
+ "-10,000,000,000"
+ "Limit"
+ "Rejected: Margin check failed"
@@ -308,9 +325,11 @@ def test_order_status_rejected(page: Page):
def test_order_status_parked(page: Page):
# 7002-SORD-048
# 7003-MORD-016
expect(page.get_by_test_id(order_tab)).to_contain_text(
expect(page.locator('[row-index="3"]').first).to_contain_text(
"market-5Futr"
+ "0"
)
expect(page.locator('[row-index="3"]').nth(1)).to_contain_text(
"0"
+ "-60"
+ "Ask + 15.00 Peg limit"
+ "Parked"
@@ -321,9 +340,11 @@ def test_order_status_parked(page: Page):
def test_order_status_pegged_ask(page: Page):
# 7003-MORD-016
expect(page.get_by_test_id(order_tab)).to_contain_text(
expect(page.locator('[row-index="4"]').first).to_contain_text(
"market-4Futr"
+ "0"
)
expect(page.locator('[row-index="4"]').nth(1)).to_contain_text(
"0"
+ "-60"
+ "Ask + 15.00 Peg limit"
+ "Active"
@@ -334,9 +355,11 @@ def test_order_status_pegged_ask(page: Page):
def test_order_status_pegged_bid(page: Page):
# 7003-MORD-016
expect(page.get_by_test_id(order_tab)).to_contain_text(
expect(page.locator('[row-index="5"]').first).to_contain_text(
"market-4Futr"
+ "0"
)
expect(page.locator('[row-index="5"]').nth(1)).to_contain_text(
"0"
+ "+40"
+ "Bid - 10.00 Peg limit"
+ "Active"
@@ -347,9 +370,11 @@ def test_order_status_pegged_bid(page: Page):
def test_order_status_pegged_mid(page: Page):
# 7003-MORD-016
expect(page.get_by_test_id(order_tab)).to_contain_text(
expect(page.locator('[row-index="6"]').first).to_contain_text(
"market-4Futr"
+ "0"
)
expect(page.locator('[row-index="6"]').nth(1)).to_contain_text(
"0"
+ "+20"
+ "Mid - 5.00 Peg limit"
+ "Active"
@@ -372,9 +397,11 @@ def test_order_amend_order(vega: VegaServiceNull, page: Page):
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-2Futr" + "0" + "-15" + "Limit" + "Active" + "170.00" + "GTC"
expect(page.locator('[row-index="1"]').first).to_contain_text(
"market-2Futr"
)
expect(page.locator('[row-index="1"]').nth(1)).to_contain_text(
"0" + "-15" + "Limit" + "Active" + "170.00" + "GTC"
)
@@ -389,9 +416,11 @@ def test_order_cancel_single_order(vega: VegaServiceNull, page: Page):
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
expect(page.get_by_test_id(order_tab)).to_contain_text(
"market-3Futr" + "0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC"
expect(page.locator('[row-index="0"]').first).to_contain_text(
"market-3Futr"
)
expect(page.locator('[row-index="0"]').nth(1)).to_contain_text(
"0" + "+10" + "Limit" + "Cancelled" + "60.00" + "GTC"
)
@@ -22,7 +22,7 @@ def test_closed_market_position(vega: VegaServiceNull, page: Page):
expect(page.locator(".ag-overlay-panel")).to_have_text("No positions")
page.get_by_test_id("open-transfer").click()
tab = page.get_by_test_id("tab-positions")
table = tab.locator(".ag-center-cols-container")
table = tab.locator('[class="ag-body ag-layout-normal"]')
market = table.locator("[col-id='marketCode']")
expect(market.get_by_test_id("stack-cell-primary")).to_have_text("BTC:DAI_2023")
page.get_by_test_id("open-transfer").click()
+269 -195
View File
@@ -1,13 +1,34 @@
import pytest
import logging
import vega_sim.proto.vega as vega_protos
from typing import Tuple, Any
from playwright.sync_api import Page, expect
from conftest import init_vega, init_page, auth_setup
from fixtures.market import setup_continuous_market, market_exists
from actions.utils import next_epoch, change_keys
from wallet_config import MM_WALLET, PARTY_A, PARTY_B, PARTY_C, PARTY_D
from vega_sim.service import VegaService
from vega_sim.null_service import VegaServiceNull
# region Constants
ACTIVITY = "activity"
HOARDER = "hoarder"
COMBO = "combo"
REWARDS_URL = "/#/rewards"
# test IDs
COMBINED_MULTIPLIERS = "combined-multipliers"
TOTAL_REWARDS = "total-rewards"
PRICE_TAKING_COL_ID = '[col-id="priceTaking"]'
TOTAL_COL_ID = '[col-id="total"]'
ROW = "row"
STREAK_REWARD_MULTIPLIER_VALUE = "streak-reward-multiplier-value"
HOARDER_REWARD_MULTIPLIER_VALUE = "hoarder-reward-multiplier-value"
HOARDER_BONUS_TOTAL_HOARDED = "hoarder-bonus-total-hoarded"
EARNED_BY_ME_BUTTON = "earned-by-me-button"
TRANSFER_AMOUNT = "transfer-amount"
EPOCH_STREAK = "epoch-streak"
# endregion
@pytest.fixture(scope="module")
@@ -24,19 +45,19 @@ def market_ids():
@pytest.fixture(scope="module")
def vega_activity_tier_0(request):
# with init_vega(request) as vega_activity_tier_0:
with init_vega(request) as vega_activity_tier_0:
yield vega_activity_tier_0
@pytest.fixture(scope="module")
def vega_hoarder_tier_0(request):
# with init_vega(request) as vega_hoarder_tier_0:
with init_vega(request) as vega_hoarder_tier_0:
yield vega_hoarder_tier_0
@pytest.fixture(scope="module")
def vega_combo_tier_0(request):
# with init_vega(request) as vega_combo_tier_0:
with init_vega(request) as vega_combo_tier_0:
yield vega_combo_tier_0
@@ -48,79 +69,48 @@ def vega_activity_tier_1(request):
@pytest.fixture(scope="module")
def vega_hoarder_tier_1(request):
#with init_vega(request) as vega_hoarder_tier_1:
with init_vega(request) as vega_hoarder_tier_1:
yield vega_hoarder_tier_1
@pytest.fixture(scope="module")
def vega_combo_tier_1(request):
# with init_vega(request) as vega_combo_tier_1:
with init_vega(request) as vega_combo_tier_1:
yield vega_combo_tier_1
@pytest.fixture
def auth(vega_instance, page):
vega, _, _ = vega_instance
return auth_setup(vega, page)
return auth_setup(vega_instance, page)
@pytest.fixture
def page(vega_instance, browser, request):
vega, _, _ = vega_instance
with init_page(vega, browser, request) as page_instance:
with init_page(vega_instance, browser, request) as page_instance:
yield page_instance
@pytest.fixture
def vega_instance(
reward_program: str,
vega_activity_tier_0: Any,
vega_hoarder_tier_0: Any,
vega_combo_tier_0: Any,
vega_activity_tier_1: Any,
vega_hoarder_tier_1: Any,
vega_combo_tier_1: Any,
market_ids: list,
tier: int,
) -> Tuple[Any, Any, Any]:
"""
Create a Vega instance based on the reward program and tier.
:param reward_program: The reward program type.
:param vega_activity_tier_0: The Vega instance for activity tier 0.
:param vega_hoarder_tier_0: The Vega instance for hoarder tier 0.
:param vega_combo_tier_0: The Vega instance for combo tier 0.
:param vega_activity_tier_1: The Vega instance for activity tier 1.
:param vega_hoarder_tier_1: The Vega instance for hoarder tier 1.
:param vega_combo_tier_1: The Vega instance for combo tier 1.
:param market_ids: List of market IDs.
:param tier: The tier level.
:return: Tuple containing the Vega instance, market ID, and tDAI asset ID.
"""
vega_tiers = {
"activity": (vega_activity_tier_0, vega_activity_tier_1),
"hoarder": (vega_hoarder_tier_0, vega_hoarder_tier_1),
"combo": (vega_combo_tier_0, vega_combo_tier_1),
}
if reward_program not in vega_tiers or tier not in (0, 1):
logging.error(f"Invalid reward_program '{reward_program}' or tier '{tier}'")
raise ValueError(f"Invalid reward_program '{reward_program}' or tier '{tier}'")
vega = vega_tiers[reward_program][tier]
# Set up market with the reward program
logging.info("Setting up Vega Instance")
market_id, tDAI_asset_id = set_market_reward_program(
vega, reward_program, market_ids, tier
)
return vega, market_id, tDAI_asset_id
reward_program,
vega_activity_tier_0,
vega_hoarder_tier_0,
vega_combo_tier_0,
vega_activity_tier_1,
vega_hoarder_tier_1,
vega_combo_tier_1,
tier,
):
if reward_program == "activity":
return vega_activity_tier_0 if tier == 1 else vega_activity_tier_1
elif reward_program == "hoarder":
return vega_hoarder_tier_0 if tier == 1 else vega_hoarder_tier_1
elif reward_program == "combo":
return vega_combo_tier_0 if tier == 1 else vega_combo_tier_1
def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
print("Started setup_market_with_reward_program")
def setup_market_with_reward_program(vega: VegaServiceNull, reward_programs, tier):
print(f"Started setup_market_with_{reward_programs}_{tier}")
tDAI_market = setup_continuous_market(vega)
tDAI_asset_id = vega.find_asset_id(symbol="tDAI")
vega.mint(key_name=PARTY_B.name, asset=tDAI_asset_id, amount=100000)
@@ -128,7 +118,7 @@ def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
vega.mint(key_name=PARTY_A.name, asset=tDAI_asset_id, amount=100000)
vega.mint(key_name=PARTY_D.name, asset=tDAI_asset_id, amount=100000)
next_epoch(vega=vega)
if "activity" in reward_programs:
if ACTIVITY in reward_programs:
vega.update_network_parameter(
proposal_key=MM_WALLET.name,
parameter="rewards.activityStreak.benefitTiers",
@@ -137,7 +127,7 @@ def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
print("update_network_parameter activity done")
next_epoch(vega=vega)
if "hoarder" in reward_programs:
if HOARDER in reward_programs:
vega.update_network_parameter(
proposal_key=MM_WALLET.name,
parameter="rewards.vesting.benefitTiers",
@@ -160,6 +150,7 @@ def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
# lock_period= 5,
# TODO test lock period
amount=100,
factor=1.0,
)
@@ -181,7 +172,17 @@ def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
)
vega.wait_for_total_catchup()
""" if tier == 1:
if tier == 1:
next_epoch(vega=vega)
vega.submit_order(
trading_key=PARTY_B.name,
market_id=tDAI_market,
order_type="TYPE_LIMIT",
time_in_force="TIME_IN_FORCE_GTC",
side="SIDE_BUY",
price=1,
volume=1,
)
next_epoch(vega=vega)
vega.submit_order(
trading_key=PARTY_B.name,
@@ -199,46 +200,62 @@ def setup_market_with_reward_program(vega: VegaService, reward_programs, tier):
side="SIDE_BUY",
volume=1,
)
vega.wait_for_total_catchup() """
#next_epoch(vega=vega)
vega.wait_for_total_catchup()
next_epoch(vega=vega)
next_epoch(vega=vega)
if HOARDER in reward_programs:
vega.submit_order(
trading_key=PARTY_B.name,
market_id=tDAI_market,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.submit_order(
trading_key=PARTY_D.name,
market_id=tDAI_market,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.wait_for_total_catchup()
next_epoch(vega=vega)
next_epoch(vega=vega)
next_epoch(vega=vega)
return tDAI_market, tDAI_asset_id
def set_market_reward_program(vega, reward_program, market_ids, tier):
market_id_key = f"vega_{reward_program}"
if reward_program == "combo":
market_id_key = "combo"
market_id_key = f"vega_{reward_program}_tier_{tier}"
if reward_program == COMBO:
market_id_key = COMBO
market_id = market_ids.get(market_id_key, "default_id")
print(f"Checking if market exists: {market_id}")
if not market_exists(vega, market_id):
print(f"Market doesn't exist for {reward_program}. Setting up new market.")
print(
f"Market doesn't exist for {reward_program} {tier}. Setting up new market."
)
reward_programs = [reward_program]
if reward_program == "combo":
reward_programs = ["activity", "hoarder"]
if reward_program == COMBO:
reward_programs = [ACTIVITY, HOARDER]
market_id = setup_market_with_reward_program(vega, reward_programs, tier)
market_id, _ = setup_market_with_reward_program(vega, reward_programs, tier)
market_ids[market_id_key] = market_id
print(f"Using market ID: {market_id}")
return market_id
return market_id, market_ids
ACTIVITY_STREAKS = """
{
"tiers": [
{
"minimum_activity_streak": 1,
"minimum_activity_streak": 2,
"reward_multiplier": "2.0",
"vesting_multiplier": "1.1"
},
{
"minimum_activity_streak": 5,
"reward_multiplier": "3.0",
"vesting_multiplier": "1.2"
}
]
}
@@ -247,12 +264,8 @@ VESTING = """
{
"tiers": [
{
"minimum_quantum_balance": "5000000",
"minimum_quantum_balance": "10000000",
"reward_multiplier": "2"
},
{
"minimum_quantum_balance": "11666668",
"reward_multiplier": "3"
}
]
}
@@ -262,134 +275,195 @@ VESTING = """
@pytest.mark.parametrize(
"reward_program, tier, total_rewards",
[
#("activity", 0, "50.00 tDAI"),
#("hoarder", 0, "50.00 tDAI"),
#("combo", 0, "50.00 tDAI"),
("activity", 1, "110.00 tDAI"),
#("hoarder", 1, "116.66666 tDAI"),
#("combo", 1, "125.00 tDAI"),
(ACTIVITY, 0, "50.00 tDAI"),
(HOARDER, 0, "50.00 tDAI"),
(COMBO, 0, "50.00 tDAI"),
(ACTIVITY, 1, "116.66666 tDAI"),
(HOARDER, 1, "166.66666 tDAI "),
(COMBO, 1, "183.33333 tDAI"),
],
)
@pytest.mark.skip("tbd")
@pytest.mark.usefixtures("auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_network_reward_pot(
reward_program, vega_instance: VegaService, page: Page, total_rewards, tier
reward_program,
vega_instance: VegaServiceNull,
page: Page,
total_rewards,
tier,
market_ids,
):
vega, market_id, tDAI_asset_id = vega_instance
next_epoch(vega=vega)
page.goto(f"/#/rewards")
if tier == 1:
page.pause()
next_epoch(vega=vega)
next_epoch(vega=vega)
vega.submit_order(
trading_key=PARTY_B.name,
market_id=market_id,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.submit_order(
trading_key=PARTY_D.name,
market_id=market_id,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.wait_for_total_catchup()
page.pause()
next_epoch(vega=vega)
page.pause()
next_epoch(vega=vega)
change_keys(page, vega, PARTY_B.name)
page.pause()
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
expect(page.get_by_test_id("total-rewards")).to_have_text(total_rewards)
# TODO Add test ID and Assert for locked,
""" @pytest.mark.parametrize(
"reward_program",
[
("activity"),
# ("hoarder"),
# ("combo"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_vesting(vega_setup, vega: VegaService, page: Page):
expect() """
@pytest.mark.skip("tbd")
@pytest.mark.parametrize(
"reward_program, tier, reward_multiplier",
[
("activity", 0, "1x"),
("hoarder", 0, "1x"),
("combo", 0, "1x"),
("activity", 1, "2x"),
("hoarder", 1, "2x"),
("combo", 1, "4x"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_reward_multiplier(reward_program, vega_instance: VegaService, page: Page, reward_multiplier, tier):
vega, market_id, tDAI_asset_id = vega_instance
page.goto(f"/#/rewards")
change_keys(page, vega, PARTY_B.name)
expect(page.get_by_test_id("combined-multipliers")).to_have_text(reward_multiplier)
#TODO add test ids and assert for individual multipliers
"""
@pytest.mark.parametrize(
"reward_program",
[
("activity"),
# ("hoarder"),
# ("combo"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_activity_streak(vega_setup, vega: VegaService, page: Page):
expect()
change_keys(page, vega_instance, PARTY_B.name)
expect(page.get_by_test_id(TOTAL_REWARDS)).to_have_text(total_rewards)
@pytest.mark.parametrize(
"reward_program",
"reward_program, tier, reward_multiplier, streak_multiplier, hoarder_multiplier",
[
("activity"),
# ("hoarder"),
# ("combo"),
(ACTIVITY, 0, "1x", "1x", "1x"),
(HOARDER, 0, "1x", "1x", "1x"),
(COMBO, 0, "1x", "1x", "1x"),
(ACTIVITY, 1, "2x", "2x", "1x"),
(HOARDER, 1, "2x", "1x", "2x"),
(COMBO, 1, "4x", "2x", "2x"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_hoarder_Bonus(vega_setup, vega: VegaService, page: Page):
expect()
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_reward_multiplier(
reward_program,
vega_instance: VegaServiceNull,
page: Page,
reward_multiplier,
streak_multiplier,
hoarder_multiplier,
tier,
market_ids,
):
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B.name)
expect(page.get_by_test_id(COMBINED_MULTIPLIERS)).to_have_text(reward_multiplier)
expect(page.get_by_test_id(STREAK_REWARD_MULTIPLIER_VALUE)).to_have_text(
streak_multiplier
)
expect(page.get_by_test_id(HOARDER_REWARD_MULTIPLIER_VALUE)).to_have_text(
hoarder_multiplier
)
@pytest.mark.parametrize(
"reward_program",
"reward_program, tier, epoch_streak",
[
("activity"),
# ("hoarder"),
# ("combo"),
(ACTIVITY, 0, "1"),
(ACTIVITY, 1, "7"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_Rewards_history(vega_setup, vega: VegaService, page: Page):
expect()
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_activity_streak(
reward_program,
vega_instance: VegaServiceNull,
page: Page,
epoch_streak,
tier,
market_ids,
):
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B.name)
if tier == 1:
expect(page.get_by_test_id(EPOCH_STREAK)).to_have_text(
"Active trader: " + epoch_streak + " epochs so far (Tier 1 as of last epoch)"
)
else:
expect(page.get_by_test_id(EPOCH_STREAK)).to_have_text(
"Active trader: " + epoch_streak + " epochs so far "
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_redeem(vega_setup, vega: VegaService, page: Page):
expect()
@pytest.mark.parametrize(
"reward_program, tier, rewards_hoarded",
[
(HOARDER, 0, "5,000,000"),
(HOARDER, 1, "16,666,666"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_hoarder_bonus(
reward_program,
vega_instance: VegaServiceNull,
page: Page,
rewards_hoarded,
tier,
market_ids,
):
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B.name)
expect(page.get_by_test_id(HOARDER_BONUS_TOTAL_HOARDED)).to_contain_text(
rewards_hoarded
)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_redeem(vega_setup, vega: VegaService, page: Page):
expect()
"""
@pytest.mark.parametrize(
"reward_program, tier, price_taking, total, earned_by_me",
[
(ACTIVITY, 0, "100.00100.00%", "100.00", "50.00"),
(HOARDER, 0, "100.00100.00%", "100.00", "50.00"),
(COMBO, 0, "100.00100.00%", "100.00", "50.00"),
(ACTIVITY, 1, "300.00100.00%", "300.00", "116.66666"),
(HOARDER, 1, "299.99999100.00%", "299.99999", "166.66666"),
(COMBO, 1, "299.99999100.00%", "299.99999", "183.33333"),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_reward_history(
reward_program,
vega_instance: VegaServiceNull,
page: Page,
price_taking,
total,
earned_by_me,
tier,
market_ids,
):
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B.name)
page.locator('[name="fromEpoch"]').fill("1")
expect((page.get_by_role(ROW).locator(PRICE_TAKING_COL_ID)).nth(1)).to_have_text(
price_taking
)
expect((page.get_by_role(ROW).locator(TOTAL_COL_ID)).nth(1)).to_have_text(total)
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(
earned_by_me
)
@pytest.mark.parametrize(
"reward_program, tier",
[
(ACTIVITY, 1),
],
)
@pytest.mark.usefixtures("auth", "risk_accepted", "market_ids")
def test_redeem(
reward_program, vega_instance: VegaServiceNull, page: Page, tier, market_ids
):
print("reward program: " + reward_program, " tier:", tier)
market_id, market_ids = set_market_reward_program(
vega_instance, reward_program, market_ids, tier
)
page.goto(REWARDS_URL)
change_keys(page, vega_instance, PARTY_B.name)
page.get_by_test_id("redeem-rewards-button").click()
available_to_withdraw = page.get_by_test_id(
"available-to-withdraw-value"
).text_content()
option_value = page.locator(
'[data-testid="transfer-form"] [name="fromAccount"] option[value^="ACCOUNT_TYPE_VESTED_REWARDS"]'
).first.get_attribute("value")
page.select_option(
'[data-testid="transfer-form"] [name="fromAccount"]', option_value
)
page.get_by_test_id("use-max-button").first.click()
expect(page.get_by_test_id(TRANSFER_AMOUNT)).to_have_text(available_to_withdraw)
@@ -0,0 +1,65 @@
import pytest
import vega_sim.proto.vega as vega_protos
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.utils import next_epoch, change_keys
from wallet_config import MM_WALLET, PARTY_A, PARTY_B
@pytest.mark.usefixtures("risk_accepted", "auth")
def test_vesting(continuous_market, vega: VegaServiceNull, page: Page):
tDAI_asset_id = vega.find_asset_id(symbol="tDAI")
vega.update_network_parameter(
MM_WALLET.name, parameter="reward.asset", new_value=tDAI_asset_id
)
vega.mint(key_name=PARTY_B.name, asset=tDAI_asset_id, amount=100000)
vega.mint(key_name=PARTY_A.name, asset=tDAI_asset_id, amount=100000)
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_REWARD_MAKER_PAID_FEES,
asset=tDAI_asset_id,
reference="reward",
asset_for_metric=tDAI_asset_id,
metric=vega_protos.vega.DISPATCH_METRIC_MAKER_FEES_PAID,
lock_period=5,
amount=100,
factor=1.0,
)
vega.submit_order(
trading_key=PARTY_B.name,
market_id=continuous_market,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.submit_order(
trading_key=PARTY_A.name,
market_id=continuous_market,
order_type="TYPE_MARKET",
time_in_force="TIME_IN_FORCE_IOC",
side="SIDE_BUY",
volume=1,
)
vega.wait_for_total_catchup()
page.goto("/#/rewards")
change_keys(page, vega, PARTY_B.name)
next_epoch(vega=vega)
page.reload()
expect(page.get_by_test_id("locked-value")).to_have_text("50.00")
# Proceed through the 5 epoch lock period
next_epoch(vega=vega)
next_epoch(vega=vega)
next_epoch(vega=vega)
next_epoch(vega=vega)
next_epoch(vega=vega)
page.reload()
expect(page.get_by_test_id("locked-value")).to_have_text("0.00")
expect(page.get_by_test_id("vesting-value")).to_have_text("37.50")
expect(page.get_by_test_id("available-to-withdraw-value")).to_have_text("12.50")
@@ -73,6 +73,6 @@ def test_limit_order_new_trade_top_of_list(
def test_price_copied_to_deal_ticket(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id("Trades").click()
page.locator("[col-id=price]").last.click()
page.locator("[col-id=price]").nth(1).click()
# 6005-THIS-007
expect(page.get_by_test_id("order-price")).to_have_value("107.50000")
@@ -146,7 +146,7 @@ def test_transfer_vesting_below_minimum(
vega.wait_fn(10)
vega.wait_for_total_catchup()
page.get_by_text("Use max").first.click()
page.get_by_test_id("use-max-button").first.click()
page.locator('[data-testid=transfer-form] [type="submit"]').click()
wait_for_toast_confirmation(page)
vega.forward("10s")
+1 -1
View File
@@ -116,7 +116,7 @@ def test_wallet_transaction_rejected(continuous_market, page: Page):
page.get_by_test_id(order_price).fill("120")
page.route("**/*", handle_route_connection_rejected)
page.get_by_test_id(place_order).click()
expect(page.get_by_test_id("toast-content")).to_have_text(
expect(page.get_by_test_id("toast-content").nth(0)).to_have_text(
"Error occurredthe user rejected the wallet connection"
)
+5 -2
View File
@@ -67,6 +67,7 @@ const defaultColDef = {
sortable: true,
tooltipComponent: TooltipCellComponent,
comparator: accountValuesComparator,
minWidth: 150,
};
export interface GetRowsParams extends Omit<IGetRowsParams, 'successCallback'> {
successCallback(rowsThisBlock: AccountFields[], lastRow?: number): void;
@@ -139,6 +140,8 @@ export const AccountTable = ({
{
headerName: t('Asset'),
field: 'asset.symbol',
pinned: true,
minWidth: 75,
headerTooltip: t(
'Asset is the collateral that is deposited into the Vega protocol.'
),
@@ -253,8 +256,8 @@ export const AccountTable = ({
colId: 'accounts-actions',
field: 'asset.id',
...COL_DEFS.actions,
minWidth: showDepositButton ? 105 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 105 : COL_DEFS.actions.maxWidth,
minWidth: showDepositButton ? 110 : COL_DEFS.actions.minWidth,
maxWidth: showDepositButton ? 110 : COL_DEFS.actions.maxWidth,
cellRenderer: ({
value: assetId,
node,
+6 -3
View File
@@ -23,7 +23,9 @@ import { AccountType } from '@vegaprotocol/types';
const defaultColDef = {
resizable: true,
sortable: true,
minWidth: 100,
};
interface BreakdownTableProps extends AgGridReactProps {
data: AccountFields[] | null;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
@@ -32,12 +34,13 @@ interface BreakdownTableProps extends AgGridReactProps {
const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
({ data }, ref) => {
const t = useT();
const coldefs = useMemo(() => {
const colDefs = useMemo(() => {
const defs: ColDef[] = [
{
headerName: t('Market'),
field: 'market.tradableInstrument.instrument.code',
minWidth: 200,
width: 90,
pinned: true,
sort: 'desc',
cellRenderer: ({
value,
@@ -141,7 +144,7 @@ const BreakdownTable = forwardRef<AgGridReact, BreakdownTableProps>(
components={{ PriceCell, ProgressBarCell }}
tooltipShowDelay={500}
defaultColDef={defaultColDef}
columnDefs={coldefs}
columnDefs={colDefs}
domLayout="autoHeight"
/>
);
+1
View File
@@ -438,6 +438,7 @@ export const TransferForm = ({
shouldValidate: true,
})
}
data-testid="use-max-button"
>
{t('Use max')}
</button>
@@ -15,7 +15,6 @@ export async function proposeMarket(publicKey: string) {
log('sending proposal tx');
const proposalTx = createNewMarketProposal();
const result = await sendVegaTx(publicKey, proposalTx);
return result.result;
}
@@ -119,6 +118,7 @@ function createNewMarketProposal(): ProposalSubmissionBody {
timeWindow: '3600',
scalingFactor: 10,
},
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: '0.7',
auctionExtension: '1',
},
@@ -9,4 +9,7 @@ export const COL_DEFS = {
type: 'rightAligned',
pinned: 'right' as const,
},
default: {
minWidth: 100,
},
};
@@ -329,8 +329,9 @@ export const DealTicket = ({
const marketTradingModeError = validateMarketTradingMode(
marketTradingMode,
t('Trading terminated')
t('No trading')
);
if (marketTradingModeError !== true) {
return {
message: marketTradingModeError,
-4
View File
@@ -11,9 +11,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
positionDecimalPlaces: 1,
tradingMode: Schema.MarketTradingMode.TRADING_MODE_CONTINUOUS,
state: Schema.MarketState.STATE_ACTIVE,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '',
@@ -75,7 +72,6 @@ export function generateMarket(override?: PartialDeep<Market>): Market {
__typename: 'Instrument',
},
},
fees: {
factors: {
makerFee: '0.001',
+9 -3
View File
@@ -6,7 +6,7 @@ import {
isNumeric,
} from '@vegaprotocol/utils';
import { type ColDef } from 'ag-grid-community';
import { AgGrid } from '@vegaprotocol/datagrid';
import { AgGrid, COL_DEFS } from '@vegaprotocol/datagrid';
import {
type VegaICellRendererParams,
type VegaValueFormatterParams,
@@ -21,7 +21,7 @@ export const DepositsTable = (
) => {
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: 'Asset', field: 'asset.symbol' },
{ headerName: 'Asset', field: 'asset.symbol', pinned: true },
{
headerName: 'Amount',
field: 'amount',
@@ -74,5 +74,11 @@ export const DepositsTable = (
],
[]
);
return <AgGrid columnDefs={columnDefs} {...props} />;
return (
<AgGrid
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
{...props}
/>
);
};
+2
View File
@@ -48,6 +48,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
},
{
headerName: t('Size'),
@@ -143,6 +144,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
<AgGrid
ref={ref}
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
overlayNoRowsTemplate={t('No fills')}
getRowId={({ data }) => data?.id}
tooltipShowDelay={0}
-3
View File
@@ -54,9 +54,6 @@ export const generateFill = (override?: PartialDeep<Trade>) => {
decimalPlaces: 5,
state: MarketState.STATE_ACTIVE,
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
fees: {
__typename: 'Fees',
factors: {
@@ -22,9 +22,7 @@ export const generateFundingPayment = (
decimalPlaces: 5,
state: MarketState.STATE_ACTIVE,
tradingMode: MarketTradingMode.TRADING_MODE_CONTINUOUS,
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
fees: {
__typename: 'Fees',
factors: {
+1 -1
View File
@@ -116,7 +116,7 @@
"Total fees": "Total fees",
"Total margin available": "Total margin available",
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
"Trading terminated": "Trading terminated",
"No trading": "No trading",
"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",
+1 -1
View File
@@ -383,7 +383,7 @@
"MoreMarketsInfo": "To see Explorer data on existing markets visit",
"MoreNetParamsInfo": "To see Explorer data on network params visit",
"MoreProposalsInfo": "To see Explorer data on proposals visit",
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
"multisigContractIncorrect": "was incorrectly configured as at the end of the last epoch so rewards were penalised. Validator and delegator rewards will continue to be penalised until this is resolved.",
"multisigContractLink": "Ethereum Multisig Contract",
"multisigPenalty": "Multisig penalty",
"myPendingStake": "My pending stake",
@@ -83,11 +83,6 @@ describe('LiquidityTable', () => {
h.querySelector('[ref="eText"]')?.textContent?.trim()
);
const expectedHeaders = [
undefined,
undefined,
undefined,
undefined,
undefined,
'Party',
'Status',
'Commitment ()',
+277 -309
View File
@@ -18,7 +18,7 @@ import {
truncateMiddle,
} from '@vegaprotocol/ui-toolkit';
import type {
ColGroupDef,
ColDef,
ITooltipParams,
ValueFormatterParams,
} from 'ag-grid-community';
@@ -60,10 +60,11 @@ const dateValueFormatter = ({ value }: { value?: string | null }) => {
return getDateTimeFormat().format(new Date(value));
};
const defaultColDef = {
const defaultColDef: ColDef = {
resizable: true,
sortable: true,
tooltipComponent: TooltipCellComponent,
minWidth: 120,
};
export interface LiquidityTableProps
@@ -168,333 +169,300 @@ export const LiquidityTable = ({
)}`;
};
const defs: ColGroupDef[] = [
const defs: ColDef[] = [
{
headerName: '',
children: [
{
headerName: t('Party'),
field: 'partyId',
headerTooltip: t(
'The public key of the party making this commitment.'
),
cellRenderer: copyCellRenderer,
},
],
headerName: t('Party'),
field: 'partyId',
headerTooltip: t('The public key of the party making this commitment.'),
cellRenderer: copyCellRenderer,
pinned: 'left',
},
{
headerName: t('Commitment details'),
marryChildren: true,
children: [
{
headerName: t('Status'),
headerTooltip: t('The current status of this liquidity provision.'),
field: 'status',
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<LiquidityProvisionData, 'status'>) => {
if (!value) return value;
if (
data?.status === LiquidityProvisionStatus.STATUS_PENDING &&
(data?.currentCommitmentAmount || data?.currentFee)
) {
return (
<span className="text-warning">
{t('Updating next epoch')}
</span>
);
headerName: t('Status'),
headerTooltip: t('The current status of this liquidity provision.'),
field: 'status',
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<LiquidityProvisionData, 'status'>) => {
if (!value) return value;
if (
data?.status === LiquidityProvisionStatus.STATUS_PENDING &&
(data?.currentCommitmentAmount || data?.currentFee)
) {
return (
<span className="text-warning">{t('Updating next epoch')}</span>
);
}
return (
<span>
{
LiquidityProvisionStatusMapping[
value as LiquidityProvisionStatus
]
}
return (
<span>
{
LiquidityProvisionStatusMapping[
value as LiquidityProvisionStatus
]
}
</span>
</span>
);
},
},
{
headerName: t(`Commitment ({{symbol}})`, { symbol }),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
'The amount committed to the market by this liquidity provider.'
),
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<
LiquidityProvisionData,
'commitmentAmount'
>) => {
if (!value) return '-';
const currentCommitmentAmount = data?.currentCommitmentAmount;
const pendingCommitmentAmount = value;
const formattedPendingCommitmentAmount =
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
if (
currentCommitmentAmount &&
currentCommitmentAmount !== pendingCommitmentAmount
) {
const formattedCurrentCommitmentAmount =
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
},
},
{
headerName: t(`Commitment ({{symbol}})`, { symbol }),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
'The amount committed to the market by this liquidity provider.'
),
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<
LiquidityProvisionData,
'commitmentAmount'
>) => {
if (!value) return '-';
const currentCommitmentAmount = data?.currentCommitmentAmount;
const pendingCommitmentAmount = value;
const formattedPendingCommitmentAmount =
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
return (
<>
<span>{formattedCurrentCommitmentAmount}</span> (
<span className="text-warning">
{formattedPendingCommitmentAmount}
</span>
)
</>
);
} else {
return formattedPendingCommitmentAmount;
}
},
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t('Obligation'),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
`The liquidity provider's obligation to the market, calculated as the liquidity commitment amount multiplied by the value of the stake_to_ccy_volume network parameter to convert into units of liquidity volume.`
),
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<
LiquidityProvisionData,
'commitmentAmount'
>) => {
if (!value) return '-';
if (
currentCommitmentAmount &&
currentCommitmentAmount !== pendingCommitmentAmount
) {
const formattedCurrentCommitmentAmount =
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
return (
<>
<span>{formattedCurrentCommitmentAmount}</span> (
<span className="text-warning">
{formattedPendingCommitmentAmount}
</span>
)
</>
);
} else {
return formattedPendingCommitmentAmount;
}
},
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t('Obligation'),
field: 'commitmentAmount',
type: 'rightAligned',
headerTooltip: t(
`The liquidity provider's obligation to the market, calculated as the liquidity commitment amount multiplied by the value of the stake_to_ccy_volume network parameter to convert into units of liquidity volume.`
),
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<
LiquidityProvisionData,
'commitmentAmount'
>) => {
if (!value) return '-';
const currentCommitmentAmount = data?.currentCommitmentAmount
? new BigNumber(data?.currentCommitmentAmount)
.times(Number(stakeToCcyVolume) || 1)
.toString()
: undefined;
const pendingCommitmentAmount = new BigNumber(value)
const currentCommitmentAmount = data?.currentCommitmentAmount
? new BigNumber(data?.currentCommitmentAmount)
.times(Number(stakeToCcyVolume) || 1)
.toString();
.toString()
: undefined;
const formattedPendingCommitmentAmount =
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
const pendingCommitmentAmount = new BigNumber(value)
.times(Number(stakeToCcyVolume) || 1)
.toString();
if (
currentCommitmentAmount &&
currentCommitmentAmount !== pendingCommitmentAmount
) {
const formattedCurrentCommitmentAmount =
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
const formattedPendingCommitmentAmount =
addDecimalsFormatNumberQuantum(
pendingCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
return (
<>
<span>{formattedCurrentCommitmentAmount}</span> (
<span className="text-warning">
{formattedPendingCommitmentAmount}
</span>
)
</>
);
} else {
return formattedPendingCommitmentAmount;
}
},
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Fee'),
headerTooltip: t(
'The fee percentage (per trade) proposed by each liquidity provider.'
),
field: 'fee',
type: 'rightAligned',
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<LiquidityProvisionData, 'fee'>) => {
if (!value) return '-';
const formattedPendingFee =
formatNumberPercentage(new BigNumber(value).times(100), 2) ||
'-';
if (data?.currentFee && data?.currentFee !== value) {
const formattedCurrentFee = formatNumberPercentage(
new BigNumber(data.currentFee).times(100),
2
);
return (
<>
<span>{formattedCurrentFee}</span> (
<span className="text-warning">{formattedPendingFee}</span>)
</>
);
}
return formattedPendingFee;
},
},
{
headerName: t('Adjusted stake'),
field: 'feeShare.virtualStake',
type: 'rightAligned',
headerTooltip: t(
'The effective stake of the liquidity provider, adjusted for length of commitment and impact on equity like share.'
),
if (
currentCommitmentAmount &&
currentCommitmentAmount !== pendingCommitmentAmount
) {
const formattedCurrentCommitmentAmount =
addDecimalsFormatNumberQuantum(
currentCommitmentAmount,
assetDecimalPlaces ?? 0,
quantum ?? 0
);
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: t(`Share`),
field: 'feeShare.equityLikeShare',
type: 'rightAligned',
headerTooltip: t(
'The equity-like share of liquidity of the market used to determine allocation of LP fees. Calculated based on share of total liquidity, with a premium added for length of commitment.'
),
valueFormatter: percentageFormatter,
},
],
return (
<>
<span>{formattedCurrentCommitmentAmount}</span> (
<span className="text-warning">
{formattedPendingCommitmentAmount}
</span>
)
</>
);
} else {
return formattedPendingCommitmentAmount;
}
},
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Live liquidity data'),
marryChildren: true,
children: [
{
headerName: t('Live supplied liquidity'),
field: 'balance',
type: 'rightAligned',
headerTooltip: t(
`The amount of liquidity volume supplied by the LP order in order to meet the obligation. If the obligation is already met in full by other limit orders from the same Vega key the LP order is not required and this value will be zero. Also note if the target stake for the market is less than the obligation the full value of the obligation may not be required.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Fees accrued this epoch'),
field: 'earmarkedFees',
type: 'rightAligned',
headerTooltip: t(
`The liquidity fees accrued by each provider, which will be distributed at the end of the epoch after applying any penalties.`
),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: feesAccruedTooltip,
cellClassRules: {
'text-warning': ({ data }: { data: LiquidityProvisionData }) => {
if (!data.sla) return false;
return (
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(1) &&
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isGreaterThan(data.commitmentMinTimeFraction)
);
},
'text-red-500': ({ data }: { data: LiquidityProvisionData }) => {
if (!data.sla) return false;
return new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(data.commitmentMinTimeFraction);
},
},
},
{
headerName: t(`Live time on book`),
field: 'sla.currentEpochFractionOfTimeOnBook',
type: 'rightAligned',
headerTooltip: t('Current epoch fraction of time on the book.'),
valueFormatter: percentageFormatter,
},
{
headerName: t('Live liquidity score (%)'),
field: 'feeShare.averageScore',
type: 'rightAligned',
headerTooltip: t(
'The liquidity score of the provider, used to determine allocation of fees to the best performing LPs. Posting volume closer to the mid on both sides of the book will improve this score.'
),
valueFormatter: percentageFormatter,
},
],
headerName: t('Fee'),
headerTooltip: t(
'The fee percentage (per trade) proposed by each liquidity provider.'
),
field: 'fee',
type: 'rightAligned',
cellRenderer: ({
data,
value,
}: VegaICellRendererParams<LiquidityProvisionData, 'fee'>) => {
if (!value) return '-';
const formattedPendingFee =
formatNumberPercentage(new BigNumber(value).times(100), 2) || '-';
if (data?.currentFee && data?.currentFee !== value) {
const formattedCurrentFee = formatNumberPercentage(
new BigNumber(data.currentFee).times(100),
2
);
return (
<>
<span>{formattedCurrentFee}</span> (
<span className="text-warning">{formattedPendingFee}</span>)
</>
);
}
return formattedPendingFee;
},
},
{
headerName: t('Last epoch SLA details'),
marryChildren: true,
children: [
{
headerName: t(`Last time on book`),
field: 'sla.lastEpochFractionOfTimeOnBook',
type: 'rightAligned',
headerTooltip: t(
'Fraction of time on the book at the end of the last epoch.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t(`Last fee penalty`),
field: 'sla.lastEpochFeePenalty',
type: 'rightAligned',
headerTooltip: t(
'Penalty applied on the fees a liquidity provider collected in the last epoch. This percentage increased if an LP did not meet the SLA, or if they met it but other LPs outscored them in the previous epoch.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t(`Last bond penalty`),
field: 'sla.lastEpochBondPenalty',
type: 'rightAligned',
headerTooltip: t(
`Penalty applied on a provider's bond penalty at the end of the last epoch. This percentage increased if an LP: had a shortfall and their bond needed to be used to cover it, did not meet the SLA, and/or reduced their commitment to the point that the market was below its target stake.`
),
valueFormatter: percentageFormatter,
},
],
headerName: t('Adjusted stake'),
field: 'feeShare.virtualStake',
type: 'rightAligned',
headerTooltip: t(
'The effective stake of the liquidity provider, adjusted for length of commitment and impact on equity like share.'
),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
},
{
headerName: '',
marryChildren: true,
children: [
{
headerName: t('Created'),
headerTooltip: t(
'The date and time this liquidity provision was created.'
),
field: 'createdAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
headerName: t(`Share`),
field: 'feeShare.equityLikeShare',
type: 'rightAligned',
headerTooltip: t(
'The equity-like share of liquidity of the market used to determine allocation of LP fees. Calculated based on share of total liquidity, with a premium added for length of commitment.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t('Live supplied liquidity'),
field: 'balance',
type: 'rightAligned',
headerTooltip: t(
`The amount of liquidity volume supplied by the LP order in order to meet the obligation. If the obligation is already met in full by other limit orders from the same Vega key the LP order is not required and this value will be zero. Also note if the target stake for the market is less than the obligation the full value of the obligation may not be required.`
),
valueFormatter: stakeToCcyVolumeQuantumFormatter,
tooltipValueGetter: stakeToCcyVolumeFormatter,
},
{
headerName: t('Fees accrued this epoch'),
field: 'earmarkedFees',
type: 'rightAligned',
headerTooltip: t(
`The liquidity fees accrued by each provider, which will be distributed at the end of the epoch after applying any penalties.`
),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: feesAccruedTooltip,
cellClassRules: {
'text-warning': ({ data }: { data: LiquidityProvisionData }) => {
if (!data.sla) return false;
return (
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(1) &&
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isGreaterThan(data.commitmentMinTimeFraction)
);
},
{
headerName: t('Updated'),
headerTooltip: t(
'The date and time this liquidity provision was last updated.'
),
field: 'updatedAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
'text-red-500': ({ data }: { data: LiquidityProvisionData }) => {
if (!data.sla) return false;
return new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(data.commitmentMinTimeFraction);
},
],
},
},
{
headerName: t(`Live time on book`),
field: 'sla.currentEpochFractionOfTimeOnBook',
type: 'rightAligned',
headerTooltip: t('Current epoch fraction of time on the book.'),
valueFormatter: percentageFormatter,
},
{
headerName: t('Live liquidity score (%)'),
field: 'feeShare.averageScore',
type: 'rightAligned',
headerTooltip: t(
'The liquidity score of the provider, used to determine allocation of fees to the best performing LPs. Posting volume closer to the mid on both sides of the book will improve this score.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t(`Last time on book`),
field: 'sla.lastEpochFractionOfTimeOnBook',
type: 'rightAligned',
headerTooltip: t(
'Fraction of time on the book at the end of the last epoch.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t(`Last fee penalty`),
field: 'sla.lastEpochFeePenalty',
type: 'rightAligned',
headerTooltip: t(
'Penalty applied on the fees a liquidity provider collected in the last epoch. This percentage increased if an LP did not meet the SLA, or if they met it but other LPs outscored them in the previous epoch.'
),
valueFormatter: percentageFormatter,
},
{
headerName: t(`Last bond penalty`),
field: 'sla.lastEpochBondPenalty',
type: 'rightAligned',
headerTooltip: t(
`Penalty applied on a provider's bond penalty at the end of the last epoch. This percentage increased if an LP: had a shortfall and their bond needed to be used to cover it, did not meet the SLA, and/or reduced their commitment to the point that the market was below its target stake.`
),
valueFormatter: percentageFormatter,
},
{
headerName: t('Created'),
headerTooltip: t(
'The date and time this liquidity provision was created.'
),
field: 'createdAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
{
headerName: t('Updated'),
headerTooltip: t(
'The date and time this liquidity provision was last updated.'
),
field: 'updatedAt',
type: 'rightAligned',
valueFormatter: dateValueFormatter,
},
];
return defs;
@@ -124,27 +124,11 @@ describe('getChange', () => {
});
describe('useCheckLiquidityStatus', () => {
it('should return amber if liquidity is enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '0.5',
})
);
expect(result.current).toEqual({
status: Intent.Warning,
percentage: new BigNumber('60'),
});
});
it('should return red if liquidity is not enough', () => {
const { result } = renderHook(() =>
useCheckLiquidityStatus({
suppliedStake: '60',
targetStake: '100',
triggeringRatio: '1',
})
);
@@ -159,7 +143,6 @@ describe('useCheckLiquidityStatus', () => {
useCheckLiquidityStatus({
suppliedStake: '101',
targetStake: '100',
triggeringRatio: '1',
})
);
@@ -121,11 +121,9 @@ export const getTargetStake = (
export const useCheckLiquidityStatus = ({
suppliedStake,
targetStake,
triggeringRatio,
}: {
suppliedStake: string | number;
targetStake: string | number;
triggeringRatio: string | number;
}): {
status: Intent;
percentage: BigNumber;
@@ -142,23 +140,12 @@ export const useCheckLiquidityStatus = ({
percentage,
};
}
if (new BigNumber(suppliedStake).gte(new BigNumber(targetStake))) {
if (new BigNumber(suppliedStake).gte(targetStake)) {
// show a green status, e.g. "🟢 $13,666,999 liquidity supplied"
return {
status: Intent.Success,
percentage,
};
// ELSE IF supplied_stake > NETPARAM[market.liquidity.targetstake.triggering.ratio] * target_stake THEN
} else if (
new BigNumber(suppliedStake).gte(
new BigNumber(targetStake).multipliedBy(triggeringRatio)
)
) {
// show an amber status, e.g. "🟠 $3,456,123 liquidity supplied"
return {
status: Intent.Warning,
percentage,
};
// ELSE show a red status, e.g. "🔴 $600,002 liquidity supplied"
} else {
return {
File diff suppressed because one or more lines are too long
@@ -188,7 +188,6 @@ query MarketInfo($marketId: ID!) {
long
}
liquidityMonitoringParameters {
triggeringRatio
targetStakeParameters {
timeWindow
scalingFactor
File diff suppressed because one or more lines are too long
@@ -262,6 +262,7 @@ export const MarketInfoAccordion = ({
<PriceMonitoringBoundsInfoPanel
market={market}
triggerIndex={triggerIndex}
key={id}
/>
}
/>
@@ -698,17 +698,13 @@ export const PriceMonitoringBoundsInfoPanel = ({
const quoteUnit = getQuoteName(market);
const trigger =
market.priceMonitoringSettings?.parameters?.triggers?.[triggerIndex];
const bounds = data?.priceMonitoringBounds?.[triggerIndex];
const trigger = bounds?.trigger;
if (!trigger) {
console.error(
`Could not find data for trigger ${triggerIndex} (market id: ${market.id})`
);
return null;
}
return (
<>
<div className="mb-2 grid grid-cols-2 text-sm">
@@ -749,7 +745,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
parentMarket,
}: MarketInfoProps) => {
const marketData = {
triggeringRatio: market.liquidityMonitoringParameters.triggeringRatio,
timeWindow:
market.liquidityMonitoringParameters.targetStakeParameters.timeWindow,
scalingFactor:
@@ -758,8 +753,6 @@ export const LiquidityMonitoringParametersInfoPanel = ({
const parentMarketData = parentMarket
? {
triggeringRatio:
parentMarket.liquidityMonitoringParameters.triggeringRatio,
timeWindow:
parentMarket.liquidityMonitoringParameters.targetStakeParameters
.timeWindow,
@@ -95,7 +95,6 @@ export const marketInfoQuery = (
long: '0.008508132993273576',
},
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
targetStakeParameters: {
timeWindow: 3600,
scalingFactor: 10,
@@ -101,8 +101,6 @@ export const useTooltipMapping: () => Record<string, ReactNode> = () => {
auctionExtensionSecs: t(
'Auction extension duration in seconds, should the price breach its theoretical level over the specified horizon at the specified probability level.'
),
triggeringRatio: t('The triggering ratio for entering liquidity auction.'),
timeWindow: t('The length of time over which open interest is measured.'),
scalingFactor: t(
'The scaling between the liquidity demand estimate, based on open interest and target stake.'
-3
View File
@@ -35,9 +35,6 @@ fragment MarketFields on Market {
open
close
}
liquidityMonitoringParameters {
triggeringRatio
}
}
query Markets {
-3
View File
@@ -53,9 +53,6 @@ export const createMarketFragment = (
liquidityFee: '',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
tradableInstrument: {
instrument: {
id: '',
@@ -20,9 +20,6 @@ export const generateOrder = (partialOrder?: PartialDeep<Order>) => {
makerFee: '0.1',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
marketTimestamps: {
__typename: 'MarketTimestamps',
close: '',
@@ -21,9 +21,6 @@ export const generateStopOrder = (
__typename: 'Market',
id: 'market-id',
decimalPlaces: 1,
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
},
fees: {
__typename: 'Fees',
factors: {
@@ -39,6 +39,7 @@ const defaultColDef = {
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
minWidth: 100,
};
export type OrderListTableProps = TypedDataAgGrid<Order> & {
@@ -82,6 +83,9 @@ export const OrderListTable = memo<
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
width: 130,
resizable: true,
},
{
headerName: t('Filled'),
@@ -31,9 +31,6 @@ describe('OrderViewDialog', () => {
liquidityFee: '0.001',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '1',
},
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
@@ -40,6 +40,7 @@ const defaultColDef = {
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
minWidth: 100,
};
export type StopOrdersTableProps = TypedDataAgGrid<StopOrder> & {
@@ -61,6 +62,7 @@ export const StopOrdersTable = memo(
field: 'market.tradableInstrument.instrument.code',
cellRenderer: 'MarketNameCell',
cellRendererParams: { idPath: 'market.id', onMarketClick },
pinned: true,
},
{
headerName: t('Trigger'),
+326 -343
View File
@@ -71,6 +71,7 @@ const defaultColDef = {
filterParams: { buttons: ['reset'] },
tooltipComponent: TooltipCellComponent,
resizable: true,
minWidth: 110,
};
export const PositionsTable = ({
@@ -83,6 +84,330 @@ export const PositionsTable = ({
...props
}: Props) => {
const t = useT();
const colDefs = useMemo<ColDef[]>(() => {
const columnDefs: (ColDef | null)[] = [
multipleKeys
? {
headerName: t('Vega key'),
field: 'partyId',
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
(data?.partyId &&
pubKeys &&
pubKeys.find((key) => key.publicKey === data.partyId)?.name) ||
data?.partyId,
}
: null,
{
headerName: t('Market'),
field: 'marketCode',
resizable: true,
onCellClicked: ({ data }) => {
if (!onMarketClick) return;
onMarketClick(data.marketId);
},
pinned: true,
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Position, 'marketCode'>) => {
if (!data || !value) return '-';
return (
<StackedCell
primary={value}
secondary={
<>
{data?.assetSymbol}
<MarketProductPill productType={data.productType} />
</>
}
/>
);
},
},
{
headerName: t('Size / Notional'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: { data: Position }) => {
return data?.openVolume === undefined
? undefined
: toBigNum(data?.openVolume, data.positionDecimalPlaces).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
if (!data?.openVolume) return '-';
const vol = volumePrefix(
addDecimalsFormatNumber(data.openVolume, data.positionDecimalPlaces)
);
return vol;
},
cellRenderer: OpenVolumeCell,
},
{
headerName: t('Entry / Mark'),
field: 'markPrice',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellRenderer: ({
data,
}: VegaICellRendererParams<Position, 'markPrice'>) => {
if (
!data?.averageEntryPrice ||
!data?.markPrice ||
!data?.marketDecimalPlaces
) {
return <>-</>;
}
if (
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return <>-</>;
}
const entry = addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
const mark = addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
return (
<StackedCell
primary={entry}
secondary={
<PriceFlashCell
value={Number(data.markPrice)}
valueFormatted={mark}
/>
}
/>
);
},
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
},
{
headerName: t('Margin / Leverage'),
colId: 'margin',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.assetDecimals
).toNumber();
},
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (
!data ||
!data.marginAccountBalance ||
!data.marketDecimalPlaces
) {
return null;
}
const margin = addDecimalsFormatNumberQuantum(
data.marginAccountBalance,
data.assetDecimals,
data.quantum
);
const lev = data?.currentLeverage ? data.currentLeverage : 1;
const leverage = formatNumber(Math.max(1, lev), 1);
return <StackedCell primary={margin} secondary={leverage + 'x'} />;
},
},
{
colId: 'liquidationPrice',
headerName: 'Liquidation',
headerTooltip: t('Worst case liquidation price'),
cellClass: 'font-mono text-right',
type: 'rightAligned',
// Cannot be sortable as data is fetched within the cell
sortable: false,
filter: false,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) {
return '-';
}
return (
<div className="flex h-[45px] items-center">
<LiquidationPrice
className="block text-right grow"
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.marketDecimalPlaces}
/>
</div>
);
},
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: realisedPNLValueGetter,
cellRenderer: (
args: VegaICellRendererParams<Position, 'realisedPNL'>
) => {
const LOSS_SOCIALIZATION_LINK = DocsLinks?.LOSS_SOCIALIZATION ?? '';
if (!args.data || args.value === undefined) {
return null;
}
const losses = parseInt(args.data?.lossSocializationAmount ?? '0');
if (losses <= 0) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
<Tooltip description={args.valueFormatted} align="end">
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
}
const lossesFormatted = addDecimalsFormatNumber(
args.data.lossSocializationAmount,
args.data.assetDecimals
);
return (
<Tooltip
align="end"
description={
<>
<p className="mb-2">
{t('Realised PNL: {{value}}', {
nsSeparator: '*',
replace: { value: args.value },
})}
</p>
<p className="mb-2">
{t('Lifetime loss socialisation deductions: {{losses}}', {
nsSeparator: '*',
replace: {
losses: lossesFormatted,
},
})}
</p>
<p className="mb-2">
{t(
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
{ assetSymbol: args.data.assetSymbol }
)}
</p>
{LOSS_SOCIALIZATION_LINK && (
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
{t('Read more about loss socialisation')}
</ExternalLink>
)}
</>
}
>
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumberQuantum(
data.realisedPNL,
data.assetDecimals,
data.quantum
);
},
headerTooltip: t(
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
),
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: unrealisedPNLValueGetter,
// @ts-ignore no type overlap but function can be identical
tooltipValueGetter: unrealisedPNLValueGetter,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumberQuantum(
data.unrealisedPNL,
data.assetDecimals,
data.quantum
),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
return (
<div className="flex items-center justify-end gap-2">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
title={t('Close position')}
>
<VegaIcon name={VegaIconNames.CROSS} size={16} />
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 55,
maxWidth: 55,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [isReadOnly, multipleKeys, onClose, onMarketClick, pubKey, pubKeys, t]);
return (
<AgGrid
overlayNoRowsTemplate={t('No positions')}
@@ -95,349 +420,7 @@ export const PositionsTable = ({
MarketNameCell,
}}
rowHeight={45}
columnDefs={useMemo<ColDef[]>(() => {
const columnDefs: (ColDef | null)[] = [
multipleKeys
? {
headerName: t('Vega key'),
field: 'partyId',
valueGetter: ({ data }: VegaValueGetterParams<Position>) =>
(data?.partyId &&
pubKeys &&
pubKeys.find((key) => key.publicKey === data.partyId)
?.name) ||
data?.partyId,
}
: null,
{
headerName: t('Market'),
field: 'marketCode',
onCellClicked: ({ data }) => {
if (!onMarketClick) return;
onMarketClick(data.marketId);
},
cellRenderer: ({
value,
data,
}: VegaICellRendererParams<Position, 'marketCode'>) => {
if (!data || !value) return '-';
return (
<StackedCell
primary={value}
secondary={
<>
{data?.assetSymbol}
<MarketProductPill productType={data.productType} />
</>
}
/>
);
},
},
{
headerName: t('Size / Notional'),
field: 'openVolume',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellClassRules: signedNumberCssClassRules,
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: { data: Position }) => {
return data?.openVolume === undefined
? undefined
: toBigNum(
data?.openVolume,
data.positionDecimalPlaces
).toNumber();
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'openVolume'>): string => {
if (!data?.openVolume) return '-';
const vol = volumePrefix(
addDecimalsFormatNumber(
data.openVolume,
data.positionDecimalPlaces
)
);
return vol;
},
cellRenderer: OpenVolumeCell,
},
{
headerName: t('Entry / Mark'),
field: 'markPrice',
type: 'rightAligned',
cellClass: 'font-mono text-right',
cellRenderer: ({
data,
}: VegaICellRendererParams<Position, 'markPrice'>) => {
if (
!data?.averageEntryPrice ||
!data?.markPrice ||
!data?.marketDecimalPlaces
) {
return <>-</>;
}
if (
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
) {
return <>-</>;
}
const entry = addDecimalsFormatNumber(
data.averageEntryPrice,
data.marketDecimalPlaces
);
const mark = addDecimalsFormatNumber(
data.markPrice,
data.marketDecimalPlaces
);
return (
<StackedCell
primary={entry}
secondary={
<PriceFlashCell
value={Number(data.markPrice)}
valueFormatted={mark}
/>
}
/>
);
},
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data ||
!data.markPrice ||
data.marketTradingMode ===
MarketTradingMode.TRADING_MODE_OPENING_AUCTION
? undefined
: toBigNum(data.markPrice, data.marketDecimalPlaces).toNumber();
},
},
{
headerName: t('Margin / Leverage'),
colId: 'margin',
type: 'rightAligned',
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: ({ data }: VegaValueGetterParams<Position>) => {
return !data
? undefined
: toBigNum(
data.marginAccountBalance,
data.assetDecimals
).toNumber();
},
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (
!data ||
!data.marginAccountBalance ||
!data.marketDecimalPlaces
) {
return null;
}
const margin = addDecimalsFormatNumberQuantum(
data.marginAccountBalance,
data.assetDecimals,
data.quantum
);
const lev = data?.currentLeverage ? data.currentLeverage : 1;
const leverage = formatNumber(Math.max(1, lev), 1);
return (
<StackedCell primary={margin} secondary={leverage + 'x'} />
);
},
},
{
colId: 'liquidationPrice',
headerName: 'Liquidation',
headerTooltip: t('Worst case liquidation price'),
cellClass: 'font-mono text-right',
type: 'rightAligned',
// Cannot be sortable as data is fetched within the cell
sortable: false,
filter: false,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
if (!data) {
return '-';
}
return (
<div className="flex h-[45px] items-center">
<LiquidationPrice
className="block text-right grow"
marketId={data.marketId}
openVolume={data.openVolume}
collateralAvailable={data.totalBalance}
decimalPlaces={data.marketDecimalPlaces}
/>
</div>
);
},
},
{
headerName: t('Realised PNL'),
field: 'realisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: realisedPNLValueGetter,
cellRenderer: (
args: VegaICellRendererParams<Position, 'realisedPNL'>
) => {
const LOSS_SOCIALIZATION_LINK =
DocsLinks?.LOSS_SOCIALIZATION ?? '';
if (!args.data || args.value === undefined) {
return null;
}
const losses = parseInt(
args.data?.lossSocializationAmount ?? '0'
);
if (losses <= 0) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return (
<Tooltip description={args.valueFormatted} align="end">
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
}
const lossesFormatted = addDecimalsFormatNumber(
args.data.lossSocializationAmount,
args.data.assetDecimals
);
return (
<Tooltip
align="end"
description={
<>
<p className="mb-2">
{t('Realised PNL: {{value}}', {
nsSeparator: '*',
replace: { value: args.value },
})}
</p>
<p className="mb-2">
{t(
'Lifetime loss socialisation deductions: {{losses}}',
{
nsSeparator: '*',
replace: {
losses: lossesFormatted,
},
}
)}
</p>
<p className="mb-2">
{t(
`You received less {{assetSymbol}} in gains that you should have when the market moved in your favour. This occurred because one or more other trader(s) were closed out and did not have enough funds to cover their losses, and the market's insurance pool was empty.`,
{ assetSymbol: args.data.assetSymbol }
)}
</p>
{LOSS_SOCIALIZATION_LINK && (
<ExternalLink href={LOSS_SOCIALIZATION_LINK}>
{t('Read more about loss socialisation')}
</ExternalLink>
)}
</>
}
>
<div>
<PNLCell {...args} />
</div>
</Tooltip>
);
},
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'realisedPNL'>) => {
return !data
? ''
: addDecimalsFormatNumberQuantum(
data.realisedPNL,
data.assetDecimals,
data.quantum
);
},
headerTooltip: t(
'Profit or loss is realised whenever your position is reduced to zero and the margin is released back to your collateral balance. P&L excludes any fees paid.'
),
},
{
headerName: t('Unrealised PNL'),
field: 'unrealisedPNL',
type: 'rightAligned',
cellClassRules: signedNumberCssClassRules,
cellClass: 'font-mono text-right',
filter: 'agNumberColumnFilter',
valueGetter: unrealisedPNLValueGetter,
// @ts-ignore no type overlap but function can be identical
tooltipValueGetter: unrealisedPNLValueGetter,
valueFormatter: ({
data,
}: VegaValueFormatterParams<Position, 'unrealisedPNL'>) =>
!data
? ''
: addDecimalsFormatNumberQuantum(
data.unrealisedPNL,
data.assetDecimals,
data.quantum
),
headerTooltip: t(
'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.'
),
},
onClose && !isReadOnly
? {
...COL_DEFS.actions,
cellRenderer: ({ data }: VegaICellRendererParams<Position>) => {
return (
<div className="flex items-center justify-end gap-2">
{data?.openVolume &&
data?.openVolume !== '0' &&
data.partyId === pubKey ? (
<ButtonLink
data-testid="close-position"
onClick={() => data && onClose(data)}
title={t('Close position')}
>
<VegaIcon name={VegaIconNames.CROSS} size={16} />
</ButtonLink>
) : null}
{data?.assetId && (
<PositionActionsDropdown assetId={data?.assetId} />
)}
</div>
);
},
minWidth: 55,
maxWidth: 55,
}
: null,
];
return columnDefs.filter<ColDef>(
(colDef: ColDef | null): colDef is ColDef => colDef !== null
);
}, [
isReadOnly,
multipleKeys,
onClose,
onMarketClick,
pubKey,
pubKeys,
t,
])}
columnDefs={colDefs}
{...props}
/>
);
@@ -29,6 +29,7 @@ export const useColumnDefs = () => {
colId: 'market',
headerName: t('Market'),
field: 'terms.change.instrument.code',
pinned: true,
cellStyle: { lineHeight: '14px' },
cellRenderer: ({
value,
@@ -134,7 +134,6 @@ fragment NewMarketFields on NewMarket {
# timeWindow
# scalingFactor
# }
# triggeringRatio
# auctionExtensionSecs
# }
# linearSlippageFactor
@@ -299,7 +298,6 @@ fragment UpdateMarketFields on UpdateMarket {
timeWindow
scalingFactor
}
triggeringRatio
# auctionExtensionSecs
}
riskParameters {
File diff suppressed because one or more lines are too long
@@ -92,7 +92,6 @@ export const marketUpdateProposal: ProposalListFieldsFragment = {
triggers: [],
},
liquidityMonitoringParameters: {
triggeringRatio: '0',
targetStakeParameters: {
scalingFactor: 0,
timeWindow: 0,
@@ -162,7 +162,6 @@ const generateUpdateMarketProposal = (
__typename: liquidityMonitoring
? 'LiquidityMonitoringParameters'
: undefined,
triggeringRatio: '0',
targetStakeParameters: {
__typename: undefined,
scalingFactor: 0,
+2 -2
View File
@@ -100,12 +100,12 @@ export const TradingView = ({
},
// No theme in deps to avoid full chart reload when the theme changes
// Instead the theme is changed programmitcally in a separate useEffect
// Instead the theme is changed programmatically in a separate useEffect
// eslint-disable-next-line react-hooks/exhaustive-deps
[datafeed, marketId, language, libraryPath, isMobile]
);
// Update the trading view theme every time the app theme updates, doen separately
// Update the trading view theme every time the app theme updates, done separately
// to avoid full chart reload
useEffect(() => {
if (!widgetRef.current || !widgetRef.current._ready) return;
+2
View File
@@ -5585,6 +5585,8 @@ export enum StopOrderRejectionReason {
REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED = 'REJECTION_REASON_MAX_STOP_ORDERS_PER_PARTY_REACHED',
/** Stop orders submission must be reduce only */
REJECTION_REASON_MUST_BE_REDUCE_ONLY = 'REJECTION_REASON_MUST_BE_REDUCE_ONLY',
/** Stop orders are not allowed during the opening auction */
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION',
/** Stop orders are not allowed without a position */
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION = 'REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION',
/** This stop order does not close the position */
@@ -85,19 +85,21 @@ export function Dialog({
<VegaIcon name={VegaIconNames.CROSS} size={24} />
</DialogPrimitives.Close>
)}
<div className="flex gap-4 max-w-full">
{icon && <div className="fill-current">{icon}</div>}
<div data-testid="dialog-content" className="flex-1 max-w-full">
{title && (
<h1
className="text-xl uppercase mb-4 pr-2"
data-testid="dialog-title"
>
{title}
</h1>
)}
<div>{children}</div>
</div>
<div data-testid="dialog-content" className="flex-1 max-w-full">
{title && (
<span
className="text-xl uppercase flex gap-4"
data-testid="dialog-title"
>
{icon && (
<span className="fill-current flex items-center">
{icon}
</span>
)}
{title}
</span>
)}
<div>{children}</div>
</div>
</div>
</div>
@@ -0,0 +1,36 @@
import type { Story, Meta } from '@storybook/react';
import { LeverageSlider } from './leverage-slider';
import { useState } from 'react';
export default {
component: LeverageSlider,
title: 'LeverageSlider',
} as Meta;
const Template: Story = ({ value: val, min, max, ...args }) => {
const [value, setValue] = useState(val);
const onValueChange = (val: [number]) => {
setValue(val);
};
return (
<>
<LeverageSlider
onValueChange={onValueChange}
value={value}
max={max}
{...args}
/>
<div className="mt-10">{value}</div>
</>
);
};
export const Default = Template.bind({});
Default.args = {
max: 100,
step: 0.1,
value: [100],
};
@@ -0,0 +1,58 @@
import * as SliderPrimitive from '@radix-ui/react-slider';
import type { SliderProps } from '@radix-ui/react-slider';
import classNames from 'classnames';
export const LeverageSlider = (
props: Omit<SliderProps, 'min' | 'max'> & Required<Pick<SliderProps, 'max'>>
) => {
const step = [2, 5, 10, 20, 25].find((step) => props.max / step <= 6);
const min = 1;
const value = props.value?.[0] || props.defaultValue?.[0];
return (
<SliderPrimitive.Root
{...props}
min={min}
className="relative flex items-center select-none touch-none h-10 pb-5 w-full"
>
<SliderPrimitive.Track className=" relative grow h-[4px]">
<span className="bg-vega-clight-500 dark:bg-vega-cdark-500 absolute left-2 right-2 top-0 bottom-0"></span>
<span className="block absolute top-[-2px] left-[8px] right-[8px]">
{step &&
new Array(Math.floor(props.max / step) + 1)
.fill(null)
.map((v, i) => {
const labelValue = step * i || 1;
const higherThanValue = value && labelValue > value;
return (
<span
className="absolute flex flex-col items-center translate-x-[-50%]"
style={{
left: `${
((labelValue - min) / (props.max - min)) * 100
}%`,
}}
>
<span
className={classNames(
'block w-[8px] h-[8px] border-[4px] rotate-45',
{
'border-black dark:border-white bg-white dark:bg-white':
!higherThanValue,
'border-vega-clight-500 dark:border-vega-cdark-500 bg-vega-clight-500 dark:bg-vega-cdark-500':
higherThanValue,
}
)}
></span>
<span className="text-sm mt-1">{labelValue}x</span>
</span>
);
})}
</span>
<SliderPrimitive.Range className="absolute h-full">
<span className="absolute left-2 right-0 h-full bg-black dark:bg-white"></span>
</SliderPrimitive.Range>
</SliderPrimitive.Track>
<SliderPrimitive.Thumb className="block w-[16px] h-[16px] border-[3px] border-black dark:border-white bg-white dark:bg-black rotate-45 focus-visible:outline-0" />
</SliderPrimitive.Root>
);
};
@@ -5,15 +5,16 @@ import { ToastPosition, useToastsConfiguration, useToasts } from './use-toasts';
import { useCallback } from 'react';
import { Intent } from '../../utils/intent';
import { useT } from '../../use-t';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
export const ToastPositionSetter = () => {
const t = useT();
const setPostion = useToastsConfiguration((store) => store.setPosition);
const setPosition = useToastsConfiguration((store) => store.setPosition);
const position = useToastsConfiguration((store) => store.position);
const setToast = useToasts((store) => store.setToast);
const handleChange = useCallback(
(position: ToastPosition) => {
setPostion(position);
setPosition(position);
setToast({
id: 'test-toast',
intent: Intent.Primary,
@@ -21,11 +22,49 @@ export const ToastPositionSetter = () => {
onClose: () => useToasts.getState().remove('test-toast'),
});
},
[setToast, setPostion, t]
[setToast, setPosition, t]
);
const buttonCssClasses =
'flex items-center px-1 py-1 relative rounded bg-vega-clight-400 dark:bg-vega-cdark-400';
const activeIcon = 'fill-vega-clight-900 dark:fill-vega-cdark-900';
const { screenSize } = useScreenDimensions();
const isMobileScreen = screenSize === 'xs';
if (isMobileScreen) {
return (
<div className="flex justify-between">
<div className={classNames('grid grid-cols-1 grid-rows-2 gap-1')}>
<button
className={buttonCssClasses}
onClick={() => handleChange(ToastPosition.TopCenter)}
>
<Icon
className={classNames(
position === ToastPosition.TopCenter && activeIcon
)}
size={3}
name={IconNames.ARROW_UP}
/>
</button>
<button
className={buttonCssClasses}
onClick={() => handleChange(ToastPosition.BottomCenter)}
>
<Icon
className={classNames(
position === ToastPosition.BottomCenter && activeIcon
)}
size={3}
name={IconNames.ARROW_DOWN}
/>
</button>
</div>
</div>
);
}
return (
<div className="flex justify-between">
<div className={classNames('grid grid-cols-3 grid-rows-2 gap-1')}>
@@ -92,7 +92,7 @@ export const ToastsContainer = ({
className={classNames(
'absolute right-0 top-[-38px] z-20 w-full',
'transition-opacity',
'opacity-0 hover:!opacity-100 group-hover:opacity-50',
'sm:opacity-0 sm:hover:!opacity-100 sm:group-hover:opacity-50',
{
hidden: validToasts.length === 0,
}
@@ -149,6 +149,7 @@ interface ProposalNewMarketTerms {
timeWindow: string;
scalingFactor: number;
};
// FIXME: workaround because of https://github.com/vegaprotocol/vega/issues/10343
triggeringRatio: string;
auctionExtension: string;
};
@@ -34,7 +34,7 @@ export class ViewConnector implements VegaConnector {
});
return Promise.resolve([
{
name: 'View only pubkey',
name: 'View only',
publicKey: this.pubkey,
},
]);
@@ -149,9 +149,6 @@ describe('WithdrawFormContainer', () => {
liquidityFee: '0.001',
},
},
liquidityMonitoringParameters: {
triggeringRatio: '0.7',
},
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
+3 -1
View File
@@ -15,6 +15,7 @@ import {
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import {
COL_DEFS,
type TypedDataAgGrid,
type VegaICellRendererParams,
type VegaValueFormatterParams,
@@ -46,7 +47,7 @@ export const WithdrawalsTable = ({
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: t('Asset'), field: 'asset.symbol' },
{ headerName: t('Asset'), field: 'asset.symbol', pinned: true },
{
headerName: t('Amount'),
field: 'amount',
@@ -135,6 +136,7 @@ export const WithdrawalsTable = ({
<AgGrid
overlayNoRowsTemplate={t('No withdrawals')}
columnDefs={columnDefs}
defaultColDef={COL_DEFS.default}
components={{
RecipientCell,
StatusCell,
+1 -1
View File
@@ -38,7 +38,7 @@
"@radix-ui/react-slider": "^1.1.0",
"@radix-ui/react-switch": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.2",
"@radix-ui/react-tooltip": "^1.0.3",
"@radix-ui/react-tooltip": "^1.0.7",
"@sentry/nextjs": "^6.19.3",
"@sentry/react": "^6.19.2",
"@sentry/tracing": "^6.19.2",
+1 -1
View File
@@ -4864,7 +4864,7 @@
"@radix-ui/react-separator" "1.0.3"
"@radix-ui/react-toggle-group" "1.0.4"
"@radix-ui/react-tooltip@^1.0.3":
"@radix-ui/react-tooltip@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@radix-ui/react-tooltip/-/react-tooltip-1.0.7.tgz#8f55070f852e7e7450cc1d9210b793d2e5a7686e"
integrity sha512-lPh5iKNFVQ/jav/j6ZrWq3blfDJ0OH9R6FlNUHPMqdLuQ9vwDgFsRxvl8b7Asuy5c8xmoojHUxKHQSOAvMHxyw==