feat(deal-ticket): amend fee estimates for postOnly orders and when market is in auction (#4843)
This commit is contained in:
parent
a7e8b0eb01
commit
8a9b1c7874
@ -38,14 +38,16 @@ export interface DealTicketFeeDetailsProps {
|
|||||||
assetSymbol: string;
|
assetSymbol: string;
|
||||||
order: OrderSubmissionBody['orderSubmission'];
|
order: OrderSubmissionBody['orderSubmission'];
|
||||||
market: Market;
|
market: Market;
|
||||||
|
isMarketInAuction?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DealTicketFeeDetails = ({
|
export const DealTicketFeeDetails = ({
|
||||||
assetSymbol,
|
assetSymbol,
|
||||||
order,
|
order,
|
||||||
market,
|
market,
|
||||||
|
isMarketInAuction,
|
||||||
}: DealTicketFeeDetailsProps) => {
|
}: DealTicketFeeDetailsProps) => {
|
||||||
const feeEstimate = useEstimateFees(order);
|
const feeEstimate = useEstimateFees(order, isMarketInAuction);
|
||||||
const { settlementAsset: asset } =
|
const { settlementAsset: asset } =
|
||||||
market.tradableInstrument.instrument.product;
|
market.tradableInstrument.instrument.product;
|
||||||
const { decimals: assetDecimals, quantum } = asset;
|
const { decimals: assetDecimals, quantum } = asset;
|
||||||
@ -65,7 +67,7 @@ export const DealTicketFeeDetails = ({
|
|||||||
<>
|
<>
|
||||||
<span>
|
<span>
|
||||||
{t(
|
{t(
|
||||||
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}.`
|
`An estimate of the most you would be expected to pay in fees, in the market's settlement asset ${assetSymbol}. Fees estimated are "taker" fees and will only be payable if the order trades aggressively. Rebate equal to the maker portion will be paid to the trader if the order trades passively.`
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
<FeesBreakdown
|
<FeesBreakdown
|
||||||
|
@ -36,7 +36,7 @@ import {
|
|||||||
formatValue,
|
formatValue,
|
||||||
} from '@vegaprotocol/utils';
|
} from '@vegaprotocol/utils';
|
||||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||||
import { getDerivedPrice } from '@vegaprotocol/markets';
|
import { getDerivedPrice, isMarketInAuction } from '@vegaprotocol/markets';
|
||||||
import {
|
import {
|
||||||
validateExpiration,
|
validateExpiration,
|
||||||
validateMarketState,
|
validateMarketState,
|
||||||
@ -182,6 +182,7 @@ export const DealTicket = ({
|
|||||||
const iceberg = watch('iceberg');
|
const iceberg = watch('iceberg');
|
||||||
const peakSize = watch('peakSize');
|
const peakSize = watch('peakSize');
|
||||||
const expiresAt = watch('expiresAt');
|
const expiresAt = watch('expiresAt');
|
||||||
|
const postOnly = watch('postOnly');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const size = storedFormValues?.[dealTicketType]?.size;
|
const size = storedFormValues?.[dealTicketType]?.size;
|
||||||
@ -211,6 +212,7 @@ export const DealTicket = ({
|
|||||||
size: rawSize,
|
size: rawSize,
|
||||||
timeInForce,
|
timeInForce,
|
||||||
type,
|
type,
|
||||||
|
postOnly,
|
||||||
},
|
},
|
||||||
market.id,
|
market.id,
|
||||||
market.decimalPlaces,
|
market.decimalPlaces,
|
||||||
@ -219,8 +221,7 @@ export const DealTicket = ({
|
|||||||
|
|
||||||
const price =
|
const price =
|
||||||
normalizedOrder &&
|
normalizedOrder &&
|
||||||
marketPrice &&
|
getDerivedPrice(normalizedOrder, marketPrice ?? undefined);
|
||||||
getDerivedPrice(normalizedOrder, marketPrice);
|
|
||||||
|
|
||||||
const notionalSize = getNotionalSize(
|
const notionalSize = getNotionalSize(
|
||||||
price,
|
price,
|
||||||
@ -474,6 +475,7 @@ export const DealTicket = ({
|
|||||||
}
|
}
|
||||||
assetSymbol={assetSymbol}
|
assetSymbol={assetSymbol}
|
||||||
market={market}
|
market={market}
|
||||||
|
isMarketInAuction={isMarketInAuction(marketData.marketTradingMode)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Controller
|
<Controller
|
||||||
|
78
libs/deal-ticket/src/hooks/use-estimate-fees.spec.tsx
Normal file
78
libs/deal-ticket/src/hooks/use-estimate-fees.spec.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { renderHook } from '@testing-library/react';
|
||||||
|
import { useEstimateFees } from './use-estimate-fees';
|
||||||
|
import { Side, OrderTimeInForce, OrderType } from '@vegaprotocol/types';
|
||||||
|
|
||||||
|
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||||
|
|
||||||
|
const data: EstimateFeesQuery = {
|
||||||
|
estimateFees: {
|
||||||
|
totalFeeAmount: '12',
|
||||||
|
fees: {
|
||||||
|
infrastructureFee: '2',
|
||||||
|
liquidityFee: '4',
|
||||||
|
makerFee: '6',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockUseEstimateFeesQuery = jest.fn((...args) => ({
|
||||||
|
data,
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('./__generated__/EstimateOrder', () => ({
|
||||||
|
...jest.requireActual('@vegaprotocol/data-provider'),
|
||||||
|
useEstimateFeesQuery: jest.fn((...args) => mockUseEstimateFeesQuery(...args)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/wallet', () => ({
|
||||||
|
useVegaWallet: () => ({ pubKey: 'pubKey' }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('useEstimateFees', () => {
|
||||||
|
it('returns 0 as estimated values if order is postOnly', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useEstimateFees({
|
||||||
|
marketId: 'marketId',
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
size: '1',
|
||||||
|
price: '1',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
postOnly: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(result.current).toEqual({
|
||||||
|
totalFeeAmount: '0',
|
||||||
|
fees: {
|
||||||
|
infrastructureFee: '0',
|
||||||
|
liquidityFee: '0',
|
||||||
|
makerFee: '0',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(mockUseEstimateFeesQuery.mock.lastCall?.[0].skip).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('divide values by 2 if market is in auction', () => {
|
||||||
|
const { result } = renderHook(() =>
|
||||||
|
useEstimateFees(
|
||||||
|
{
|
||||||
|
marketId: 'marketId',
|
||||||
|
side: Side.SIDE_BUY,
|
||||||
|
size: '1',
|
||||||
|
price: '1',
|
||||||
|
timeInForce: OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||||
|
type: OrderType.TYPE_LIMIT,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
expect(result.current).toEqual({
|
||||||
|
totalFeeAmount: '6',
|
||||||
|
fees: {
|
||||||
|
infrastructureFee: '1',
|
||||||
|
liquidityFee: '2',
|
||||||
|
makerFee: '3',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -1,13 +1,16 @@
|
|||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||||
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
import type { OrderSubmissionBody } from '@vegaprotocol/wallet';
|
||||||
|
|
||||||
|
import type { EstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||||
import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
|
import { useEstimateFeesQuery } from './__generated__/EstimateOrder';
|
||||||
|
|
||||||
export const useEstimateFees = (
|
const divideByTwo = (n: string) => (BigInt(n) / BigInt(2)).toString();
|
||||||
order?: OrderSubmissionBody['orderSubmission']
|
|
||||||
) => {
|
|
||||||
const { pubKey } = useVegaWallet();
|
|
||||||
|
|
||||||
|
export const useEstimateFees = (
|
||||||
|
order?: OrderSubmissionBody['orderSubmission'],
|
||||||
|
isMarketInAuction?: boolean
|
||||||
|
): EstimateFeesQuery['estimateFees'] | undefined => {
|
||||||
|
const { pubKey } = useVegaWallet();
|
||||||
const { data } = useEstimateFeesQuery({
|
const { data } = useEstimateFeesQuery({
|
||||||
variables: order && {
|
variables: order && {
|
||||||
marketId: order.marketId,
|
marketId: order.marketId,
|
||||||
@ -19,7 +22,28 @@ export const useEstimateFees = (
|
|||||||
type: order.type,
|
type: order.type,
|
||||||
},
|
},
|
||||||
fetchPolicy: 'no-cache',
|
fetchPolicy: 'no-cache',
|
||||||
skip: !pubKey || !order?.size || !order?.price,
|
skip: !pubKey || !order?.size || !order?.price || order.postOnly,
|
||||||
});
|
});
|
||||||
return data?.estimateFees;
|
if (order?.postOnly) {
|
||||||
|
return {
|
||||||
|
totalFeeAmount: '0',
|
||||||
|
fees: {
|
||||||
|
infrastructureFee: '0',
|
||||||
|
liquidityFee: '0',
|
||||||
|
makerFee: '0',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return isMarketInAuction && data?.estimateFees
|
||||||
|
? {
|
||||||
|
totalFeeAmount: divideByTwo(data.estimateFees.totalFeeAmount),
|
||||||
|
fees: {
|
||||||
|
infrastructureFee: divideByTwo(
|
||||||
|
data.estimateFees.fees.infrastructureFee
|
||||||
|
),
|
||||||
|
liquidityFee: divideByTwo(data.estimateFees.fees.liquidityFee),
|
||||||
|
makerFee: divideByTwo(data.estimateFees.fees.makerFee),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: data?.estimateFees;
|
||||||
};
|
};
|
||||||
|
@ -60,6 +60,7 @@ export const FeesBreakdown = ({
|
|||||||
.plus(fees.infrastructureFee)
|
.plus(fees.infrastructureFee)
|
||||||
.plus(fees.liquidityFee)
|
.plus(fees.liquidityFee)
|
||||||
.toString();
|
.toString();
|
||||||
|
if (totalFees === '0') return null;
|
||||||
const formatValue = (value: string | number | null | undefined): string => {
|
const formatValue = (value: string | number | null | undefined): string => {
|
||||||
return value && !isNaN(Number(value))
|
return value && !isNaN(Number(value))
|
||||||
? addDecimalsFormatNumber(value, decimals)
|
? addDecimalsFormatNumber(value, decimals)
|
||||||
|
@ -33,7 +33,7 @@ export const getDerivedPrice = (
|
|||||||
type: Schema.OrderType;
|
type: Schema.OrderType;
|
||||||
price?: string | undefined;
|
price?: string | undefined;
|
||||||
},
|
},
|
||||||
marketPrice: string
|
marketPrice?: string
|
||||||
) => {
|
) => {
|
||||||
// If order type is market we should use either the mark price
|
// If order type is market we should use either the mark price
|
||||||
// or the uncrossing price. If order type is limit use the price
|
// or the uncrossing price. If order type is limit use the price
|
||||||
|
Loading…
Reference in New Issue
Block a user