diff --git a/src/components/FeeExplainer/FeeExplainer.tsx b/src/components/FeeExplainer/FeeExplainer.tsx index fab92078043..279d6962f0f 100644 --- a/src/components/FeeExplainer/FeeExplainer.tsx +++ b/src/components/FeeExplainer/FeeExplainer.tsx @@ -1,5 +1,6 @@ import type { CardProps, StackProps } from '@chakra-ui/react' import { Box, Card, CardBody, Flex, Heading, Stack, useToken } from '@chakra-ui/react' +import { foxWifHatAssetId } from '@shapeshiftoss/caip' import { bnOrZero } from '@shapeshiftoss/chain-adapters' import { LinearGradient } from '@visx/gradient' import { GridColumns, GridRows } from '@visx/grid' @@ -31,6 +32,7 @@ import { selectIsSnapshotApiQueriesRejected, selectVotingPower, } from 'state/apis/snapshot/selectors' +import { selectPortfolioCryptoBalanceBaseUnitByFilter } from 'state/slices/common-selectors' import { useAppSelector } from 'state/store' import { CHART_TRADE_SIZE_MAX_USD } from './common' @@ -149,6 +151,9 @@ const FeeChart: React.FC = ({ foxHolding, tradeSize, feeModel }) foxHeld: bn(debouncedFoxHolding), feeModel, isSnapshotApiQueriesRejected, + // This is for feeExplainer which is not supporting anything else than FOX discount for now + foxWifHatHeldCryptoBaseUnit: bn(0), + thorHeld: bn(0), }).feeBpsFloat.toNumber() return { x: trade, y: feeBps } }) @@ -161,6 +166,9 @@ const FeeChart: React.FC = ({ foxHolding, tradeSize, feeModel }) foxHeld: bn(debouncedFoxHolding), feeModel, isSnapshotApiQueriesRejected, + // This is for feeExplainer which is not supporting anything else than FOX discount for now + foxWifHatHeldCryptoBaseUnit: bn(0), + thorHeld: bn(0), }).feeBpsFloat.toNumber() return [{ x: tradeSize, y: feeBps }] @@ -262,12 +270,20 @@ type FeeOutputProps = { export const FeeOutput: React.FC = ({ tradeSizeUSD, foxHolding, feeModel }) => { const isSnapshotApiQueriesRejected = useAppSelector(selectIsSnapshotApiQueriesRejected) + + const foxWifHatHeld = useAppSelector(state => + selectPortfolioCryptoBalanceBaseUnitByFilter(state, { assetId: foxWifHatAssetId }), + ) + const { feeUsd, feeBps, foxDiscountPercent, feeUsdBeforeDiscount, feeBpsBeforeDiscount } = calculateFees({ tradeAmountUsd: bn(tradeSizeUSD), foxHeld: bn(foxHolding), feeModel, isSnapshotApiQueriesRejected, + foxWifHatHeldCryptoBaseUnit: bn(foxWifHatHeld), + // @TODO: remove this when thor swap discount is removed + thorHeld: bn(0), }) const basedOnFeeTranslation: TextPropTypes['translation'] = useMemo( diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx index 6eecf646203..94c83705cb6 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes.tsx @@ -1,5 +1,5 @@ import { skipToken as reduxSkipToken } from '@reduxjs/toolkit/query' -import { fromAccountId } from '@shapeshiftoss/caip' +import { foxWifHatAssetId, fromAccountId } from '@shapeshiftoss/caip' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import type { GetTradeQuoteInput, @@ -36,6 +36,7 @@ import { swapperApi } from 'state/apis/swapper/swapperApi' import type { ApiQuote, TradeQuoteError } from 'state/apis/swapper/types' import { selectPortfolioAccountMetadataByAccountId, + selectPortfolioCryptoBalanceBaseUnitByFilter, selectUsdRateByAssetId, } from 'state/slices/selectors' import { @@ -213,6 +214,9 @@ export const useGetTradeQuotes = () => { const votingPower = useAppSelector(state => selectVotingPower(state, votingPowerParams)) const thorVotingPower = useAppSelector(state => selectVotingPower(state, thorVotingPowerParams)) + const foxWifHatHeld = useAppSelector(state => + selectPortfolioCryptoBalanceBaseUnitByFilter(state, { assetId: foxWifHatAssetId }), + ) const walletSupportsBuyAssetChain = useWalletSupportsChain(buyAsset.chainId, wallet) const isBuyAssetChainSupported = walletSupportsBuyAssetChain @@ -273,6 +277,7 @@ export const useGetTradeQuotes = () => { tradeAmountUsd, foxHeld: bnOrZero(votingPower), thorHeld: bnOrZero(thorVotingPower), + foxWifHatHeldCryptoBaseUnit: bnOrZero(foxWifHatHeld), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -321,6 +326,7 @@ export const useGetTradeQuotes = () => { sellAsset, sellAssetUsdRate, thorVotingPower, + foxWifHatHeld, userSlippageTolerancePercentageDecimal, votingPower, wallet, diff --git a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeRates.tsx b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeRates.tsx index 1855855ab40..345a8c52373 100644 --- a/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeRates.tsx +++ b/src/components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeRates.tsx @@ -1,5 +1,5 @@ import { skipToken } from '@reduxjs/toolkit/dist/query' -import { fromAccountId } from '@shapeshiftoss/caip' +import { foxWifHatAssetId, fromAccountId } from '@shapeshiftoss/caip' import { isLedger } from '@shapeshiftoss/hdwallet-ledger' import type { GetTradeRateInput, TradeRate } from '@shapeshiftoss/swapper' import { @@ -28,6 +28,7 @@ import { } from 'state/apis/snapshot/selectors' import { swapperApi } from 'state/apis/swapper/swapperApi' import type { ApiQuote, TradeQuoteError } from 'state/apis/swapper/types' +import { selectPortfolioCryptoBalanceBaseUnitByFilter } from 'state/slices/common-selectors' import { selectUsdRateByAssetId } from 'state/slices/marketDataSlice/selectors' import { selectPortfolioAccountMetadataByAccountId } from 'state/slices/portfolioSlice/selectors' import { @@ -135,6 +136,10 @@ export const useGetTradeRates = () => { const sellAccountId = useAppSelector(selectFirstHopSellAccountId) const buyAccountId = useAppSelector(selectLastHopBuyAccountId) + const foxWifHatHeld = useAppSelector(state => + selectPortfolioCryptoBalanceBaseUnitByFilter(state, { assetId: foxWifHatAssetId }), + ) + const userSlippageTolerancePercentageDecimal = useAppSelector(selectUserSlippagePercentageDecimal) const sellAccountMetadataFilter = useMemo( @@ -193,6 +198,7 @@ export const useGetTradeRates = () => { // referentially invalidate, while ensuring the *initial* connection of a wallet when quotes were gotten without one, doesn't invalidate anything sellAccountMetadata, votingPower, + foxWifHatHeld, thorVotingPower, receiveAccountMetadata, sellAccountId, @@ -226,6 +232,7 @@ export const useGetTradeRates = () => { tradeAmountUsd, foxHeld: bnOrZero(votingPower), thorHeld: bnOrZero(thorVotingPower), + foxWifHatHeldCryptoBaseUnit: bnOrZero(foxWifHatHeld), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) diff --git a/src/lib/fees/constant.ts b/src/lib/fees/constant.ts new file mode 100644 index 00000000000..dc21ecc2443 --- /dev/null +++ b/src/lib/fees/constant.ts @@ -0,0 +1,9 @@ +import { toBaseUnit } from 'lib/math' + +// This timestamp is the 24th of January 2025 +export const FOX_WIF_HAT_CAMPAIGN_STARTING_TIME_MS = 1737707777000 +// Supposed to end the 5th of May 2025 +export const FOX_WIF_HAT_CAMPAIGN_ENDING_TIME_MS = 1746396000000 + +// Assuming foxwifhat is 18 decimals +export const FOX_WIF_HAT_MINIMUM_AMOUNT_BASE_UNIT = toBaseUnit(10000, 18) diff --git a/src/lib/fees/model.test.ts b/src/lib/fees/model.test.ts index 7d913ec9ccd..28994dd4ca9 100644 --- a/src/lib/fees/model.test.ts +++ b/src/lib/fees/model.test.ts @@ -36,6 +36,8 @@ describe('calculateFees', () => { const { feeBps } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -49,6 +51,8 @@ describe('calculateFees', () => { const { feeBps } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -62,6 +66,8 @@ describe('calculateFees', () => { const { feeBps } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -75,6 +81,8 @@ describe('calculateFees', () => { const { feeBps } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -88,6 +96,8 @@ describe('calculateFees', () => { const { feeBps } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -101,6 +111,8 @@ describe('calculateFees', () => { const { feeBps, foxDiscountPercent } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -115,6 +127,8 @@ describe('calculateFees', () => { const { feeBps, foxDiscountPercent } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) @@ -131,6 +145,8 @@ describe('calculateFees', () => { const { feeBps, foxDiscountPercent } = calculateFees({ tradeAmountUsd, foxHeld, + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(0), feeModel: 'SWAPPER', isSnapshotApiQueriesRejected, }) diff --git a/src/lib/fees/model.ts b/src/lib/fees/model.ts index f7dd288a820..5d39d24aa1f 100644 --- a/src/lib/fees/model.ts +++ b/src/lib/fees/model.ts @@ -2,6 +2,11 @@ import BigNumber from 'bignumber.js' import { getConfig } from 'config' import { bn, bnOrZero } from 'lib/bignumber/bignumber' +import { + FOX_WIF_HAT_CAMPAIGN_ENDING_TIME_MS, + FOX_WIF_HAT_CAMPAIGN_STARTING_TIME_MS, + FOX_WIF_HAT_MINIMUM_AMOUNT_BASE_UNIT, +} from './constant' import { FEE_CURVE_PARAMETERS } from './parameters' import type { ParameterModel } from './parameters/types' @@ -11,7 +16,8 @@ export const THORSWAP_MAXIMUM_YEAR_TRESHOLD = 2025 type CalculateFeeBpsArgs = { tradeAmountUsd: BigNumber foxHeld: BigNumber - thorHeld?: BigNumber + thorHeld: BigNumber + foxWifHatHeldCryptoBaseUnit: BigNumber feeModel: ParameterModel isSnapshotApiQueriesRejected: boolean } @@ -43,6 +49,7 @@ export const calculateFees: CalculateFeeBps = ({ foxHeld, feeModel, thorHeld, + foxWifHatHeldCryptoBaseUnit, isSnapshotApiQueriesRejected, }) => { const { @@ -59,13 +66,39 @@ export const calculateFees: CalculateFeeBps = ({ const midpointUsd = bn(FEE_CURVE_MIDPOINT_USD) const feeCurveSteepness = bn(FEE_CURVE_STEEPNESS_K) const isThorFreeEnabled = getConfig().REACT_APP_FEATURE_THOR_FREE_FEES + const isFoxWifHatEnabled = getConfig().REACT_APP_FEATURE_FOX_PAGE_FOX_WIF_HAT_SECTION + + const isFoxWifHatCampaignActive = + new Date().getTime() >= FOX_WIF_HAT_CAMPAIGN_STARTING_TIME_MS && + new Date().getTime() <= FOX_WIF_HAT_CAMPAIGN_ENDING_TIME_MS && + isFoxWifHatEnabled + + const isFoxWifHatDiscountEligible = + isFoxWifHatCampaignActive && + foxWifHatHeldCryptoBaseUnit && + foxWifHatHeldCryptoBaseUnit?.gte(FOX_WIF_HAT_MINIMUM_AMOUNT_BASE_UNIT) + + const currentFoxWifHatDiscountPercent = (() => { + if (!isFoxWifHatCampaignActive) return bn(0) + if (!isFoxWifHatDiscountEligible) return bn(0) + + const currentTime = new Date().getTime() + const totalCampaignDuration = + FOX_WIF_HAT_CAMPAIGN_ENDING_TIME_MS - FOX_WIF_HAT_CAMPAIGN_STARTING_TIME_MS + const timeElapsed = currentTime - FOX_WIF_HAT_CAMPAIGN_STARTING_TIME_MS + const remainingPercentage = bn(100).times( + bn(1).minus(bn(timeElapsed).div(totalCampaignDuration)), + ) + + return BigNumber.maximum(BigNumber.minimum(remainingPercentage, bn(100)), bn(0)) + })() // trades below the fee threshold are free. const isFree = tradeAmountUsd.lt(noFeeThresholdUsd) const isThorFree = isThorFreeEnabled && - thorHeld?.isGreaterThanOrEqualTo(THORSWAP_UNIT_THRESHOLD) && + thorHeld.gte(THORSWAP_UNIT_THRESHOLD) && new Date().getUTCFullYear() < THORSWAP_MAXIMUM_YEAR_TRESHOLD // failure to fetch fox discount results in free trades. @@ -76,18 +109,22 @@ export const calculateFees: CalculateFeeBps = ({ if (isFree) return bn(100) // THOR holder before TIP014 are trade free until 2025 if (isThorFree) return bn(100) - // No discount if we cannot fetch FOX holdings - if (isFallbackFees) return bn(0) - return BigNumber.minimum( - bn(100), - bnOrZero(foxHeld).times(100).div(bn(FEE_CURVE_FOX_MAX_DISCOUNT_THRESHOLD)), - ) + const foxDiscountPercent = bnOrZero(foxHeld) + .times(100) + .div(bn(FEE_CURVE_FOX_MAX_DISCOUNT_THRESHOLD)) + + console.log({ foxDiscountPercent, currentFoxWifHatDiscountPercent }) + + // No discount if we cannot fetch FOX holdings and we are not eligible for the WIF HAT campaign + if (isFallbackFees && !isFoxWifHatDiscountEligible) return bn(0) + + return BigNumber.maximum(foxDiscountPercent, currentFoxWifHatDiscountPercent) })() // the fee bps before the fox discount is applied, as a floating point number const feeBpsBeforeDiscountFloat = - isFallbackFees && !isFree && !isThorFree + isFallbackFees && !isFree && !isThorFree && !isFoxWifHatDiscountEligible ? bn(FEE_CURVE_MAX_FEE_BPS) : minFeeBps.plus( maxFeeBps @@ -107,7 +144,7 @@ export const calculateFees: CalculateFeeBps = ({ ) const feeBpsFloat = - isFallbackFees && !isFree && !isThorFree + isFallbackFees && !isFree && !isThorFree && !isFoxWifHatDiscountEligible ? bn(FEE_CURVE_MAX_FEE_BPS) : BigNumber.maximum( feeBpsBeforeDiscountFloat.multipliedBy(bn(1).minus(foxBaseDiscountPercent.div(100))), @@ -121,6 +158,12 @@ export const calculateFees: CalculateFeeBps = ({ .div(feeBpsBeforeDiscountFloat) .times(100) + console.log({ + foxDiscountPercent: foxDiscountPercent.toFixed(2), + feeBpsFloat: feeBpsFloat.toFixed(2), + feeBpsBeforeDiscountFloat: feeBpsBeforeDiscountFloat.toFixed(2), + }) + const feeBps = feeBpsAfterDiscount const feeUsdBeforeDiscount = tradeAmountUsd.multipliedBy(feeBpsBeforeDiscount.div(bn(10000))) const feeUsdDiscount = feeUsdBeforeDiscount.multipliedBy(foxDiscountPercent.div(100)) diff --git a/src/pages/Fox/components/FoxWifHat.tsx b/src/pages/Fox/components/FoxWifHat.tsx index b3293e6c5a3..239742cf979 100644 --- a/src/pages/Fox/components/FoxWifHat.tsx +++ b/src/pages/Fox/components/FoxWifHat.tsx @@ -48,7 +48,6 @@ export const FoxWifHat = () => { accountId={accountId} amountCryptoBaseUnit={bnOrZero(claim.amount).toFixed()} assetId={foxWifHatAssetId} - discountPercentDecimal={0.72} // eslint-disable-next-line react-memo/require-usememo onClaim={() => handleClaimModalOpen(accountId)} /> diff --git a/src/pages/Fox/components/FoxWifHatClaimRow.tsx b/src/pages/Fox/components/FoxWifHatClaimRow.tsx index a3bdd1b7ac1..dd0338029f0 100644 --- a/src/pages/Fox/components/FoxWifHatClaimRow.tsx +++ b/src/pages/Fox/components/FoxWifHatClaimRow.tsx @@ -6,7 +6,8 @@ import { useMemo } from 'react' import { useTranslate } from 'react-polyglot' import { Amount } from 'components/Amount/Amount' import { WalletIcon } from 'components/Icons/WalletIcon' -import { useLocaleFormatter } from 'hooks/useLocaleFormatter/useLocaleFormatter' +import { bn } from 'lib/bignumber/bignumber' +import { calculateFees } from 'lib/fees/model' import { fromBaseUnit } from 'lib/math' import { middleEllipsis } from 'lib/utils' import { @@ -23,7 +24,6 @@ type FoxWifHatClaimRowProps = { accountId: string amountCryptoBaseUnit: string assetId: AssetId - discountPercentDecimal: number onClaim?: () => void } @@ -34,11 +34,12 @@ const columnAlignItems = { md: 'center' } const columnJustifyContent = { md: 'space-between' } const columnSpacing = { base: 4, md: 12, lg: 24, xl: 48 } +const DUMMY_TRADE_AMOUNT_OVER_TRESHOLD_USD = 1000000 + export const FoxWifHatClaimRow = ({ accountId, amountCryptoBaseUnit, assetId, - discountPercentDecimal, onClaim, }: FoxWifHatClaimRowProps) => { const textColor = useColorModeValue('gray.500', 'gray.400') @@ -48,9 +49,6 @@ export const FoxWifHatClaimRow = ({ selectAccountNumberByAccountId(state, { accountId }), ) const accountIdsByChainId = useAppSelector(selectAccountIdsByChainId) - const { - number: { toPercent }, - } = useLocaleFormatter() const getFoxWifHatMerkleTreeQuery = useFoxWifHatMerkleTreeQuery() const numberAccounts = useMemo(() => { @@ -70,6 +68,17 @@ export const FoxWifHatClaimRow = ({ const { data: isClaimed } = useFoxWifHatClaimedQueryQuery({ index: claim?.index }) + const discountPercent = useMemo(() => { + return calculateFees({ + tradeAmountUsd: bn(DUMMY_TRADE_AMOUNT_OVER_TRESHOLD_USD), + foxHeld: bn(0), + feeModel: 'SWAPPER', + thorHeld: bn(0), + foxWifHatHeldCryptoBaseUnit: bn(amountCryptoBaseUnit), + isSnapshotApiQueriesRejected: false, + }).foxDiscountPercent.toFixed(2) + }, [amountCryptoBaseUnit]) + return ( {translate('foxPage.foxWifHat.discountText', { - percent: toPercent(discountPercentDecimal), + percent: discountPercent, })} diff --git a/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx b/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx index 926c3f7c65f..5a91b761cf1 100644 --- a/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx +++ b/src/pages/ThorChainLP/components/AddLiquidity/AddLiquidityInput.tsx @@ -18,7 +18,13 @@ import { usePrevious, } from '@chakra-ui/react' import type { AccountId, AssetId, ChainId } from '@shapeshiftoss/caip' -import { fromAccountId, fromAssetId, thorchainAssetId, thorchainChainId } from '@shapeshiftoss/caip' +import { + foxWifHatAssetId, + fromAccountId, + fromAssetId, + thorchainAssetId, + thorchainChainId, +} from '@shapeshiftoss/caip' import { SwapperName } from '@shapeshiftoss/swapper' import { assetIdToPoolAssetId, @@ -202,6 +208,10 @@ export const AddLiquidityInput: React.FC = ({ const [virtualRuneDepositAmountFiatUserCurrency, setVirtualRuneDepositAmountFiatUserCurrency] = useState() + const foxWifHatHeld = useAppSelector(state => + selectPortfolioCryptoBalanceBaseUnitByFilter(state, { assetId: foxWifHatAssetId }), + ) + const [slippageDecimalPercentage, setSlippageDecimalPercentage] = useState() const isUnsafeQuote = useMemo( @@ -1014,6 +1024,9 @@ export const AddLiquidityInput: React.FC = ({ const { feeBps, feeUsd } = calculateFees({ tradeAmountUsd: bn(totalAmountUsd), foxHeld: bnOrZero(votingPower), + foxWifHatHeldCryptoBaseUnit: bn(foxWifHatHeld), + // @TODO: remove this when thor swap discount is removed + thorHeld: bn(0), feeModel: 'THORCHAIN_LP', isSnapshotApiQueriesRejected, }) @@ -1058,6 +1071,7 @@ export const AddLiquidityInput: React.FC = ({ userCurrencyToUsdRate, votingPower, isSnapshotApiQueriesRejected, + foxWifHatHeld, ]) const percentOptions = useMemo(() => [], []) diff --git a/src/state/apis/snapshot/selectors.ts b/src/state/apis/snapshot/selectors.ts index 1be612c23b7..26ae239097d 100644 --- a/src/state/apis/snapshot/selectors.ts +++ b/src/state/apis/snapshot/selectors.ts @@ -1,5 +1,5 @@ import { QueryStatus } from '@reduxjs/toolkit/dist/query' -import { ethChainId } from '@shapeshiftoss/caip' +import { ethChainId, foxWifHatAssetId } from '@shapeshiftoss/caip' import { bnOrZero } from '@shapeshiftoss/utils' import createCachedSelector from 're-reselect' import type { Selector } from 'reselect' @@ -10,6 +10,7 @@ import type { ParameterModel } from 'lib/fees/parameters/types' import { isSome } from 'lib/utils' import type { ReduxState } from 'state/reducer' import { selectFeeModelParamFromFilter } from 'state/selectors' +import { selectPortfolioAssetBalancesBaseUnit } from 'state/slices/common-selectors' import { selectAccountIdsByChainId } from 'state/slices/portfolioSlice/selectors' const selectSnapshotApiQueries = (state: ReduxState) => state.snapshotApi.queries @@ -64,11 +65,22 @@ export const selectCalculatedFees: Selector = selectVotingPower, selectThorVotingPower, selectIsSnapshotApiQueriesRejected, - (feeModel, inputAmountUsd, votingPower, thorVotingPower, isSnapshotApiQueriesRejected) => { + selectPortfolioAssetBalancesBaseUnit, + ( + feeModel, + inputAmountUsd, + votingPower, + thorVotingPower, + isSnapshotApiQueriesRejected, + assetBalances, + ) => { + const foxWifHatHeld = assetBalances[foxWifHatAssetId] + const fees: CalculateFeeBpsReturn = calculateFees({ tradeAmountUsd: bnOrZero(inputAmountUsd), foxHeld: bnOrZero(votingPower), thorHeld: bnOrZero(thorVotingPower), + foxWifHatHeldCryptoBaseUnit: bnOrZero(foxWifHatHeld), feeModel, isSnapshotApiQueriesRejected, })