From c8e624eabada5f20954d7d6d6d8644c16bcffb1c Mon Sep 17 00:00:00 2001 From: Matthew Russell Date: Fri, 27 Oct 2023 06:20:45 -0700 Subject: [PATCH] chore(trading): update fee factor with discount calculation (#5138) --- .../fees-container/fees-container.tsx | 4 +- .../components/fees-container/utils.spec.ts | 74 +++++++++++++++++++ .../components/fees-container/utils.ts | 18 +++-- libs/utils/src/lib/format/number.ts | 30 ++++++++ 4 files changed, 117 insertions(+), 9 deletions(-) create mode 100644 apps/trading/components/fees-container/utils.spec.ts diff --git a/apps/trading/components/fees-container/fees-container.tsx b/apps/trading/components/fees-container/fees-container.tsx index 64f6bb018..6e4298cb3 100644 --- a/apps/trading/components/fees-container/fees-container.tsx +++ b/apps/trading/components/fees-container/fees-container.tsx @@ -7,7 +7,7 @@ import { NetworkParams, } from '@vegaprotocol/network-parameters'; import { useMarketList } from '@vegaprotocol/markets'; -import { formatNumber } from '@vegaprotocol/utils'; +import { formatNumber, formatNumberRounded } from '@vegaprotocol/utils'; import { useDiscountProgramsQuery, useFeesQuery } from './__generated__/Fees'; import { FeeCard } from './fees-card'; import { MarketFees } from './market-fees'; @@ -284,7 +284,7 @@ export const CurrentVolume = ({ return (
{requiredForNextTier > 0 && ( diff --git a/apps/trading/components/fees-container/utils.spec.ts b/apps/trading/components/fees-container/utils.spec.ts new file mode 100644 index 000000000..c2b3cd86e --- /dev/null +++ b/apps/trading/components/fees-container/utils.spec.ts @@ -0,0 +1,74 @@ +import BigNumber from 'bignumber.js'; +import { getAdjustedFee } from './utils'; + +describe('getAdjustedFee', () => { + it('simple', () => { + const volumeDiscount = 0.5; + const referralDiscount = 0.5; + + const infraFee = 0.1; + const makerFee = 0.1; + const liqFee = 0.1; + + const fees = [ + new BigNumber(infraFee), + new BigNumber(makerFee), + new BigNumber(liqFee), + ]; + + const discounts = [ + new BigNumber(volumeDiscount), + new BigNumber(referralDiscount), + ]; + + // 1 - 0.5 - 0.5 + const v = new BigNumber(1).minus(new BigNumber(volumeDiscount)); + + // 1 - 0.5 = 0.5 + const r = new BigNumber(1).minus(new BigNumber(referralDiscount)); + + // 0.5 * 0.5 = 0.25 + // 1 - 0.25 = 0.75 + const factor = new BigNumber(1).minus(v.times(r)); + + // 0.1 + 0.1 + 0.1 = 0.3 + const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0)); + + // 0.3 * 0.75 = 0.225 + const expected = new BigNumber(totalFees).times(factor).toNumber(); + + expect(getAdjustedFee(fees, discounts)).toBe(expected); + }); + + it('combines discount factors multiplicativly', () => { + const volumeDiscount = 0.4; + const referralDiscount = 0.1; + + const infraFee = 0.0005; + const makerFee = 0.0002; + const liqFee = 0.01; + + const fees = [ + new BigNumber(infraFee), + new BigNumber(makerFee), + new BigNumber(liqFee), + ]; + + const discounts = [ + new BigNumber(volumeDiscount), + new BigNumber(referralDiscount), + ]; + + // formula for calculating adjusted fees + const v = new BigNumber(1).minus(new BigNumber(volumeDiscount)); + const r = new BigNumber(1).minus(new BigNumber(referralDiscount)); + const factor = new BigNumber(1).minus(v.times(r)); + + // summed fees + const totalFees = fees.reduce((sum, x) => sum.plus(x), new BigNumber(0)); + + const expected = new BigNumber(totalFees).times(factor).toNumber(); + + expect(getAdjustedFee(fees, discounts)).toBe(expected); + }); +}); diff --git a/apps/trading/components/fees-container/utils.ts b/apps/trading/components/fees-container/utils.ts index e0ce161c2..30a4be30a 100644 --- a/apps/trading/components/fees-container/utils.ts +++ b/apps/trading/components/fees-container/utils.ts @@ -88,14 +88,18 @@ export const getReferralBenefitTier = ( /** * Given a set of fees and a set of discounts return * the adjusted fee factor + * + * Formula for calculating the adjusted fees + * total_discount_factor = 1 - (1 - volumeDiscount) * (1 - referralDiscount) */ export const getAdjustedFee = (fees: BigNumber[], discounts: BigNumber[]) => { const totalFee = fees.reduce((sum, f) => sum.plus(f), new BigNumber(0)); - const totalDiscount = discounts.reduce( - (sum, d) => sum.plus(d), - new BigNumber(0) - ); - return totalFee - .times(BigNumber.max(0, new BigNumber(1).minus(totalDiscount))) - .toNumber(); + + const combinedFactors = discounts.reduce((acc, d) => { + return acc.times(new BigNumber(1).minus(d)); + }, new BigNumber(1)); + + const totalFactor = new BigNumber(1).minus(combinedFactors); + + return totalFee.times(BigNumber.max(0, totalFactor)).toNumber(); }; diff --git a/libs/utils/src/lib/format/number.ts b/libs/utils/src/lib/format/number.ts index 261502a38..783dfaf5c 100644 --- a/libs/utils/src/lib/format/number.ts +++ b/libs/utils/src/lib/format/number.ts @@ -179,3 +179,33 @@ export const toNumberParts = ( export const isNumeric = ( value?: string | number | BigNumber | bigint | null ): value is NonNullable => /^-?\d*\.?\d+$/.test(String(value)); + +/** + * Format a number greater than 1 million with m for million, b for billion + * and t for trillion + */ +export const formatNumberRounded = (num: BigNumber) => { + let value = ''; + + const format = (divisor: string) => { + const result = num.dividedBy(divisor); + return result.isInteger() ? result.toString() : result.toFixed(1); + }; + + if (num.isGreaterThan(new BigNumber('1e14'))) { + value = '>100t'; + } else if (num.isGreaterThanOrEqualTo(new BigNumber('1e12'))) { + // Trillion + value = `${format('1e12')}t`; + } else if (num.isGreaterThanOrEqualTo(new BigNumber('1e9'))) { + // Billion + value = `${format('1e9')}b`; + } else if (num.isGreaterThanOrEqualTo(new BigNumber('1e6'))) { + // Million + value = `${format('1e6')}m`; + } else { + value = formatNumber(num); + } + + return value; +};