import maxBy from 'lodash/maxBy'; import minBy from 'lodash/minBy'; import { t } from '@vegaprotocol/i18n'; import { useVegaWallet } from '@vegaprotocol/wallet'; import { useNetworkParams, NetworkParams, } from '@vegaprotocol/network-parameters'; import { useMarketList } from '@vegaprotocol/markets'; import { formatNumber, formatNumberRounded } from '@vegaprotocol/utils'; import { useDiscountProgramsQuery, useFeesQuery } from './__generated__/Fees'; import { FeeCard } from './fees-card'; import { MarketFees } from './market-fees'; import { Stat } from './stat'; import { useVolumeStats } from './use-volume-stats'; import { useReferralStats } from './use-referral-stats'; import { formatPercentage, getAdjustedFee } from './utils'; import { Table, Td, Th, THead, Tr } from './table'; import BigNumber from 'bignumber.js'; import { Links } from '../../lib/links'; import { Link } from 'react-router-dom'; import { Tooltip, VegaIcon, VegaIconNames, truncateMiddle, } from '@vegaprotocol/ui-toolkit'; export const FeesContainer = () => { const { pubKey } = useVegaWallet(); const { params, loading: paramsLoading } = useNetworkParams([ NetworkParams.market_fee_factors_makerFee, NetworkParams.market_fee_factors_infrastructureFee, ]); const { data: markets, loading: marketsLoading } = useMarketList(); const { data: programData, loading: programLoading } = useDiscountProgramsQuery(); const volumeDiscountWindowLength = programData?.currentVolumeDiscountProgram?.windowLength || 1; const referralDiscountWindowLength = programData?.currentReferralProgram?.windowLength || 1; const { data: feesData, loading: feesLoading } = useFeesQuery({ variables: { partyId: pubKey || '', volumeDiscountEpochs: volumeDiscountWindowLength, referralDiscountEpochs: referralDiscountWindowLength, }, skip: !pubKey || !programData, }); const { volumeDiscount, volumeTierIndex, volumeInWindow, volumeTiers } = useVolumeStats( feesData?.volumeDiscountStats, programData?.currentVolumeDiscountProgram ); const { referralDiscount, referralVolumeInWindow, referralTierIndex, referralTiers, epochsInSet, code, isReferrer, } = useReferralStats( feesData?.referralSetStats, feesData?.referralSetReferees, programData?.currentReferralProgram, feesData?.epoch, feesData?.referrer, feesData?.referee ); const loading = paramsLoading || feesLoading || programLoading; const isConnected = Boolean(pubKey); const isReferralProgramRunning = Boolean(programData?.currentReferralProgram); const isVolumeDiscountProgramRunning = Boolean( programData?.currentVolumeDiscountProgram ); return (
{isConnected && ( <> {isVolumeDiscountProgramRunning ? ( ) : (

{t('No volume discount program active')}

)}
{isReferrer ? ( ) : isReferralProgramRunning ? ( ) : (

{t('No referral program active')}

)}
)}
); }; export const TradingFees = ({ params, markets, referralDiscount, volumeDiscount, }: { params: { market_fee_factors_infrastructureFee: string; market_fee_factors_makerFee: string; }; markets: Array<{ fees: { factors: { liquidityFee: string } } }> | null; referralDiscount: number; volumeDiscount: number; }) => { const referralDiscountBigNum = new BigNumber(referralDiscount); const volumeDiscountBigNum = new BigNumber(volumeDiscount); // Show min and max liquidity fees from all markets const minLiq = minBy(markets, (m) => Number(m.fees.factors.liquidityFee)); const maxLiq = maxBy(markets, (m) => Number(m.fees.factors.liquidityFee)); const total = new BigNumber(params.market_fee_factors_makerFee).plus( new BigNumber(params.market_fee_factors_infrastructureFee) ); const adjustedTotal = getAdjustedFee( [total], [referralDiscountBigNum, volumeDiscountBigNum] ); let minTotal; let maxTotal; let minAdjustedTotal; let maxAdjustedTotal; if (minLiq && maxLiq) { const minLiqFee = new BigNumber(minLiq.fees.factors.liquidityFee); const maxLiqFee = new BigNumber(maxLiq.fees.factors.liquidityFee); minTotal = total.plus(minLiqFee); maxTotal = total.plus(maxLiqFee); minAdjustedTotal = getAdjustedFee( [total, minLiqFee], [referralDiscountBigNum, volumeDiscountBigNum] ); maxAdjustedTotal = getAdjustedFee( [total, maxLiqFee], [referralDiscountBigNum, volumeDiscountBigNum] ); } return (

{minAdjustedTotal !== undefined && maxAdjustedTotal !== undefined ? `${formatPercentage(minAdjustedTotal)}%-${formatPercentage( maxAdjustedTotal )}%` : `${formatPercentage(adjustedTotal)}%`}

{minLiq && maxLiq && ( )}
{t('Total fee before discount')} {minTotal !== undefined && maxTotal !== undefined ? `${formatPercentage( minTotal.toNumber() )}%-${formatPercentage(maxTotal.toNumber())}%` : `${formatPercentage(total.toNumber())}%`}
{t('Infrastructure')} {formatPercentage( Number(params.market_fee_factors_infrastructureFee) )} %
{t('Maker')} {formatPercentage(Number(params.market_fee_factors_makerFee))}%
{t('Liquidity')} {formatPercentage(Number(minLiq.fees.factors.liquidityFee))}% {'-'} {formatPercentage(Number(maxLiq.fees.factors.liquidityFee))}%
); }; export const CurrentVolume = ({ tiers, tierIndex, windowLengthVolume, windowLength, }: { tiers: Array<{ minimumRunningNotionalTakerVolume: string }>; tierIndex: number; windowLengthVolume: number; windowLength: number; }) => { const nextTier = tiers[tierIndex + 1]; const requiredForNextTier = nextTier ? Number(nextTier.minimumRunningNotionalTakerVolume) - windowLengthVolume : 0; return (
{requiredForNextTier > 0 && ( )}
); }; const ReferralBenefits = ({ epochsInSet, setRunningNotionalTakerVolume, epochs, }: { epochsInSet: number; setRunningNotionalTakerVolume: number; epochs: number; }) => { return (
); }; const TotalDiscount = ({ referralDiscount, volumeDiscount, isReferralProgramRunning, isVolumeDiscountProgramRunning, }: { referralDiscount: number; volumeDiscount: number; isReferralProgramRunning: boolean; isVolumeDiscountProgramRunning: boolean; }) => { const totalDiscount = 1 - (1 - volumeDiscount) * (1 - referralDiscount); const totalDiscountDescription = t( 'The total discount is calculated according to the following formula: ' ); const formula = ( 1 - (1 - dvolume) ⋇ (1 - dreferral) ); return (
{totalDiscountDescription} {formula} } value={formatPercentage(totalDiscount) + '%'} highlight={true} />
{t('Volume discount')} {formatPercentage(volumeDiscount)}% {!isVolumeDiscountProgramRunning && ( {' '} )}
{t('Referral discount')} {formatPercentage(referralDiscount)}% {!isReferralProgramRunning && ( {' '} )}
); }; const VolumeTiers = ({ tiers, tierIndex, lastEpochVolume, windowLength, }: { tiers: Array<{ volumeDiscountFactor: string; minimumRunningNotionalTakerVolume: string; }>; tierIndex: number; lastEpochVolume: number; windowLength: number; }) => { if (!tiers.length) { return (

{t('No volume discount program active')}

); } return (
{Array.from(tiers) .reverse() .map((tier, i) => { const isUserTier = tiers.length - 1 - tierIndex === i; return ( ); })}
{t('Tier')} {t('Discount')} {t('Min. trading volume')} {t('My volume (last %s epochs)', windowLength.toString())}
{i + 1} {formatPercentage(Number(tier.volumeDiscountFactor))}% {formatNumber(tier.minimumRunningNotionalTakerVolume)} {isUserTier ? formatNumber(lastEpochVolume) : ''} {isUserTier ? : null}
); }; const ReferralTiers = ({ tiers, tierIndex, epochsInSet, referralVolumeInWindow, }: { tiers: Array<{ referralDiscountFactor: string; minimumRunningNotionalTakerVolume: string; minimumEpochs: number; }>; tierIndex: number; epochsInSet: number; referralVolumeInWindow: number; }) => { if (!tiers.length) { return (

{t('No referral program active')}

); } return (
{Array.from(tiers) .reverse() .map((t, i) => { const isUserTier = tiers.length - 1 - tierIndex === i; const requiredVolume = Number( t.minimumRunningNotionalTakerVolume ); let unlocksIn = null; if ( referralVolumeInWindow >= requiredVolume && epochsInSet < t.minimumEpochs ) { unlocksIn = ( Unlocks in {t.minimumEpochs - epochsInSet} epochs ); } return ( ); })}
{t('Tier')} {t('Discount')} {t('Min. trading volume')} {t('Required epochs')}
{i + 1} {formatPercentage(Number(t.referralDiscountFactor))}% {formatNumber(t.minimumRunningNotionalTakerVolume)} {t.minimumEpochs} {isUserTier ? : unlocksIn}
); }; const YourTier = () => { return ( {t('Your tier')} ); }; const ReferrerInfo = ({ code }: { code?: string }) => (

{t('Connected key is owner of the referral set')} {code && ( <> {' '} {truncateMiddle(code)} )} {'. '} {t('As owner, it is eligible for commission not fee discounts.')}

{t('See')}{' '} {t('Referrals')} {' '} {t('for more information.')}

);