From a2b9b0da053303d8eb73d1a08dba9cdd3c4adbbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20G=C5=82ownia?=
Date: Wed, 29 Nov 2023 13:59:42 +0100
Subject: [PATCH] feat(deal-ticket): enhance for fee discounts (#5353)
---
.../deal-ticket/deal-ticket-fee-details.tsx | 11 +-
.../src/components/discounts.spec.ts | 12 +-
libs/deal-ticket/src/components/discounts.ts | 39 ++++--
.../fees-breakdown/fees-breakdown.spec.tsx | 11 +-
.../fees-breakdown/fees-breakdown.tsx | 130 ++++++++----------
libs/i18n/src/locales/en/deal-ticket.json | 4 +-
6 files changed, 108 insertions(+), 99 deletions(-)
diff --git a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
index c6adb8531..2b3b88f48 100644
--- a/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
+++ b/libs/deal-ticket/src/components/deal-ticket/deal-ticket-fee-details.tsx
@@ -77,12 +77,10 @@ export const DealTicketFeeDetails = ({
label={
<>
{t('Fees')}
- {totalDiscountFactor ? (
+ {totalDiscountFactor !== '0' ? (
- -
{formatNumberPercentage(
- new BigNumber(totalDiscountFactor).multipliedBy(100),
- 2
+ new BigNumber(totalDiscountFactor).multipliedBy(100)
)}
) : null}
@@ -105,10 +103,7 @@ export const DealTicketFeeDetails = ({
)}
{
discountedFee: '100',
volumeDiscount: '0',
referralDiscount: '0',
+ totalDiscount: '0',
});
expect(getDiscountedFee('100', undefined, '0.1')).toEqual({
discountedFee: '90',
volumeDiscount: '10',
referralDiscount: '0',
+ totalDiscount: '10',
});
expect(getDiscountedFee('100', '0.1', undefined)).toEqual({
discountedFee: '90',
volumeDiscount: '0',
referralDiscount: '10',
+ totalDiscount: '10',
});
});
@@ -24,6 +27,7 @@ describe('getDiscountedFee', () => {
discountedFee: '',
volumeDiscount: '0',
referralDiscount: '0',
+ totalDiscount: '0',
});
});
});
@@ -35,7 +39,7 @@ describe('getTotalDiscountFactor', () => {
volumeDiscountFactor: '0',
referralDiscountFactor: '0',
})
- ).toEqual(0);
+ ).toEqual('0');
});
it('returns volumeDiscountFactor if referralDiscountFactor is 0', () => {
@@ -44,7 +48,7 @@ describe('getTotalDiscountFactor', () => {
volumeDiscountFactor: '0.1',
referralDiscountFactor: '0',
})
- ).toEqual(0.1);
+ ).toEqual('-0.1');
});
it('returns referralDiscountFactor if volumeDiscountFactor is 0', () => {
expect(
@@ -52,7 +56,7 @@ describe('getTotalDiscountFactor', () => {
volumeDiscountFactor: '0',
referralDiscountFactor: '0.1',
})
- ).toEqual(0.1);
+ ).toEqual('-0.1');
});
it('calculates discount using referralDiscountFactor and volumeDiscountFactor', () => {
@@ -61,6 +65,6 @@ describe('getTotalDiscountFactor', () => {
volumeDiscountFactor: '0.2',
referralDiscountFactor: '0.1',
})
- ).toBeCloseTo(0.28);
+ ).toBe('-0.28');
});
});
diff --git a/libs/deal-ticket/src/components/discounts.ts b/libs/deal-ticket/src/components/discounts.ts
index 452e193db..9ddf8e632 100644
--- a/libs/deal-ticket/src/components/discounts.ts
+++ b/libs/deal-ticket/src/components/discounts.ts
@@ -15,6 +15,7 @@ export const getDiscountedFee = (
discountedFee: feeAmount,
volumeDiscount: '0',
referralDiscount: '0',
+ totalDiscount: '0',
};
}
const referralDiscount = new BigNumber(referralDiscountFactor || '0')
@@ -23,12 +24,14 @@ export const getDiscountedFee = (
const volumeDiscount = new BigNumber(volumeDiscountFactor || '0')
.multipliedBy((BigInt(feeAmount) - BigInt(referralDiscount)).toString())
.toFixed(0, BigNumber.ROUND_FLOOR);
+ const totalDiscount = (
+ BigInt(referralDiscount) + BigInt(volumeDiscount)
+ ).toString();
const discountedFee = (
- BigInt(feeAmount || '0') -
- BigInt(referralDiscount) -
- BigInt(volumeDiscount)
+ BigInt(feeAmount || '0') - BigInt(totalDiscount)
).toString();
return {
+ totalDiscount,
referralDiscount,
volumeDiscount,
discountedFee,
@@ -39,16 +42,28 @@ export const getTotalDiscountFactor = (feeEstimate?: {
volumeDiscountFactor?: string;
referralDiscountFactor?: string;
}) => {
- if (!feeEstimate) {
- return 0;
+ if (
+ !feeEstimate ||
+ (feeEstimate.referralDiscountFactor === '0' &&
+ feeEstimate.volumeDiscountFactor === '0')
+ ) {
+ return '0';
}
- const volumeFactor = Number(feeEstimate?.volumeDiscountFactor) || 0;
- const referralFactor = Number(feeEstimate?.referralDiscountFactor) || 0;
- if (!volumeFactor) {
- return referralFactor;
+ const volumeFactor = new BigNumber(
+ feeEstimate?.volumeDiscountFactor || 0
+ ).minus(1);
+ const referralFactor = new BigNumber(
+ feeEstimate?.referralDiscountFactor || 0
+ ).minus(1);
+ if (volumeFactor.isZero()) {
+ return feeEstimate.referralDiscountFactor
+ ? `-${feeEstimate.referralDiscountFactor}`
+ : '0';
}
- if (!referralFactor) {
- return volumeFactor;
+ if (referralFactor.isZero()) {
+ return feeEstimate.volumeDiscountFactor
+ ? `-${feeEstimate.volumeDiscountFactor}`
+ : '0';
}
- return 1 - (1 - volumeFactor) * (1 - referralFactor);
+ return volumeFactor.multipliedBy(referralFactor).minus(1).toString();
};
diff --git a/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.spec.tsx b/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.spec.tsx
index 9d99789c5..b6383bf15 100644
--- a/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.spec.tsx
+++ b/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.spec.tsx
@@ -14,13 +14,16 @@ describe('FeesBreakdown', () => {
liquidityFee: '100',
};
const props = {
- totalFeeAmount: '100',
- fees,
feeFactors,
symbol: 'USD',
decimals: 2,
- referralDiscountFactor: '0.01',
- volumeDiscountFactor: '0.01',
+
+ feeEstimate: {
+ referralDiscountFactor: '0.01',
+ volumeDiscountFactor: '0.01',
+ totalFeeAmount: '100',
+ fees,
+ },
};
render();
expect(screen.getByText('Maker fee').nextElementSibling).toHaveTextContent(
diff --git a/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.tsx b/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.tsx
index bfeaad205..7fca7ce33 100644
--- a/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.tsx
+++ b/libs/deal-ticket/src/components/fees-breakdown/fees-breakdown.tsx
@@ -1,12 +1,13 @@
import { sumFeesFactors } from '@vegaprotocol/markets';
-import type { TradeFee, FeeFactors } from '@vegaprotocol/types';
+import type { FeeFactors } from '@vegaprotocol/types';
import {
addDecimalsFormatNumber,
formatNumberPercentage,
} from '@vegaprotocol/utils';
import BigNumber from 'bignumber.js';
-import { getDiscountedFee } from '../discounts';
+import { getDiscountedFee, getTotalDiscountFactor } from '../discounts';
import { useT } from '../../use-t';
+import { type useEstimateFees } from '../../hooks/use-estimate-fees';
const formatValue = (
value: string | number | null | undefined,
@@ -24,18 +25,16 @@ const FeesBreakdownItem = ({
decimals,
}: {
label: string;
- factor?: string;
+ factor?: string | number;
value: string;
symbol?: string;
decimals: number;
}) => (
<>
{label}
- {factor && (
-
- {formatNumberPercentage(new BigNumber(factor).times(100))}
-
- )}
+
+ {factor ? formatNumberPercentage(new BigNumber(factor).times(100)) : ''}
+
{formatValue(value, decimals)} {symbol || ''}
@@ -43,59 +42,35 @@ const FeesBreakdownItem = ({
);
export const FeesBreakdown = ({
- totalFeeAmount,
- fees,
+ feeEstimate,
feeFactors,
symbol,
decimals,
- referralDiscountFactor,
- volumeDiscountFactor,
}: {
- totalFeeAmount?: string;
- fees?: TradeFee;
+ feeEstimate: ReturnType;
feeFactors?: FeeFactors;
symbol?: string;
decimals: number;
- referralDiscountFactor?: string;
- volumeDiscountFactor?: string;
}) => {
const t = useT();
+ const { fees, totalFeeAmount, referralDiscountFactor, volumeDiscountFactor } =
+ feeEstimate || {};
if (!fees || !totalFeeAmount || totalFeeAmount === '0') return null;
- const { discountedFee: discountedInfrastructureFee } = getDiscountedFee(
- fees.infrastructureFee,
- referralDiscountFactor,
- volumeDiscountFactor
- );
-
- const { discountedFee: discountedLiquidityFee } = getDiscountedFee(
- fees.liquidityFee,
- referralDiscountFactor,
- volumeDiscountFactor
- );
-
- const { discountedFee: discountedMakerFee } = getDiscountedFee(
- fees.makerFee,
- referralDiscountFactor,
- volumeDiscountFactor
- );
-
- const {
- discountedFee: discountedTotalFeeAmount,
- volumeDiscount,
- referralDiscount,
- } = getDiscountedFee(
- totalFeeAmount,
- referralDiscountFactor,
- volumeDiscountFactor
- );
+ const totalDiscountFactor = getTotalDiscountFactor(feeEstimate);
+ const { discountedFee: discountedTotalFeeAmount, totalDiscount } =
+ getDiscountedFee(
+ totalFeeAmount,
+ referralDiscountFactor,
+ volumeDiscountFactor
+ );
return (
@@ -103,7 +78,7 @@ export const FeesBreakdown = ({
@@ -111,35 +86,50 @@ export const FeesBreakdown = ({
- {volumeDiscountFactor && volumeDiscount !== '0' && (
-
+ {totalDiscount && totalDiscount !== '0' ? (
+ <>
+
+
+
+
+ >
+ ) : (
+ <>
+
+
+ >
)}
- {referralDiscountFactor && referralDiscount !== '0' && (
-
- )}
-
);
};
diff --git a/libs/i18n/src/locales/en/deal-ticket.json b/libs/i18n/src/locales/en/deal-ticket.json
index e7d4cc346..e2ca3324d 100644
--- a/libs/i18n/src/locales/en/deal-ticket.json
+++ b/libs/i18n/src/locales/en/deal-ticket.json
@@ -21,6 +21,7 @@
"DEDUCTION_FROM_COLLATERAL_TOOLTIP_TEXT": "To cover the required margin, this amount will be drawn from your general ({{assetSymbol}}) account.",
"Deposit {{assetSymbol}}": "Deposit {{assetSymbol}}",
"Devnet": "Devnet",
+ "Discount": "Discount",
"EST_TOTAL_MARGIN_TOOLTIP_TEXT": "Estimated total margin that will cover open positions, active orders and this order.",
"Est. uncrossing price": "Est. uncrossing price",
"Est. uncrossing vol": "Est. uncrossing vol",
@@ -78,6 +79,7 @@
"Size": "Size",
"Size cannot be lower than {{sizeStep}}": "Size cannot be lower than {{sizeStep}}",
"sizeAtPrice-market": "market",
+ "Subtotal": "Subtotal",
"Stagnet": "Stagnet",
"Stop": "Stop",
"Stop Limit": "Stop Limit",
@@ -104,7 +106,7 @@
"Time in force": "Time in force",
"TIME_IN_FORCE_SELECTOR_LIQUIDITY_MONITORING_AUCTION": "This market is in auction until it reaches <0>sufficient liquidity0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.",
"TIME_IN_FORCE_SELECTOR_PRICE_MONITORING_AUCTION": "This market is in auction due to <0>high price volatility0>. Until the auction ends, you can only place GFA, GTT, or GTC limit orders.",
- "Total fees": "Total fees",
+ "Total": "Total",
"Total margin available": "Total margin available",
"TOTAL_MARGIN_AVAILABLE": "Total margin available = general {{assetSymbol}} balance ({{generalAccountBalance}} {{assetSymbol}}) + margin balance ({{marginAccountBalance}} {{assetSymbol}}) - maintenance level ({{marginMaintenance}} {{assetSymbol}}).",
"Trading terminated": "Trading terminated",