Commit f5087880 authored by Zach Pomerantz's avatar Zach Pomerantz Committed by GitHub

fix: impact rendering (#3553)

* fix: price impact rendering

* fix: webkit-compat gradient
parent 377026bc
...@@ -18,7 +18,7 @@ const ExpandoColumn = styled(Column)<{ height: number; open: boolean }>` ...@@ -18,7 +18,7 @@ const ExpandoColumn = styled(Column)<{ height: number; open: boolean }>`
transition: height 0.25s, padding 0.25s; transition: height 0.25s, padding 0.25s;
:after { :after {
background: linear-gradient(#ffffff00, ${({ theme }) => theme.dialog}); background: linear-gradient(transparent, ${({ theme }) => theme.dialog});
bottom: 0; bottom: 0;
content: ''; content: '';
height: 0.75em; height: 0.75em;
......
...@@ -83,7 +83,7 @@ export default function Output({ disabled, focused, children }: PropsWithChildre ...@@ -83,7 +83,7 @@ export default function Output({ disabled, focused, children }: PropsWithChildre
<Row> <Row>
<USDC gap={0.5} isLoading={isRouteLoading}> <USDC gap={0.5} isLoading={isRouteLoading}>
{outputUSDC ? `$${formatCurrencyAmount(outputUSDC, 6, 'en', 2)}` : '-'}{' '} {outputUSDC ? `$${formatCurrencyAmount(outputUSDC, 6, 'en', 2)}` : '-'}{' '}
{impact.display && <ThemedText.Body2 color={impact.warning}>({impact.display})</ThemedText.Body2>} {impact && <ThemedText.Body2 color={impact.warning}>({impact.toString()})</ThemedText.Body2>}
</USDC> </USDC>
{balance && ( {balance && (
<Balance focused={focused}> <Balance focused={focused}>
......
import { t } from '@lingui/macro' import { t } from '@lingui/macro'
import { useLingui } from '@lingui/react' import { useLingui } from '@lingui/react'
import { Trade } from '@uniswap/router-sdk' import { Trade } from '@uniswap/router-sdk'
import { Currency, Percent, TradeType } from '@uniswap/sdk-core' import { Currency, TradeType } from '@uniswap/sdk-core'
import { useAtomValue } from 'jotai/utils' import { useAtomValue } from 'jotai/utils'
import Column from 'lib/components/Column' import Column from 'lib/components/Column'
import Row from 'lib/components/Row' import Row from 'lib/components/Row'
import { Slippage } from 'lib/hooks/useSlippage'
import { PriceImpact } from 'lib/hooks/useUSDCPriceImpact'
import { feeOptionsAtom } from 'lib/state/swap' import { feeOptionsAtom } from 'lib/state/swap'
import styled, { Color, ThemedText } from 'lib/theme' import styled, { Color, ThemedText } from 'lib/theme'
import { useMemo } from 'react' import { useMemo } from 'react'
...@@ -36,11 +38,11 @@ function Detail({ label, value, color }: DetailProps) { ...@@ -36,11 +38,11 @@ function Detail({ label, value, color }: DetailProps) {
interface DetailsProps { interface DetailsProps {
trade: Trade<Currency, Currency, TradeType> trade: Trade<Currency, Currency, TradeType>
slippage: { auto: boolean; allowed: Percent; warning?: Color } slippage: Slippage
priceImpact: { priceImpact?: string; warning?: Color } impact?: PriceImpact
} }
export default function Details({ trade, slippage, priceImpact }: DetailsProps) { export default function Details({ trade, slippage, impact }: DetailsProps) {
const { inputAmount, outputAmount } = trade const { inputAmount, outputAmount } = trade
const inputCurrency = inputAmount.currency const inputCurrency = inputAmount.currency
const outputCurrency = outputAmount.currency const outputCurrency = outputAmount.currency
...@@ -61,8 +63,8 @@ export default function Details({ trade, slippage, priceImpact }: DetailsProps) ...@@ -61,8 +63,8 @@ export default function Details({ trade, slippage, priceImpact }: DetailsProps)
} }
} }
if (priceImpact.priceImpact) { if (impact) {
rows.push([t`Price impact`, priceImpact.priceImpact, priceImpact.warning]) rows.push([t`Price impact`, impact.toString(), impact.warning])
} }
if (lpFeeAmount) { if (lpFeeAmount) {
...@@ -85,15 +87,15 @@ export default function Details({ trade, slippage, priceImpact }: DetailsProps) ...@@ -85,15 +87,15 @@ export default function Details({ trade, slippage, priceImpact }: DetailsProps)
return rows return rows
}, [ }, [
feeOptions, feeOptions,
priceImpact,
lpFeeAmount,
trade,
slippage,
outputAmount,
i18n.locale, i18n.locale,
impact,
inputCurrency,
integrator, integrator,
lpFeeAmount,
outputAmount,
outputCurrency, outputCurrency,
inputCurrency, slippage,
trade,
]) ])
return ( return (
......
...@@ -42,16 +42,16 @@ interface SummaryProps { ...@@ -42,16 +42,16 @@ interface SummaryProps {
output: CurrencyAmount<Currency> output: CurrencyAmount<Currency>
inputUSDC?: CurrencyAmount<Currency> inputUSDC?: CurrencyAmount<Currency>
outputUSDC?: CurrencyAmount<Currency> outputUSDC?: CurrencyAmount<Currency>
priceImpact?: PriceImpact impact?: PriceImpact
} }
export default function Summary({ input, output, inputUSDC, outputUSDC, priceImpact }: SummaryProps) { export default function Summary({ input, output, inputUSDC, outputUSDC, impact }: SummaryProps) {
return ( return (
<Row gap={priceImpact ? 1 : 0.25}> <Row gap={impact ? 1 : 0.25}>
<TokenValue input={input} usdc={inputUSDC} /> <TokenValue input={input} usdc={inputUSDC} />
<ArrowRight /> <ArrowRight />
<TokenValue input={output} usdc={outputUSDC}> <TokenValue input={output} usdc={outputUSDC}>
{priceImpact && <ThemedText.Caption color={priceImpact.warning}>({priceImpact.display})</ThemedText.Caption>} {impact && <ThemedText.Caption color={impact.warning}>({impact.toString()})</ThemedText.Caption>}
</TokenValue> </TokenValue>
</Row> </Row>
) )
......
...@@ -10,7 +10,7 @@ import Row from 'lib/components/Row' ...@@ -10,7 +10,7 @@ import Row from 'lib/components/Row'
import { Slippage } from 'lib/hooks/useSlippage' import { Slippage } from 'lib/hooks/useSlippage'
import { PriceImpact } from 'lib/hooks/useUSDCPriceImpact' import { PriceImpact } from 'lib/hooks/useUSDCPriceImpact'
import { AlertTriangle, BarChart, Info } from 'lib/icons' import { AlertTriangle, BarChart, Info } from 'lib/icons'
import styled, { Color, ThemedText } from 'lib/theme' import styled, { ThemedText } from 'lib/theme'
import { useCallback, useMemo, useState } from 'react' import { useCallback, useMemo, useState } from 'react'
import { formatCurrencyAmount } from 'utils/formatCurrencyAmount' import { formatCurrencyAmount } from 'utils/formatCurrencyAmount'
import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer' import { tradeMeaningfullyDiffers } from 'utils/tradeMeaningFullyDiffer'
...@@ -41,16 +41,16 @@ const Body = styled(Column)<{ open: boolean }>` ...@@ -41,16 +41,16 @@ const Body = styled(Column)<{ open: boolean }>`
} }
` `
function Subhead({ priceImpact, slippage }: { priceImpact: { warning?: Color }; slippage: Slippage }) { function Subhead({ impact, slippage }: { impact?: PriceImpact; slippage: Slippage }) {
return ( return (
<Row gap={0.5}> <Row gap={0.5}>
{priceImpact.warning || slippage.warning ? ( {impact?.warning || slippage.warning ? (
<AlertTriangle color={priceImpact.warning || slippage.warning} /> <AlertTriangle color={impact?.warning || slippage.warning} />
) : ( ) : (
<Info color="secondary" /> <Info color="secondary" />
)} )}
<ThemedText.Subhead2 color={priceImpact.warning || slippage.warning || 'secondary'}> <ThemedText.Subhead2 color={impact?.warning || slippage.warning || 'secondary'}>
{priceImpact.warning ? ( {impact?.warning ? (
<Trans>High price impact</Trans> <Trans>High price impact</Trans>
) : slippage.warning ? ( ) : slippage.warning ? (
<Trans>High slippage</Trans> <Trans>High slippage</Trans>
...@@ -132,7 +132,7 @@ interface SummaryDialogProps { ...@@ -132,7 +132,7 @@ interface SummaryDialogProps {
slippage: Slippage slippage: Slippage
inputUSDC?: CurrencyAmount<Currency> inputUSDC?: CurrencyAmount<Currency>
outputUSDC?: CurrencyAmount<Currency> outputUSDC?: CurrencyAmount<Currency>
impact: PriceImpact impact?: PriceImpact
onConfirm: () => void onConfirm: () => void
} }
...@@ -152,23 +152,23 @@ export function SummaryDialog({ trade, slippage, inputUSDC, outputUSDC, impact, ...@@ -152,23 +152,23 @@ export function SummaryDialog({ trade, slippage, inputUSDC, outputUSDC, impact,
output={outputAmount} output={outputAmount}
inputUSDC={inputUSDC} inputUSDC={inputUSDC}
outputUSDC={outputUSDC} outputUSDC={outputUSDC}
priceImpact={impact} impact={impact}
/> />
<Price trade={trade} /> <Price trade={trade} />
</Heading> </Heading>
<Column gap={open ? 0 : 0.75} style={{ transition: 'gap 0.25s' }}> <Column gap={open ? 0 : 0.75} style={{ transition: 'gap 0.25s' }}>
<Expando <Expando
title={<Subhead priceImpact={impact} slippage={slippage} />} title={<Subhead impact={impact} slippage={slippage} />}
open={open} open={open}
onExpand={onExpand} onExpand={onExpand}
height={7} height={7}
> >
<Details trade={trade} slippage={slippage} priceImpact={impact} /> <Details trade={trade} slippage={slippage} impact={impact} />
</Expando> </Expando>
<Footing> <Footing>
<Estimate trade={trade} slippage={slippage} /> <Estimate trade={trade} slippage={slippage} />
</Footing> </Footing>
<ConfirmButton trade={trade} highPriceImpact={impact.warning === 'error'} onConfirm={onConfirm} /> <ConfirmButton trade={trade} highPriceImpact={impact?.warning === 'error'} onConfirm={onConfirm} />
</Column> </Column>
</Body> </Body>
</> </>
......
...@@ -84,16 +84,17 @@ export function Trade({ ...@@ -84,16 +84,17 @@ export function Trade({
}: { }: {
trade: InterfaceTrade<Currency, Currency, TradeType> trade: InterfaceTrade<Currency, Currency, TradeType>
outputUSDC?: CurrencyAmount<Currency> outputUSDC?: CurrencyAmount<Currency>
impact: PriceImpact impact?: PriceImpact
}) { }) {
return ( return (
<> <>
<Tooltip placement="bottom" icon={impact.warning ? AlertTriangle : Info}> <Tooltip placement="bottom" icon={impact?.warning ? AlertTriangle : Info}>
<Column gap={0.75}> <Column gap={0.75}>
{impact.warning && ( {impact?.warning && (
<> <>
<ThemedText.Caption> <ThemedText.Caption>
The output amount is estimated at {impact.display} less than the input amount due to high price impact The output amount is estimated at {impact.toString()} less than the input amount due to high price
impact
</ThemedText.Caption> </ThemedText.Caption>
<Rule /> <Rule />
</> </>
......
...@@ -17,6 +17,7 @@ function TokenImg({ token, ...rest }: TokenImgProps) { ...@@ -17,6 +17,7 @@ function TokenImg({ token, ...rest }: TokenImgProps) {
// Use the wrapped token info so that it includes the logoURI. // Use the wrapped token info so that it includes the logoURI.
const tokenInfo = useToken(token.isToken ? token.wrapped.address : undefined) ?? token const tokenInfo = useToken(token.isToken ? token.wrapped.address : undefined) ?? token
// TODO(zzmp): TokenImg takes a frame to switch.
const srcs = useCurrencyLogoURIs(tokenInfo) const srcs = useCurrencyLogoURIs(tokenInfo)
const [src, setSrc] = useState<string | undefined>() const [src, setSrc] = useState<string | undefined>()
useEffect(() => { useEffect(() => {
......
...@@ -28,7 +28,7 @@ interface SwapInfo { ...@@ -28,7 +28,7 @@ interface SwapInfo {
state: TradeState state: TradeState
} }
slippage: Slippage slippage: Slippage
impact: PriceImpact impact?: PriceImpact
} }
// from the current swap inputs, compute the best trade and return it. // from the current swap inputs, compute the best trade and return it.
...@@ -65,11 +65,7 @@ function useComputeSwapInfo(): SwapInfo { ...@@ -65,11 +65,7 @@ function useComputeSwapInfo(): SwapInfo {
// Compute slippage and impact off of the trade so that it refreshes with the trade. // Compute slippage and impact off of the trade so that it refreshes with the trade.
// (Using amountIn/amountOut would show (incorrect) intermediate values.) // (Using amountIn/amountOut would show (incorrect) intermediate values.)
const slippage = useSlippage(trade.trade) const slippage = useSlippage(trade.trade)
const { const { inputUSDC, outputUSDC, impact } = useUSDCPriceImpact(trade.trade?.inputAmount, trade.trade?.outputAmount)
inputUSDC: usdcIn,
outputUSDC: usdcOut,
priceImpact: impact,
} = useUSDCPriceImpact(trade.trade?.inputAmount, trade.trade?.outputAmount)
return useMemo( return useMemo(
() => ({ () => ({
...@@ -77,19 +73,31 @@ function useComputeSwapInfo(): SwapInfo { ...@@ -77,19 +73,31 @@ function useComputeSwapInfo(): SwapInfo {
currency: currencyIn, currency: currencyIn,
amount: amountIn, amount: amountIn,
balance: balanceIn, balance: balanceIn,
usdc: usdcIn, usdc: inputUSDC,
}, },
[Field.OUTPUT]: { [Field.OUTPUT]: {
currency: currencyOut, currency: currencyOut,
amount: amountOut, amount: amountOut,
balance: balanceOut, balance: balanceOut,
usdc: usdcOut, usdc: outputUSDC,
}, },
trade, trade,
slippage, slippage,
impact, impact,
}), }),
[amountIn, amountOut, balanceIn, balanceOut, currencyIn, currencyOut, impact, slippage, trade, usdcIn, usdcOut] [
amountIn,
amountOut,
balanceIn,
balanceOut,
currencyIn,
currencyOut,
impact,
inputUSDC,
outputUSDC,
slippage,
trade,
]
) )
} }
...@@ -98,7 +106,6 @@ const swapInfoAtom = atom<SwapInfo>({ ...@@ -98,7 +106,6 @@ const swapInfoAtom = atom<SwapInfo>({
[Field.OUTPUT]: {}, [Field.OUTPUT]: {},
trade: { state: TradeState.INVALID }, trade: { state: TradeState.INVALID },
slippage: { auto: true, allowed: new Percent(0) }, slippage: { auto: true, allowed: new Percent(0) },
impact: {},
}) })
export function SwapInfoUpdater() { export function SwapInfoUpdater() {
......
...@@ -5,8 +5,9 @@ import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact' ...@@ -5,8 +5,9 @@ import { computeFiatValuePriceImpact } from 'utils/computeFiatValuePriceImpact'
import { getPriceImpactWarning } from 'utils/prices' import { getPriceImpactWarning } from 'utils/prices'
export interface PriceImpact { export interface PriceImpact {
display?: string percent: Percent
warning?: 'warning' | 'error' warning?: 'warning' | 'error'
toString(): string
} }
/** /**
...@@ -19,22 +20,20 @@ export default function useUSDCPriceImpact( ...@@ -19,22 +20,20 @@ export default function useUSDCPriceImpact(
): { ): {
inputUSDC?: CurrencyAmount<Token> inputUSDC?: CurrencyAmount<Token>
outputUSDC?: CurrencyAmount<Token> outputUSDC?: CurrencyAmount<Token>
priceImpact: PriceImpact impact?: PriceImpact
} { } {
const inputUSDC = useUSDCValue(inputAmount) ?? undefined const inputUSDC = useUSDCValue(inputAmount) ?? undefined
const outputUSDC = useUSDCValue(outputAmount) ?? undefined const outputUSDC = useUSDCValue(outputAmount) ?? undefined
return useMemo(() => { return useMemo(() => {
const priceImpact = computeFiatValuePriceImpact(inputUSDC, outputUSDC) const priceImpact = computeFiatValuePriceImpact(inputUSDC, outputUSDC)
const warning = getPriceImpactWarning(priceImpact) const impact = priceImpact
return { ? {
inputUSDC, percent: priceImpact,
outputUSDC, warning: getPriceImpactWarning(priceImpact),
priceImpact: { toString: () => toHumanReadablePriceImpact(priceImpact),
priceImpact,
display: priceImpact && toHumanReadablePriceImpact(priceImpact),
warning,
},
} }
: undefined
return { inputUSDC, outputUSDC, impact }
}, [inputUSDC, outputUSDC]) }, [inputUSDC, outputUSDC])
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment