From f54a62917952fb22d0f057907690076d5b8fa8a6 Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Mon, 6 Mar 2023 08:01:31 -0800 Subject: [PATCH 1/4] feat(positions): positions losses and maintenance warnings (#2985) --- libs/positions/src/lib/Positions.graphql | 4 + .../src/lib/__generated__/Positions.ts | 10 +- .../src/lib/positions-data-providers.spec.ts | 13 ++ .../src/lib/positions-data-providers.ts | 11 ++ .../src/lib/positions-table.spec.tsx | 103 +++++++++++++- libs/positions/src/lib/positions-table.tsx | 130 ++++++++++++++++-- libs/positions/src/lib/positions.mock.ts | 9 +- libs/positions/src/setup-tests.ts | 3 + libs/types/src/global-types-mappings.ts | 9 ++ 9 files changed, 276 insertions(+), 16 deletions(-) diff --git a/libs/positions/src/lib/Positions.graphql b/libs/positions/src/lib/Positions.graphql index d13ade8e0..cc80a0aea 100644 --- a/libs/positions/src/lib/Positions.graphql +++ b/libs/positions/src/lib/Positions.graphql @@ -4,6 +4,8 @@ fragment PositionFields on Position { unrealisedPNL averageEntryPrice updatedAt + positionStatus + lossSocializationAmount market { id } @@ -30,6 +32,8 @@ subscription PositionsSubscription($partyId: ID!) { averageEntryPrice updatedAt marketId + lossSocializationAmount + positionStatus } } diff --git a/libs/positions/src/lib/__generated__/Positions.ts b/libs/positions/src/lib/__generated__/Positions.ts index 5abf64ea6..96a130c3a 100644 --- a/libs/positions/src/lib/__generated__/Positions.ts +++ b/libs/positions/src/lib/__generated__/Positions.ts @@ -3,21 +3,21 @@ import * as Types from '@vegaprotocol/types'; import { gql } from '@apollo/client'; import * as Apollo from '@apollo/client'; const defaultOptions = {} as const; -export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, market: { __typename?: 'Market', id: string } }; +export type PositionFieldsFragment = { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } }; export type PositionsQueryVariables = Types.Exact<{ partyId: Types.Scalars['ID']; }>; -export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, market: { __typename?: 'Market', id: string } } }> | null } | null } | null }; +export type PositionsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, positionsConnection?: { __typename?: 'PositionConnection', edges?: Array<{ __typename?: 'PositionEdge', node: { __typename?: 'Position', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, positionStatus: Types.PositionStatus, lossSocializationAmount: string, market: { __typename?: 'Market', id: string } } }> | null } | null } | null }; export type PositionsSubscriptionSubscriptionVariables = Types.Exact<{ partyId: Types.Scalars['ID']; }>; -export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string }> }; +export type PositionsSubscriptionSubscription = { __typename?: 'Subscription', positions: Array<{ __typename?: 'PositionUpdate', realisedPNL: string, openVolume: string, unrealisedPNL: string, averageEntryPrice: string, updatedAt?: any | null, marketId: string, lossSocializationAmount: string, positionStatus: Types.PositionStatus }> }; export type MarginFieldsFragment = { __typename?: 'MarginLevels', maintenanceLevel: string, searchLevel: string, initialLevel: string, collateralReleaseLevel: string, asset: { __typename?: 'Asset', id: string }, market: { __typename?: 'Market', id: string } }; @@ -42,6 +42,8 @@ export const PositionFieldsFragmentDoc = gql` unrealisedPNL averageEntryPrice updatedAt + positionStatus + lossSocializationAmount market { id } @@ -112,6 +114,8 @@ export const PositionsSubscriptionDocument = gql` averageEntryPrice updatedAt marketId + lossSocializationAmount + positionStatus } } `; diff --git a/libs/positions/src/lib/positions-data-providers.spec.ts b/libs/positions/src/lib/positions-data-providers.spec.ts index 9dbbd80c9..798bb6734 100644 --- a/libs/positions/src/lib/positions-data-providers.spec.ts +++ b/libs/positions/src/lib/positions-data-providers.spec.ts @@ -6,6 +6,7 @@ import type { MarginFieldsFragment, } from './__generated__/Positions'; import { getMetrics, rejoinPositionData } from './positions-data-providers'; +import { PositionStatus } from '@vegaprotocol/types'; const accounts = [ { @@ -78,6 +79,8 @@ const positions: PositionFieldsFragment[] = [ __typename: 'Market', id: '5e6035fe6a6df78c9ec44b333c231e63d357acef0a0620d2c243f5865d1dc0d8', }, + lossSocializationAmount: '0', + positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED, }, { __typename: 'Position', @@ -90,6 +93,8 @@ const positions: PositionFieldsFragment[] = [ __typename: 'Market', id: '10c4b1114d2f6fda239b73d018bca55888b6018f0ac70029972a17fea0a6a56e', }, + lossSocializationAmount: '100', + positionStatus: PositionStatus.POSITION_STATUS_ORDERS_CLOSED, }, ]; @@ -225,6 +230,10 @@ describe('getMetrics && rejoinPositionData', () => { expect(metrics[0].totalBalance).toEqual('926178496'); expect(metrics[0].unrealisedPNL).toEqual('43804770'); expect(metrics[0].updatedAt).toEqual('2022-07-28T14:53:54.725477Z'); + expect(metrics[0].lossSocializationAmount).toEqual( + positions[0].lossSocializationAmount + ); + expect(metrics[0].status).toEqual(positions[0].positionStatus); expect(metrics[1].assetSymbol).toEqual('tDAI'); expect(metrics[1].averageEntryPrice).toEqual('840158'); @@ -248,5 +257,9 @@ describe('getMetrics && rejoinPositionData', () => { expect(metrics[1].totalBalance).toEqual('896098819'); expect(metrics[1].unrealisedPNL).toEqual('-9112700'); expect(metrics[1].updatedAt).toEqual('2022-07-28T15:09:34.441143Z'); + expect(metrics[1].lossSocializationAmount).toEqual( + positions[1].lossSocializationAmount + ); + expect(metrics[1].status).toEqual(positions[1].positionStatus); }); }); diff --git a/libs/positions/src/lib/positions-data-providers.ts b/libs/positions/src/lib/positions-data-providers.ts index 902ae62cd..8582e078b 100644 --- a/libs/positions/src/lib/positions-data-providers.ts +++ b/libs/positions/src/lib/positions-data-providers.ts @@ -24,6 +24,7 @@ import { PositionsSubscriptionDocument, } from './__generated__/Positions'; import { marginsDataProvider } from './margin-data-provider'; +import type { PositionStatus } from '@vegaprotocol/types'; type PositionMarginLevel = Pick< MarginFieldsFragment, @@ -38,6 +39,8 @@ interface PositionRejoined { updatedAt?: string | null; market: MarketMaybeWithData | null; margins: PositionMarginLevel | null; + lossSocializationAmount: string | null; + status: PositionStatus; } export interface Position { @@ -62,6 +65,8 @@ export interface Position { unrealisedPNL: string; searchPrice: string | undefined; updatedAt: string | null; + lossSocializationAmount: string; + status: PositionStatus; } export interface Data { @@ -178,6 +183,8 @@ export const getMetrics = ( ? searchPrice.multipliedBy(10 ** marketDecimalPlaces).toFixed(0) : undefined, updatedAt: position.updatedAt || null, + lossSocializationAmount: position.lossSocializationAmount || '0', + status: position.status, }); }); return metrics; @@ -201,6 +208,8 @@ export const update = ( openVolume: delta.openVolume, averageEntryPrice: delta.averageEntryPrice, updatedAt: delta.updatedAt, + lossSocializationAmount: delta.lossSocializationAmount, + positionStatus: delta.positionStatus, }; } else { draft.unshift({ @@ -267,6 +276,8 @@ export const rejoinPositionData = ( market: marketsData?.find((market) => market.id === node.market.id) || null, margins: upgradeMarginsConnection(node.market.id, margins), + lossSocializationAmount: node.lossSocializationAmount, + status: node.positionStatus, }; }); } diff --git a/libs/positions/src/lib/positions-table.spec.tsx b/libs/positions/src/lib/positions-table.spec.tsx index f4f093bc6..2dde68a29 100644 --- a/libs/positions/src/lib/positions-table.spec.tsx +++ b/libs/positions/src/lib/positions-table.spec.tsx @@ -1,8 +1,11 @@ import type { RenderResult } from '@testing-library/react'; -import { act, render, screen } from '@testing-library/react'; -import PositionsTable from './positions-table'; +import { act, render, screen, within } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import PositionsTable, { OpenVolumeCell, PNLCell } from './positions-table'; import type { Position } from './positions-data-providers'; import * as Schema from '@vegaprotocol/types'; +import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types'; +import type { ICellRendererParams } from 'ag-grid-community'; const singleRow: Position = { marketName: 'ETH/BTC (31 july 2022)', @@ -26,6 +29,8 @@ const singleRow: Position = { searchPrice: '0', updatedAt: '2022-07-27T15:02:58.400Z', marginAccountBalance: '12345600', + status: PositionStatus.POSITION_STATUS_UNSPECIFIED, + lossSocializationAmount: '0', }; const singleRowData = [singleRow]; @@ -219,3 +224,97 @@ it('do not display close button if openVolume is zero', async () => { const cells = screen.getAllByRole('gridcell'); expect(cells[12].textContent).toEqual(''); }); + +describe('PNLCell', () => { + const props = { + data: undefined, + valueFormatted: '100', + }; + it('renders a dash if no data', () => { + render(); + expect(screen.getByText('-')).toBeInTheDocument(); + }); + + it('renders value if no loss socialisation has occurred', () => { + const props = { + data: { + ...singleRow, + lossSocialisationAmount: '0', + }, + valueFormatted: '100', + }; + render(); + expect(screen.getByText(props.valueFormatted)).toBeInTheDocument(); + expect(screen.queryByRole('img')).not.toBeInTheDocument(); + }); + + it('renders value with warning tooltip if loss socialisation occurred', async () => { + const props = { + data: { + ...singleRow, + lossSocializationAmount: '500', + decimals: 2, + }, + valueFormatted: '100', + }; + render(); + const content = screen.getByText(props.valueFormatted); + expect(content).toBeInTheDocument(); + expect(screen.getByRole('img')).toBeInTheDocument(); + + await userEvent.hover(content); + const tooltip = await screen.findByRole('tooltip'); + expect(tooltip).toBeInTheDocument(); + expect( + // using within as radix renders tooltip content twice + within(tooltip).getByText('Lifetime loss socialisation deductions: 5.00') + ).toBeInTheDocument(); + }); +}); + +describe('OpenVolumeCell', () => { + const props = { + data: undefined, + valueFormatted: '100', + }; + it('renders a dash if no data', () => { + render(); + expect(screen.getByText('-')).toBeInTheDocument(); + }); + + it('renders value if no status is normal', () => { + const props = { + data: { + ...singleRow, + status: PositionStatus.POSITION_STATUS_UNSPECIFIED, + }, + valueFormatted: '100', + }; + render(); + expect(screen.getByText(props.valueFormatted)).toBeInTheDocument(); + expect(screen.queryByRole('img')).not.toBeInTheDocument(); + }); + + it('renders status with warning tooltip if not normal', async () => { + const props = { + data: { + ...singleRow, + status: PositionStatus.POSITION_STATUS_ORDERS_CLOSED, + }, + valueFormatted: '100', + }; + render(); + const content = screen.getByText(props.valueFormatted); + expect(content).toBeInTheDocument(); + expect(screen.getByRole('img')).toBeInTheDocument(); + await userEvent.hover(content); + const tooltip = await screen.findByRole('tooltip'); + expect(tooltip).toBeInTheDocument(); + expect( + // using within as radix renders tooltip content twice + within(tooltip).getByText( + `Status: ${PositionStatusMapping[props.data.status]}` + ) + ).toBeInTheDocument(); + }); +}); diff --git a/libs/positions/src/lib/positions-table.tsx b/libs/positions/src/lib/positions-table.tsx index 0acec30bd..58a808cfe 100644 --- a/libs/positions/src/lib/positions-table.tsx +++ b/libs/positions/src/lib/positions-table.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import { forwardRef } from 'react'; -import type { CSSProperties } from 'react'; +import type { CSSProperties, ReactNode } from 'react'; import type { CellRendererSelectorResult } from 'ag-grid-community'; import type { VegaValueFormatterParams, @@ -15,6 +15,15 @@ import { signedNumberCssClass, signedNumberCssClassRules, } from '@vegaprotocol/datagrid'; +import { + ButtonLink, + Tooltip, + TooltipCellComponent, + Link, + ExternalLink, + Icon, + ProgressBarCell, +} from '@vegaprotocol/ui-toolkit'; import { volumePrefix, toBigNum, @@ -27,13 +36,9 @@ import { AgGridColumn } from 'ag-grid-react'; import type { AgGridReact } from 'ag-grid-react'; import type { Position } from './positions-data-providers'; import * as Schema from '@vegaprotocol/types'; -import { - ButtonLink, - Link, - ProgressBarCell, - TooltipCellComponent, -} from '@vegaprotocol/ui-toolkit'; import { getRowId } from './use-positions-data'; +import { PositionStatus, PositionStatusMapping } from '@vegaprotocol/types'; +import { useEnvironment } from '@vegaprotocol/environment'; interface Props extends TypedDataAgGrid { onClose?: (data: Position) => void; @@ -167,6 +172,7 @@ export const PositionsTable = forwardRef( ) ); }} + cellRenderer={OpenVolumeCell} /> ( }} valueFormatter={({ data, - node, }: VegaValueFormatterParams): | string | undefined => { @@ -329,7 +334,7 @@ export const PositionsTable = forwardRef( field="realisedPNL" type="rightAligned" cellClassRules={signedNumberCssClassRules} - cellClass="text-right font-mono" + cellClass="font-mono text-right" filter="agNumberColumnFilter" valueGetter={({ data, @@ -348,13 +353,14 @@ export const PositionsTable = forwardRef( 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.' )} + cellRenderer={PNLCell} /> ( headerTooltip={t( 'Unrealised profit is the current profit on your open position. Margin is still allocated to your position.' )} + cellRenderer={PNLCell} /> ( ); export default PositionsTable; + +export const PNLCell = ({ + valueFormatted, + data, +}: VegaICellRendererParams) => { + const { VEGA_DOCS_URL } = useEnvironment(); + + if (!data) { + return <>-; + } + + const losses = parseInt(data?.lossSocializationAmount ?? '0'); + if (losses <= 0) { + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{valueFormatted}; + } + + const lossesFormatted = addDecimalsFormatNumber( + data.lossSocializationAmount, + data.decimals + ); + + return ( + +

+ {t('Lifetime loss socialisation deductions: %s', lossesFormatted)} +

+ {VEGA_DOCS_URL && ( + + {t('Read more about loss socialisation')} + + )} + + } + > + {valueFormatted} +
+ ); +}; + +export const OpenVolumeCell = ({ + valueFormatted, + data, +}: VegaICellRendererParams) => { + const { VEGA_DOCS_URL } = useEnvironment(); + + if (!data) { + return <>-; + } + + if (data.status === PositionStatus.POSITION_STATUS_UNSPECIFIED) { + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{valueFormatted}; + } + + return ( + +

+ {t('Your position was affected by market conditions')} +

+

+ {t( + 'Status: %s', + PositionStatusMapping[ + PositionStatus.POSITION_STATUS_ORDERS_CLOSED + ] + )} +

+ {VEGA_DOCS_URL && ( + + {t('Read more about position resolution')} + + )} + + } + > + {valueFormatted} +
+ ); +}; + +const WarningCell = ({ + children, + tooltipContent, +}: { + children: ReactNode; + tooltipContent: ReactNode; +}) => { + return ( + +
+ + + + {children} +
+
+ ); +}; diff --git a/libs/positions/src/lib/positions.mock.ts b/libs/positions/src/lib/positions.mock.ts index 87735f88b..95c24bdfa 100644 --- a/libs/positions/src/lib/positions.mock.ts +++ b/libs/positions/src/lib/positions.mock.ts @@ -1,3 +1,4 @@ +import { PositionStatus } from '@vegaprotocol/types'; import merge from 'lodash/merge'; import type { PartialDeep } from 'type-fest'; import type { @@ -58,6 +59,8 @@ const positionFields: PositionFieldsFragment[] = [ id: 'market-0', __typename: 'Market', }, + lossSocializationAmount: '0', + positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED, }, { __typename: 'Position', @@ -70,8 +73,11 @@ const positionFields: PositionFieldsFragment[] = [ id: 'market-1', __typename: 'Market', }, + lossSocializationAmount: '0', + positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED, }, { + __typename: 'Position', realisedPNL: '230000', openVolume: '1', unrealisedPNL: '-22519', @@ -81,7 +87,8 @@ const positionFields: PositionFieldsFragment[] = [ id: 'market-2', __typename: 'Market', }, - __typename: 'Position', + lossSocializationAmount: '0', + positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED, }, ]; diff --git a/libs/positions/src/setup-tests.ts b/libs/positions/src/setup-tests.ts index 7b0828bfa..68773380a 100644 --- a/libs/positions/src/setup-tests.ts +++ b/libs/positions/src/setup-tests.ts @@ -1 +1,4 @@ import '@testing-library/jest-dom'; +import ResizeObserver from 'resize-observer-polyfill'; + +global.ResizeObserver = ResizeObserver; diff --git a/libs/types/src/global-types-mappings.ts b/libs/types/src/global-types-mappings.ts index 09bd6ced9..ddd07cb61 100644 --- a/libs/types/src/global-types-mappings.ts +++ b/libs/types/src/global-types-mappings.ts @@ -12,6 +12,7 @@ import type { OrderStatus, OrderTimeInForce, OrderType, + PositionStatus, ProposalRejectionReason, ProposalState, Side, @@ -445,3 +446,11 @@ export const DispatchMetricLabels: DispatchMetricLabel = { DISPATCH_METRIC_MAKER_FEES_RECEIVED: 'Price maker fees earned', DISPATCH_METRIC_MARKET_VALUE: 'Total market Value', }; + +export const PositionStatusMapping: { + [T in PositionStatus]: string; +} = { + POSITION_STATUS_CLOSED_OUT: 'Closed by network', + POSITION_STATUS_ORDERS_CLOSED: 'Maintained by network', + POSITION_STATUS_UNSPECIFIED: 'Normal', +}; From 3b3fcab4f442f539feec272cebfd1ef7d5c59172 Mon Sep 17 00:00:00 2001 From: "m.ray" <16125548+MadalinaRaicu@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:24:18 -0500 Subject: [PATCH 2/4] fix(trading): fills table fees display (#3071) --- libs/accounts/src/lib/accounts-table.tsx | 3 + libs/fills/src/lib/fills-table.spec.tsx | 36 ++++++- libs/fills/src/lib/fills-table.tsx | 132 +++++++++++++++++------ 3 files changed, 138 insertions(+), 33 deletions(-) diff --git a/libs/accounts/src/lib/accounts-table.tsx b/libs/accounts/src/lib/accounts-table.tsx index 2958a10f7..b98f168d1 100644 --- a/libs/accounts/src/lib/accounts-table.tsx +++ b/libs/accounts/src/lib/accounts-table.tsx @@ -104,6 +104,7 @@ export const AccountTable = forwardRef( /> ( /> ( { @@ -75,7 +75,7 @@ describe('FillsTable', () => { '1.00 BTC', '3.00 BTC', 'Maker', - '0.06 BTC', + '2.00 BTC', getDateTimeFormat().format(new Date(buyerFill.createdAt)), ]; cells.forEach((cell, i) => { @@ -176,4 +176,36 @@ describe('FillsTable', () => { await screen.findByTestId('fee-breakdown-tooltip') ).toBeInTheDocument(); }); + + describe('getFeesBreakdown', () => { + it('should return correct fees breakdown for a taker', () => { + const fees = { + makerFee: '1000', + infrastructureFee: '2000', + liquidityFee: '3000', + }; + const expectedBreakdown = { + infrastructureFee: '2000', + liquidityFee: '3000', + makerFee: '1000', + totalFee: '6000', + }; + expect(getFeesBreakdown('TAKER', fees)).toEqual(expectedBreakdown); + }); + + it('should return correct fees breakdown for a maker', () => { + const fees = { + makerFee: '1000', + infrastructureFee: '2000', + liquidityFee: '3000', + }; + const expectedBreakdown = { + infrastructureFee: '2000', + liquidityFee: '3000', + makerFee: '-1000', + totalFee: '4000', + }; + expect(getFeesBreakdown('MAKER', fees)).toEqual(expectedBreakdown); + }); + }); }); diff --git a/libs/fills/src/lib/fills-table.tsx b/libs/fills/src/lib/fills-table.tsx index f500f9d98..dbfe59d33 100644 --- a/libs/fills/src/lib/fills-table.tsx +++ b/libs/fills/src/lib/fills-table.tsx @@ -27,6 +27,10 @@ import type { import { forwardRef } from 'react'; import BigNumber from 'bignumber.js'; import type { Trade } from './fills-data-provider'; +import type { FillFieldsFragment } from './__generated__/Fills'; + +const TAKER = 'TAKER'; +const MAKER = 'MAKER'; export type Props = (AgGridReactProps | AgReactUiProps) & { partyId: string; @@ -228,66 +232,132 @@ const formatFee = (partyId: string) => { return '-'; } const asset = value.settlementAsset; - let feesObj; - if (data?.buyer.id === partyId) { - feesObj = data?.buyerFee; - } else if (data?.seller.id === partyId) { - feesObj = data?.sellerFee; - } else { - return '-'; - } + const { fees: feesObj, role } = getRoleAndFees({ data, partyId }); + if (!feesObj) return '-'; - const fee = new BigNumber(feesObj.makerFee) - .plus(feesObj.infrastructureFee) - .plus(feesObj.liquidityFee); - const totalFees = addDecimalsFormatNumber(fee.toString(), asset.decimals); + const { totalFee } = getFeesBreakdown(role, feesObj); + const totalFees = addDecimalsFormatNumber(totalFee, asset.decimals); return `${totalFees} ${asset.symbol}`; }; }; +export const getRoleAndFees = ({ + data, + partyId, +}: { + data: Pick< + FillFieldsFragment, + 'buyerFee' | 'sellerFee' | 'buyer' | 'seller' | 'aggressor' + >; + partyId?: string; +}) => { + let role; + let feesObj; + if (data?.buyer.id === partyId) { + role = data.aggressor === Schema.Side.SIDE_BUY ? TAKER : MAKER; + feesObj = role === TAKER ? data?.buyerFee : data.sellerFee; + } else if (data?.seller.id === partyId) { + role = data.aggressor === Schema.Side.SIDE_SELL ? TAKER : MAKER; + feesObj = role === TAKER ? data?.sellerFee : data.buyerFee; + } else { + return { role: '-', feesObj: '-' }; + } + return { role, fees: feesObj }; +}; + const FeesBreakdownTooltip = ({ data, value, - valueFormatted, partyId, }: ITooltipParams & { partyId?: string }) => { if (!value?.settlementAsset || !data) { return null; } + const asset = value.settlementAsset; - let feesObj; - if (data?.buyer.id === partyId) { - feesObj = data?.buyerFee; - } else if (data?.seller.id === partyId) { - feesObj = data?.sellerFee; - } else { - return null; - } + + const { role, fees: feesObj } = getRoleAndFees({ data, partyId }) ?? {}; + if (!feesObj) return null; + const { infrastructureFee, liquidityFee, makerFee, totalFee } = + getFeesBreakdown(role, feesObj); return (
-
+ {role === MAKER && ( + <> +

{t('The maker will receive the maker fee.')}

+

+ {t( + 'If the market is in monitoring auction the maker will pay half of the infrastructure and liquidity fees.' + )} +

+

+ {t( + 'If the market is active the maker will pay zero infrastructure and liquidity fees.' + )} +

+ + )} + {role === TAKER && ( + <> +

{t('Fees to be paid by the taker.')}

+

+ {t( + 'If the market is in monitoring auction the taker will pay half of the infrastructure and liquidity fees.' + )} +

+ + )} +
{t('Infrastructure fee')}
-
- {addDecimalsFormatNumber(feesObj.infrastructureFee, asset.decimals)}{' '} +
+ {addDecimalsFormatNumber(infrastructureFee, asset.decimals)}{' '} {asset.symbol}
{t('Liquidity fee')}
-
- {addDecimalsFormatNumber(feesObj.liquidityFee, asset.decimals)}{' '} - {asset.symbol} +
+ {addDecimalsFormatNumber(liquidityFee, asset.decimals)} {asset.symbol}
{t('Maker fee')}
-
- {addDecimalsFormatNumber(feesObj.makerFee, asset.decimals)}{' '} - {asset.symbol} +
+ {addDecimalsFormatNumber(makerFee, asset.decimals)} {asset.symbol}
{t('Total fees')}
-
{valueFormatted}
+
+ {addDecimalsFormatNumber(totalFee, asset.decimals)} {asset.symbol} +
); }; + +export const getFeesBreakdown = ( + role: string, + feesObj: { + __typename?: 'TradeFee' | undefined; + makerFee: string; + infrastructureFee: string; + liquidityFee: string; + } +) => { + const makerFee = + role === MAKER + ? new BigNumber(feesObj.makerFee).times(-1).toString() + : feesObj.makerFee; + const infrastructureFee = feesObj.infrastructureFee; + const liquidityFee = feesObj.liquidityFee; + + const totalFee = new BigNumber(infrastructureFee) + .plus(makerFee) + .plus(liquidityFee) + .toString(); + return { + infrastructureFee, + liquidityFee, + makerFee, + totalFee, + }; +}; From 16d345eb4ac31b9468a32e85c92952ce53e291f5 Mon Sep 17 00:00:00 2001 From: daro-maj <119658839+daro-maj@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:48:33 +0100 Subject: [PATCH 3/4] test(trading): revert nonparallel tests option (#3098) --- .github/workflows/cypress-run.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/cypress-run.yml b/.github/workflows/cypress-run.yml index 81b6f96be..36696f31b 100644 --- a/.github/workflows/cypress-run.yml +++ b/.github/workflows/cypress-run.yml @@ -16,8 +16,6 @@ jobs: strategy: fail-fast: false matrix: - # run copies of the current job in parallel - containers: [1, 2] project: ${{ fromJSON(inputs.projects) }} runs-on: self-hosted-runner timeout-minutes: 30 @@ -66,7 +64,7 @@ jobs: ###### - name: Run Cypress tests - run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --parallel --browser chrome --env.grepTags="${{ inputs.tags }}" + run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome --env.grepTags="${{ inputs.tags }}" working-directory: frontend-monorepo env: CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }} From 26020b5f712e5ffa59cc78b31d51b40b2c609090 Mon Sep 17 00:00:00 2001 From: dexturr Date: Mon, 6 Mar 2023 18:07:54 +0000 Subject: [PATCH 4/4] chore: update tranches Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- apps/static/src/assets/mainnet-tranches.json | 74 +++++++++++++------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/apps/static/src/assets/mainnet-tranches.json b/apps/static/src/assets/mainnet-tranches.json index 9120e3cc7..f65862ed0 100644 --- a/apps/static/src/assets/mainnet-tranches.json +++ b/apps/static/src/assets/mainnet-tranches.json @@ -104,7 +104,7 @@ "tranche_end": "2023-04-06T00:00:00.000Z", "total_added": "14099", "total_removed": "0", - "locked_amount": "13868.6594825268810079", + "locked_amount": "13755.4000425627232917", "deposits": [ { "amount": "30", @@ -2761,7 +2761,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "86666.297", "total_removed": "0", - "locked_amount": "64938.8287936033066636389", + "locked_amount": "64879.699162507033901597", "deposits": [ { "amount": "86666.297", @@ -2827,7 +2827,7 @@ "tranche_end": "2023-06-01T00:00:00.000Z", "total_added": "2500", "total_removed": "0", - "locked_amount": "1188.09809981684975", + "locked_amount": "1184.67738858363875", "deposits": [ { "amount": "2500", @@ -2948,7 +2948,7 @@ "tranche_end": "2023-09-01T00:00:00.000Z", "total_added": "17500", "total_removed": "0", - "locked_amount": "16976.28793025362225", + "locked_amount": "16952.60322312802", "deposits": [ { "amount": "12500", @@ -3215,7 +3215,7 @@ "tranche_end": "2023-08-01T00:00:00.000Z", "total_added": "37500", "total_removed": "3639.24714135", - "locked_amount": "30558.054212707185", + "locked_amount": "30506.46005985267", "deposits": [ { "amount": "7500", @@ -3414,7 +3414,7 @@ "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "129999.45", "total_removed": "0", - "locked_amount": "64879.58028324589941117", + "locked_amount": "64820.504600498023491807", "deposits": [ { "amount": "129999.45", @@ -3480,7 +3480,7 @@ "tranche_end": "2023-09-03T00:00:00.000Z", "total_added": "62600", "total_removed": "0", - "locked_amount": "30955.878652968038482", + "locked_amount": "30913.16868340943934", "deposits": [ { "amount": "10000", @@ -3673,7 +3673,7 @@ "tranche_end": "2023-09-17T00:00:00.000Z", "total_added": "5000", "total_removed": "0", - "locked_amount": "2664.295091324201", + "locked_amount": "2660.8837519025875", "deposits": [ { "amount": "5000", @@ -3884,7 +3884,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "97499.58", "total_removed": "0", - "locked_amount": "6863.026074492837718089", + "locked_amount": "6805.0783328659465926588", "deposits": [ { "amount": "97499.58", @@ -3917,7 +3917,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "135173.4239508", "total_removed": "98230.390980249184455396", - "locked_amount": "9380.571791809224940956129324", + "locked_amount": "9301.36723326549382327399362", "deposits": [ { "amount": "135173.4239508", @@ -3963,7 +3963,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "32499.86", "total_removed": "0", - "locked_amount": "2887.156551418174054713", + "locked_amount": "2862.778937219710450476", "deposits": [ { "amount": "32499.86", @@ -3996,7 +3996,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "10833.29", "total_removed": "0", - "locked_amount": "939.741441182597763073", + "locked_amount": "931.8067643157678690636", "deposits": [ { "amount": "10833.29", @@ -4029,7 +4029,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "22749.93", "total_removed": "4720.860935375", - "locked_amount": "3512.9633946007858719399", + "locked_amount": "3483.3018002945028144642", "deposits": [ { "amount": "6500", @@ -4181,7 +4181,7 @@ "tranche_end": "2023-05-01T00:00:00.000Z", "total_added": "22500", "total_removed": "5176.28289975", - "locked_amount": "6898.368439226519325", + "locked_amount": "6867.411947513811", "deposits": [ { "amount": "7500", @@ -4500,7 +4500,7 @@ "tranche_end": "2023-06-02T00:00:00.000Z", "total_added": "1939928.38", "total_removed": "928642.9598472029154", - "locked_amount": "465016.998755833268669054", + "locked_amount": "463693.4479242731446593956", "deposits": [ { "amount": "1852091.69", @@ -36891,7 +36891,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "3732368.4671", "total_removed": "618385.665327542135993", - "locked_amount": "739070.550093799918600823126", + "locked_amount": "737036.71327714871630535621", "deposits": [ { "amount": "1998.95815", @@ -38250,8 +38250,8 @@ "tranche_start": "2022-06-05T00:00:00.000Z", "tranche_end": "2023-12-05T00:00:00.000Z", "total_added": "15870102.715470999700000001", - "total_removed": "588645.45995589773910452", - "locked_amount": "7920384.3034086642682516019633546259610706", + "total_removed": "589039.38568474293710452", + "locked_amount": "7913172.44864162203140200442918599770965126", "deposits": [ { "amount": "16249.93", @@ -38840,6 +38840,11 @@ "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", "tx": "0xa7e962b98ed06faceaf876c50efca7276439f25eeb25ec574e88767fa0c5fb30" }, + { + "amount": "393.925728845198", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tx": "0x3ea8ed6b0441f5a71a645193ec134c51626bc8c9eb6442600da8482d41dd7c68" + }, { "amount": "535.4042378778335", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -40669,6 +40674,12 @@ "tranche_id": 2, "tx": "0xa7e962b98ed06faceaf876c50efca7276439f25eeb25ec574e88767fa0c5fb30" }, + { + "amount": "393.925728845198", + "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", + "tranche_id": 2, + "tx": "0x3ea8ed6b0441f5a71a645193ec134c51626bc8c9eb6442600da8482d41dd7c68" + }, { "amount": "535.4042378778335", "user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b", @@ -41883,8 +41894,8 @@ } ], "total_tokens": "259998.8875", - "withdrawn_tokens": "129961.943486458545", - "remaining_tokens": "130036.944013541455" + "withdrawn_tokens": "130355.869215303743", + "remaining_tokens": "129643.018284696257" }, { "address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c", @@ -44157,8 +44168,8 @@ "tranche_start": "2021-11-05T00:00:00.000Z", "tranche_end": "2023-05-05T00:00:00.000Z", "total_added": "14597706.0446472999", - "total_removed": "5580710.615452109580396656", - "locked_amount": "1590602.990485304826063424761930942", + "total_removed": "5581251.054619088719392406", + "locked_amount": "1583945.05221249870701141180921643", "deposits": [ { "amount": "129284.449", @@ -44422,6 +44433,11 @@ "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", "tx": "0x8636472fc307ddde44f9a3deca4a1f79fc49ae3741a3025c2b1dbc2cffc7e927" }, + { + "amount": "540.43916697913899575", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tx": "0xafba0370359a26f6fb9cceebc691c927bc820834384ffa51cd9565240e3a2a59" + }, { "amount": "800.792718381487871", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -47443,6 +47459,12 @@ "tranche_id": 3, "tx": "0x8636472fc307ddde44f9a3deca4a1f79fc49ae3741a3025c2b1dbc2cffc7e927" }, + { + "amount": "540.43916697913899575", + "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", + "tranche_id": 3, + "tx": "0xafba0370359a26f6fb9cceebc691c927bc820834384ffa51cd9565240e3a2a59" + }, { "amount": "800.792718381487871", "user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b", @@ -49959,8 +49981,8 @@ } ], "total_tokens": "359123.469575", - "withdrawn_tokens": "319607.63912160270538325", - "remaining_tokens": "39515.83045339729461675" + "withdrawn_tokens": "320148.078288581844379", + "remaining_tokens": "38975.391286418155621" }, { "address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB", @@ -51304,7 +51326,7 @@ "tranche_end": "2023-04-05T00:00:00.000Z", "total_added": "5778205.3912159303", "total_removed": "3077573.36197477479025324", - "locked_amount": "311553.4578873581959790921124072965", + "locked_amount": "308922.8662088976060115537634935795", "deposits": [ { "amount": "552496.6455", @@ -53376,7 +53398,7 @@ "tranche_end": "2023-06-05T00:00:00.000Z", "total_added": "472355.6199999996", "total_removed": "35108.9309805010685", - "locked_amount": "117109.953369737365406394633789936", + "locked_amount": "116787.680300232033164283987519012", "deposits": [ { "amount": "3000",