From 429483403e7226730eb877093886a1747b3283e8 Mon Sep 17 00:00:00 2001 From: tenderdeve Date: Tue, 23 Jun 2026 16:07:35 +0530 Subject: [PATCH 1/2] fix(bridge): hide stale price impact while a new quote is loading When the destination token is changed in swap-and-bridge mode, the price impact briefly rendered a huge/scary value derived from the previous quote while the new bridge quote was still being fetched. `PriceImpactIndicator` already received the `loading` flag but rendered the (stale) percentage *and* the spinner side by side. Gate the percentage on `!priceImpactLoading` so only the spinner shows while a fresh quote is being computed, matching the rest of the bridge-quote loading UX. Closes #7429 --- .../src/common/pure/PriceImpactIndicator/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/cowswap-frontend/src/common/pure/PriceImpactIndicator/index.tsx b/apps/cowswap-frontend/src/common/pure/PriceImpactIndicator/index.tsx index f0e40d719b2..9728855a51e 100644 --- a/apps/cowswap-frontend/src/common/pure/PriceImpactIndicator/index.tsx +++ b/apps/cowswap-frontend/src/common/pure/PriceImpactIndicator/index.tsx @@ -32,7 +32,7 @@ export function PriceImpactIndicator({ priceImpactParams, isBridging = false }: return ( - {priceImpact ? ( + {priceImpact && !priceImpactLoading ? ( {' '} Date: Tue, 23 Jun 2026 17:53:13 +0530 Subject: [PATCH 2/2] fix(trade): treat in-flight quote as price-impact loading state The previous fix only stopped \`PriceImpactIndicator\` from rendering a stale percentage during USD-loading. The reporter still saw a scary value on same-chain quote refreshes: when the destination token changes, the trade form keeps the previous \`outputCurrencyAmount\` until the new quote lands, and during that window \`useFiatValuePriceImpact\` happily divides the new input USD by the old output USD, producing a huge nonsense %. Pull \`isLoading\` / \`hasParamsChanged\` out of \`useTradeQuote()\` and fold them into the price-impact loading flag. While that combined flag is true, return \`{ priceImpact: undefined, isLoading: true }\` so every consumer falls back to the loading state (spinner) instead of rendering the stale value. Closes #7429 --- .../usePriceImpact/useFiatValuePriceImpact.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/apps/cowswap-frontend/src/legacy/hooks/usePriceImpact/useFiatValuePriceImpact.ts b/apps/cowswap-frontend/src/legacy/hooks/usePriceImpact/useFiatValuePriceImpact.ts index 62fdc9197d5..ee3550161cd 100644 --- a/apps/cowswap-frontend/src/legacy/hooks/usePriceImpact/useFiatValuePriceImpact.ts +++ b/apps/cowswap-frontend/src/legacy/hooks/usePriceImpact/useFiatValuePriceImpact.ts @@ -8,6 +8,7 @@ import { Fraction, Percent } from '@cowprotocol/currency' import ms from 'ms.macro' import { useDerivedTradeState } from 'modules/trade' +import { useTradeQuote } from 'modules/tradeQuote' import { useTradeUsdAmounts } from 'modules/usdAmount' import { useSafeMemo } from 'common/hooks/useSafeMemo' @@ -31,7 +32,12 @@ export function useFiatValuePriceImpact(): { priceImpact: Percent | undefined; i outputAmount: { value: fiatValueOutput, isLoading: outputIsLoading }, } = useTradeUsdAmounts(inputCurrencyAmount, outputCurrencyAmount, inputToken, outputToken) - const isLoading = inputIsLoading || outputIsLoading + const { isLoading: isQuoteLoading, hasParamsChanged: quoteParamsChanged } = useTradeQuote() + + // Trade-quote signals indicate the current output amount is stale (token just changed + // or a fresh quote is in flight). Compute price impact only once the quote catches up, + // otherwise we'd display a huge nonsense % derived from mismatched in/out amounts. + const isLoading = inputIsLoading || outputIsLoading || isQuoteLoading || quoteParamsChanged const [hasLoadingTimedOut, setHasLoadingTimedOut] = useState(false) useEffect(() => { @@ -51,12 +57,20 @@ export function useFiatValuePriceImpact(): { priceImpact: Percent | undefined; i // Don't calculate price impact if trade is not set up (both trade assets are not set) if (!isTradeSetUp) return null + const stillLoading = isLoading && !hasLoadingTimedOut + + // While a fresh quote is loading, don't expose the stale value at all — consumers + // hide the percentage when `priceImpact` is undefined, leaving just the spinner. + if (stillLoading) { + return { priceImpact: undefined, isLoading: true } + } + const priceImpact = computeFiatValuePriceImpact( fiatValueInput ? FractionUtils.fractionLikeToFraction(fiatValueInput) : null, fiatValueOutput ? FractionUtils.fractionLikeToFraction(fiatValueOutput) : null, ) - return { priceImpact, isLoading: isLoading && !hasLoadingTimedOut } + return { priceImpact, isLoading: false } }, [isTradeSetUp, fiatValueInput, fiatValueOutput, isLoading, hasLoadingTimedOut]) }