From 184d515e162401ac000da6fde3bb4a3dd5ba3239 Mon Sep 17 00:00:00 2001 From: Jack Short Date: Mon, 11 Sep 2023 14:42:48 -0400 Subject: [PATCH] chore: formatter to use local currency price (#7273) * chore: updating formatCurrencyAmount to handle multiple langs and currencies * missed advanced swap details * missed confirmed swap modal * removing updating visual effects * removing it from parseLocale * chore: displaying local currency and language formatting * making effects visible * moving to hook * useFormatCurrencyAmount * moving it to useformatter hook * exporting formatting locales * missed one parsed remote * initial passover of using conversion rate in formatting function * moving hook to bottom of file * moving to bottom * moving to bottom * fallback to usd * refactors * better fallback protection * moving to function * fixing typecheck --- src/components/swap/SwapModalHeader.tsx | 6 +- src/components/swap/TradePrice.tsx | 6 +- src/hooks/useLocalCurrencyPrice.ts | 27 --------- src/nft/hooks/useUsdPrice.ts | 4 +- src/pages/Swap/index.tsx | 12 ++-- src/utils/formatNumbers.ts | 73 +++++++++++++++++++++---- 6 files changed, 76 insertions(+), 52 deletions(-) delete mode 100644 src/hooks/useLocalCurrencyPrice.ts diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/swap/SwapModalHeader.tsx index a37b22588f..df4bef453d 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/swap/SwapModalHeader.tsx @@ -1,7 +1,7 @@ import { Trans } from '@lingui/macro' import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import Column, { AutoColumn } from 'components/Column' -import { useLocalCurrencyPrice } from 'hooks/useLocalCurrencyPrice' +import { useUSDPrice } from 'hooks/useUSDPrice' import { InterfaceTrade } from 'state/routing/types' import { Field } from 'state/swap/actions' import styled from 'styled-components' @@ -26,8 +26,8 @@ export default function SwapModalHeader({ inputCurrency?: Currency allowedSlippage: Percent }) { - const fiatValueInput = useLocalCurrencyPrice(trade.inputAmount) - const fiatValueOutput = useLocalCurrencyPrice(trade.postTaxOutputAmount) + const fiatValueInput = useUSDPrice(trade.inputAmount) + const fiatValueOutput = useUSDPrice(trade.postTaxOutputAmount) return ( diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx index 1a3afb603f..7ff0b0065d 100644 --- a/src/components/swap/TradePrice.tsx +++ b/src/components/swap/TradePrice.tsx @@ -1,6 +1,6 @@ import { Trans } from '@lingui/macro' import { Currency, Price } from '@uniswap/sdk-core' -import { useLocalCurrencyPrice } from 'hooks/useLocalCurrencyPrice' +import { useUSDPrice } from 'hooks/useUSDPrice' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { useCallback, useMemo, useState } from 'react' import styled from 'styled-components' @@ -33,9 +33,7 @@ export default function TradePrice({ price }: TradePriceProps) { const [showInverted, setShowInverted] = useState(false) const { baseCurrency, quoteCurrency } = price - const { data: usdPrice } = useLocalCurrencyPrice( - tryParseCurrencyAmount('1', showInverted ? baseCurrency : quoteCurrency) - ) + const { data: usdPrice } = useUSDPrice(tryParseCurrencyAmount('1', showInverted ? baseCurrency : quoteCurrency)) const formattedPrice = useMemo(() => { try { diff --git a/src/hooks/useLocalCurrencyPrice.ts b/src/hooks/useLocalCurrencyPrice.ts deleted file mode 100644 index 8cd6cf4f01..0000000000 --- a/src/hooks/useLocalCurrencyPrice.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Currency } from 'graphql/data/__generated__/types-and-hooks' -import { useLocalCurrencyConversionRate } from 'graphql/data/ConversionRate' - -import { useActiveLocalCurrency } from './useActiveLocalCurrency' -import { useUSDPrice } from './useUSDPrice' - -type useUSDPriceParameters = Parameters - -export function useLocalCurrencyPrice(...useUSDPriceParameters: useUSDPriceParameters) { - const activeLocalCurrency = useActiveLocalCurrency() - const activeLocalCurrencyIsUSD = activeLocalCurrency === Currency.Usd - - const { data: usdPrice, isLoading: isUSDPriceLoading } = useUSDPrice(...useUSDPriceParameters) - const { data: localCurrencyConversionRate, isLoading: isLocalCurrencyConversionRateLoading } = - useLocalCurrencyConversionRate(activeLocalCurrency, activeLocalCurrencyIsUSD) - - if (activeLocalCurrencyIsUSD) { - return { data: usdPrice, isLoading: isUSDPriceLoading } - } - - const isLoading = isUSDPriceLoading || isLocalCurrencyConversionRateLoading - if (!usdPrice || !localCurrencyConversionRate) { - return { data: undefined, isLoading } - } - - return { data: usdPrice * localCurrencyConversionRate, isLoading: false } -} diff --git a/src/nft/hooks/useUsdPrice.ts b/src/nft/hooks/useUsdPrice.ts index af947e9d27..9cdbf04288 100644 --- a/src/nft/hooks/useUsdPrice.ts +++ b/src/nft/hooks/useUsdPrice.ts @@ -1,6 +1,6 @@ import { formatEther } from '@ethersproject/units' import { ChainId } from '@uniswap/sdk-core' -import { useLocalCurrencyPrice } from 'hooks/useLocalCurrencyPrice' +import { useUSDPrice } from 'hooks/useUSDPrice' import useNativeCurrency from 'lib/hooks/useNativeCurrency' import tryParseCurrencyAmount from 'lib/utils/tryParseCurrencyAmount' import { GenieAsset } from 'nft/types' @@ -8,7 +8,7 @@ import { GenieAsset } from 'nft/types' export const useNativeUsdPrice = (chainId: number = ChainId.MAINNET): number => { const nativeCurrency = useNativeCurrency(chainId) const parsedAmount = tryParseCurrencyAmount('1', nativeCurrency) - const usdcValue = useLocalCurrencyPrice(parsedAmount)?.data ?? 0 + const usdcValue = useUSDPrice(parsedAmount)?.data ?? 0 return usdcValue } diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index a5dbf4eb7e..d0716b211e 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -35,12 +35,12 @@ import { getSwapCurrencyId, TOKEN_SHORTHANDS } from 'constants/tokens' import { useUniswapXDefaultEnabled } from 'featureFlags/flags/uniswapXDefault' import { useCurrency, useDefaultActiveTokens } from 'hooks/Tokens' import { useIsSwapUnsupported } from 'hooks/useIsSwapUnsupported' -import { useLocalCurrencyPrice } from 'hooks/useLocalCurrencyPrice' import { useMaxAmountIn } from 'hooks/useMaxAmountIn' import usePermit2Allowance, { AllowanceState } from 'hooks/usePermit2Allowance' import usePrevious from 'hooks/usePrevious' import { SwapResult, useSwapCallback } from 'hooks/useSwapCallback' import { useSwitchChain } from 'hooks/useSwitchChain' +import { useUSDPrice } from 'hooks/useUSDPrice' import useWrapCallback, { WrapErrorText, WrapType } from 'hooks/useWrapCallback' import JSBI from 'jsbi' import { formatSwapQuoteReceivedEventProperties } from 'lib/utils/analytics' @@ -314,8 +314,8 @@ export function Swap({ [independentField, parsedAmount, showWrap, trade] ) - const fiatValueInput = useLocalCurrencyPrice(parsedAmounts[Field.INPUT], currencies[Field.INPUT] ?? undefined) - const fiatValueOutput = useLocalCurrencyPrice(parsedAmounts[Field.OUTPUT], currencies[Field.OUTPUT] ?? undefined) + const fiatValueInput = useUSDPrice(parsedAmounts[Field.INPUT], currencies[Field.INPUT] ?? undefined) + const fiatValueOutput = useUSDPrice(parsedAmounts[Field.OUTPUT], currencies[Field.OUTPUT] ?? undefined) const showFiatValueInput = Boolean(parsedAmounts[Field.INPUT]) const showFiatValueOutput = Boolean(parsedAmounts[Field.OUTPUT]) @@ -328,9 +328,9 @@ export function Swap({ [trade, tradeState] ) - const fiatValueTradeInput = useLocalCurrencyPrice(trade?.inputAmount) - const fiatValueTradeOutput = useLocalCurrencyPrice(trade?.postTaxOutputAmount) - const preTaxFiatValueTradeOutput = useLocalCurrencyPrice(trade?.outputAmount) + const fiatValueTradeInput = useUSDPrice(trade?.inputAmount) + const fiatValueTradeOutput = useUSDPrice(trade?.postTaxOutputAmount) + const preTaxFiatValueTradeOutput = useUSDPrice(trade?.outputAmount) const [stablecoinPriceImpact, preTaxStablecoinPriceImpact] = useMemo( () => routeIsSyncing || !isClassicTrade(trade) diff --git a/src/utils/formatNumbers.ts b/src/utils/formatNumbers.ts index a3d0ededb4..6d490ceb93 100644 --- a/src/utils/formatNumbers.ts +++ b/src/utils/formatNumbers.ts @@ -6,8 +6,11 @@ import { } from 'constants/localCurrencies' import { DEFAULT_LOCALE, SupportedLocale } from 'constants/locales' import { useCurrencyConversionFlagEnabled } from 'featureFlags/flags/currencyConversion' +import { Currency as GqlCurrency } from 'graphql/data/__generated__/types-and-hooks' +import { useLocalCurrencyConversionRate } from 'graphql/data/ConversionRate' import { useActiveLocalCurrency } from 'hooks/useActiveLocalCurrency' import { useActiveLocale } from 'hooks/useActiveLocale' +import usePrevious from 'hooks/usePrevious' import { useCallback, useMemo } from 'react' type Nullish = T | null | undefined @@ -362,12 +365,15 @@ const TYPE_TO_FORMATTER_RULES = { [NumberType.NFTCollectionStats]: ntfCollectionStatsFormatter, } -function getFormatterRule(input: number, type: NumberType): FormatterRule { +function getFormatterRule(input: number, type: NumberType, conversionRate?: number): FormatterRule { const rules = TYPE_TO_FORMATTER_RULES[type] for (const rule of rules) { + const shouldConvertInput = rule.formatterOptions.currency && conversionRate + const convertedInput = shouldConvertInput ? input * conversionRate : input + if ( - (rule.exact !== undefined && input === rule.exact) || - (rule.upperBound !== undefined && input < rule.upperBound) + (rule.exact !== undefined && convertedInput === rule.exact) || + (rule.upperBound !== undefined && convertedInput < rule.upperBound) ) { return rule } @@ -382,6 +388,7 @@ interface FormatNumberOptions { placeholder?: string locale?: SupportedLocale localCurrency?: SupportedLocalCurrency + conversionRate?: number } export function formatNumber({ @@ -390,14 +397,16 @@ export function formatNumber({ placeholder = '-', locale = DEFAULT_LOCALE, localCurrency = DEFAULT_LOCAL_CURRENCY, + conversionRate, }: FormatNumberOptions): string { if (input === null || input === undefined) { return placeholder } - const { hardCodedInput, formatterOptions } = getFormatterRule(input, type) + const { hardCodedInput, formatterOptions } = getFormatterRule(input, type, conversionRate) if (formatterOptions.currency) { + input = conversionRate ? input * conversionRate : input formatterOptions.currency = localCurrency formatterOptions.currencyDisplay = LOCAL_CURRENCY_SYMBOL_DISPLAY_TYPE[localCurrency] } @@ -421,6 +430,7 @@ interface FormatCurrencyAmountOptions { placeholder?: string locale?: SupportedLocale localCurrency?: SupportedLocalCurrency + conversionRate?: number } export function formatCurrencyAmount({ @@ -429,6 +439,7 @@ export function formatCurrencyAmount({ placeholder, locale = DEFAULT_LOCALE, localCurrency = DEFAULT_LOCAL_CURRENCY, + conversionRate, }: FormatCurrencyAmountOptions): string { return formatNumber({ input: amount ? parseFloat(amount.toSignificant()) : undefined, @@ -436,6 +447,7 @@ export function formatCurrencyAmount({ placeholder, locale, localCurrency, + conversionRate, }) } @@ -567,20 +579,61 @@ export function useFormatterLocales(): { } } +function handleFallbackCurrency( + selectedCurrency: SupportedLocalCurrency, + previousSelectedCurrency: SupportedLocalCurrency | undefined, + shouldFallbackToUSD: boolean, + shouldFallbackToPrevious: boolean +) { + if (shouldFallbackToUSD) return DEFAULT_LOCAL_CURRENCY + if (shouldFallbackToPrevious) return previousSelectedCurrency + return selectedCurrency +} + // Constructs an object that injects the correct locale and local currency into each of the above formatter functions. export function useFormatter() { + const activeLocalCurrency = useActiveLocalCurrency() const { formatterLocale, formatterLocalCurrency } = useFormatterLocales() + const activeLocalCurrencyIsUSD = activeLocalCurrency === GqlCurrency.Usd + const { data: localCurrencyConversionRate, isLoading: localCurrencyConversionRateIsLoading } = + useLocalCurrencyConversionRate(activeLocalCurrency, activeLocalCurrencyIsUSD) + + const previousSelectedCurrency = usePrevious(activeLocalCurrency) + const previousConversionRate = usePrevious(localCurrencyConversionRate) + + const shouldFallbackToPrevious = !localCurrencyConversionRate && localCurrencyConversionRateIsLoading + const shouldFallbackToUSD = !localCurrencyConversionRate && !localCurrencyConversionRateIsLoading + const currencyToFormatWith = handleFallbackCurrency( + formatterLocalCurrency, + previousSelectedCurrency, + shouldFallbackToUSD, + shouldFallbackToPrevious + ) + const localCurrencyConversionRateToFormatWith = shouldFallbackToPrevious + ? previousConversionRate + : localCurrencyConversionRate + const formatNumberWithLocales = useCallback( - (options: Omit) => - formatNumber({ ...options, locale: formatterLocale, localCurrency: formatterLocalCurrency }), - [formatterLocalCurrency, formatterLocale] + (options: Omit) => + formatNumber({ + ...options, + locale: formatterLocale, + localCurrency: currencyToFormatWith, + conversionRate: localCurrencyConversionRateToFormatWith, + }), + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] ) const formatCurrencyAmountWithLocales = useCallback( - (options: Omit) => - formatCurrencyAmount({ ...options, locale: formatterLocale, localCurrency: formatterLocalCurrency }), - [formatterLocalCurrency, formatterLocale] + (options: Omit) => + formatCurrencyAmount({ + ...options, + locale: formatterLocale, + localCurrency: currencyToFormatWith, + conversionRate: localCurrencyConversionRateToFormatWith, + }), + [currencyToFormatWith, formatterLocale, localCurrencyConversionRateToFormatWith] ) return useMemo(