diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx index b438d6a450..dc6ccc6431 100644 --- a/src/components/Settings/index.tsx +++ b/src/components/Settings/index.tsx @@ -246,7 +246,7 @@ export default function SettingsTab() { {/* WIP */} toggleDarkMode()}> - {darkMode ? : } + {darkMode ? : } diff --git a/src/components/swap/BetterTradeLink.tsx b/src/components/swap/BetterTradeLink.tsx index c0840085be..06265f94e3 100644 --- a/src/components/swap/BetterTradeLink.tsx +++ b/src/components/swap/BetterTradeLink.tsx @@ -14,7 +14,7 @@ function VersionLinkContainer({ children }: { children: React.ReactNode }) { const theme = useContext(ThemeContext) return ( - + {children} diff --git a/src/components/swap/ConfirmSwapModal.tsx b/src/components/swap/ConfirmSwapModal.tsx index 93e55b81a0..2c0cc3f1c6 100644 --- a/src/components/swap/ConfirmSwapModal.tsx +++ b/src/components/swap/ConfirmSwapModal.tsx @@ -1,4 +1,4 @@ -import { currencyEquals } from '@uniswap/sdk-core' +import { currencyEquals, Percent } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' import React, { useCallback, useMemo } from 'react' @@ -86,10 +86,12 @@ export default function ConfirmSwapModal({ ) : null }, [allowedSlippage, onConfirm, showAcceptChanges, swapErrorMessage, trade]) + const slippageTolerancePercent = new Percent(allowedSlippage, 10_000) + // text to show while loading - const pendingText = `Swapping ${trade?.inputAmount?.toSignificant(6)} ${ + const pendingText = `Swapping ${trade?.maximumAmountIn(slippageTolerancePercent)?.toSignificant(6)} ${ trade?.inputAmount?.currency?.symbol - } for ${trade?.outputAmount?.toSignificant(6)} ${trade?.outputAmount?.currency?.symbol}` + } for ${trade?.minimumAmountOut(slippageTolerancePercent)?.toSignificant(6)} ${trade?.outputAmount?.currency?.symbol}` const confirmationContent = useCallback( () => diff --git a/src/components/swap/SwapModalFooter.tsx b/src/components/swap/SwapModalFooter.tsx index 774b09b9a1..3732d3a726 100644 --- a/src/components/swap/SwapModalFooter.tsx +++ b/src/components/swap/SwapModalFooter.tsx @@ -1,23 +1,14 @@ import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import { TradeType } from '@uniswap/sdk-core' +import { Percent } from '@uniswap/sdk-core' import React, { useContext, useMemo, useState } from 'react' import { Repeat } from 'react-feather' import { Text } from 'rebass' import { ThemeContext } from 'styled-components' -import { Field } from '../../state/swap/actions' -import { TYPE } from '../../theme' -import { - computeSlippageAdjustedAmounts, - computeTradePriceBreakdown, - formatExecutionPrice, - warningSeverity, -} from '../../utils/prices' +import { computeTradePriceBreakdown, formatExecutionPrice, warningSeverity } from '../../utils/prices' import { ButtonError } from '../Button' import { AutoColumn } from '../Column' -import QuestionHelper from '../QuestionHelper' -import { AutoRow, RowBetween, RowFixed } from '../Row' -import FormattedPriceImpact from './FormattedPriceImpact' +import { AutoRow, RowBetween } from '../Row' import { StyledBalanceMaxMini, SwapCallbackError } from './styleds' export default function SwapModalFooter({ @@ -35,11 +26,8 @@ export default function SwapModalFooter({ }) { const [showInverted, setShowInverted] = useState(false) const theme = useContext(ThemeContext) - const slippageAdjustedAmounts = useMemo(() => computeSlippageAdjustedAmounts(trade, allowedSlippage), [ - allowedSlippage, - trade, - ]) - const { priceImpactWithoutFee, realizedLPFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade]) + const slippageTolerancePercent = new Percent(allowedSlippage, 10_000) + const { priceImpactWithoutFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade]) const severity = warningSeverity(priceImpactWithoutFee) return ( @@ -61,53 +49,12 @@ export default function SwapModalFooter({ paddingLeft: '10px', }} > - {formatExecutionPrice(trade, showInverted)} + {formatExecutionPrice(trade, showInverted, slippageTolerancePercent)} setShowInverted(!showInverted)}> - - - - - {trade.tradeType === TradeType.EXACT_INPUT ? 'Minimum received' : 'Maximum sold'} - - - - - - {trade.tradeType === TradeType.EXACT_INPUT - ? slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(4) ?? '-' - : slippageAdjustedAmounts[Field.INPUT]?.toSignificant(4) ?? '-'} - - - {trade.tradeType === TradeType.EXACT_INPUT - ? trade.outputAmount.currency.symbol - : trade.inputAmount.currency.symbol} - - - - - - - Price Impact - - - - - - - - - Liquidity Provider Fee - - - - - {realizedLPFee ? realizedLPFee?.toSignificant(6) + ' ' + trade.inputAmount.currency.symbol : '-'} - - diff --git a/src/components/swap/SwapModalHeader.tsx b/src/components/swap/SwapModalHeader.tsx index 6f9c2c11bc..616d641648 100644 --- a/src/components/swap/SwapModalHeader.tsx +++ b/src/components/swap/SwapModalHeader.tsx @@ -1,15 +1,13 @@ -import { TradeType } from '@uniswap/sdk-core' +import { Percent, TradeType } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' -import React, { useContext, useMemo } from 'react' +import React, { useContext } from 'react' import { ArrowDown, AlertTriangle } from 'react-feather' import { Text } from 'rebass' import { ThemeContext } from 'styled-components' -import { Field } from '../../state/swap/actions' import { TYPE } from '../../theme' import { ButtonPrimary } from '../Button' import { isAddress, shortenAddress } from '../../utils' -import { computeSlippageAdjustedAmounts, computeTradePriceBreakdown, warningSeverity } from '../../utils/prices' import { AutoColumn } from '../Column' import CurrencyLogo from '../CurrencyLogo' import { RowBetween, RowFixed } from '../Row' @@ -28,12 +26,9 @@ export default function SwapModalHeader({ showAcceptChanges: boolean onAcceptChanges: () => void }) { - const slippageAdjustedAmounts = useMemo(() => computeSlippageAdjustedAmounts(trade, allowedSlippage), [ - trade, - allowedSlippage, - ]) - const { priceImpactWithoutFee } = useMemo(() => computeTradePriceBreakdown(trade), [trade]) - const priceImpactSeverity = warningSeverity(priceImpactWithoutFee) + const slippageTolerancePercent = new Percent(allowedSlippage, 10_000) + const maximumAmountIn = trade.maximumAmountIn(slippageTolerancePercent) + const minimumAmountOut = trade.minimumAmountOut(slippageTolerancePercent) const theme = useContext(ThemeContext) @@ -47,7 +42,7 @@ export default function SwapModalHeader({ fontWeight={500} color={showAcceptChanges && trade.tradeType === TradeType.EXACT_OUTPUT ? theme.primary1 : ''} > - {trade.inputAmount.toSignificant(6)} + {maximumAmountIn.toSignificant(6)} @@ -62,18 +57,8 @@ export default function SwapModalHeader({ - 2 - ? theme.red1 - : showAcceptChanges && trade.tradeType === TradeType.EXACT_INPUT - ? theme.primary1 - : '' - } - > - {trade.outputAmount.toSignificant(6)} + + {minimumAmountOut.toSignificant(6)} @@ -103,7 +88,7 @@ export default function SwapModalHeader({ {`Output is estimated. You will receive at least `} - {slippageAdjustedAmounts[Field.OUTPUT]?.toSignificant(6)} {trade.outputAmount.currency.symbol} + {minimumAmountOut.toSignificant(6)} {trade.outputAmount.currency.symbol} {' or the transaction will revert.'} @@ -111,7 +96,7 @@ export default function SwapModalHeader({ {`Input is estimated. You will sell at most `} - {slippageAdjustedAmounts[Field.INPUT]?.toSignificant(6)} {trade.inputAmount.currency.symbol} + {maximumAmountIn.toSignificant(6)} {trade.inputAmount.currency.symbol} {' or the transaction will revert.'} diff --git a/src/components/swap/TradePrice.tsx b/src/components/swap/TradePrice.tsx index c2f275cf39..77016112b1 100644 --- a/src/components/swap/TradePrice.tsx +++ b/src/components/swap/TradePrice.tsx @@ -8,7 +8,7 @@ import { StyledBalanceMaxMini } from './styleds' import Switch from '../../assets/svg/switch.svg' interface TradePriceProps { - price?: Price + price: Price showInverted: boolean setShowInverted: (showInverted: boolean) => void } @@ -23,10 +23,10 @@ const StyledPriceContainer = styled.div` export default function TradePrice({ price, showInverted, setShowInverted }: TradePriceProps) { const theme = useContext(ThemeContext) - const formattedPrice = showInverted ? price?.toSignificant(6) : price?.invert()?.toSignificant(6) + const formattedPrice = showInverted ? price.toSignificant(6) : price.invert()?.toSignificant(6) - const label = showInverted ? `${price?.quoteCurrency?.symbol}` : `${price?.baseCurrency?.symbol} ` - const labelInverted = showInverted ? `${price?.baseCurrency?.symbol} ` : `${price?.quoteCurrency?.symbol}` + const label = showInverted ? `${price.quoteCurrency?.symbol}` : `${price.baseCurrency?.symbol} ` + const labelInverted = showInverted ? `${price.baseCurrency?.symbol} ` : `${price.quoteCurrency?.symbol}` const flipPrice = useCallback(() => setShowInverted(!showInverted), [setShowInverted, showInverted]) return ( diff --git a/src/hooks/useSwapCallback.ts b/src/hooks/useSwapCallback.ts index b0cad08e1d..e1f14534bf 100644 --- a/src/hooks/useSwapCallback.ts +++ b/src/hooks/useSwapCallback.ts @@ -233,8 +233,7 @@ export function useSwapCallback( : recipientAddressOrName }` - const withVersion = - tradeVersion === Version.v2 ? withRecipient : `${withRecipient} on ${(tradeVersion as any).toUpperCase()}` + const withVersion = tradeVersion === Version.v3 ? withRecipient : `${withRecipient} on ${tradeVersion}` addTransaction(response, { summary: withVersion, diff --git a/src/pages/Swap/index.tsx b/src/pages/Swap/index.tsx index da9e2e8c94..501baf6dc3 100644 --- a/src/pages/Swap/index.tsx +++ b/src/pages/Swap/index.tsx @@ -1,36 +1,38 @@ -import JSBI from 'jsbi' import { CurrencyAmount, Percent, Token } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' +import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter' +import JSBI from 'jsbi' import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { ArrowDown, Repeat, Unlock } from 'react-feather' import ReactGA from 'react-ga' +import { RouteComponentProps } from 'react-router-dom' import { Text } from 'rebass' import { ThemeContext } from 'styled-components' import AddressInputPanel from '../../components/AddressInputPanel' -import { ButtonError, ButtonLight, ButtonPrimary, ButtonConfirmed } from '../../components/Button' +import { ButtonConfirmed, ButtonError, ButtonLight, ButtonPrimary } from '../../components/Button' import { GreyCard } from '../../components/Card' import { AutoColumn } from '../../components/Column' -import BetterTradeLink from '../../components/swap/BetterTradeLink' -import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal' import CurrencyInputPanel from '../../components/CurrencyInputPanel' +import CurrencyLogo from '../../components/CurrencyLogo' +import Loader from '../../components/Loader' +import ProgressSteps from '../../components/ProgressSteps' import { AutoRow } from '../../components/Row' +import BetterTradeLink from '../../components/swap/BetterTradeLink' import confirmPriceImpactWithoutFee from '../../components/swap/confirmPriceImpactWithoutFee' +import ConfirmSwapModal from '../../components/swap/ConfirmSwapModal' import { ArrowWrapper, BottomGrouping, SwapCallbackError, Wrapper } from '../../components/swap/styleds' +import SwapHeader from '../../components/swap/SwapHeader' import TradePrice from '../../components/swap/TradePrice' import TokenWarningModal from '../../components/TokenWarningModal' -import ProgressSteps from '../../components/ProgressSteps' -import SwapHeader from '../../components/swap/SwapHeader' -import CurrencyLogo from '../../components/CurrencyLogo' -import { V3TradeState } from '../../hooks/useBestV3Trade' -import useToggledVersion, { Version } from '../../hooks/useToggledVersion' -import { getTradeVersion } from '../../utils/getTradeVersion' import { useActiveWeb3React } from '../../hooks' -import { useCurrency, useAllTokens } from '../../hooks/Tokens' +import { useAllTokens, useCurrency } from '../../hooks/Tokens' import { ApprovalState, useApproveCallbackFromTrade } from '../../hooks/useApproveCallback' +import { V3TradeState } from '../../hooks/useBestV3Trade' import useENSAddress from '../../hooks/useENSAddress' import { useIsSwapUnsupported } from '../../hooks/useIsSwapUnsupported' import { useSwapCallback } from '../../hooks/useSwapCallback' +import useToggledVersion, { Version } from '../../hooks/useToggledVersion' import useWrapCallback, { WrapType } from '../../hooks/useWrapCallback' import { useWalletModalToggle } from '../../state/application/hooks' import { Field } from '../../state/swap/actions' @@ -40,15 +42,13 @@ import { useSwapActionHandlers, useSwapState, } from '../../state/swap/hooks' -import { useExpertModeManager, useUserSlippageTolerance, useUserSingleHopOnly } from '../../state/user/hooks' +import { useExpertModeManager, useUserSingleHopOnly, useUserSlippageTolerance } from '../../state/user/hooks' import { LinkStyledButton, TYPE } from '../../theme' +import { getTradeVersion } from '../../utils/getTradeVersion' import { isTradeBetter } from '../../utils/isTradeBetter' import { maxAmountSpend } from '../../utils/maxAmountSpend' import { computeTradePriceBreakdown, warningSeverity } from '../../utils/prices' import AppBody from '../AppBody' -import Loader from '../../components/Loader' -import UnsupportedCurrencyFooter from 'components/swap/UnsupportedCurrencyFooter' -import { RouteComponentProps } from 'react-router-dom' export default function Swap({ history }: RouteComponentProps) { const loadedUrlParams = useDefaultsFromURLSearch() @@ -391,11 +391,10 @@ export default function Swap({ history }: RouteComponentProps) { setShowInverted={setShowInverted} /> ) : null} - {toggledVersion === Version.v3 && v3TradeState === V3TradeState.VALID && isTradeBetter(v3Trade, v2Trade) ? ( + {![V3TradeState.VALID, V3TradeState.SYNCING].includes(v3TradeState) ? null : toggledVersion === + Version.v3 && isTradeBetter(v3Trade, v2Trade) ? ( - ) : toggledVersion === Version.v2 && - v3TradeState === V3TradeState.VALID && - isTradeBetter(v2Trade, v3Trade) ? ( + ) : toggledVersion === Version.v2 && isTradeBetter(v2Trade, v3Trade) ? ( ) : null} diff --git a/src/utils/isTradeBetter.ts b/src/utils/isTradeBetter.ts index 7622f39184..2c4a9197a7 100644 --- a/src/utils/isTradeBetter.ts +++ b/src/utils/isTradeBetter.ts @@ -18,6 +18,7 @@ export function isTradeBetter( !currencyEquals(tradeA.inputAmount.currency, tradeB.inputAmount.currency) || !currencyEquals(tradeB.outputAmount.currency, tradeB.outputAmount.currency) ) { + console.error('Comparing incomparable trades', tradeA, tradeB) return false } diff --git a/src/utils/prices.ts b/src/utils/prices.ts index ab0631956e..49884cc298 100644 --- a/src/utils/prices.ts +++ b/src/utils/prices.ts @@ -1,5 +1,5 @@ import JSBI from 'jsbi' -import { BLOCKED_PRICE_IMPACT_NON_EXPERT } from '../constants' +import { BLOCKED_PRICE_IMPACT_NON_EXPERT, ZERO_PERCENT } from '../constants' import { CurrencyAmount, Fraction, Percent, TokenAmount } from '@uniswap/sdk-core' import { Trade as V2Trade } from '@uniswap/v2-sdk' import { Trade as V3Trade } from '@uniswap/v3-sdk' @@ -104,15 +104,19 @@ export function warningSeverity(priceImpact: Percent | undefined): 0 | 1 | 2 | 3 return 0 } -export function formatExecutionPrice(trade: V2Trade | V3Trade | undefined, inverted: boolean | undefined): string { +export function formatExecutionPrice( + trade: V2Trade | V3Trade | undefined, + inverted: boolean | undefined, + slippageTolerance: Percent = ZERO_PERCENT +): string { if (!trade) { return '' } return inverted - ? `${trade.executionPrice.invert().toSignificant(6)} ${trade.inputAmount.currency.symbol} / ${ - trade.outputAmount.currency.symbol - }` - : `${trade.executionPrice.toSignificant(6)} ${trade.outputAmount.currency.symbol} / ${ + ? `${trade.worstExecutionPrice(slippageTolerance).invert().toSignificant(6)} ${ + trade.inputAmount.currency.symbol + } / ${trade.outputAmount.currency.symbol}` + : `${trade.worstExecutionPrice(slippageTolerance).toSignificant(6)} ${trade.outputAmount.currency.symbol} / ${ trade.inputAmount.currency.symbol }` }