Compare commits

..
Author SHA1 Message Date
Madalina Raicu 361622dc5b Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts-develop 2023-12-01 16:47:43 +00:00
Madalina Raicu 21888cac64 Merge branch 'main' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts-develop 2023-12-01 16:47:34 +00:00
m.ray a59f7dfd29 fix(trading): fills fees maker discounts (#5406) 2023-12-01 16:34:22 +00:00
Bartłomiej Głowniaandasiaznik 61471228aa fix(trading): use discount stats only from previous epoch (#5411)
Co-authored-by: asiaznik <artur@vegaprotocol.io>
2023-12-01 16:34:05 +00:00
Madalina Raicu cd8374ad56 Merge branch '5400-volume-discount-tier-incorrect' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts-develop 2023-12-01 12:52:00 +00:00
Madalina Raicu 996a8b8722 fix: translation format 2023-12-01 12:49:37 +00:00
Madalina Raicu c1fb1c692a chore: update imports 2023-12-01 12:29:59 +00:00
Madalina Raicu 9113c0fabe Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts-develop 2023-12-01 12:23:03 +00:00
asiaznik 5923eac56b fix(trading): next tier in referral stats 2023-12-01 13:13:12 +01:00
Madalina Raicu e1c9323e3c fix: update unit test 2023-12-01 11:36:02 +00:00
Bartłomiej Głownia aa7c33b1a9 fix(trading): use discount stats only from previous epoch 2023-12-01 12:34:55 +01:00
Madalina Raicu b83c10a532 Merge branch 'main' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts 2023-12-01 11:24:40 +00:00
m.ray 70d748fb15 fix(trading): fills fees fixes for maker (#5405) 2023-12-01 11:23:04 +00:00
Madalina Raicu 6551c2b02b fix: rename to fills-utils 2023-12-01 11:20:15 +00:00
Madalina Raicu dcf36acb0b Merge branch 'fix/fills-fees-maker' of github.com:vegaprotocol/frontend-monorepo into fix/fills-fees-maker-discounts 2023-12-01 11:18:50 +00:00
Madalina Raicu 44719c9ea5 fix: re-group types 2023-12-01 10:51:58 +00:00
Madalina Raicu 773b1cdc13 fix: re-group imports and add extra tests 2023-12-01 10:49:47 +00:00
Madalina Raicu 8d9f2cdda3 fix: rename new fees to role fees 2023-11-30 18:32:02 +00:00
Madalina Raicu 3b4fa06927 fix: layout of discounts 2023-11-30 18:15:40 +00:00
Madalina Raicu 6de1e0b83d fix(trading): override fees discounts if aggresor is maker and refactor 2023-11-30 18:07:44 +00:00
Madalina Raicu 85e9c1300c fix: if the market was active tootip copy change 2023-11-30 16:19:19 +00:00
Madalina Raicu 4e9c47c27e fix: market suspended tooltip 2023-11-30 16:12:41 +00:00
Madalina Raicu 9c3c0a30f6 fix: show correct total of fills fees 2023-11-30 16:08:19 +00:00
18 changed files with 303 additions and 586 deletions
@@ -23,13 +23,10 @@ export const Heading = ({
})}
>
<h1
className={classNames(
'font-alpha calt text-5xl [word-break:break-word]',
{
'mt-0': !marginTop,
'mb-0': !marginBottom,
}
)}
className={classNames('font-alpha calt text-5xl break-words', {
'mt-0': !marginTop,
'mb-0': !marginBottom,
})}
>
{title}
</h1>
@@ -7,10 +7,8 @@ import type { AssetFieldsFragment } from '@vegaprotocol/assets';
export const ProposalAssetDetails = ({
asset,
originalAsset,
}: {
asset: AssetFieldsFragment;
originalAsset?: AssetFieldsFragment;
}) => {
const { t } = useTranslation();
const [showAssetDetails, setShowAssetDetails] = useState(false);
@@ -29,7 +27,6 @@ export const ProposalAssetDetails = ({
<div className="mb-10 pb-4">
<AssetDetailsTable
asset={asset}
originalAsset={originalAsset}
omitRows={[
AssetDetail.STATUS,
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
@@ -65,13 +65,10 @@ export const Proposal = ({
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
: undefined;
const originalAsset = asset;
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
asset = {
...asset,
quantum: proposal.terms.change.quantum,
source: { ...asset.source },
};
if (asset.source.__typename === 'ERC20') {
@@ -231,7 +228,7 @@ export const Proposal = ({
proposal.terms.change.__typename === 'UpdateAsset') &&
asset && (
<div className="mb-4">
<ProposalAssetDetails asset={asset} originalAsset={originalAsset} />
<ProposalAssetDetails asset={asset} />
</div>
)}
@@ -1,5 +1,4 @@
import { act, render, screen, waitFor, within } from '@testing-library/react';
// import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import { Closed } from './closed';
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
@@ -27,7 +26,6 @@ import {
marketsDataQuery,
createMarketsDataFragment,
} from '@vegaprotocol/mock';
import userEvent from '@testing-library/user-event';
describe('Closed', () => {
let originalNow: typeof Date.now;
@@ -170,11 +168,14 @@ describe('Closed', () => {
Date.now = originalNow;
});
const renderComponent = async (mocks: MockedResponse[]) => {
// eslint-disable-next-line jest/no-disabled-tests
it.skip('renders correctly formatted and filtered rows', async () => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider mocks={mocks}>
<MockedProvider
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
@@ -184,10 +185,6 @@ describe('Closed', () => {
</MemoryRouter>
);
});
};
it('renders correct headers', async () => {
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
@@ -203,10 +200,6 @@ describe('Closed', () => {
];
expect(headers).toHaveLength(expectedHeaders.length);
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
});
it('renders correctly formatted and filtered rows', async () => {
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
const assetSymbol = getAsset(market).symbol;
@@ -280,8 +273,21 @@ describe('Closed', () => {
},
},
};
await renderComponent([mixedMarketsMock, marketsDataMock, oracleDataMock]);
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
// check that the number of rows in datagrid is 2
const container = within(
@@ -313,67 +319,8 @@ describe('Closed', () => {
);
});
it('display market actions', async () => {
// Use market with a succcessor Id as the actions dropdown will optionally
// show a link to the successor market
const marketsWithSuccessorAndParent = [
{
__typename: 'MarketEdge' as const,
node: createMarketFragment({
id: 'include-0',
state: MarketState.STATE_SETTLED,
successorMarketID: 'successor',
parentMarketID: 'parent',
}),
},
];
const mockWithSuccessorAndParent: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: {
marketsConnection: {
__typename: 'MarketConnection',
edges: marketsWithSuccessorAndParent,
},
},
},
};
await renderComponent([
mockWithSuccessorAndParent,
marketsDataMock,
oracleDataMock,
]);
const actionCell = screen
.getAllByRole('gridcell')
.find((el) => el.getAttribute('col-id') === 'market-actions');
await userEvent.click(
within(actionCell as HTMLElement).getByTestId('dropdown-menu')
);
expect(screen.getByRole('menu')).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'Copy Market ID' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View on Explorer' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View settlement asset details' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View parent market' })
).toBeInTheDocument();
expect(
screen.getByRole('menuitem', { name: 'View successor market' })
).toBeInTheDocument();
});
it('successor market should be visible', async () => {
// eslint-disable-next-line jest/no-disabled-tests
it.skip('successor marked should be visible', async () => {
const marketsWithSuccessorID = [
{
__typename: 'MarketEdge' as const,
@@ -398,11 +345,21 @@ describe('Closed', () => {
},
};
await renderComponent([
mockWithSuccessors,
marketsDataMock,
oracleDataMock,
]);
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mockWithSuccessors, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
@@ -1,145 +0,0 @@
import { act, render, screen, within } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { OpenMarkets } from './open-markets';
import { Interval } from '@vegaprotocol/types';
import type { MockedResponse } from '@apollo/client/testing';
import { MockedProvider } from '@apollo/client/testing';
import type {
MarketsDataQuery,
MarketsQuery,
MarketCandlesQuery,
MarketFieldsFragment,
} from '@vegaprotocol/markets';
import {
MarketsDataDocument,
MarketsDocument,
MarketsCandlesDocument,
} from '@vegaprotocol/markets';
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import {
marketsQuery,
marketsDataQuery,
marketsCandlesQuery,
} from '@vegaprotocol/mock';
import userEvent from '@testing-library/user-event';
describe('Open', () => {
let originalNow: typeof Date.now;
const mockNowTimestamp = 1672531200000;
const pubKey = 'pubKey';
const marketsQueryData = marketsQuery();
const marketsMock: MockedResponse<MarketsQuery> = {
request: {
query: MarketsDocument,
},
result: {
data: marketsQueryData,
},
};
const marketsCandlesQueryData = marketsCandlesQuery();
const marketsCandlesMock: MockedResponse<MarketCandlesQuery> = {
request: {
query: MarketsCandlesDocument,
variables: {
interval: Interval.INTERVAL_I1H,
since: '2022-12-31T00:00:00.000Z',
},
},
result: {
data: marketsCandlesQueryData,
},
};
const marketsDataQueryData = marketsDataQuery();
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
request: {
query: MarketsDataDocument,
},
result: {
data: marketsDataQueryData,
},
};
beforeAll(() => {
originalNow = Date.now;
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
});
afterAll(() => {
Date.now = originalNow;
});
const renderComponent = async () => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsCandlesMock, marketsDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<OpenMarkets />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
};
it('renders correct headers', async () => {
await renderComponent();
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
'Market',
'Description',
'Settlement asset',
'Trading mode',
'Status',
'Mark price',
'24h volume',
'Open Interest',
'Spread',
'', // Action row
];
expect(headers).toHaveLength(expectedHeaders.length);
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
});
it('sort columns', async () => {
await renderComponent();
const headers = screen.getAllByRole('columnheader');
const marketHeader = headers.find(
(h) => h.getAttribute('col-id') === 'tradableInstrument.instrument.code'
);
if (!marketHeader) {
throw new Error('No market header found');
}
expect(marketHeader).toHaveAttribute('aria-sort', 'none');
await userEvent.click(within(marketHeader).getByText(/market/i));
// 6001-MARK-064
expect(marketHeader).toHaveAttribute('aria-sort', 'ascending');
});
// eslint-disable-next-line jest/no-disabled-tests, jest/expect-expect
it('renders row', async () => {
await renderComponent();
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
);
const markets = marketsQueryData.marketsConnection?.edges.map(
(e) => e.node
) as MarketFieldsFragment[];
const rows = container.getAllByRole('row');
expect(rows).toHaveLength(markets.length);
});
});
-2
View File
@@ -12,7 +12,6 @@ from contextlib import contextmanager
from vega_sim.null_service import VegaServiceNull
from playwright.sync_api import Browser, Page
from config import console_image_name, vega_version
from datetime import datetime, timedelta
from fixtures.market import (
setup_simple_market,
setup_opening_auction_market,
@@ -79,7 +78,6 @@ def init_vega(request=None):
store_transactions=True,
transactions_per_block=1000,
seconds_per_block=seconds_per_block,
genesis_time= datetime.now() - timedelta(days=1),
) as vega:
try:
container = docker_client.containers.run(
+1 -1
View File
@@ -1161,7 +1161,7 @@ profile = ["pytest-profiling", "snakeviz"]
type = "git"
url = "https://github.com/vegaprotocol/vega-market-sim.git"
reference = "HEAD"
resolved_reference = "fbcb974b2055bbc80169cdfd69987f087f9969fb"
resolved_reference = "e93f7dfa8463c59cfd0e299362b845511cebeef6"
[[package]]
name = "websocket-client"
+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"
@@ -58,6 +58,7 @@ class TestSettledMarket:
def test_settled_rows(self, page: Page, create_settled_market):
page.goto(f"/#/markets/all")
page.get_by_test_id("Closed markets").click()
row_selector = page.locator(
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row'
).first
@@ -71,7 +72,7 @@ class TestSettledMarket:
# 6001-MARK-009
# 6001-MARK-008
# 6001-MARK-010
pattern = r"(\d+)\s+(months|hours|days)\s+ago"
pattern = r"(\d+)\s+months\s+ago"
date_text = row_selector.locator('[col-id="settlementDate"]').inner_text()
assert re.match(pattern, date_text), f"Expected text to match pattern but got {date_text}"
@@ -0,0 +1,160 @@
import pytest
from playwright.sync_api import Page, expect
from fixtures.market import setup_continuous_market
from conftest import init_vega
market_names = ["ETHBTC.QM21", "BTCUSD.MF21", "SOLUSD", "AAPL.MF21"]
@pytest.fixture(scope="module")
def vega():
with init_vega() as vega:
yield vega
@pytest.fixture(scope="module")
def create_markets(vega):
for market_name in market_names:
setup_continuous_market(vega, custom_market_name=market_name)
@pytest.mark.usefixtures("risk_accepted")
def test_table_headers(page: Page, create_markets):
page.goto(f"/#/markets/all")
headers = [
"Market",
"Description",
"Settlement asset",
"Trading mode",
"Status",
"Mark price",
"24h volume",
"Open Interest",
"Spread",
"",
]
page.wait_for_selector('[data-testid="tab-open-markets"]', state="visible")
page_headers = (
page.get_by_test_id("tab-open-markets").locator(".ag-header-cell-text").all()
)
for i, header in enumerate(headers):
expect(page_headers[i]).to_have_text(header)
@pytest.mark.usefixtures("risk_accepted")
def test_markets_tab(page: Page, create_markets):
page.goto(f"/#/markets/all")
expect(page.get_by_test_id("Open markets")).to_have_attribute(
"data-state", "active"
)
expect(page.get_by_test_id("Proposed markets")).to_have_attribute(
"data-state", "inactive"
)
expect(page.get_by_test_id("Closed markets")).to_have_attribute(
"data-state", "inactive"
)
@pytest.mark.usefixtures("risk_accepted")
def test_markets_content(page: Page, create_markets):
page.goto(f"/#/markets/all")
row_selector = page.locator(
'[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row'
).first
instrument_code_locator = '[col-id="tradableInstrument.instrument.code"] [data-testid="stack-cell-primary"]'
# 6001-MARK-035
expect(row_selector.locator(instrument_code_locator)).to_have_text("ETHBTC.QM21")
# 6001-MARK-073
expect(row_selector.locator('[title="Future"]')).to_have_text("Futr")
# 6001-MARK-036
expect(
row_selector.locator('[col-id="tradableInstrument.instrument.name"]')
).to_have_text("ETHBTC.QM21")
# 6001-MARK-037
expect(row_selector.locator('[col-id="tradingMode"]')).to_have_text("Continuous")
# 6001-MARK-038
expect(row_selector.locator('[col-id="state"]')).to_have_text("Active")
# 6001-MARK-039
expect(row_selector.locator('[col-id="data.markPrice"]')).to_have_text("107.50")
# 6001-MARK-040
expect(row_selector.locator('[col-id="data.candles"]')).to_have_text("0.00")
# 6001-MARK-042
expect(
row_selector.locator(
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"]'
)
).to_have_text("tDAI")
expect(row_selector.locator('[col-id="data.bestBidPrice"]')).to_have_text("2")
# 6001-MARK-043
row_selector.locator(
'[col-id="tradableInstrument.instrument.product.settlementAsset.symbol"] button'
).click()
expect(page.get_by_test_id("dialog-title")).to_have_text("Asset details - tDAI")
# 6001-MARK-019
page.get_by_test_id("close-asset-details-dialog").click()
@pytest.mark.usefixtures("risk_accepted")
def test_market_actions(page: Page, create_markets):
# 6001-MARK-044
# 6001-MARK-045
# 6001-MARK-046
# 6001-MARK-047
page.goto(f"/#/markets/all")
page.locator(
'.ag-pinned-right-cols-container [col-id="market-actions"]'
).first.locator("button").click()
actions = [
"Copy Market ID",
"View on Explorer",
"View settlement asset details",
]
action_elements = (
page.get_by_test_id("market-actions-content").get_by_role("menuitem").all()
)
for i, action in enumerate(actions):
expect(action_elements[i]).to_have_text(action)
@pytest.mark.usefixtures("risk_accepted")
def test_sort_markets(page: Page, create_markets):
# 6001-MARK-064
page.goto(f"/#/markets/all")
sorted_market_names = [
"AAPL.MF21",
"BTCUSD.MF21",
"ETHBTC.QM21",
"SOLUSD",
]
page.locator('.ag-header-row [col-id="tradableInstrument.instrument.code"]').click()
for i, market_name in enumerate(sorted_market_names):
expect(
page.locator(
f'[row-index="{i}"] [col-id="tradableInstrument.instrument.name"]'
)
).to_have_text(market_name)
@pytest.mark.usefixtures("risk_accepted")
def test_drag_and_drop_column(page: Page, create_markets):
# 6001-MARK-065
page.goto(f"/#/markets/all")
col_instrument_code = '.ag-header-row [col-id="tradableInstrument.instrument.code"]'
page.locator(col_instrument_code).drag_to(
page.locator('.ag-header-row [col-id="data.bestBidPrice"]')
)
expect(page.locator(col_instrument_code)).to_have_attribute("aria-colindex", "9")
@@ -1,138 +0,0 @@
import pytest
import re
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from vega_sim.service import MarketStateUpdateType
from datetime import datetime, timedelta
from conftest import init_vega
from actions.utils import change_keys
from actions.vega import submit_multiple_orders
from fixtures.market import setup_perps_market
from wallet_config import MM_WALLET, MM_WALLET2, TERMINATE_WALLET
row_selector = '[data-testid="tab-funding-payments"] .ag-center-cols-container .ag-row'
col_amount = '[col-id="amount"]'
class TestPerpetuals:
@pytest.fixture(scope="class")
def vega(self, request):
with init_vega(request) as vega:
yield vega
@pytest.fixture(scope="class")
def perps_market(self, vega: VegaService):
perps_market = setup_perps_market(vega)
submit_multiple_orders(
vega, MM_WALLET.name, perps_market, "SIDE_SELL", [[1, 110], [1, 105]]
)
submit_multiple_orders(
vega, MM_WALLET2.name, perps_market, "SIDE_BUY", [[1, 90], [1, 95]]
)
vega.submit_settlement_data(
settlement_key=TERMINATE_WALLET.name,
settlement_price=110,
market_id=perps_market,
)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
submit_multiple_orders(
vega, MM_WALLET.name, perps_market, "SIDE_SELL", [[1, 110], [1, 105]]
)
submit_multiple_orders(
vega, MM_WALLET2.name, perps_market, "SIDE_BUY", [[1, 112], [1, 115]]
)
vega.submit_settlement_data(
settlement_key=TERMINATE_WALLET.name,
settlement_price=110,
market_id=perps_market,
)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
return perps_market
@pytest.mark.usefixtures("page","risk_accepted", "auth")
def test_funding_payment_profit(self, perps_market, page: Page):
page.goto(f"/#/markets/{perps_market}")
page.get_by_test_id("Funding payments").click()
row = page.locator(row_selector)
expect(row.locator(col_amount)).to_have_text("9.00 tDAI")
@pytest.mark.usefixtures("page","risk_accepted", "auth")
def test_funding_payment_loss(self, perps_market, page: Page, vega):
page.goto(f"/#/markets/{perps_market}")
change_keys(page, vega, "market_maker")
page.get_by_test_id("Funding payments").click()
row = page.locator(row_selector)
expect(row.locator(col_amount)).to_have_text("-27.00 tDAI")
@pytest.mark.usefixtures("page","risk_accepted", "auth")
def test_funding_header(self, perps_market, page: Page):
page.goto(f"/#/markets/{perps_market}")
expect(page.get_by_test_id("market-funding")).to_contain_text("Funding Rate / Countdown-8.1818%")
expect(page.get_by_test_id("index-price")).to_have_text("Index Price110.00")
@pytest.mark.skip("Skipped due to issue #5421")
@pytest.mark.usefixtures("page","risk_accepted", "auth")
def test_funding_payment_history(perps_market, page: Page, vega):
page.goto(f"/#/markets/{perps_market}")
change_keys(page, vega, "market_maker")
page.get_by_test_id("Funding history").click()
element = page.get_by_test_id("tab-funding-history")
# Get the bounding box of the element
bounding_box = element.bounding_box()
if bounding_box:
bottom_right_x = bounding_box["x"] + bounding_box["width"]
bottom_right_y = bounding_box["y"] + bounding_box["height"]
# Hover over the bottom-right corner of the element
element.hover(position={"x": bottom_right_x, "y": bottom_right_y})
else:
print("Bounding box not found for the element")
@pytest.mark.usefixtures("page","risk_accepted", "auth")
def test_perps_market_termination_proposed(page: Page, vega: VegaService):
perpetual_market = setup_perps_market(vega)
page.goto(f"/#/markets/{perpetual_market}")
vega.update_market_state(
proposal_key=MM_WALLET.name,
market_id=perpetual_market,
market_state=MarketStateUpdateType.Terminate,
price=100,
vote_closing_time = datetime.now() + timedelta(seconds=15),
vote_enactment_time = datetime.now() + timedelta(seconds=60),
approve_proposal = True,
forward_time_to_enactment = False,
)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
banner_text = page.get_by_test_id(f"termination-warning-banner-{perpetual_market}").text_content()
pattern = re.compile(
r"Trading on Market BTC:DAI_Perpetual may stop on \d{2} [A-Za-z]+\. There is open proposal to close this market\.Proposed final price is 100\.00 BTC\.View proposal"
)
assert pattern.search(banner_text), f"Text did not match pattern. Text was: {banner_text}"
@pytest.mark.usefixtures("page","risk_accepted", "auth" )
def test_perps_market_terminated(page: Page, vega: VegaService):
perpetual_market = setup_perps_market(vega)
page.goto(f"/#/markets/{perpetual_market}")
vega.update_market_state(
proposal_key=MM_WALLET.name,
market_id=perpetual_market,
market_state=MarketStateUpdateType.Terminate,
price=100,
approve_proposal = True,
forward_time_to_enactment = True,
)
expect(page.get_by_test_id("market-price")).to_have_text("Mark Price100.00")
expect(page.get_by_test_id("market-change")).to_have_text("Change (24h)-")
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)-")
expect(page.get_by_test_id("market-trading-mode")).to_have_text("Trading modeNo trading")
expect(page.get_by_test_id("market-state")).to_have_text("StatusClosed")
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 0.00 (0.00%)")
expect(page.get_by_test_id("market-funding")).to_have_text("Funding Rate / Countdown-Unknown")
expect(page.get_by_test_id("index-price")).to_have_text("Index Price-")
expect(page.get_by_test_id("deal-ticket-error-message-summary")).to_have_text("This market is closed and not accepting orders")
+1 -2
View File
@@ -23,7 +23,7 @@ export const ALLOWED_ACCOUNTS = [
export const TransferContainer = ({ assetId }: { assetId?: string }) => {
const t = useT();
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
const { pubKey, pubKeys } = useVegaWallet();
const { params } = useNetworkParams([
NetworkParams.transfer_fee_factor,
NetworkParams.transfer_minTransferQuantumMultiple,
@@ -70,7 +70,6 @@ export const TransferContainer = ({ assetId }: { assetId?: string }) => {
<TransferForm
pubKey={pubKey}
pubKeys={pubKeys ? pubKeys?.map((pk) => pk.publicKey) : null}
isReadOnly={isReadOnly}
assetId={assetId}
feeFactor={params.transfer_fee_factor}
minQuantumMultiple={params.transfer_minTransferQuantumMultiple}
@@ -73,28 +73,6 @@ describe('TransferForm', () => {
minQuantumMultiple: '1',
};
const propsNoAssets = {
pubKey,
pubKeys: [
pubKey,
'a4b6e3de5d7ef4e31ae1b090be49d1a2ef7bcefff60cccf7658a0d4922651cce',
],
feeFactor: '0.001',
submitTransfer: jest.fn(),
accounts: [],
minQuantumMultiple: '1',
};
it('renders no assets', async () => {
renderComponent(propsNoAssets);
expect(screen.getByTestId('no-assets-available')).toBeVisible();
});
it('renders no accounts', async () => {
renderComponent(propsNoAssets);
expect(screen.getByTestId('no-accounts-available')).toBeVisible();
});
it.each([
{
targetText: 'Include transfer fee',
+62 -82
View File
@@ -45,7 +45,6 @@ interface Asset {
export interface TransferFormProps {
pubKey: string | null;
pubKeys: string[] | null;
isReadOnly?: boolean;
accounts: Array<{
type: AccountType;
balance: string;
@@ -60,7 +59,6 @@ export interface TransferFormProps {
export const TransferForm = ({
pubKey,
pubKeys,
isReadOnly,
assetId: initialAssetId,
feeFactor,
submitTransfer,
@@ -203,36 +201,27 @@ export const TransferForm = ({
<Controller
control={control}
name="asset"
render={({ field }) =>
assets.length > 0 ? (
<TradingRichSelect
data-testid="select-asset"
id={field.name}
name={field.name}
onValueChange={(value) => {
field.onChange(value);
setValue('fromAccount', '');
}}
placeholder={t('Please select an asset')}
value={field.value}
>
{assets.map((a) => (
<AssetOption
key={a.key}
asset={a}
balance={<Balance balance={a.balance} symbol={a.symbol} />}
/>
))}
</TradingRichSelect>
) : (
<span
data-testid="no-assets-available"
className="text-xs text-vega-clight-100 dark:text-vega-cdark-100"
>
{t('No assets available')}
</span>
)
}
render={({ field }) => (
<TradingRichSelect
data-testid="select-asset"
id={field.name}
name={field.name}
onValueChange={(value) => {
field.onChange(value);
setValue('fromAccount', '');
}}
placeholder={t('Please select an asset')}
value={field.value}
>
{assets.map((a) => (
<AssetOption
key={a.key}
asset={a}
balance={<Balance balance={a.balance} symbol={a.symbol} />}
/>
))}
</TradingRichSelect>
)}
/>
{errors.asset?.message && (
<TradingInputError forInput="asset">
@@ -260,57 +249,48 @@ export const TransferForm = ({
},
},
}}
render={({ field }) =>
accounts.length > 0 ? (
<TradingSelect
id="fromAccount"
defaultValue=""
{...field}
onChange={(e) => {
field.onChange(e);
render={({ field }) => (
<TradingSelect
id="fromAccount"
defaultValue=""
{...field}
onChange={(e) => {
field.onChange(e);
const [type] = parseFromAccount(e.target.value);
const [type] = parseFromAccount(e.target.value);
// Enforce that if transferring from a vested rewards account it must go to
// the current connected general account
if (
type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS &&
pubKey
) {
setValue('toVegaKey', pubKey);
setToVegaKeyMode('select');
setIncludeFee(false);
}
}}
>
<option value="" disabled={true}>
{t('Please select')}
</option>
{accounts
.filter((a) => {
if (!selectedAssetId) return true;
return selectedAssetId === a.asset.id;
})
.map((a) => {
const id = `${a.type}-${a.asset.id}`;
return (
<option value={id} key={id}>
{AccountTypeMapping[a.type]} (
{addDecimal(a.balance, a.asset.decimals)}{' '}
{a.asset.symbol})
</option>
);
})}
</TradingSelect>
) : (
<span
data-testid="no-accounts-available"
className="text-xs text-vega-clight-100 dark:text-vega-cdark-100"
>
{t('No accounts available')}
</span>
)
}
// Enforce that if transferring from a vested rewards account it must go to
// the current connected general account
if (
type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS &&
pubKey
) {
setValue('toVegaKey', pubKey);
setToVegaKeyMode('select');
setIncludeFee(false);
}
}}
>
<option value="" disabled={true}>
{t('Please select')}
</option>
{accounts
.filter((a) => {
if (!selectedAssetId) return true;
return selectedAssetId === a.asset.id;
})
.map((a) => {
const id = `${a.type}-${a.asset.id}`;
return (
<option value={id} key={id}>
{AccountTypeMapping[a.type]} (
{addDecimal(a.balance, a.asset.decimals)} {a.asset.symbol}
)
</option>
);
})}
</TradingSelect>
)}
/>
{errors.fromAccount?.message && (
<TradingInputError forInput="fromAccount">
@@ -474,7 +454,7 @@ export const TransferForm = ({
decimals={asset?.decimals}
/>
)}
<TradingButton type="submit" fill={true} disabled={isReadOnly}>
<TradingButton type="submit" fill={true}>
{t('Confirm transfer')}
</TradingButton>
</form>
+7 -66
View File
@@ -18,7 +18,7 @@ type Rows = {
key: AssetDetail;
label: string;
tooltip: string;
value: (asset: Asset, orignalAsset?: Asset) => ReactNode | undefined;
value: (asset: Asset) => ReactNode | undefined;
valueTooltip?: (asset: Asset) => string | null | undefined;
}[];
@@ -52,21 +52,6 @@ const num = (asset: Asset, n: string | undefined | null) => {
return addDecimalsFormatNumber(n, asset.decimals);
};
const Diff = ({
oldValue,
newValue,
}: {
oldValue: ReactNode;
newValue: ReactNode;
}) => (
<span className="flex gap-1">
<span className="line-through bg-vega-red-300 dark:bg-vega-red-600">
{oldValue}
</span>
<span className="bg-vega-green-300 dark:bg-vega-green-600">{newValue}</span>
</span>
);
export const useRows = () => {
const t = useT();
const AssetTypeMapping = useAssetTypeMapping();
@@ -118,14 +103,7 @@ export const useRows = () => {
key: AssetDetail.QUANTUM,
label: t('Quantum'),
tooltip: t('The minimum economically meaningful amount of the asset'),
value: (asset, originalAsset) => {
const value = num(asset, asset.quantum);
if (originalAsset && originalAsset.quantum !== asset.quantum) {
const original = num(originalAsset, originalAsset.quantum);
return <Diff oldValue={original} newValue={value} />;
}
return value;
},
value: (asset) => num(asset, asset.quantum),
},
{
key: AssetDetail.STATUS,
@@ -165,24 +143,8 @@ export const useRows = () => {
tooltip: t('WITHDRAW_THRESHOLD_TOOLTIP_TEXT', {
defaultValue: WITHDRAW_THRESHOLD_TOOLTIP_TEXT,
}),
value: (asset, originalAsset) => {
const value = num(
asset,
(asset.source as Schema.ERC20).withdrawThreshold
);
if (
originalAsset &&
(originalAsset.source as Schema.ERC20).withdrawThreshold !==
(asset.source as Schema.ERC20).withdrawThreshold
) {
const original = num(
asset,
(originalAsset.source as Schema.ERC20).withdrawThreshold
);
return <Diff oldValue={original} newValue={value} />;
}
return value;
},
value: (asset) =>
num(asset, (asset.source as Schema.ERC20).withdrawThreshold),
},
{
key: AssetDetail.LIFETIME_LIMIT,
@@ -190,26 +152,8 @@ export const useRows = () => {
tooltip: t(
'The lifetime deposit limit per address. Note: this is a temporary measure that can be changed or removed through governance'
),
value: (asset, originalAsset) => {
const value = num(
asset,
(asset.source as Schema.ERC20).lifetimeLimit
);
if (
originalAsset &&
(originalAsset.source as Schema.ERC20).lifetimeLimit !==
(asset.source as Schema.ERC20).lifetimeLimit
) {
const original = num(
asset,
(originalAsset.source as Schema.ERC20).lifetimeLimit
);
return <Diff oldValue={original} newValue={value} />;
}
return value;
},
value: (asset) =>
num(asset, (asset.source as Schema.ERC20).lifetimeLimit),
},
{
key: AssetDetail.MAX_FAUCET_AMOUNT_MINT,
@@ -317,13 +261,10 @@ export const testId = (detail: AssetDetail, field: 'label' | 'value') =>
export type AssetDetailsTableProps = {
asset: Asset;
originalAsset?: Asset;
omitRows?: AssetDetail[];
} & Omit<KeyValueTableRowProps, 'children'>;
export const AssetDetailsTable = ({
asset,
originalAsset,
omitRows = [],
...props
}: AssetDetailsTableProps) => {
@@ -334,7 +275,7 @@ export const AssetDetailsTable = ({
const details = useRows().map((r) => ({
...r,
value: r.value(asset, originalAsset),
value: r.value(asset),
valueTooltip: r.valueTooltip?.(asset),
}));
+8 -8
View File
@@ -1,5 +1,5 @@
{
"Adjusted stake": "Adjusted stake",
"Adjusted stake share": "Adjusted stake share",
"Commitment ({{symbol}})": "Commitment ({{symbol}})",
"Commitment details": "Commitment details",
"Created": "Created",
@@ -7,14 +7,14 @@
"Fee": "Fee",
"Fees accrued this epoch": "Fees accrued this epoch",
"Last bond penalty": "Last bond penalty",
"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.": "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.",
"Penalty applied on the fees a liquidity provider collected in the last epoch. This number increases if an LP did not meet the SLA, or if they met it but other LPs outscored them in the previous epoch.": "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.",
"Fraction of time on the book at the end of the last epoch.": "Fraction of time on the book at the end of the last epoch.",
"Last epoch bond penalty.": "Last epoch bond penalty.",
"Last epoch fee penalty.": "Last epoch fee penalty.",
"Last epoch fraction of time on the book.": "Last epoch fraction of time on the book.",
"Last epoch SLA details": "Last epoch SLA details",
"Last fee penalty": "Last fee penalty",
"Last time on book": "Last time on book",
"Last time on the book": "Last time on the book",
"Live liquidity data": "Live liquidity data",
"Live liquidity score (%)": "Live liquidity score (%)",
"Live liquidity quality score (%)": "Live liquidity quality score (%)",
"Live supplied liquidity": "Live supplied liquidity",
"Live time on book": "Live time on book",
"No liquidity provisions": "No liquidity provisions",
@@ -24,7 +24,7 @@
"Status": "Status",
"The amount committed to the market by this liquidity provider.": "The amount committed to the market by this liquidity provider.",
"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.": "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.",
"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.": "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.",
"The average score of the liquidity provider.": "The average score of the liquidity provider.",
"The current status of this liquidity provision.": "The current status of this liquidity provision.",
"The date and time this liquidity provision was created.": "The date and time this liquidity provision was created.",
"The date and time this liquidity provision was last updated.": "The date and time this liquidity provision was last updated.",
@@ -33,7 +33,7 @@
"The liquidity fees accrued by each provider, which will be distributed at the end of the epoch after applying any penalties.": "The liquidity fees accrued by each provider, which will be distributed at the end of the epoch after applying any penalties.",
"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.": "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.",
"The public key of the party making this commitment.": "The public key of the party making this commitment.",
"The effective stake of the liquidity provider, adjusted for length of commitment and impact on equity like share.": "The effective stake of the liquidity provider, adjusted for length of commitment and impact on equity like share.",
"The virtual stake of the liquidity provider.": "The virtual stake of the liquidity provider.",
"This LP's time on the book in the current epoch ({{currentEpoch}}) is less than 100%, so they could lose some fees to a better performing LP.": "This LP's time on the book in the current epoch ({{currentEpoch}}) is less than 100%, so they could lose some fees to a better performing LP.",
"This LP's time on the book in the current epoch ({{currentEpoch}}) is less than the minimum required ({{minimumRequired}}), so they could lose all fee revenue for this epoch.": "This LP's time on the book in the current epoch ({{currentEpoch}}) is less than the minimum required ({{minimumRequired}}), so they could lose all fee revenue for this epoch.",
"Updated": "Updated",
@@ -93,13 +93,13 @@ describe('LiquidityTable', () => {
'Commitment ()',
'Obligation',
'Fee',
'Adjusted stake',
'Adjusted stake share',
'Share',
'Live supplied liquidity',
'Fees accrued this epoch',
'Live time on book',
'Live liquidity score (%)',
'Last time on book',
'Live liquidity quality score (%)',
'Last time on the book',
'Last fee penalty',
'Last bond penalty',
'Created',
+16 -21
View File
@@ -357,12 +357,10 @@ export const LiquidityTable = ({
},
},
{
headerName: t('Adjusted stake'),
headerName: t('Adjusted stake share'),
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.'
),
headerTooltip: t('The virtual stake of the liquidity provider.'),
valueFormatter: assetDecimalsQuantumFormatter,
tooltipValueGetter: assetDecimalsFormatter,
@@ -415,9 +413,14 @@ export const LiquidityTable = ({
},
'text-red-500': ({ data }: { data: LiquidityProvisionData }) => {
if (!data.sla) return false;
return new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(data.commitmentMinTimeFraction);
return (
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isLessThan(data.commitmentMinTimeFraction) &&
new BigNumber(
data.sla.currentEpochFractionOfTimeOnBook
).isGreaterThan(0)
);
},
},
},
@@ -429,12 +432,10 @@ export const LiquidityTable = ({
valueFormatter: percentageFormatter,
},
{
headerName: t('Live liquidity score (%)'),
headerName: t('Live liquidity quality 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.'
),
headerTooltip: t('The average score of the liquidity provider.'),
valueFormatter: percentageFormatter,
},
],
@@ -444,30 +445,24 @@ export const LiquidityTable = ({
marryChildren: true,
children: [
{
headerName: t(`Last time on book`),
headerName: t(`Last time on the book`),
field: 'sla.lastEpochFractionOfTimeOnBook',
type: 'rightAligned',
headerTooltip: t(
'Fraction of time on the book at the end of the last epoch.'
),
headerTooltip: t('Last epoch fraction of time on the book.'),
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.'
),
headerTooltip: t('Last epoch fee penalty.'),
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.`
),
headerTooltip: t('Last epoch bond penalty.'),
valueFormatter: percentageFormatter,
},
],