save chart direction

This commit is contained in:
saml33 2023-08-03 20:34:57 +10:00
parent bb9354baa4
commit ce1939b63c
6 changed files with 204 additions and 96 deletions

View File

@ -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

View File

@ -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<FormErrors>({})
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 = ({
: ''}
</span>
</p>
<IconButton hideBg onClick={() => handleFlipPrices(!flipPrices)}>
<IconButton hideBg onClick={() => toggleFlipPrices(!flipPrices)}>
<ArrowsRightLeftIcon className="h-4 w-4" />
</IconButton>
</div>
@ -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 = ({
/>
<div className="absolute top-1/2 -translate-y-1/2 left-2">
<TokenLogo
bank={flipPrices ? inputBank : outputBank}
bank={flipPrices ? outputBank : inputBank}
size={16}
/>
</div>

View File

@ -122,6 +122,8 @@ const SwapOrders = () => {
}
}
console.log(orders)
return orders.length ? (
<Table>
<thead>

View File

@ -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<ChartDataItem>()
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 = () => {
<p className="text-base text-th-fgd-3">{swapMarketName}</p>
<IconButton
className="px-2 text-th-fgd-3"
onClick={() => setFlipPrices(!flipPrices)}
onClick={() =>
handleFlipPrices(
!flipPrices,
flipPrices,
inputBank.name,
outputBank.name,
swapChartSettings,
setSwapChartSettings,
)
}
hideBg
>
<ArrowsRightLeftIcon className="h-5 w-5" />

View File

@ -466,3 +466,8 @@ export interface ContributionDetails {
perpMarketContributions: PerpMarketContribution[]
spotUi: number
}
export type SwapChartSettings = {
flipPrices: boolean
pair: string
}

View File

@ -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'