diff --git a/components/swap/SwapTokenChart.tsx b/components/swap/SwapTokenChart.tsx index d6f76d0c..12e7b212 100644 --- a/components/swap/SwapTokenChart.tsx +++ b/components/swap/SwapTokenChart.tsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from 'react' +import { MouseEventHandler, useEffect, useMemo, useState } from 'react' import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { @@ -14,7 +14,7 @@ import { } from 'recharts' import FlipNumbers from 'react-flip-numbers' import ContentBox from '../shared/ContentBox' -import { formatNumericValue } from '../../utils/numbers' +import { formatCurrencyValue, formatNumericValue } from '../../utils/numbers' import SheenLoader from '../shared/SheenLoader' import { COLORS } from '../../styles/colors' import { useTheme } from 'next-themes' @@ -94,10 +94,25 @@ interface ExtendedReferenceDotProps extends ReferenceDotProps { swapHistory: SwapHistoryItem[] swapMarketName: string flipPrices: boolean + mouseEnter: ( + swap: SwapHistoryItem | undefined, + coingeckoPrice: string | number | undefined + ) => void + mouseLeave: MouseEventHandler } const SwapHistoryArrows = (props: ExtendedReferenceDotProps) => { - const { cx, cy, x, swapHistory, swapMarketName, flipPrices } = props + const { + cx, + cy, + x, + y, + swapHistory, + swapMarketName, + flipPrices, + mouseEnter, + mouseLeave, + } = props const swapDetails = swapHistory.find( (swap) => dayjs(swap.block_datetime).unix() * 1000 === x ) @@ -113,27 +128,32 @@ const SwapHistoryArrows = (props: ExtendedReferenceDotProps) => { const buy = { pathCoords: 'M11 0.858312L0.857867 15.0004H21.1421L11 0.858312Z', fill: 'var(--up)', - yOffset: 1, + yOffset: 0, } const sell = { pathCoords: 'M11 14.1427L21.1421 0.000533306L0.857865 0.000529886L11 14.1427Z', fill: 'var(--down)', - yOffset: -11, + yOffset: -15, } const sideArrowProps = side === 'buy' ? (!flipPrices ? buy : sell) : !flipPrices ? sell : buy + + const coingeckoPrice = y ? Number(y) : 0 return cx && cy ? ( mouseEnter(swapDetails, coingeckoPrice)} + onMouseLeave={mouseLeave} > { const swapHistory = mangoStore((s) => s.mangoAccount.swapHistory.data) const loadSwapHistory = mangoStore((s) => s.mangoAccount.swapHistory.loading) const [showSwaps, setShowSwaps] = useState(true) + const [swapTooltipData, setSwapTooltipData] = + useState(null) + const [swapTooltipCoingeckoPrice, setSwapTooltipCoingeckoPrice] = useState< + string | number | undefined + >(undefined) + + const handleSwapMouseEnter = ( + swap: SwapHistoryItem | undefined, + coingeckoPrice: string | number | undefined + ) => { + if (swap) { + setSwapTooltipData(swap) + } + if (coingeckoPrice) { + setSwapTooltipCoingeckoPrice(coingeckoPrice) + } + } + + const handleSwapMouseLeave = () => { + setSwapTooltipData(null) + } + + const renderTooltipContent = (swap: SwapHistoryItem) => { + const { + swap_in_amount, + swap_in_symbol, + swap_out_price_usd, + swap_out_amount, + swap_out_symbol, + } = swap + + const swapOutValue = swap_out_price_usd * swap_out_amount + + const baseMarketToken = swapMarketName.split('/')[0] + + const swapSide = + swap_in_symbol === baseMarketToken + ? !flipPrices + ? 'sell' + : 'buy' + : !flipPrices + ? 'buy' + : 'sell' + + const buy = { + price: swap_in_amount / swap_out_amount, + priceSymbol: swap_in_symbol, + amount: swap_out_amount, + side: 'buy', + symbol: swap_out_symbol, + value: swapOutValue, + } + + const sell = { + price: swap_out_amount / swap_in_amount, + priceSymbol: swap_out_symbol, + amount: swap_in_amount, + side: 'sell', + symbol: swap_in_symbol, + value: swapOutValue, + } + + const swapProps = + swapSide === 'buy' ? (!flipPrices ? buy : sell) : !flipPrices ? sell : buy + + const { amount, price, priceSymbol, side, symbol, value } = swapProps + + let coingeckoPercentageDifference = 0 + if ( + swapTooltipCoingeckoPrice && + typeof swapTooltipCoingeckoPrice === 'number' + ) { + const difference = ((price - swapTooltipCoingeckoPrice) / price) * 100 + coingeckoPercentageDifference = difference + } + + const betterThanCoingecko = + swapSide === 'buy' + ? flipPrices + ? coingeckoPercentageDifference > 0 + : coingeckoPercentageDifference < 0 + : flipPrices + ? coingeckoPercentageDifference < 0 + : coingeckoPercentageDifference > 0 + + return ( + <> +

{`${t( + side + )} ${amount} ${symbol} at ${formatNumericValue( + price + )} ${priceSymbol} for ${formatCurrencyValue(value)}`}

+ {coingeckoPercentageDifference ? ( +

+ + {coingeckoPercentageDifference.toFixed(2)}% + {' '} + {betterThanCoingecko ? 'better than' : 'worse than'} Coingecko +

+ ) : null} + + ) + } const { data: coingeckoDataQuery, @@ -206,8 +333,8 @@ const SwapTokenChart = () => { ) return [] const chartSymbols = [ - formatTokenSymbol(inputBank.name), - formatTokenSymbol(outputBank.name), + inputBank.name === 'ETH (Portal)' ? 'ETH' : inputBank.name, + outputBank.name === 'ETH (Portal)' ? 'ETH' : outputBank.name, ] return swapHistory .filter( @@ -298,8 +425,8 @@ const SwapTokenChart = () => { const swapMarketName = useMemo(() => { if (!inputBank || !outputBank) return '' - const inputSymbol = formatTokenSymbol(inputBank.name?.toUpperCase()) - const outputSymbol = formatTokenSymbol(outputBank.name?.toUpperCase()) + const inputSymbol = formatTokenSymbol(inputBank.name) + const outputSymbol = formatTokenSymbol(outputBank.name) return ['usd-coin', 'tether'].includes(inputCoingeckoId || '') ? !flipPrices ? `${outputSymbol}/${inputSymbol}` @@ -324,7 +451,12 @@ const SwapTokenChart = () => { ) : chartData?.length && baseTokenId && quoteTokenId ? ( -
+
+ {swapTooltipData ? ( +
+ {renderTooltipContent(swapTooltipData)} +
+ ) : null}
{inputBank && outputBank ? ( @@ -496,6 +628,8 @@ const SwapTokenChart = () => { swapHistory={swapHistory} swapMarketName={swapMarketName} flipPrices={flipPrices} + mouseEnter={handleSwapMouseEnter} + mouseLeave={handleSwapMouseLeave} /> } />