diff --git a/src/components/CurrencyInputPanel/index.tsx b/src/components/CurrencyInputPanel/index.tsx index a26112a124..8d20e20021 100644 --- a/src/components/CurrencyInputPanel/index.tsx +++ b/src/components/CurrencyInputPanel/index.tsx @@ -1,10 +1,8 @@ import { Pair } from '@uniswap/v2-sdk' import { Currency, CurrencyAmount } from '@uniswap/sdk-core' -import React, { useState, useCallback, useMemo } from 'react' +import React, { useState, useCallback } from 'react' import styled from 'styled-components' import { darken } from 'polished' -import useUSDCPrice from '../../hooks/useUSDCPrice' -import { tryParseAmount } from '../../state/swap/hooks' import { useCurrencyBalance } from '../../state/wallet/hooks' import CurrencySearchModal from '../SearchModal/CurrencySearchModal' import CurrencyLogo from '../CurrencyLogo' @@ -159,7 +157,7 @@ interface CurrencyInputPanelProps { pair?: Pair | null hideInput?: boolean otherCurrency?: Currency | null - showFiatValue?: boolean + fiatValue?: CurrencyAmount id: string showCommonBases?: boolean customBalanceText?: string @@ -177,7 +175,7 @@ export default function CurrencyInputPanel({ id, showCommonBases, customBalanceText, - showFiatValue = false, + fiatValue, hideBalance = false, pair = null, // used for double token logo hideInput = false, @@ -195,14 +193,6 @@ export default function CurrencyInputPanel({ setModalOpen(false) }, [setModalOpen]) - const price = useUSDCPrice(showFiatValue ? currency ?? undefined : undefined) - - const fiatValueOfTypedAmount: CurrencyAmount | null = useMemo(() => { - if (!price) return null - const amount = tryParseAmount(value, currency ?? undefined) - return amount ? price.quote(amount) : null - }, [currency, price, value]) - return ( {locked && ( @@ -295,9 +285,8 @@ export default function CurrencyInputPanel({ )} - - {fiatValueOfTypedAmount ? '~' : ''}$ - {fiatValueOfTypedAmount ? Number(fiatValueOfTypedAmount?.toSignificant(6)).toLocaleString('en') : '-'} + + {fiatValue ? '~' : ''}${fiatValue ? Number(fiatValue?.toSignificant(6)).toLocaleString('en') : '-'} diff --git a/src/components/swap/SwapRoute.tsx b/src/components/swap/SwapRoute.tsx index 1e38d4cb9b..c32873cd7b 100644 --- a/src/components/swap/SwapRoute.tsx +++ b/src/components/swap/SwapRoute.tsx @@ -1,17 +1,31 @@ +import { Percent } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' -import { Trade as V3Trade } from '@uniswap/v3-sdk' +import { Trade as V3Trade, FeeAmount } from '@uniswap/v3-sdk' import React, { Fragment, memo, useContext } from 'react' -import { ChevronRight } from 'react-feather' +import { ChevronLeft, ChevronRight } from 'react-feather' import { Flex } from 'rebass' import { ThemeContext } from 'styled-components' import { TYPE } from '../../theme' import { unwrappedToken } from 'utils/wrappedCurrency' +function LabeledArrow({ fee }: { fee: FeeAmount }) { + const theme = useContext(ThemeContext) + + // todo: improve the rendering of this labeled arrow + return ( + <> + + {new Percent(fee, 1_000_000).toSignificant()}% + + + ) +} + export default memo(function SwapRoute({ trade }: { trade: V2Trade | V3Trade }) { const tokenPath = trade instanceof V2Trade ? trade.route.path : trade.route.tokenPath const theme = useContext(ThemeContext) return ( - + {tokenPath.map((token, i, path) => { const isLastItem: boolean = i === path.length - 1 const currency = unwrappedToken(token) @@ -22,7 +36,11 @@ export default memo(function SwapRoute({ trade }: { trade: V2Trade | V3Trade }) {currency.symbol} - {isLastItem ? null : } + {isLastItem ? null : trade instanceof V2Trade ? ( + + ) : ( + + )} ) })} diff --git a/src/hooks/useUSDCPrice.ts b/src/hooks/useUSDCPrice.ts index ef9068e0de..ea86688c85 100644 --- a/src/hooks/useUSDCPrice.ts +++ b/src/hooks/useUSDCPrice.ts @@ -1,4 +1,4 @@ -import { ChainId, Currency, currencyEquals, Price, Token, WETH9 } from '@uniswap/sdk-core' +import { ChainId, Currency, CurrencyAmount, currencyEquals, Price, Token, WETH9 } from '@uniswap/sdk-core' import { JSBI } from '@uniswap/v2-sdk' import { useMemo } from 'react' import { USDC } from '../constants' @@ -75,3 +75,12 @@ export default function useUSDCPrice(currency?: Currency): Price | undefined { return undefined }, [chainId, currency, ethPair, ethPairState, usdcEthPair, usdcEthPairState, usdcPair, usdcPairState, weth, wrapped]) } + +export function useUSDCValue(currencyAmount: CurrencyAmount | undefined | null) { + const price = useUSDCPrice(currencyAmount?.currency) + + return useMemo(() => { + if (!price || !currencyAmount) return null + return price.quote(currencyAmount) + }, [currencyAmount, price]) +} diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index 9ee3430648..dfe6c7478c 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -34,6 +34,7 @@ import { useERC20PermitFromTrade, UseERC20PermitState } from '../../hooks/useERC import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported' import { useSwapCallback } from '../../hooks/useSwapCallback' import useToggledVersion, { Version } from '../../hooks/useToggledVersion' +import { useUSDCValue } from '../../hooks/useUSDCPrice' import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback' import { useWalletModalToggle } from '../../state/application/hooks' import { Field } from '../../state/swap/actions' @@ -132,6 +133,9 @@ export default function Swap({ history }: RouteComponentProps) { [allowedSlippage, independentField, parsedAmount, showWrap, trade] ) + const fiatValueInput = useUSDCValue(parsedAmounts[Field.INPUT]) + const fiatValueOutput = useUSDCValue(parsedAmounts[Field.OUTPUT]) + const { onSwitchTokens, onCurrencySelection, onUserInput, onChangeRecipient } = useSwapActionHandlers() const isValid = !swapInputError const dependentField: Field = independentField === Field.INPUT ? Field.OUTPUT : Field.INPUT @@ -216,15 +220,15 @@ export default function Swap({ history }: RouteComponentProps) { signatureData ) - const { priceImpactWithoutFee } = computeTradePriceBreakdown(trade) + const { priceImpactWithoutFee: priceImpact } = computeTradePriceBreakdown(trade) const [singleHopOnly] = useUserSingleHopOnly() const handleSwap = useCallback(() => { - if (priceImpactWithoutFee && !confirmPriceImpactWithoutFee(priceImpactWithoutFee)) { + if (!swapCallback) { return } - if (!swapCallback) { + if (priceImpact && !confirmPriceImpactWithoutFee(priceImpact)) { return } setSwapState({ attemptingTxn: true, tradeToConfirm, showConfirm, swapErrorMessage: undefined, txHash: undefined }) @@ -258,7 +262,7 @@ export default function Swap({ history }: RouteComponentProps) { }) }) }, [ - priceImpactWithoutFee, + priceImpact, swapCallback, tradeToConfirm, showConfirm, @@ -273,7 +277,7 @@ export default function Swap({ history }: RouteComponentProps) { const [showInverted, setShowInverted] = useState(false) // warnings on price impact - const priceImpactSeverity = warningSeverity(priceImpactWithoutFee) + const priceImpactSeverity = warningSeverity(priceImpact) // show approve flow when: no error on inputs, not approved or pending, or approved in current session // never show if price impact is above threshold in non expert mode @@ -350,7 +354,7 @@ export default function Swap({ history }: RouteComponentProps) { currency={currencies[Field.INPUT]} onUserInput={handleTypeInput} onMax={handleMaxInput} - showFiatValue + fiatValue={fiatValueInput ?? undefined} onCurrencySelect={handleInputSelect} otherCurrency={currencies[Field.OUTPUT]} showCommonBases={true} @@ -372,7 +376,7 @@ export default function Swap({ history }: RouteComponentProps) { label={independentField === Field.INPUT && !showWrap ? 'To (at least)' : 'To'} showMaxButton={false} hideBalance={false} - showFiatValue + fiatValue={fiatValueOutput ?? undefined} currency={currencies[Field.OUTPUT]} onCurrencySelect={handleOutputSelect} otherCurrency={currencies[Field.INPUT]} diff --git a/src/state/lists/hooks.ts b/src/state/lists/hooks.ts index 94c93cbdd1..5d5a6a02a2 100644 --- a/src/state/lists/hooks.ts +++ b/src/state/lists/hooks.ts @@ -108,10 +108,8 @@ function combineMaps(map1: TokenAddressMap, map2: TokenAddressMap): TokenAddress // merge tokens contained within lists from urls function useCombinedTokenMapFromUrls(urls: string[] | undefined): TokenAddressMap { const lists = useAllLists() - return useMemo(() => { if (!urls) return EMPTY_LIST - return ( urls .slice()