Compare commits

...
Author SHA1 Message Date
Matthew Russell 2c59073576 fix: condition to only show reward assets with withdrawable rewards 2023-12-05 16:41:42 -08:00
Matthew Russell 76d1057555 fix: dont render child components until asset map is loaded 2023-12-05 16:10:02 -08:00
Matthew Russell 0cec755980 fix: test for percentage in row 2023-12-05 15:39:23 -08:00
Matthew Russell 1601a83346 chore: add check for usdt vested rewards before showing reward pot 2023-12-05 15:22:42 -08:00
Matthew Russell 0cb8844094 fix: total vs party toggle for rewards history 2023-12-05 14:39:47 -08:00
Matthew Russell af5e701537 fix: missing or empty reward pot entries 2023-12-05 14:30:11 -08:00
Madalina Raicu 351a117c3f fix: try to fix rewards-history.spec.tsx 2023-12-05 17:23:21 +00:00
Madalina Raicu 0b536faec6 Merge branches 'fix/rewards-history' and 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/rewards-history 2023-12-05 16:24:59 +00:00
Ben ffada1b93d feat(trading): auction uncross fees test (#5445) 2023-12-05 15:33:03 +00:00
Madalina Raicu a9e0f9adc7 Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/rewards-history 2023-12-05 10:54:49 +00:00
BenandMatthew Russell 9dda3f712b chore(trading): market python tests to jest (#5346)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-12-05 10:29:01 +00:00
m.ray df20dbeee0 chore(trading): revert moving rewards container to portfolio (#5441) 2023-12-05 10:19:42 +00:00
Madalina Raicu 1b64c7c5eb fix: refactor test 2023-12-04 20:59:21 +00:00
Madalina Raicu b78a3c5648 chore: cover multiple reward types 2023-12-04 20:31:34 +00:00
Madalina Raicu c21a69caf6 fix: refactor use-reward-row-data test 2023-12-04 20:24:38 +00:00
Madalina Raicu c7dfeb735f fix: show infra fees in reward history 2023-12-04 19:43:48 +00:00
m.ray cdfd8a2d00 fix(trading): revert disabling sortable as it breaks view (#5435) 2023-12-04 18:12:13 +00:00
16 changed files with 596 additions and 262 deletions
@@ -1,4 +1,5 @@
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';
@@ -26,6 +27,7 @@ import {
marketsDataQuery,
createMarketsDataFragment,
} from '@vegaprotocol/mock';
import userEvent from '@testing-library/user-event';
describe('Closed', () => {
let originalNow: typeof Date.now;
@@ -168,14 +170,11 @@ describe('Closed', () => {
Date.now = originalNow;
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip('renders correctly formatted and filtered rows', async () => {
const renderComponent = async (mocks: MockedResponse[]) => {
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[marketsMock, marketsDataMock, oracleDataMock]}
>
<MockedProvider mocks={mocks}>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
@@ -185,6 +184,10 @@ describe('Closed', () => {
</MemoryRouter>
);
});
};
it('renders correct headers', async () => {
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
const headers = screen.getAllByRole('columnheader');
const expectedHeaders = [
@@ -200,6 +203,10 @@ 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;
@@ -273,21 +280,8 @@ describe('Closed', () => {
},
},
};
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mixedMarketsMock, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
await renderComponent([mixedMarketsMock, marketsDataMock, oracleDataMock]);
// check that the number of rows in datagrid is 2
const container = within(
@@ -319,8 +313,67 @@ describe('Closed', () => {
);
});
// eslint-disable-next-line jest/no-disabled-tests
it.skip('successor marked should be visible', async () => {
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 () => {
const marketsWithSuccessorID = [
{
__typename: 'MarketEdge' as const,
@@ -345,21 +398,11 @@ describe('Closed', () => {
},
};
await act(async () => {
render(
<MemoryRouter>
<MockedProvider
mocks={[mockWithSuccessors, marketsDataMock, oracleDataMock]}
>
<VegaWalletContext.Provider
value={{ pubKey } as VegaWalletContextShape}
>
<Closed />
</VegaWalletContext.Provider>
</MockedProvider>
</MemoryRouter>
);
});
await renderComponent([
mockWithSuccessors,
marketsDataMock,
oracleDataMock,
]);
const container = within(
document.querySelector('.ag-center-cols-container') as HTMLElement
@@ -0,0 +1,145 @@
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);
});
});
@@ -14,7 +14,6 @@ import { PositionsMenu } from '../../components/positions-menu';
import { WithdrawalsContainer } from '../../components/withdrawals-container';
import { OrdersContainer } from '../../components/orders-container';
import { LedgerContainer } from '../../components/ledger-container';
import { RewardsContainer } from '../../components/rewards-container';
import {
ResizableGrid,
ResizableGridPanel,
@@ -87,9 +86,6 @@ export const Portfolio = () => {
<Tab id="ledger-entries" name={t('Ledger entries')}>
<LedgerContainer />
</Tab>
<Tab id="rewards" name={t('Rewards')}>
<RewardsContainer />
</Tab>
</Tabs>
</PortfolioGridChild>
</ResizableGridPanel>
@@ -68,6 +68,7 @@ describe('Navbar', () => {
['/portfolio', 'Portfolio'],
['/referrals', 'Referrals'],
['/fees', 'Fees'],
['/rewards', 'Rewards'],
[expect.stringContaining('governance'), 'Governance'],
];
@@ -102,6 +103,7 @@ describe('Navbar', () => {
['/portfolio', 'Portfolio'],
['/referrals', 'Referrals'],
['/fees', 'Fees'],
['/rewards', 'Rewards'],
[expect.stringContaining('governance'), 'Governance'],
];
const links = menu.getAllByRole('link');
@@ -213,6 +213,11 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
{t('Fees')}
</NavbarLink>
</NavbarItem>
<NavbarItem>
<NavbarLink to={Links.REWARDS()} onClick={onClick}>
{t('Rewards')}
</NavbarLink>
</NavbarItem>
<NavbarItem>
<NavbarLinkExternal to={useLinks(DApp.Governance)()}>
{t('Governance')}
@@ -1,4 +1,5 @@
import groupBy from 'lodash/groupBy';
import uniq from 'lodash/uniq';
import type { Account } from '@vegaprotocol/accounts';
import { useAccounts } from '@vegaprotocol/accounts';
import {
@@ -31,6 +32,12 @@ import { ViewType, useSidebar } from '../sidebar';
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
import { RewardsHistoryContainer } from './rewards-history';
import { useT } from '../../lib/use-t';
import { useAssetsMapProvider } from '@vegaprotocol/assets';
const ASSETS_WITH_INCORRECT_VESTING_REWARD_DATA = [
'bf1e88d19db4b3ca0d1d5bdb73718a01686b18cf731ca26adedf3c8b83802bba', // USDT mainnet
'8ba0b10971f0c4747746cd01ff05a53ae75ca91eba1d4d050b527910c983e27e', // USDT testnet
];
export const RewardsContainer = () => {
const t = useT();
@@ -40,37 +47,70 @@ export const RewardsContainer = () => {
NetworkParams.rewards_activityStreak_benefitTiers,
NetworkParams.rewards_vesting_baseRate,
]);
const { data: accounts, loading: accountsLoading } = useAccounts(pubKey);
const { data: assetMap } = useAssetsMapProvider();
const { data: epochData } = useRewardsEpochQuery();
// No need to specify the fromEpoch as it will by default give you the last
// Note activityStreak in query will fail
const { data: rewardsData, loading: rewardsLoading } = useRewardsPageQuery({
variables: {
partyId: pubKey || '',
},
// Inclusion of activity streak in query currently fails
errorPolicy: 'ignore',
});
if (!epochData?.epoch) return null;
if (!epochData?.epoch || !assetMap) return null;
const loading = paramsLoading || accountsLoading || rewardsLoading;
const rewardAccounts = accounts
? accounts.filter((a) =>
[
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
].includes(a.type)
)
? accounts
.filter((a) =>
[
AccountType.ACCOUNT_TYPE_VESTED_REWARDS,
AccountType.ACCOUNT_TYPE_VESTING_REWARDS,
].includes(a.type)
)
.filter((a) => new BigNumber(a.balance).isGreaterThan(0))
: [];
const rewardAssetsMap = groupBy(
rewardAccounts.filter((a) => a.asset.id !== params.reward_asset),
'asset.id'
);
const rewardAccountsAssetMap = groupBy(rewardAccounts, 'asset.id');
const lockedBalances = rewardsData?.party?.vestingBalancesSummary
.lockedBalances
? rewardsData.party.vestingBalancesSummary.lockedBalances.filter((b) =>
new BigNumber(b.balance).isGreaterThan(0)
)
: [];
const lockedAssetMap = groupBy(lockedBalances, 'asset.id');
const vestingBalances = rewardsData?.party?.vestingBalancesSummary
.vestingBalances
? rewardsData.party.vestingBalancesSummary.vestingBalances.filter((b) =>
new BigNumber(b.balance).isGreaterThan(0)
)
: [];
const vestingAssetMap = groupBy(vestingBalances, 'asset.id');
// each asset reward pot is made up of:
// available to withdraw - ACCOUNT_TYPE_VESTED_REWARDS
// vesting - vestingBalancesSummary.vestingBalances
// locked - vestingBalancesSummary.lockedBalances
//
// there can be entires for the same asset in each list so we need a uniq list of assets
const assets = uniq([
...Object.keys(rewardAccountsAssetMap),
...Object.keys(lockedAssetMap),
...Object.keys(vestingAssetMap),
]);
return (
<div className="grid auto-rows-min grid-cols-6 gap-3 p-2">
<div className="grid auto-rows-min grid-cols-6 gap-3">
{/* Always show reward information for vega */}
<Card
key={params.reward_asset}
@@ -117,28 +157,72 @@ export const RewardsContainer = () => {
</Card>
{/* Show all other reward pots, most of the time users will not have other rewards */}
{Object.keys(rewardAssetsMap).map((assetId) => {
const asset = rewardAssetsMap[assetId][0].asset;
return (
<Card
key={assetId}
title={t('{{assetSymbol}} Reward pot', {
assetSymbol: asset.symbol,
})}
className="lg:col-span-3 xl:col-span-2"
loading={loading}
>
<RewardPot
pubKey={pubKey}
accounts={accounts}
assetId={assetId}
vestingBalancesSummary={
rewardsData?.party?.vestingBalancesSummary
}
/>
</Card>
);
})}
{assets
.filter((assetId) => assetId !== params.reward_asset)
.map((assetId) => {
const asset = assetMap ? assetMap[assetId] : null;
if (!asset) return null;
// Following code is for mitigating an issue due to a core bug where locked and vesting
// balances were incorrectly increased for infrastructure rewards for USDT on mainnet
//
// We don't want to incorrectly show the wring locked/vesting values, but we DO want to
// show the user that they have rewards available to withdraw
if (ASSETS_WITH_INCORRECT_VESTING_REWARD_DATA.includes(asset.id)) {
const accountsForAsset = rewardAccountsAssetMap[asset.id];
const vestedAccount = accountsForAsset?.find(
(a) => a.type === AccountType.ACCOUNT_TYPE_VESTED_REWARDS
);
// No vested rewards available to withdraw, so skip over USDT
if (!vestedAccount || Number(vestedAccount.balance) <= 0) {
return null;
}
return (
<Card
key={assetId}
title={t('{{assetSymbol}} Reward pot', {
assetSymbol: asset.symbol,
})}
className="lg:col-span-3 xl:col-span-2"
loading={loading}
>
<RewardPot
pubKey={pubKey}
accounts={accounts}
assetId={assetId}
// Ensure that these values are shown as 0
vestingBalancesSummary={{
lockedBalances: [],
vestingBalances: [],
}}
/>
</Card>
);
}
return (
<Card
key={assetId}
title={t('{{assetSymbol}} Reward pot', {
assetSymbol: asset.symbol,
})}
className="lg:col-span-3 xl:col-span-2"
loading={loading}
>
<RewardPot
pubKey={pubKey}
accounts={accounts}
assetId={assetId}
vestingBalancesSummary={
rewardsData?.party?.vestingBalancesSummary
}
/>
</Card>
);
})}
<Card
title={t('Rewards history')}
className="lg:col-span-full"
@@ -147,6 +231,7 @@ export const RewardsContainer = () => {
<RewardsHistoryContainer
epoch={Number(epochData?.epoch.id)}
pubKey={pubKey}
assets={assetMap}
/>
</Card>
</div>
@@ -313,14 +398,14 @@ export const RewardPot = ({
export const Vesting = ({
pubKey,
baseRate,
multiplier = '1',
multiplier,
}: {
pubKey: string | null;
baseRate: string;
multiplier?: string;
}) => {
const t = useT();
const rate = new BigNumber(baseRate).times(multiplier);
const rate = new BigNumber(baseRate).times(multiplier || 1);
const rateFormatted = formatPercentage(Number(rate));
const baseRateFormatted = formatPercentage(Number(baseRate));
@@ -335,7 +420,7 @@ export const Vesting = ({
{pubKey && (
<tr>
<CardTableTH>{t('Vesting multiplier')}</CardTableTH>
<CardTableTD>{multiplier}x</CardTableTD>
<CardTableTD>{multiplier ? `${multiplier}x` : '-'}</CardTableTD>
</tr>
)}
</CardTable>
@@ -345,16 +430,16 @@ export const Vesting = ({
export const Multipliers = ({
pubKey,
streakMultiplier = '1',
hoarderMultiplier = '1',
streakMultiplier,
hoarderMultiplier,
}: {
pubKey: string | null;
streakMultiplier?: string;
hoarderMultiplier?: string;
}) => {
const t = useT();
const combinedMultiplier = new BigNumber(streakMultiplier).times(
hoarderMultiplier
const combinedMultiplier = new BigNumber(streakMultiplier || 1).times(
hoarderMultiplier || 1
);
if (!pubKey) {
@@ -375,11 +460,15 @@ export const Multipliers = ({
<CardTable>
<tr>
<CardTableTH>{t('Streak reward multiplier')}</CardTableTH>
<CardTableTD>{streakMultiplier}x</CardTableTD>
<CardTableTD>
{streakMultiplier ? `${streakMultiplier}x` : '-'}
</CardTableTD>
</tr>
<tr>
<CardTableTH>{t('Hoarder reward multiplier')}</CardTableTH>
<CardTableTD>{hoarderMultiplier}x</CardTableTD>
<CardTableTD>
{hoarderMultiplier ? `${hoarderMultiplier}x` : '-'}
</CardTableTD>
</tr>
</CardTable>
</div>
@@ -61,6 +61,14 @@ const rewardSummaries = [
rewardType: AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS,
},
},
{
node: {
epoch: 7,
assetId: assets.asset2.id,
amount: '300',
rewardType: AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
},
},
];
const getCell = (cells: HTMLElement[], colId: string) => {
@@ -69,7 +77,7 @@ const getCell = (cells: HTMLElement[], colId: string) => {
);
};
describe('RewarsHistoryTable', () => {
describe('RewardsHistoryTable', () => {
const props = {
epochRewardSummaries: {
edges: rewardSummaries,
@@ -88,7 +96,7 @@ describe('RewarsHistoryTable', () => {
loading: false,
};
it('Renders table with accounts summed up by asset', () => {
it('renders table with accounts summed up by asset', () => {
render(<RewardHistoryTable {...props} />);
const container = within(
@@ -110,17 +118,27 @@ describe('RewarsHistoryTable', () => {
assets.asset2.name
);
// First row
const marketCreationCell = getCell(cells, 'marketCreation');
expect(
marketCreationCell.getByTestId('stack-cell-primary')
).toHaveTextContent('300');
expect(
marketCreationCell.getByTestId('stack-cell-secondary')
).toHaveTextContent('100.00%');
).toHaveTextContent('50.00%');
const infrastructureFeesCell = getCell(cells, 'infrastructureFees');
expect(
infrastructureFeesCell.getByTestId('stack-cell-primary')
).toHaveTextContent('300');
expect(
infrastructureFeesCell.getByTestId('stack-cell-secondary')
).toHaveTextContent('50.00%');
let totalCell = getCell(cells, 'total');
expect(totalCell.getByText('300.00')).toBeInTheDocument();
expect(totalCell.getByText('600.00')).toBeInTheDocument();
// Second row
row = within(rows[1]);
cells = row.getAllByRole('gridcell');
@@ -2,10 +2,7 @@ import debounce from 'lodash/debounce';
import { useMemo, useState } from 'react';
import BigNumber from 'bignumber.js';
import type { ColDef, ValueFormatterFunc } from 'ag-grid-community';
import {
useAssetsMapProvider,
type AssetFieldsFragment,
} from '@vegaprotocol/assets';
import { type AssetFieldsFragment } from '@vegaprotocol/assets';
import {
addDecimalsFormatNumberQuantum,
formatNumberPercentage,
@@ -26,17 +23,17 @@ import { useT } from '../../lib/use-t';
export const RewardsHistoryContainer = ({
epoch,
pubKey,
assets,
}: {
pubKey: string | null;
epoch: number;
assets: Record<string, AssetFieldsFragment>;
}) => {
const [epochVariables, setEpochVariables] = useState(() => ({
from: epoch - 1,
to: epoch,
}));
const { data: assets } = useAssetsMapProvider();
// No need to specify the fromEpoch as it will by default give you the last
const { refetch, data, loading } = useRewardsHistoryQuery({
variables: {
@@ -154,10 +151,12 @@ export const RewardHistoryTable = ({
const rewardValueFormatter: ValueFormatterFunc<RewardRow> = ({
data,
value,
...rest
}) => {
if (!value || !data) {
return '-';
}
return addDecimalsFormatNumberQuantum(
value,
data.asset.decimals,
@@ -197,6 +196,11 @@ export const RewardHistoryTable = ({
},
sort: 'desc',
},
{
field: 'infrastructureFees',
valueFormatter: rewardValueFormatter,
cellRenderer: rewardCellRenderer,
},
{
field: 'staking',
valueFormatter: rewardValueFormatter,
@@ -0,0 +1,159 @@
import { type AssetFieldsFragment } from '@vegaprotocol/assets';
import { getRewards } from './use-reward-row-data';
import * as Schema from '@vegaprotocol/types';
const asset1 = {
id: 'asset1',
name: 'USD (KRW)',
symbol: 'USD-KRW',
decimals: 6,
quantum: '1000000',
status: Schema.AssetStatus.STATUS_ENABLED,
// @ts-ignore not needed
source: {},
} as AssetFieldsFragment;
const asset2 = {
id: 'asset2',
name: 'tDAI TEST',
symbol: 'tDAI',
decimals: 5,
quantum: '1',
status: Schema.AssetStatus.STATUS_ENABLED,
// @ts-ignore not needed
source: {},
} as AssetFieldsFragment;
const asset3 = {
id: 'asset3',
name: 'Tether USD',
symbol: 'USDT',
decimals: 6,
quantum: '1000000',
status: Schema.AssetStatus.STATUS_ENABLED,
// @ts-ignore not needed
source: {},
} as AssetFieldsFragment;
const asset4 = {
id: 'asset4',
name: 'USDT-T',
symbol: 'USDT-T',
decimals: 18,
quantum: '1',
status: Schema.AssetStatus.STATUS_ENABLED,
// @ts-ignore not needed
source: {},
} as AssetFieldsFragment;
const assets: Record<string, AssetFieldsFragment> = {
asset1,
asset2,
asset3,
asset4,
};
const testData = {
rewards: [
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
assetId: 'asset1',
amount: '31897424',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
assetId: 'asset2',
amount: '57',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
assetId: 'asset3',
amount: '5501',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_AVERAGE_POSITION,
assetId: 'asset3',
amount: '5501',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES,
assetId: 'asset4',
amount: '5501',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES,
assetId: 'asset4',
amount: '456',
},
{
rewardType: Schema.AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING,
assetId: 'asset4',
amount: '4565',
},
],
assets,
};
describe('getRewards', () => {
it('should return the correct rewards when infra fees are included', () => {
const rewards = getRewards(testData.rewards, testData.assets);
expect(rewards).toEqual([
{
asset: asset1,
infrastructureFees: 31897424,
staking: 0,
priceTaking: 0,
priceMaking: 0,
liquidityProvision: 0,
marketCreation: 0,
averagePosition: 0,
relativeReturns: 0,
returnsVolatility: 0,
validatorRanking: 0,
total: 31897424,
},
{
asset: asset2,
infrastructureFees: 57,
staking: 0,
priceTaking: 0,
priceMaking: 0,
liquidityProvision: 0,
marketCreation: 0,
averagePosition: 0,
relativeReturns: 0,
returnsVolatility: 0,
validatorRanking: 0,
total: 57,
},
{
asset: asset3,
infrastructureFees: 5501,
staking: 0,
priceTaking: 0,
priceMaking: 0,
liquidityProvision: 0,
marketCreation: 0,
averagePosition: 5501,
relativeReturns: 0,
returnsVolatility: 0,
validatorRanking: 0,
total: 11002,
},
{
asset: asset4,
infrastructureFees: 0,
staking: 0,
priceTaking: 0,
priceMaking: 5501,
liquidityProvision: 456,
marketCreation: 0,
averagePosition: 0,
relativeReturns: 0,
returnsVolatility: 0,
validatorRanking: 4565,
total: 10522,
},
]);
});
});
@@ -16,9 +16,10 @@ const REWARD_ACCOUNT_TYPES = [
AccountType.ACCOUNT_TYPE_REWARD_RELATIVE_RETURN,
AccountType.ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY,
AccountType.ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING,
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE,
];
const getRewards = (
export const getRewards = (
rewards: Array<{
rewardType: AccountType;
assetId: string;
@@ -56,6 +57,9 @@ const getRewards = (
return {
asset,
infrastructureFees: totals.get(
AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE
),
staking: totals.get(AccountType.ACCOUNT_TYPE_GLOBAL_REWARD),
priceTaking: totals.get(AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES),
priceMaking: totals.get(
@@ -101,7 +105,8 @@ export const useRewardsRowData = ({
assetId: r.asset.id,
amount: r.amount,
}));
return getRewards(rewards, assets);
const result = getRewards(rewards, assets);
return result;
}
const rewards = removePaginationWrapper(epochRewardSummaries?.edges);
@@ -2,6 +2,7 @@ import pytest
from playwright.sync_api import Page, expect
from vega_sim.service import VegaService
from actions.vega import submit_order
from actions.utils import change_keys
from wallet_config import MM_WALLET, MM_WALLET2
import logging
@@ -196,3 +197,17 @@ def test_price_monitoring(simple_market, vega: VegaService, page: Page):
expect(
page.get_by_test_id(liquidity_supplied).get_by_test_id(item_value)
).to_have_text("50.00 (>100%)")
COL_ID_FEE = ".ag-center-cols-container [col-id='fee'] .ag-cell-value"
@pytest.mark.usefixtures("vega", "page", "continuous_market", "risk_accepted", "auth")
def test_auction_uncross_fees(continuous_market, vega: VegaService, page: Page):
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id("Fills").click()
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
page.locator(COL_ID_FEE).hover()
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text("If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI")
change_keys(page,vega, "market_maker")
expect(page.locator(COL_ID_FEE)).to_have_text("0.00 tDAI")
page.locator(COL_ID_FEE).hover()
expect(page.get_by_test_id("fee-breakdown-tooltip")).to_have_text("If the market was suspendedIf the market is in monitoring auction, half of the infrastructure and liquidity fees will be paid.Infrastructure fee0.00 tDAILiquidity fee0.00 tDAIMaker fee0.00 tDAITotal fees0.00 tDAI")
@@ -1,160 +0,0 @@
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")
@@ -5,6 +5,7 @@ from playwright.sync_api import Page, expect
from vega_sim.service import VegaService, PeggedOrder
import vega_sim.api.governance as governance
from actions.vega import submit_order
from actions.utils import next_epoch
from wallet_config import MM_WALLET, MM_WALLET2, GOVERNANCE_WALLET
@@ -58,9 +59,9 @@ def test_market_lifecycle(proposed_market, vega: VegaService, page: Page):
# "wait" for market to be approved and enacted
vega.forward("60s")
vega.wait_fn(1)
vega.wait_fn(10)
vega.wait_for_total_catchup()
next_epoch(vega=vega)
# check that market is in pending state
expect(trading_mode).to_have_text("Opening auction")
expect(market_state).to_have_text("Pending")
@@ -118,7 +118,6 @@ def test_perps_market_termination_proposed(page: Page, vega: VegaService):
@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,
@@ -127,6 +126,11 @@ def test_perps_market_terminated(page: Page, vega: VegaService):
approve_proposal = True,
forward_time_to_enactment = True,
)
vega.forward("10s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
page.goto(f"/#/markets/{perpetual_market}")
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)-")
+11
View File
@@ -13,6 +13,7 @@ import { Deposit } from '../client-pages/deposit';
import { Withdraw } from '../client-pages/withdraw';
import { Transfer } from '../client-pages/transfer';
import { Fees } from '../client-pages/fees';
import { Rewards } from '../client-pages/rewards';
import { Routes as AppRoutes } from '../lib/links';
import { LayoutWithSky } from '../client-pages/referrals/layout';
import { Referrals } from '../client-pages/referrals/referrals';
@@ -99,6 +100,16 @@ export const routerConfig: RouteObject[] = compact([
},
],
},
{
path: 'rewards/*',
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
children: [
{
index: true,
element: <Rewards />,
},
],
},
{
path: 'markets/*',
element: (
-3
View File
@@ -59,7 +59,6 @@ export const TradesTable = ({ onClick, ...props }: Props) => {
field: 'price',
type: 'rightAligned',
cellClass: changeCellClass,
sortable: false,
valueFormatter: ({
value,
data,
@@ -93,7 +92,6 @@ export const TradesTable = ({ onClick, ...props }: Props) => {
headerName: t('Size'),
field: 'size',
type: 'rightAligned',
sortable: false,
valueFormatter: ({
value,
data,
@@ -113,7 +111,6 @@ export const TradesTable = ({ onClick, ...props }: Props) => {
field: 'createdAt',
type: 'rightAligned',
cellClass: 'text-right',
sortable: false,
flex: 1, // make created at always fill remaining space
valueFormatter: ({
value,