From 99d12acdad6f36676ff5500b15736880529233f7 Mon Sep 17 00:00:00 2001 From: Apotheosis <97164662+0xApotheosis@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:03:05 +1000 Subject: [PATCH 1/2] fix: handle positive price impact --- .../MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx | 6 +++--- .../MultiHopTrade/components/TradeInput/TradeInput.tsx | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx index 64141904798..c2885e40d62 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx @@ -157,9 +157,9 @@ export const TradeConfirm = () => { const priceImpactPercentage = useMemo(() => { if (!sellAmountBeforeFeesUserCurrency || !buyAmountBeforeFeesUserCurrency) return bn(0) - const tradeDifference = bn(sellAmountBeforeFeesUserCurrency) - .minus(buyAmountBeforeFeesUserCurrency) - .abs() + const tradeDifference = bn(sellAmountBeforeFeesUserCurrency).minus( + buyAmountBeforeFeesUserCurrency, + ) return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100) }, [sellAmountBeforeFeesUserCurrency, buyAmountBeforeFeesUserCurrency]) diff --git a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx index 70ba3fb9ee5..ed3e79e32b1 100644 --- a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx @@ -110,9 +110,9 @@ export const TradeInput = memo(() => { const priceImpactPercentage = useMemo(() => { if (!sellAmountBeforeFeesUserCurrency || !buyAmountBeforeFeesUserCurrency) return bn('0') - const tradeDifference = bn(sellAmountBeforeFeesUserCurrency) - .minus(buyAmountBeforeFeesUserCurrency) - .abs() + const tradeDifference = bn(sellAmountBeforeFeesUserCurrency).minus( + buyAmountBeforeFeesUserCurrency, + ) return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100) }, [sellAmountBeforeFeesUserCurrency, buyAmountBeforeFeesUserCurrency]) From a0023d2fdb0183e51b2886577f5dfaa7445301c1 Mon Sep 17 00:00:00 2001 From: Apotheosis <97164662+0xApotheosis@users.noreply.github.com> Date: Wed, 16 Aug 2023 13:08:34 +1000 Subject: [PATCH 2/2] chore: extract price impact logic to hook --- .../components/TradeConfirm/TradeConfirm.tsx | 26 ++------------ .../components/TradeInput/TradeInput.tsx | 25 +++---------- .../hooks/quoteValidation/usePriceImpact.tsx | 36 +++++++++++++++++++ 3 files changed, 42 insertions(+), 45 deletions(-) create mode 100644 src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx diff --git a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx index c2885e40d62..249f0ee4e27 100644 --- a/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx +++ b/src/components/MultiHopTrade/components/TradeConfirm/TradeConfirm.tsx @@ -28,6 +28,7 @@ import { AssetToAsset } from 'components/MultiHopTrade/components/TradeConfirm/A import { ReceiveSummary } from 'components/MultiHopTrade/components/TradeConfirm/ReceiveSummary' import { WithBackButton } from 'components/MultiHopTrade/components/WithBackButton' import { getMixpanelEventData } from 'components/MultiHopTrade/helpers' +import { usePriceImpact } from 'components/MultiHopTrade/hooks/quoteValidation/usePriceImpact' import { useTradeExecution } from 'components/MultiHopTrade/hooks/useTradeExecution/useTradeExecution' import { chainSupportsTxHistory } from 'components/MultiHopTrade/utils' import { Row } from 'components/Row/Row' @@ -50,7 +51,6 @@ import { selectActiveStepOrDefault, selectActiveSwapperName, selectBuyAmountBeforeFeesCryptoPrecision, - selectBuyAmountBeforeFeesUserCurrency, selectFirstHop, selectFirstHopNetworkFeeCryptoPrecision, selectFirstHopSellAsset, @@ -76,6 +76,7 @@ export const TradeConfirm = () => { const mixpanel = getMixPanel() const borderColor = useColorModeValue('gray.100', 'gray.750') const alertColor = useColorModeValue('yellow.500', 'yellow.200') + const { isModeratePriceImpact, priceImpactPercentage, isHighPriceImpact } = usePriceImpact() const [hasMixpanelFired, setHasMixpanelFired] = useState(false) const { handleSubmit, @@ -129,7 +130,6 @@ export const TradeConfirm = () => { const buyAmountAfterFeesCryptoPrecision = useAppSelector(selectNetReceiveAmountCryptoPrecision) const slippageDecimal = useAppSelector(selectTradeSlippagePercentageDecimal) const netBuyAmountUserCurrency = useAppSelector(selectReceiveBuyAmountUserCurrency) - const buyAmountBeforeFeesUserCurrency = useAppSelector(selectBuyAmountBeforeFeesUserCurrency) const sellAmountBeforeFeesUserCurrency = useAppSelector(selectSellAmountUserCurrency) const networkFeeCryptoHuman = useAppSelector(selectFirstHopNetworkFeeCryptoPrecision) const networkFeeUserCurrency = useAppSelector(selectTotalNetworkFeeUserCurrencyPrecision) @@ -154,28 +154,6 @@ export const TradeConfirm = () => { const txHash = buyTxHash ?? sellTxHash - const priceImpactPercentage = useMemo(() => { - if (!sellAmountBeforeFeesUserCurrency || !buyAmountBeforeFeesUserCurrency) return bn(0) - - const tradeDifference = bn(sellAmountBeforeFeesUserCurrency).minus( - buyAmountBeforeFeesUserCurrency, - ) - - return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100) - }, [sellAmountBeforeFeesUserCurrency, buyAmountBeforeFeesUserCurrency]) - - const isHighPriceImpact = useMemo(() => { - if (!priceImpactPercentage) return false - - return priceImpactPercentage.gt(10) - }, [priceImpactPercentage]) - - const isModeratePriceImpact = useMemo(() => { - if (!priceImpactPercentage) return false - - return bn(priceImpactPercentage).gt(5) - }, [priceImpactPercentage]) - const getSellTxLink = useCallback( (sellTxHash: string) => getTxLink({ diff --git a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx index ed3e79e32b1..bea5b446cd6 100644 --- a/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx +++ b/src/components/MultiHopTrade/components/TradeInput/TradeInput.tsx @@ -26,6 +26,7 @@ import { ManualAddressEntry } from 'components/MultiHopTrade/components/TradeInp import { getSwapperSupportsSlippage } from 'components/MultiHopTrade/components/TradeInput/getSwapperSupportsSlippage' import { getMixpanelEventData } from 'components/MultiHopTrade/helpers' import { useActiveQuoteStatus } from 'components/MultiHopTrade/hooks/quoteValidation/useActiveQuoteStatus' +import { usePriceImpact } from 'components/MultiHopTrade/hooks/quoteValidation/usePriceImpact' import { checkApprovalNeeded } from 'components/MultiHopTrade/hooks/useAllowanceApproval/helpers' import { useGetTradeQuotes } from 'components/MultiHopTrade/hooks/useGetTradeQuotes/useGetTradeQuotes' import { TradeRoutePaths } from 'components/MultiHopTrade/types' @@ -36,7 +37,7 @@ import { useModal } from 'hooks/useModal/useModal' import { useToggle } from 'hooks/useToggle/useToggle' import { useWallet } from 'hooks/useWallet/useWallet' import type { Asset } from 'lib/asset-service' -import { bn, bnOrZero, positiveOrZero } from 'lib/bignumber/bignumber' +import { bnOrZero, positiveOrZero } from 'lib/bignumber/bignumber' import { getMixPanel } from 'lib/mixpanel/mixPanelSingleton' import { MixPanelEvents } from 'lib/mixpanel/types' import { @@ -55,11 +56,9 @@ import { selectActiveQuoteError, selectActiveSwapperName, selectBuyAmountBeforeFeesCryptoPrecision, - selectBuyAmountBeforeFeesUserCurrency, selectFirstHop, selectNetReceiveAmountCryptoPrecision, selectReceiveBuyAmountUserCurrency, - selectSellAmountUserCurrency, selectSwapperSupportsCrossAccountTrade, selectTotalNetworkFeeUserCurrencyPrecision, selectTotalProtocolFeeByAsset, @@ -95,34 +94,18 @@ export const TradeInput = memo(() => { const sellAssetSearch = useModal('sellAssetSearch') const buyAsset = useAppSelector(selectBuyAsset) const sellAsset = useAppSelector(selectSellAsset) + const { isModeratePriceImpact, priceImpactPercentage } = usePriceImpact() + const tradeQuoteStep = useAppSelector(selectFirstHop) const swapperSupportsCrossAccountTrade = useAppSelector(selectSwapperSupportsCrossAccountTrade) const totalProtocolFees = useAppSelector(selectTotalProtocolFeeByAsset) const buyAmountAfterFeesCryptoPrecision = useAppSelector(selectNetReceiveAmountCryptoPrecision) - const buyAmountBeforeFeesUserCurrency = useAppSelector(selectBuyAmountBeforeFeesUserCurrency) const buyAmountAfterFeesUserCurrency = useAppSelector(selectReceiveBuyAmountUserCurrency) const totalNetworkFeeFiatPrecision = useAppSelector(selectTotalNetworkFeeUserCurrencyPrecision) const manualReceiveAddressIsValidating = useAppSelector(selectManualReceiveAddressIsValidating) const sellAmountCryptoPrecision = useAppSelector(selectSellAmountCryptoPrecision) - const sellAmountBeforeFeesUserCurrency = useAppSelector(selectSellAmountUserCurrency) const slippageDecimal = useAppSelector(selectTradeSlippagePercentageDecimal) - const priceImpactPercentage = useMemo(() => { - if (!sellAmountBeforeFeesUserCurrency || !buyAmountBeforeFeesUserCurrency) return bn('0') - - const tradeDifference = bn(sellAmountBeforeFeesUserCurrency).minus( - buyAmountBeforeFeesUserCurrency, - ) - - return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100) - }, [sellAmountBeforeFeesUserCurrency, buyAmountBeforeFeesUserCurrency]) - - const isModeratePriceImpact = useMemo(() => { - if (!priceImpactPercentage) return false - - return bn(priceImpactPercentage).gt(5) - }, [priceImpactPercentage]) - const hasUserEnteredAmount = useMemo( () => bnOrZero(sellAmountCryptoPrecision).gt(0), [sellAmountCryptoPrecision], diff --git a/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx b/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx new file mode 100644 index 00000000000..bd8e23c6274 --- /dev/null +++ b/src/components/MultiHopTrade/hooks/quoteValidation/usePriceImpact.tsx @@ -0,0 +1,36 @@ +import { useMemo } from 'react' +import { bn } from 'lib/bignumber/bignumber' +import { + selectBuyAmountBeforeFeesUserCurrency, + selectSellAmountUserCurrency, +} from 'state/slices/tradeQuoteSlice/selectors' +import { useAppSelector } from 'state/store' + +export const usePriceImpact = () => { + const buyAmountBeforeFeesUserCurrency = useAppSelector(selectBuyAmountBeforeFeesUserCurrency) + const sellAmountBeforeFeesUserCurrency = useAppSelector(selectSellAmountUserCurrency) + + const priceImpactPercentage = useMemo(() => { + if (!sellAmountBeforeFeesUserCurrency || !buyAmountBeforeFeesUserCurrency) return bn('0') + + const tradeDifference = bn(sellAmountBeforeFeesUserCurrency).minus( + buyAmountBeforeFeesUserCurrency, + ) + + return tradeDifference.div(sellAmountBeforeFeesUserCurrency).times(100) + }, [sellAmountBeforeFeesUserCurrency, buyAmountBeforeFeesUserCurrency]) + + const isModeratePriceImpact = useMemo(() => { + if (!priceImpactPercentage) return false + + return bn(priceImpactPercentage).gt(5) + }, [priceImpactPercentage]) + + const isHighPriceImpact = useMemo(() => { + if (!priceImpactPercentage) return false + + return priceImpactPercentage.gt(10) + }, [priceImpactPercentage]) + + return { isModeratePriceImpact, priceImpactPercentage, isHighPriceImpact } +}