2023-06-16 05:23:50 -07:00
|
|
|
import {
|
|
|
|
useState,
|
|
|
|
useCallback,
|
|
|
|
useEffect,
|
|
|
|
useMemo,
|
|
|
|
Dispatch,
|
|
|
|
SetStateAction,
|
|
|
|
} from 'react'
|
2023-08-01 22:32:20 -07:00
|
|
|
import { ArrowDownIcon, ArrowsRightLeftIcon } from '@heroicons/react/20/solid'
|
2023-06-16 05:23:50 -07:00
|
|
|
import NumberFormat, {
|
|
|
|
NumberFormatValues,
|
|
|
|
SourceInfo,
|
|
|
|
} from 'react-number-format'
|
|
|
|
import Decimal from 'decimal.js'
|
|
|
|
import mangoStore from '@store/mangoStore'
|
|
|
|
import { useTranslation } from 'next-i18next'
|
2023-07-25 19:48:13 -07:00
|
|
|
import { SIZE_INPUT_UI_KEY } from '../../utils/constants'
|
2023-06-16 05:23:50 -07:00
|
|
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
|
|
|
import SwapSlider from './SwapSlider'
|
|
|
|
import PercentageSelectButtons from './PercentageSelectButtons'
|
2023-07-25 19:48:13 -07:00
|
|
|
import { floorToDecimal } from 'utils/numbers'
|
|
|
|
import { withValueLimit } from './MarketSwapForm'
|
|
|
|
import SellTokenInput from './SellTokenInput'
|
|
|
|
import BuyTokenInput from './BuyTokenInput'
|
2023-07-30 23:05:26 -07:00
|
|
|
import { notify } from 'utils/notifications'
|
|
|
|
import * as sentry from '@sentry/nextjs'
|
|
|
|
import { isMangoError } from 'types'
|
2023-08-01 22:32:20 -07:00
|
|
|
import Button, { IconButton } from '@components/shared/Button'
|
2023-07-30 23:05:26 -07:00
|
|
|
import Loading from '@components/shared/Loading'
|
2023-07-31 05:25:46 -07:00
|
|
|
import TokenLogo from '@components/shared/TokenLogo'
|
2023-07-31 17:57:53 -07:00
|
|
|
import InlineNotification from '@components/shared/InlineNotification'
|
2023-06-16 05:23:50 -07:00
|
|
|
|
|
|
|
type LimitSwapFormProps = {
|
2023-08-01 22:32:20 -07:00
|
|
|
showTokenSelect: 'input' | 'output' | undefined
|
2023-06-16 05:23:50 -07:00
|
|
|
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
|
|
|
}
|
|
|
|
|
2023-07-31 17:57:53 -07:00
|
|
|
type LimitSwapForm = {
|
2023-08-01 22:32:20 -07:00
|
|
|
amountIn: number
|
2023-07-31 17:57:53 -07:00
|
|
|
triggerPrice: string
|
|
|
|
}
|
|
|
|
type FormErrors = Partial<Record<keyof LimitSwapForm, string>>
|
|
|
|
|
2023-07-25 19:48:13 -07:00
|
|
|
const set = mangoStore.getState().set
|
2023-06-16 05:23:50 -07:00
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
const LimitSwapForm = ({
|
|
|
|
showTokenSelect,
|
|
|
|
setShowTokenSelect,
|
|
|
|
}: LimitSwapFormProps) => {
|
2023-06-16 05:23:50 -07:00
|
|
|
const { t } = useTranslation(['common', 'swap', 'trade'])
|
|
|
|
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
|
|
|
|
const [triggerPrice, setTriggerPrice] = useState('')
|
2023-08-01 22:32:20 -07:00
|
|
|
const [flipPrices, setFlipPrices] = useState(false)
|
2023-07-30 23:05:26 -07:00
|
|
|
const [submitting, setSubmitting] = useState(false)
|
2023-06-16 05:23:50 -07:00
|
|
|
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
2023-07-31 17:57:53 -07:00
|
|
|
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
2023-06-16 05:23:50 -07:00
|
|
|
|
|
|
|
const {
|
|
|
|
margin: useMargin,
|
|
|
|
inputBank,
|
|
|
|
outputBank,
|
|
|
|
amountIn: amountInFormValue,
|
|
|
|
amountOut: amountOutFormValue,
|
|
|
|
limitPrice,
|
|
|
|
} = mangoStore((s) => s.swap)
|
|
|
|
|
|
|
|
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
2023-07-30 23:05:26 -07:00
|
|
|
return Number(amountInFormValue)
|
|
|
|
? new Decimal(amountInFormValue)
|
2023-06-16 05:23:50 -07:00
|
|
|
: new Decimal(0)
|
2023-07-30 23:05:26 -07:00
|
|
|
}, [amountInFormValue])
|
2023-06-16 05:23:50 -07:00
|
|
|
|
|
|
|
const setAmountInFormValue = useCallback((amountIn: string) => {
|
|
|
|
set((s) => {
|
|
|
|
s.swap.amountIn = amountIn
|
|
|
|
if (!parseFloat(amountIn)) {
|
|
|
|
s.swap.amountOut = ''
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
const setAmountOutFormValue = useCallback((amountOut: string) => {
|
|
|
|
set((s) => {
|
|
|
|
s.swap.amountOut = amountOut
|
|
|
|
if (!parseFloat(amountOut)) {
|
|
|
|
s.swap.amountIn = ''
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}, [])
|
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
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])
|
2023-07-31 17:57:53 -07:00
|
|
|
|
2023-07-31 05:25:46 -07:00
|
|
|
// set default limit and trigger price
|
2023-07-30 23:05:26 -07:00
|
|
|
useEffect(() => {
|
2023-08-01 22:32:20 -07:00
|
|
|
if (!quotePrice) return
|
|
|
|
if (!triggerPrice && !showTokenSelect) {
|
|
|
|
setTriggerPrice(quotePrice.toFixed(outputBank?.mintDecimals))
|
2023-07-30 23:05:26 -07:00
|
|
|
}
|
2023-08-01 22:32:20 -07:00
|
|
|
}, [quotePrice, outputBank, showTokenSelect, triggerPrice])
|
2023-07-31 17:57:53 -07:00
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
const triggerPriceDifference = useMemo(() => {
|
|
|
|
if ((!flipPrices && !quotePrice) || (flipPrices && !flippedQuotePrice))
|
|
|
|
return 0
|
|
|
|
const oraclePrice = !flipPrices ? quotePrice : flippedQuotePrice
|
2023-07-31 17:57:53 -07:00
|
|
|
const triggerDifference = triggerPrice
|
2023-08-01 22:32:20 -07:00
|
|
|
? ((parseFloat(triggerPrice) - oraclePrice) / oraclePrice) * 100
|
2023-07-31 17:57:53 -07:00
|
|
|
: 0
|
2023-08-01 22:32:20 -07:00
|
|
|
return triggerDifference
|
|
|
|
}, [flippedQuotePrice, quotePrice, triggerPrice])
|
|
|
|
|
|
|
|
const handleTokenSelect = (type: 'input' | 'output') => {
|
|
|
|
setShowTokenSelect(type)
|
|
|
|
setTriggerPrice('')
|
|
|
|
}
|
|
|
|
|
|
|
|
const isFormValid = useCallback((form: LimitSwapForm) => {
|
|
|
|
const invalidFields: FormErrors = {}
|
|
|
|
setFormErrors({})
|
|
|
|
const requiredFields: (keyof LimitSwapForm)[] = ['amountIn', 'triggerPrice']
|
|
|
|
for (const key of requiredFields) {
|
|
|
|
const value = form[key] as string
|
|
|
|
if (!value) {
|
|
|
|
invalidFields[key] = t('settings:error-required-field')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Object.keys(invalidFields).length) {
|
|
|
|
setFormErrors(invalidFields)
|
|
|
|
}
|
|
|
|
return invalidFields
|
|
|
|
}, [])
|
2023-07-30 23:05:26 -07:00
|
|
|
|
2023-06-16 05:23:50 -07:00
|
|
|
/*
|
|
|
|
If the use margin setting is toggled, clear the form values
|
|
|
|
*/
|
|
|
|
useEffect(() => {
|
|
|
|
setAmountInFormValue('')
|
|
|
|
setAmountOutFormValue('')
|
|
|
|
}, [useMargin, setAmountInFormValue, setAmountOutFormValue])
|
|
|
|
|
2023-08-01 03:49:01 -07:00
|
|
|
// get the out amount from the in amount and trigger or limit price
|
|
|
|
const getAmountOut = useCallback(
|
|
|
|
(amountIn: string, price: string) => {
|
2023-08-01 22:32:20 -07:00
|
|
|
const amountOut = !flipPrices
|
|
|
|
? floorToDecimal(
|
|
|
|
parseFloat(amountIn) * parseFloat(price),
|
|
|
|
outputBank?.mintDecimals || 0,
|
|
|
|
)
|
|
|
|
: floorToDecimal(
|
|
|
|
parseFloat(amountIn) / parseFloat(price),
|
|
|
|
outputBank?.mintDecimals || 0,
|
|
|
|
)
|
2023-08-01 03:49:01 -07:00
|
|
|
return amountOut
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[outputBank, flipPrices],
|
2023-08-01 03:49:01 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
// get the in amount from the out amount and trigger or limit price
|
|
|
|
const getAmountIn = useCallback(
|
|
|
|
(amountOut: string, price: string) => {
|
2023-08-01 22:32:20 -07:00
|
|
|
const amountIn = !flipPrices
|
|
|
|
? floorToDecimal(
|
|
|
|
parseFloat(amountOut) / parseFloat(price),
|
|
|
|
inputBank?.mintDecimals || 0,
|
|
|
|
)
|
|
|
|
: floorToDecimal(
|
|
|
|
parseFloat(amountOut) * parseFloat(price),
|
|
|
|
inputBank?.mintDecimals || 0,
|
|
|
|
)
|
2023-08-01 03:49:01 -07:00
|
|
|
return amountIn
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[inputBank, outputBank, flipPrices],
|
2023-08-01 03:49:01 -07:00
|
|
|
)
|
|
|
|
|
2023-08-01 05:21:19 -07:00
|
|
|
const handleMax = useCallback(
|
|
|
|
(amountIn: string) => {
|
|
|
|
setAmountInFormValue(amountIn)
|
2023-08-01 22:32:20 -07:00
|
|
|
if (parseFloat(amountIn) > 0 && triggerPrice) {
|
|
|
|
const amountOut = getAmountOut(amountIn, triggerPrice)
|
2023-08-01 05:21:19 -07:00
|
|
|
setAmountOutFormValue(amountOut.toString())
|
|
|
|
}
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
2023-08-01 05:21:19 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const handleRepay = useCallback(
|
|
|
|
(amountOut: string) => {
|
|
|
|
setAmountOutFormValue(amountOut)
|
2023-08-01 22:32:20 -07:00
|
|
|
if (parseFloat(amountOut) > 0 && triggerPrice) {
|
|
|
|
const amountIn = getAmountIn(amountOut, triggerPrice)
|
2023-08-01 05:21:19 -07:00
|
|
|
setAmountInFormValue(amountIn.toString())
|
|
|
|
}
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
2023-08-01 05:21:19 -07:00
|
|
|
)
|
|
|
|
|
2023-06-16 05:23:50 -07:00
|
|
|
const handleAmountInChange = useCallback(
|
|
|
|
(e: NumberFormatValues, info: SourceInfo) => {
|
|
|
|
if (info.source !== 'event') return
|
2023-08-01 22:32:20 -07:00
|
|
|
setFormErrors({})
|
2023-06-16 05:23:50 -07:00
|
|
|
setAmountInFormValue(e.value)
|
2023-08-01 22:32:20 -07:00
|
|
|
if (parseFloat(e.value) > 0 && triggerPrice) {
|
|
|
|
const amountOut = getAmountOut(e.value, triggerPrice)
|
2023-08-01 03:49:01 -07:00
|
|
|
setAmountOutFormValue(amountOut.toString())
|
2023-06-16 05:23:50 -07:00
|
|
|
}
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
2023-06-16 05:23:50 -07:00
|
|
|
)
|
|
|
|
|
2023-06-17 05:22:05 -07:00
|
|
|
const handleAmountOutChange = useCallback(
|
|
|
|
(e: NumberFormatValues, info: SourceInfo) => {
|
|
|
|
if (info.source !== 'event') return
|
|
|
|
setAmountOutFormValue(e.value)
|
2023-08-01 22:32:20 -07:00
|
|
|
if (parseFloat(e.value) > 0 && triggerPrice) {
|
|
|
|
const amountIn = getAmountIn(e.value, triggerPrice)
|
2023-08-01 03:49:01 -07:00
|
|
|
setAmountInFormValue(amountIn.toString())
|
2023-06-17 05:22:05 -07:00
|
|
|
}
|
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
2023-06-17 05:22:05 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const handleAmountInUi = useCallback(
|
2023-06-16 05:23:50 -07:00
|
|
|
(amountIn: string) => {
|
|
|
|
setAmountInFormValue(amountIn)
|
2023-07-31 17:57:53 -07:00
|
|
|
setFormErrors({})
|
2023-08-01 22:32:20 -07:00
|
|
|
if (triggerPrice) {
|
|
|
|
const amountOut = getAmountOut(amountIn, triggerPrice)
|
2023-08-01 03:49:01 -07:00
|
|
|
setAmountOutFormValue(amountOut.toString())
|
2023-06-17 05:22:05 -07:00
|
|
|
}
|
2023-06-16 05:23:50 -07:00
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
2023-06-16 05:23:50 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
const handleTriggerPrice = useCallback(
|
|
|
|
(e: NumberFormatValues, info: SourceInfo) => {
|
|
|
|
if (info.source !== 'event') return
|
2023-07-31 17:57:53 -07:00
|
|
|
setFormErrors({})
|
2023-06-16 05:23:50 -07:00
|
|
|
setTriggerPrice(e.value)
|
2023-08-01 22:32:20 -07:00
|
|
|
if (parseFloat(e.value) > 0 && parseFloat(amountInFormValue) > 0) {
|
2023-08-01 03:49:01 -07:00
|
|
|
const amountOut = getAmountOut(amountInFormValue, e.value)
|
|
|
|
setAmountOutFormValue(amountOut.toString())
|
2023-07-31 17:57:53 -07:00
|
|
|
}
|
2023-06-16 05:23:50 -07:00
|
|
|
},
|
2023-08-01 22:32:20 -07:00
|
|
|
[amountInFormValue, flipPrices, setTriggerPrice],
|
2023-06-16 05:23:50 -07:00
|
|
|
)
|
|
|
|
|
2023-07-31 17:57:53 -07:00
|
|
|
const handleSwitchTokens = useCallback(() => {
|
2023-08-01 22:32:20 -07:00
|
|
|
if (amountInAsDecimal?.gt(0) && triggerPrice) {
|
|
|
|
const amountOut = amountInAsDecimal.div(triggerPrice)
|
2023-07-31 17:57:53 -07:00
|
|
|
setAmountOutFormValue(amountOut.toString())
|
|
|
|
}
|
|
|
|
set((s) => {
|
|
|
|
s.swap.inputBank = outputBank
|
|
|
|
s.swap.outputBank = inputBank
|
|
|
|
})
|
2023-08-01 22:32:20 -07:00
|
|
|
|
|
|
|
if (flippedQuotePrice) {
|
|
|
|
setTriggerPrice(flippedQuotePrice.toFixed(inputBank?.mintDecimals))
|
|
|
|
}
|
2023-07-31 17:57:53 -07:00
|
|
|
setAnimateSwitchArrow(
|
|
|
|
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
|
|
|
|
)
|
|
|
|
}, [
|
|
|
|
setAmountInFormValue,
|
|
|
|
amountInAsDecimal,
|
2023-08-01 22:32:20 -07:00
|
|
|
flipPrices,
|
|
|
|
flippedQuotePrice,
|
2023-07-31 17:57:53 -07:00
|
|
|
inputBank,
|
|
|
|
outputBank,
|
2023-08-01 22:32:20 -07:00
|
|
|
triggerPrice,
|
2023-07-31 17:57:53 -07:00
|
|
|
])
|
|
|
|
|
|
|
|
const handlePlaceStopLoss = useCallback(async () => {
|
2023-08-01 22:32:20 -07:00
|
|
|
const invalidFields = isFormValid({
|
|
|
|
amountIn: amountInAsDecimal.toNumber(),
|
|
|
|
triggerPrice,
|
|
|
|
})
|
|
|
|
if (Object.keys(invalidFields).length) {
|
|
|
|
return
|
|
|
|
}
|
2023-07-30 23:05:26 -07:00
|
|
|
try {
|
|
|
|
const client = mangoStore.getState().client
|
|
|
|
const group = mangoStore.getState().group
|
|
|
|
const actions = mangoStore.getState().actions
|
|
|
|
const mangoAccount = mangoStore.getState().mangoAccount.current
|
|
|
|
const inputBank = mangoStore.getState().swap.inputBank
|
|
|
|
const outputBank = mangoStore.getState().swap.outputBank
|
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
if (!mangoAccount || !group || !inputBank || !outputBank || !triggerPrice)
|
2023-07-30 23:05:26 -07:00
|
|
|
return
|
|
|
|
setSubmitting(true)
|
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
const inputMint = !flipPrices ? inputBank.mint : outputBank.mint
|
|
|
|
const outputMint = !flipPrices ? outputBank.mint : inputBank.mint
|
|
|
|
const amountIn = !flipPrices
|
|
|
|
? amountInAsDecimal.toNumber()
|
|
|
|
: parseFloat(amountOutFormValue)
|
2023-07-30 23:05:26 -07:00
|
|
|
|
|
|
|
try {
|
|
|
|
const tx = await client.tokenConditionalSwapStopLoss(
|
|
|
|
group,
|
|
|
|
mangoAccount,
|
2023-08-01 22:32:20 -07:00
|
|
|
inputMint,
|
|
|
|
parseFloat(triggerPrice),
|
|
|
|
outputMint,
|
|
|
|
null,
|
|
|
|
amountIn,
|
2023-07-30 23:05:26 -07:00
|
|
|
null,
|
|
|
|
null,
|
|
|
|
)
|
|
|
|
notify({
|
|
|
|
title: 'Transaction confirmed',
|
|
|
|
type: 'success',
|
|
|
|
txid: tx,
|
|
|
|
noSound: true,
|
|
|
|
})
|
|
|
|
actions.fetchGroup()
|
|
|
|
await actions.reloadMangoAccount()
|
|
|
|
} catch (e) {
|
|
|
|
console.error('onSwap error: ', e)
|
|
|
|
sentry.captureException(e)
|
|
|
|
if (isMangoError(e)) {
|
|
|
|
notify({
|
|
|
|
title: 'Transaction failed',
|
|
|
|
description: e.message,
|
|
|
|
txid: e?.txid,
|
|
|
|
type: 'error',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (e) {
|
|
|
|
console.error('Swap error:', e)
|
|
|
|
} finally {
|
|
|
|
setSubmitting(false)
|
|
|
|
}
|
2023-08-01 22:32:20 -07:00
|
|
|
}, [
|
|
|
|
flipPrices,
|
|
|
|
limitPrice,
|
|
|
|
triggerPrice,
|
|
|
|
amountInAsDecimal,
|
|
|
|
amountOutFormValue,
|
|
|
|
])
|
2023-07-30 23:05:26 -07:00
|
|
|
|
2023-08-01 22:32:20 -07:00
|
|
|
const triggerPriceLabel = useMemo(() => {
|
|
|
|
if (!inputBank || !outputBank) return t('trade:trigger-price')
|
|
|
|
if (inputBank.name === 'USDC') {
|
|
|
|
return t('trade:trigger-order-rate', {
|
|
|
|
side: t('buy').toLowerCase(),
|
|
|
|
symbol: outputBank.name,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
return t('trade:trigger-order-rate', {
|
|
|
|
side: t('sell').toLowerCase(),
|
|
|
|
symbol: inputBank.name,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}, [inputBank, outputBank])
|
|
|
|
|
|
|
|
const handleFlipPrices = useCallback(
|
|
|
|
(flip: boolean) => {
|
|
|
|
setFlipPrices(flip)
|
|
|
|
if (flip) {
|
|
|
|
setTriggerPrice(flippedQuotePrice.toFixed(inputBank?.mintDecimals))
|
|
|
|
} else {
|
|
|
|
setTriggerPrice(quotePrice.toFixed(outputBank?.mintDecimals))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
[flippedQuotePrice, inputBank, outputBank, quotePrice],
|
|
|
|
)
|
2023-06-16 05:23:50 -07:00
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
2023-07-25 19:48:13 -07:00
|
|
|
<SellTokenInput
|
|
|
|
className="rounded-b-none"
|
2023-08-01 22:32:20 -07:00
|
|
|
error={formErrors.amountIn}
|
2023-07-25 19:48:13 -07:00
|
|
|
handleAmountInChange={handleAmountInChange}
|
2023-08-01 22:32:20 -07:00
|
|
|
setShowTokenSelect={() => handleTokenSelect('input')}
|
2023-08-01 05:21:19 -07:00
|
|
|
handleMax={handleMax}
|
2023-07-25 19:48:13 -07:00
|
|
|
/>
|
2023-06-16 05:23:50 -07:00
|
|
|
<div
|
2023-08-01 22:32:20 -07:00
|
|
|
className={`grid grid-cols-2 gap-2 rounded-b-xl bg-th-bkg-2 p-3 pt-1`}
|
2023-06-16 05:23:50 -07:00
|
|
|
id="swap-step-two"
|
|
|
|
>
|
2023-08-01 22:32:20 -07:00
|
|
|
<div className="col-span-2">
|
|
|
|
<p className="mb-2 text-th-fgd-2">
|
|
|
|
{triggerPriceLabel}{' '}
|
|
|
|
<span className="text-xs text-th-fgd-3">
|
|
|
|
{triggerPriceDifference
|
|
|
|
? `(${triggerPriceDifference.toFixed(2)}%)`
|
|
|
|
: ''}
|
|
|
|
</span>
|
|
|
|
</p>
|
|
|
|
<div className="relative">
|
|
|
|
<NumberFormat
|
|
|
|
inputMode="decimal"
|
|
|
|
thousandSeparator=","
|
|
|
|
allowNegative={false}
|
|
|
|
isNumericString={true}
|
|
|
|
decimalScale={outputBank?.mintDecimals || 6}
|
|
|
|
name="triggerPrice"
|
|
|
|
id="triggerPrice"
|
|
|
|
className="h-10 w-full rounded-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"
|
|
|
|
placeholder="0.00"
|
|
|
|
value={triggerPrice}
|
|
|
|
onValueChange={handleTriggerPrice}
|
|
|
|
isAllowed={withValueLimit}
|
|
|
|
/>
|
|
|
|
<div className="absolute top-1/2 -translate-y-1/2 left-2">
|
|
|
|
<TokenLogo bank={flipPrices ? inputBank : outputBank} size={16} />
|
|
|
|
</div>
|
|
|
|
<div className="absolute top-1/2 -translate-y-1/2 right-2">
|
|
|
|
<IconButton hideBg onClick={() => handleFlipPrices(!flipPrices)}>
|
|
|
|
<ArrowsRightLeftIcon className="h-4 w-4" />
|
|
|
|
</IconButton>
|
2023-07-31 05:25:46 -07:00
|
|
|
</div>
|
2023-06-16 05:23:50 -07:00
|
|
|
</div>
|
2023-08-01 22:32:20 -07:00
|
|
|
{formErrors.triggerPrice ? (
|
|
|
|
<div className="mt-1">
|
|
|
|
<InlineNotification
|
|
|
|
type="error"
|
|
|
|
desc={formErrors.triggerPrice}
|
|
|
|
hideBorder
|
|
|
|
hidePadding
|
2023-07-31 05:25:46 -07:00
|
|
|
/>
|
|
|
|
</div>
|
2023-08-01 22:32:20 -07:00
|
|
|
) : null}
|
|
|
|
</div>
|
2023-06-16 05:23:50 -07:00
|
|
|
</div>
|
|
|
|
<div className="my-2 flex justify-center">
|
|
|
|
<button
|
|
|
|
className="rounded-full border border-th-fgd-4 p-1.5 text-th-fgd-3 focus-visible:border-th-active md:hover:border-th-active md:hover:text-th-active"
|
|
|
|
onClick={handleSwitchTokens}
|
|
|
|
>
|
|
|
|
<ArrowDownIcon
|
|
|
|
className="h-5 w-5"
|
|
|
|
style={
|
|
|
|
animateSwitchArrow % 2 == 0
|
|
|
|
? { transform: 'rotate(0deg)' }
|
|
|
|
: { transform: 'rotate(360deg)' }
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</button>
|
|
|
|
</div>
|
2023-07-25 19:48:13 -07:00
|
|
|
<BuyTokenInput
|
|
|
|
handleAmountOutChange={handleAmountOutChange}
|
2023-08-01 22:32:20 -07:00
|
|
|
setShowTokenSelect={() => handleTokenSelect('output')}
|
2023-08-01 05:21:19 -07:00
|
|
|
handleRepay={handleRepay}
|
2023-07-25 19:48:13 -07:00
|
|
|
/>
|
2023-06-16 05:23:50 -07:00
|
|
|
{swapFormSizeUi === 'slider' ? (
|
|
|
|
<SwapSlider
|
|
|
|
useMargin={useMargin}
|
|
|
|
amount={amountInAsDecimal.toNumber()}
|
2023-06-17 05:22:05 -07:00
|
|
|
onChange={(v) => handleAmountInUi(v)}
|
2023-06-16 05:23:50 -07:00
|
|
|
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
|
|
|
|
/>
|
|
|
|
) : (
|
|
|
|
<PercentageSelectButtons
|
|
|
|
amountIn={amountInAsDecimal.toString()}
|
2023-06-17 05:22:05 -07:00
|
|
|
setAmountIn={(v) => handleAmountInUi(v)}
|
2023-06-16 05:23:50 -07:00
|
|
|
useMargin={useMargin}
|
|
|
|
/>
|
|
|
|
)}
|
2023-07-30 23:05:26 -07:00
|
|
|
<Button
|
2023-07-31 17:57:53 -07:00
|
|
|
onClick={handlePlaceStopLoss}
|
2023-07-30 23:05:26 -07:00
|
|
|
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
|
|
|
size="large"
|
|
|
|
>
|
|
|
|
{submitting ? <Loading /> : t('swap:place-limit-order')}
|
|
|
|
</Button>
|
2023-06-16 05:23:50 -07:00
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
export default LimitSwapForm
|