diff --git a/apis/coingecko.ts b/apis/coingecko.ts index d6e3b96c..d9000a29 100644 --- a/apis/coingecko.ts +++ b/apis/coingecko.ts @@ -39,21 +39,12 @@ export const fetchChartData = async ( (outputTokenCandle) => outputTokenCandle[0] === inputTokenCandle[0], ) if (outputTokenCandle) { - if (['usd-coin', 'tether'].includes(quoteTokenId)) { - parsedData.push({ - time: inputTokenCandle[0], - price: inputTokenCandle[4] / outputTokenCandle[4], - inputTokenPrice: inputTokenCandle[4], - outputTokenPrice: outputTokenCandle[4], - }) - } else { - parsedData.push({ - time: inputTokenCandle[0], - price: outputTokenCandle[4] / inputTokenCandle[4], - inputTokenPrice: inputTokenCandle[4], - outputTokenPrice: outputTokenCandle[4], - }) - } + parsedData.push({ + time: inputTokenCandle[0], + price: outputTokenCandle[4] / inputTokenCandle[4], + inputTokenPrice: inputTokenCandle[4], + outputTokenPrice: outputTokenCandle[4], + }) } } return parsedData diff --git a/components/swap/LimitSwapForm.tsx b/components/swap/LimitSwapForm.tsx index a60353cd..345a4eb7 100644 --- a/components/swap/LimitSwapForm.tsx +++ b/components/swap/LimitSwapForm.tsx @@ -14,7 +14,10 @@ import NumberFormat, { import Decimal from 'decimal.js' import mangoStore from '@store/mangoStore' import { useTranslation } from 'next-i18next' -import { SIZE_INPUT_UI_KEY } from '../../utils/constants' +import { + SIZE_INPUT_UI_KEY, + SWAP_CHART_SETTINGS_KEY, +} from '../../utils/constants' import useLocalStorageState from 'hooks/useLocalStorageState' import SwapSlider from './SwapSlider' import PercentageSelectButtons from './PercentageSelectButtons' @@ -24,11 +27,12 @@ import SellTokenInput from './SellTokenInput' import BuyTokenInput from './BuyTokenInput' import { notify } from 'utils/notifications' import * as sentry from '@sentry/nextjs' -import { isMangoError } from 'types' +import { SwapChartSettings, isMangoError } from 'types' import Button, { IconButton } from '@components/shared/Button' import Loading from '@components/shared/Loading' import TokenLogo from '@components/shared/TokenLogo' import InlineNotification from '@components/shared/InlineNotification' +import { handleFlipPrices } from './SwapTokenChart' type LimitSwapFormProps = { showTokenSelect: 'input' | 'output' | undefined @@ -50,10 +54,13 @@ const LimitSwapForm = ({ const { t } = useTranslation(['common', 'swap', 'trade']) const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0) const [triggerPrice, setTriggerPrice] = useState('') - const [flipPrices, setFlipPrices] = useState(false) const [submitting, setSubmitting] = useState(false) const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider') const [formErrors, setFormErrors] = useState({}) + const [swapChartSettings, setSwapChartSettings] = useLocalStorageState( + SWAP_CHART_SETTINGS_KEY, + [], + ) const { margin: useMargin, @@ -70,6 +77,18 @@ const LimitSwapForm = ({ : new Decimal(0) }, [amountInFormValue]) + const flipPrices = useMemo(() => { + if (!swapChartSettings.length || !inputBank || !outputBank) return false + const pairSettings = swapChartSettings.find( + (chart: SwapChartSettings) => + chart.pair.includes(inputBank.name) && + chart.pair.includes(outputBank.name), + ) + if (pairSettings) { + return pairSettings.flipPrices + } else return false + }, [swapChartSettings, inputBank, outputBank]) + const setAmountInFormValue = useCallback((amountIn: string) => { set((s) => { s.swap.amountIn = amountIn @@ -88,36 +107,35 @@ const LimitSwapForm = ({ }) }, []) - const [quotePrice, flippedQuotePrice] = useMemo(() => { - if (!inputBank || !outputBank) return [0, 0] - const quote = floorToDecimal( - inputBank.uiPrice / outputBank.uiPrice, - outputBank.mintDecimals, - ).toNumber() - const flipped = floorToDecimal( - outputBank.uiPrice / inputBank.uiPrice, - inputBank.mintDecimals, - ).toNumber() - return [quote, flipped] - }, [inputBank, outputBank]) + const quotePrice = useMemo(() => { + if (!inputBank || !outputBank) return 0 + const quote = !flipPrices + ? floorToDecimal( + outputBank.uiPrice / inputBank.uiPrice, + inputBank.mintDecimals, + ).toNumber() + : floorToDecimal( + inputBank.uiPrice / outputBank.uiPrice, + outputBank.mintDecimals, + ).toNumber() + return quote + }, [flipPrices, inputBank, outputBank]) // set default limit and trigger price useEffect(() => { if (!quotePrice) return if (!triggerPrice && !showTokenSelect) { - setTriggerPrice(quotePrice.toFixed(outputBank?.mintDecimals)) + setTriggerPrice(quotePrice.toFixed(inputBank?.mintDecimals)) } - }, [quotePrice, outputBank, showTokenSelect, triggerPrice]) + }, [inputBank, quotePrice, showTokenSelect, triggerPrice]) const triggerPriceDifference = useMemo(() => { - if ((!flipPrices && !quotePrice) || (flipPrices && !flippedQuotePrice)) - return 0 - const oraclePrice = !flipPrices ? quotePrice : flippedQuotePrice + if (!quotePrice) return 0 const triggerDifference = triggerPrice - ? ((parseFloat(triggerPrice) - oraclePrice) / oraclePrice) * 100 + ? ((parseFloat(triggerPrice) - quotePrice) / quotePrice) * 100 : 0 return triggerDifference - }, [flipPrices, flippedQuotePrice, quotePrice, triggerPrice]) + }, [flipPrices, quotePrice, triggerPrice]) const handleTokenSelect = (type: 'input' | 'output') => { setShowTokenSelect(type) @@ -153,11 +171,11 @@ const LimitSwapForm = ({ (amountIn: string, price: string) => { const amountOut = !flipPrices ? floorToDecimal( - parseFloat(amountIn) * parseFloat(price), + parseFloat(amountIn) / parseFloat(price), outputBank?.mintDecimals || 0, ) : floorToDecimal( - parseFloat(amountIn) / parseFloat(price), + parseFloat(amountIn) * parseFloat(price), outputBank?.mintDecimals || 0, ) return amountOut @@ -170,11 +188,11 @@ const LimitSwapForm = ({ (amountOut: string, price: string) => { const amountIn = !flipPrices ? floorToDecimal( - parseFloat(amountOut) / parseFloat(price), + parseFloat(amountOut) * parseFloat(price), inputBank?.mintDecimals || 0, ) : floorToDecimal( - parseFloat(amountOut) * parseFloat(price), + parseFloat(amountOut) / parseFloat(price), inputBank?.mintDecimals || 0, ) return amountIn @@ -255,18 +273,26 @@ const LimitSwapForm = ({ ) const handleSwitchTokens = useCallback(() => { + if (!inputBank || !outputBank) return set((s) => { s.swap.inputBank = outputBank s.swap.outputBank = inputBank }) - if (flippedQuotePrice) { - const price = flipPrices ? quotePrice : flippedQuotePrice - setTriggerPrice(price.toFixed(inputBank?.mintDecimals)) - if (amountInAsDecimal?.gt(0)) { - const amountOut = amountInAsDecimal.mul(flippedQuotePrice) - setAmountOutFormValue(amountOut.toString()) - } + const price = !flipPrices + ? floorToDecimal( + inputBank.uiPrice / outputBank.uiPrice, + outputBank.mintDecimals, + ).toString() + : floorToDecimal( + outputBank.uiPrice / inputBank.uiPrice, + inputBank.mintDecimals, + ).toString() + setTriggerPrice(price) + + if (amountInAsDecimal?.gt(0)) { + const amountOut = getAmountOut(amountInAsDecimal.toString(), price) + setAmountOutFormValue(amountOut.toString()) } setAnimateSwitchArrow( (prevanimateSwitchArrow) => prevanimateSwitchArrow + 1, @@ -275,7 +301,6 @@ const LimitSwapForm = ({ setAmountInFormValue, amountInAsDecimal, flipPrices, - flippedQuotePrice, inputBank, outputBank, triggerPrice, @@ -301,11 +326,9 @@ const LimitSwapForm = ({ return setSubmitting(true) - const inputMint = !flipPrices ? inputBank.mint : outputBank.mint - const outputMint = !flipPrices ? outputBank.mint : inputBank.mint - const amountIn = !flipPrices - ? amountInAsDecimal.toNumber() - : parseFloat(amountOutFormValue) + const inputMint = inputBank.mint + const outputMint = outputBank.mint + const amountIn = amountInAsDecimal.toNumber() try { const tx = await client.tokenConditionalSwapStopLoss( @@ -362,8 +385,8 @@ const LimitSwapForm = ({ ) return const quoteString = !flipPrices - ? `${outputBank.name} per ${inputBank.name}` - : `${inputBank.name} per ${outputBank.name}` + ? `${inputBank.name} per ${outputBank.name}` + : `${outputBank.name} per ${inputBank.name}` if (inputBank.name === 'USDC') { return t('trade:trigger-order-desc', { amount: floorToDecimal(amountOutFormValue, outputBank.mintDecimals), @@ -391,22 +414,41 @@ const LimitSwapForm = ({ const triggerPriceSuffix = useMemo(() => { if (!inputBank || !outputBank) return if (!flipPrices) { - return `${outputBank.name} per ${inputBank.name}` - } else { return `${inputBank.name} per ${outputBank.name}` + } else { + return `${outputBank.name} per ${inputBank.name}` } }, [flipPrices, inputBank, outputBank]) - const handleFlipPrices = useCallback( + const toggleFlipPrices = useCallback( (flip: boolean) => { - setFlipPrices(flip) - if (flip) { - setTriggerPrice(flippedQuotePrice.toFixed(inputBank?.mintDecimals)) - } else { - setTriggerPrice(quotePrice.toFixed(outputBank?.mintDecimals)) - } + if (!inputBank || !outputBank) return + handleFlipPrices( + flip, + flipPrices, + inputBank.name, + outputBank.name, + swapChartSettings, + setSwapChartSettings, + ) + const price = !flipPrices + ? floorToDecimal( + inputBank.uiPrice / outputBank.uiPrice, + outputBank.mintDecimals, + ).toString() + : floorToDecimal( + outputBank.uiPrice / inputBank.uiPrice, + inputBank.mintDecimals, + ).toString() + setTriggerPrice(price) }, - [flippedQuotePrice, inputBank, outputBank, quotePrice], + [ + flipPrices, + inputBank, + outputBank, + swapChartSettings, + setSwapChartSettings, + ], ) return ( @@ -432,7 +474,7 @@ const LimitSwapForm = ({ : ''}

- handleFlipPrices(!flipPrices)}> + toggleFlipPrices(!flipPrices)}> @@ -443,7 +485,11 @@ const LimitSwapForm = ({ thousandSeparator="," allowNegative={false} isNumericString={true} - decimalScale={outputBank?.mintDecimals || 6} + decimalScale={ + !flipPrices + ? inputBank?.mintDecimals + : outputBank?.mintDecimals || 6 + } name="triggerPrice" id="triggerPrice" className="h-10 w-full rounded-l-lg bg-th-input-bkg p-3 pl-8 font-mono text-sm text-th-fgd-1 focus:outline-none md:hover:bg-th-bkg-1" @@ -454,7 +500,7 @@ const LimitSwapForm = ({ />
diff --git a/components/swap/SwapOrders.tsx b/components/swap/SwapOrders.tsx index b7ec3514..7fc142af 100644 --- a/components/swap/SwapOrders.tsx +++ b/components/swap/SwapOrders.tsx @@ -122,6 +122,8 @@ const SwapOrders = () => { } } + console.log(orders) + return orders.length ? ( diff --git a/components/swap/SwapTokenChart.tsx b/components/swap/SwapTokenChart.tsx index b9340360..8d6b7a56 100644 --- a/components/swap/SwapTokenChart.tsx +++ b/components/swap/SwapTokenChart.tsx @@ -32,7 +32,10 @@ import { ChartDataItem, fetchChartData } from 'apis/coingecko' import mangoStore from '@store/mangoStore' import useJupiterSwapData from './useJupiterSwapData' import useLocalStorageState from 'hooks/useLocalStorageState' -import { ANIMATION_SETTINGS_KEY } from 'utils/constants' +import { + ANIMATION_SETTINGS_KEY, + SWAP_CHART_SETTINGS_KEY, +} from 'utils/constants' import { INITIAL_ANIMATION_SETTINGS } from '@components/settings/AnimationSettings' import { useTranslation } from 'next-i18next' import { @@ -46,11 +49,35 @@ import { CategoricalChartFunc } from 'recharts/types/chart/generateCategoricalCh import { interpolateNumber } from 'd3-interpolate' import { IconButton } from '@components/shared/Button' import Tooltip from '@components/shared/Tooltip' -import { SwapHistoryItem } from 'types' +import { SwapChartSettings, SwapHistoryItem } from 'types' import useThemeWrapper from 'hooks/useThemeWrapper' dayjs.extend(relativeTime) +export const handleFlipPrices = ( + flip: boolean, + flipPrices: boolean, + inputToken: string | undefined, + outputToken: string | undefined, + swapChartSettings: SwapChartSettings[], + setSwapChartSettings: (settings: SwapChartSettings[]) => void, +) => { + if (!inputToken || !outputToken) return + if (!flipPrices && flip) { + setSwapChartSettings([ + ...swapChartSettings, + { pair: `${inputToken}/${outputToken}`, flipPrices: true }, + ]) + } else { + setSwapChartSettings( + swapChartSettings.filter( + (chart: SwapChartSettings) => + !chart.pair.includes(inputToken) && !chart.pair.includes(outputToken), + ), + ) + } +} + const CustomizedLabel = ({ chartData, x, @@ -182,12 +209,16 @@ const SwapTokenChart = () => { const [quoteTokenId, setQuoteTokenId] = useState(outputCoingeckoId) const [mouseData, setMouseData] = useState() const [daysToShow, setDaysToShow] = useState('1') - const [flipPrices, setFlipPrices] = useState(false) + // const [flipPrices, setFlipPrices] = useState(false) const { theme } = useThemeWrapper() const [animationSettings] = useLocalStorageState( ANIMATION_SETTINGS_KEY, INITIAL_ANIMATION_SETTINGS, ) + const [swapChartSettings, setSwapChartSettings] = useLocalStorageState( + SWAP_CHART_SETTINGS_KEY, + [], + ) const swapHistory = mangoStore((s) => s.mangoAccount.swapHistory.data) const loadSwapHistory = mangoStore((s) => s.mangoAccount.swapHistory.loading) const [showSwaps, setShowSwaps] = useState(true) @@ -197,6 +228,47 @@ const SwapTokenChart = () => { string | number | undefined >(undefined) + const flipPrices = useMemo(() => { + if (!swapChartSettings.length || !inputBank || !outputBank) return false + const pairSettings = swapChartSettings.find( + (chart: SwapChartSettings) => + chart.pair.includes(inputBank.name) && + chart.pair.includes(outputBank.name), + ) + if (pairSettings) { + return pairSettings.flipPrices + } else return false + }, [swapChartSettings, inputBank, outputBank]) + + const swapMarketName = useMemo(() => { + if (!inputBank || !outputBank) return '' + const inputSymbol = formatTokenSymbol(inputBank.name) + const outputSymbol = formatTokenSymbol(outputBank.name) + return !flipPrices + ? `${outputSymbol}/${inputSymbol}` + : `${inputSymbol}/${outputSymbol}` + }, [flipPrices, inputBank, inputCoingeckoId, outputBank]) + + // const handleFlipPrices = useCallback( + // (flip: boolean) => { + // if (!flipPrices && flip) { + // setSwapChartSettings([ + // ...swapChartSettings, + // { pair: swapMarketName, flipPrices: true }, + // ]) + // } else { + // setSwapChartSettings( + // swapChartSettings.filter( + // (chart: SwapChartSettings) => + // !chart.pair.includes(inputBank!.name) && + // !chart.pair.includes(outputBank!.name), + // ), + // ) + // } + // }, + // [flipPrices, inputBank, outputBank, swapChartSettings, swapMarketName], + // ) + const handleSwapMouseEnter = useCallback( ( swap: SwapHistoryItem | undefined, @@ -216,19 +288,6 @@ const SwapTokenChart = () => { setSwapTooltipData(null) }, [setSwapTooltipData]) - const swapMarketName = useMemo(() => { - if (!inputBank || !outputBank) return '' - const inputSymbol = formatTokenSymbol(inputBank.name) - const outputSymbol = formatTokenSymbol(outputBank.name) - return ['usd-coin', 'tether'].includes(inputCoingeckoId || '') - ? !flipPrices - ? `${outputSymbol}/${inputSymbol}` - : `${inputSymbol}/${outputSymbol}` - : !flipPrices - ? `${inputSymbol}/${outputSymbol}` - : `${outputSymbol}/${inputSymbol}` - }, [flipPrices, inputBank, inputCoingeckoId, outputBank]) - const renderTooltipContent = useCallback( (swap: SwapHistoryItem) => { const { @@ -429,14 +488,8 @@ const SwapTokenChart = () => { useEffect(() => { if (!inputCoingeckoId || !outputCoingeckoId) return - - if (['usd-coin', 'tether'].includes(outputCoingeckoId)) { - setBaseTokenId(inputCoingeckoId) - setQuoteTokenId(outputCoingeckoId) - } else { - setBaseTokenId(outputCoingeckoId) - setQuoteTokenId(inputCoingeckoId) - } + setBaseTokenId(inputCoingeckoId) + setQuoteTokenId(outputCoingeckoId) }, [inputCoingeckoId, outputCoingeckoId]) const calculateChartChange = useCallback(() => { @@ -485,7 +538,16 @@ const SwapTokenChart = () => {

{swapMarketName}

setFlipPrices(!flipPrices)} + onClick={() => + handleFlipPrices( + !flipPrices, + flipPrices, + inputBank.name, + outputBank.name, + swapChartSettings, + setSwapChartSettings, + ) + } hideBg > diff --git a/types/index.ts b/types/index.ts index ff55d4a9..ae6ed324 100644 --- a/types/index.ts +++ b/types/index.ts @@ -466,3 +466,8 @@ export interface ContributionDetails { perpMarketContributions: PerpMarketContribution[] spotUi: number } + +export type SwapChartSettings = { + flipPrices: boolean + pair: string +} diff --git a/utils/constants.ts b/utils/constants.ts index f2eedf7a..286dbf9c 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -6,10 +6,10 @@ export const CLIENT_TX_TIMEOUT = 90000 export const SECONDS = 1000 -export const INPUT_TOKEN_DEFAULT = 'USDC' +export const INPUT_TOKEN_DEFAULT = 'SOL' export const MANGO_MINT = 'MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac' export const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' -export const OUTPUT_TOKEN_DEFAULT = 'SOL' +export const OUTPUT_TOKEN_DEFAULT = 'MNGO' export const JUPITER_V4_PROGRAM_ID = 'JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB' @@ -57,6 +57,8 @@ export const SWAP_MARGIN_KEY = 'swapMargin-0.1' export const SHOW_SWAP_INTRO_MODAL = 'showSwapModal-0.1' +export const SWAP_CHART_SETTINGS_KEY = 'swapChartSettings-0.1' + export const ACCEPT_TERMS_KEY = 'termsOfUseAccepted-0.1' export const TRADE_LAYOUT_KEY = 'tradeLayoutKey-0.1'