mango-v4-ui/components/swap/LimitSwapForm.tsx

810 lines
25 KiB
TypeScript
Raw Normal View History

import {
useState,
useCallback,
useEffect,
useMemo,
Dispatch,
SetStateAction,
2023-08-03 19:06:09 -07:00
useLayoutEffect,
} from 'react'
2023-08-01 22:32:20 -07:00
import { ArrowDownIcon, ArrowsRightLeftIcon } from '@heroicons/react/20/solid'
import NumberFormat, {
NumberFormatValues,
SourceInfo,
} from 'react-number-format'
import Decimal from 'decimal.js'
import mangoStore from '@store/mangoStore'
import { useTranslation } from 'next-i18next'
2023-08-03 03:34:57 -07:00
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'
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-08-07 17:09:05 -07:00
// import { notify } from 'utils/notifications'
// import * as sentry from '@sentry/nextjs'
// import { isMangoError } from 'types'
2023-08-04 05:03:19 -07:00
import Button, { LinkButton } from '@components/shared/Button'
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'
import { getChartPairSettings, handleFlipPrices } from './SwapTokenChart'
2023-08-03 06:44:23 -07:00
import Select from '@components/forms/Select'
2023-08-03 19:06:09 -07:00
import useIpAddress from 'hooks/useIpAddress'
2023-08-07 17:09:05 -07:00
// import { Bank } from '@blockworks-foundation/mango-v4'
type LimitSwapFormProps = {
2023-08-01 22:32:20 -07:00
showTokenSelect: 'input' | 'output' | undefined
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-08-03 23:14:41 -07:00
hasBorrows: number | undefined
2023-07-31 17:57:53 -07:00
triggerPrice: string
}
2023-08-03 23:14:41 -07:00
2023-07-31 17:57:53 -07:00
type FormErrors = Partial<Record<keyof LimitSwapForm, string>>
2023-08-03 23:14:41 -07:00
type OrderTypeMultiplier = 0.9 | 1 | 1.1
2023-08-03 06:44:23 -07:00
enum OrderTypes {
STOP_LOSS = 'trade:stop-loss',
TAKE_PROFIT = 'trade:take-profit',
REPAY_BORROW = 'repay-borrow',
}
const ORDER_TYPES = [
OrderTypes.STOP_LOSS,
OrderTypes.TAKE_PROFIT,
OrderTypes.REPAY_BORROW,
]
2023-07-25 19:48:13 -07:00
const set = mangoStore.getState().set
2023-08-07 17:09:05 -07:00
// const getSellTokenBalance = (inputBank: Bank | undefined) => {
// const mangoAccount = mangoStore.getState().mangoAccount.current
// if (!inputBank || !mangoAccount) return 0
// const balance = mangoAccount.getTokenBalanceUi(inputBank)
// return balance
// }
2023-08-03 23:14:41 -07:00
const getOrderTypeMultiplier = (orderType: OrderTypes, flipPrices: boolean) => {
if (orderType === OrderTypes.STOP_LOSS) {
return flipPrices ? 0.9 : 1.1
} else if (orderType === OrderTypes.TAKE_PROFIT) {
return flipPrices ? 1.1 : 0.9
} else {
return 1
}
}
2023-08-01 22:32:20 -07:00
const LimitSwapForm = ({
showTokenSelect,
setShowTokenSelect,
}: LimitSwapFormProps) => {
const { t } = useTranslation(['common', 'swap', 'trade'])
2023-08-03 19:06:09 -07:00
const { ipAllowed, ipCountry } = useIpAddress()
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
const [triggerPrice, setTriggerPrice] = useState('')
2023-08-03 06:44:23 -07:00
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
2023-08-03 23:14:41 -07:00
const [orderTypeMultiplier, setOrderTypeMultiplier] =
useState<OrderTypeMultiplier | null>(null)
2023-08-07 17:09:05 -07:00
const [
submitting,
// setSubmitting
] = useState(false)
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
2023-07-31 17:57:53 -07:00
const [formErrors, setFormErrors] = useState<FormErrors>({})
2023-08-03 03:34:57 -07:00
const [swapChartSettings, setSwapChartSettings] = useLocalStorageState(
SWAP_CHART_SETTINGS_KEY,
[],
)
const {
inputBank,
outputBank,
amountIn: amountInFormValue,
amountOut: amountOutFormValue,
2023-08-07 17:09:05 -07:00
// limitPrice,
} = mangoStore((s) => s.swap)
const [inputBankName, outputBankName, inputBankDecimals, outputBankDecimals] =
useMemo(() => {
if (!inputBank || !outputBank) return ['', '', 0, 0]
return [
inputBank.name,
outputBank.name,
inputBank.mintDecimals,
outputBank.mintDecimals,
]
}, [inputBank, outputBank])
const amountInAsDecimal: Decimal | null = useMemo(() => {
return Number(amountInFormValue)
? new Decimal(amountInFormValue)
: new Decimal(0)
}, [amountInFormValue])
2023-08-03 03:34:57 -07:00
const flipPrices = useMemo(() => {
if (!swapChartSettings.length || !inputBankName || !outputBankName)
return false
const pairSettings = getChartPairSettings(
swapChartSettings,
inputBankName,
outputBankName,
2023-08-03 03:34:57 -07:00
)
if (pairSettings) {
return pairSettings.quote === inputBankName
2023-08-03 03:34:57 -07:00
} else return false
}, [swapChartSettings, inputBankName, outputBankName])
2023-08-03 03:34:57 -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-03 03:34:57 -07:00
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])
2023-07-31 17:57:53 -07:00
2023-08-03 06:44:23 -07:00
// set default trigger price
useEffect(() => {
if (!quotePrice || triggerPrice || showTokenSelect) return
const multiplier = getOrderTypeMultiplier(OrderTypes.STOP_LOSS, flipPrices)
const decimals = !flipPrices ? inputBankDecimals : outputBankDecimals
setTriggerPrice((quotePrice * multiplier).toFixed(decimals))
}, [
flipPrices,
inputBankDecimals,
outputBankDecimals,
quotePrice,
showTokenSelect,
triggerPrice,
])
2023-07-31 17:57:53 -07:00
2023-08-03 23:14:41 -07:00
// flip trigger price and set amount out when chart direction is flipped
2023-08-03 19:06:09 -07:00
useLayoutEffect(() => {
if (!quotePrice) return
2023-08-03 23:14:41 -07:00
const multiplier = getOrderTypeMultiplier(orderType, flipPrices)
const decimals = flipPrices ? outputBankDecimals : inputBankDecimals
2023-08-03 23:14:41 -07:00
const price = (quotePrice * multiplier).toFixed(decimals)
setTriggerPrice(price)
if (amountInAsDecimal?.gt(0)) {
const amountOut = getAmountOut(amountInAsDecimal.toString(), price)
setAmountOutFormValue(amountOut.toString())
}
}, [flipPrices, inputBankDecimals, orderType, outputBankDecimals])
2023-08-03 19:06:09 -07:00
2023-08-01 22:32:20 -07:00
const triggerPriceDifference = useMemo(() => {
2023-08-03 03:34:57 -07:00
if (!quotePrice) return 0
2023-07-31 17:57:53 -07:00
const triggerDifference = triggerPrice
2023-08-03 03:34:57 -07:00
? ((parseFloat(triggerPrice) - quotePrice) / quotePrice) * 100
2023-07-31 17:57:53 -07:00
: 0
2023-08-01 22:32:20 -07:00
return triggerDifference
2023-08-03 03:34:57 -07:00
}, [flipPrices, quotePrice, triggerPrice])
2023-08-01 22:32:20 -07:00
const handleTokenSelect = (type: 'input' | 'output') => {
setShowTokenSelect(type)
setTriggerPrice('')
}
2023-08-03 23:14:41 -07:00
const hasBorrowToRepay = useMemo(() => {
const mangoAccount = mangoStore.getState().mangoAccount.current
if (orderType !== OrderTypes.REPAY_BORROW || !outputBank || !mangoAccount)
return
const borrow = mangoAccount.getTokenBorrowsUi(outputBank)
return borrow
}, [orderType, outputBank])
2023-08-07 17:09:05 -07:00
// const isFormValid = useCallback(
// (form: LimitSwapForm) => {
// const invalidFields: FormErrors = {}
// setFormErrors({})
// const requiredFields: (keyof LimitSwapForm)[] = [
// 'amountIn',
// 'triggerPrice',
// ]
// const triggerPriceNumber = parseFloat(form.triggerPrice)
// const sellTokenBalance = getSellTokenBalance(inputBank)
// for (const key of requiredFields) {
// const value = form[key] as string
// if (!value) {
// invalidFields[key] = t('settings:error-required-field')
// }
// }
// if (orderType === OrderTypes.STOP_LOSS) {
// if (!flipPrices && triggerPriceNumber <= quotePrice) {
// invalidFields.triggerPrice =
// 'Trigger price must be above oracle price'
// }
// if (flipPrices && triggerPriceNumber >= quotePrice) {
// invalidFields.triggerPrice =
// 'Trigger price must be below oracle price'
// }
// }
// if (orderType === OrderTypes.TAKE_PROFIT) {
// if (!flipPrices && triggerPriceNumber >= quotePrice) {
// invalidFields.triggerPrice =
// 'Trigger price must be below oracle price'
// }
// if (flipPrices && triggerPriceNumber <= quotePrice) {
// invalidFields.triggerPrice =
// 'Trigger price must be above oracle price'
// }
// }
// if (orderType === OrderTypes.REPAY_BORROW && !hasBorrowToRepay) {
// invalidFields.hasBorrows = t('swap:no-borrow')
// }
// if (form.amountIn > sellTokenBalance) {
// invalidFields.amountIn = t('swap:insufficient-balance', {
// symbol: inputBank?.name,
// })
// }
// if (Object.keys(invalidFields).length) {
// setFormErrors(invalidFields)
// }
// return invalidFields
// },
// [
// flipPrices,
// hasBorrowToRepay,
// inputBank,
// orderType,
// quotePrice,
// setFormErrors,
// ],
// )
2023-08-03 23:14:41 -07:00
// set order type multiplier on page load
useEffect(() => {
if (!orderTypeMultiplier) {
const multiplier = getOrderTypeMultiplier(orderType, flipPrices)
setOrderTypeMultiplier(multiplier)
2023-08-01 22:32:20 -07:00
}
2023-08-03 23:14:41 -07:00
}, [flipPrices, orderType, orderTypeMultiplier])
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(
2023-08-03 03:34:57 -07:00
parseFloat(amountIn) / parseFloat(price),
2023-08-01 22:32:20 -07:00
outputBank?.mintDecimals || 0,
)
: floorToDecimal(
2023-08-03 03:34:57 -07:00
parseFloat(amountIn) * parseFloat(price),
2023-08-01 22:32:20 -07:00
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(
2023-08-03 03:34:57 -07:00
parseFloat(amountOut) * parseFloat(price),
2023-08-01 22:32:20 -07:00
inputBank?.mintDecimals || 0,
)
: floorToDecimal(
2023-08-03 03:34:57 -07:00
parseFloat(amountOut) / parseFloat(price),
2023-08-01 22:32:20 -07:00
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
)
const handleAmountInChange = useCallback(
(e: NumberFormatValues, info: SourceInfo) => {
if (info.source !== 'event') return
2023-08-01 22:32:20 -07:00
setFormErrors({})
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())
}
},
[
getAmountOut,
setAmountInFormValue,
setAmountOutFormValue,
setFormErrors,
triggerPrice,
],
)
2023-06-17 05:22:05 -07:00
const handleAmountOutChange = useCallback(
(e: NumberFormatValues, info: SourceInfo) => {
if (info.source !== 'event') return
2023-08-03 23:14:41 -07:00
setFormErrors({})
2023-06-17 05:22:05 -07:00
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
}
},
[
getAmountIn,
setAmountInFormValue,
setAmountOutFormValue,
setFormErrors,
triggerPrice,
],
2023-06-17 05:22:05 -07:00
)
const handleAmountInUi = useCallback(
(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
}
},
[
getAmountOut,
setAmountInFormValue,
setAmountOutFormValue,
setFormErrors,
triggerPrice,
],
)
const handleTriggerPrice = useCallback(
(e: NumberFormatValues, info: SourceInfo) => {
if (info.source !== 'event') return
2023-07-31 17:57:53 -07:00
setFormErrors({})
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
}
},
[amountInFormValue, flipPrices, setFormErrors, setTriggerPrice],
)
2023-07-31 17:57:53 -07:00
const handleSwitchTokens = useCallback(() => {
2023-08-03 03:34:57 -07:00
if (!inputBank || !outputBank) return
2023-08-03 23:14:41 -07:00
setFormErrors({})
2023-07-31 17:57:53 -07:00
set((s) => {
s.swap.inputBank = outputBank
s.swap.outputBank = inputBank
})
2023-08-03 23:14:41 -07:00
const multiplier = getOrderTypeMultiplier(orderType, flipPrices)
2023-08-03 03:34:57 -07:00
const price = !flipPrices
? floorToDecimal(
2023-08-03 23:14:41 -07:00
(inputBank.uiPrice / outputBank.uiPrice) * multiplier,
2023-08-03 03:34:57 -07:00
outputBank.mintDecimals,
).toString()
: floorToDecimal(
2023-08-03 23:14:41 -07:00
(outputBank.uiPrice / inputBank.uiPrice) * multiplier,
2023-08-03 03:34:57 -07:00
inputBank.mintDecimals,
).toString()
setTriggerPrice(price)
if (amountInAsDecimal?.gt(0)) {
const amountOut = getAmountOut(amountInAsDecimal.toString(), price)
setAmountOutFormValue(amountOut.toString())
2023-08-01 22:32:20 -07:00
}
2023-07-31 17:57:53 -07:00
setAnimateSwitchArrow(
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
)
}, [
amountInAsDecimal,
2023-08-01 22:32:20 -07:00
flipPrices,
2023-07-31 17:57:53 -07:00
inputBank,
2023-08-03 23:14:41 -07:00
orderType,
2023-07-31 17:57:53 -07:00
outputBank,
setAmountInFormValue,
setFormErrors,
2023-08-01 22:32:20 -07:00
triggerPrice,
2023-07-31 17:57:53 -07:00
])
2023-08-07 17:09:05 -07:00
// const handlePlaceStopLoss = useCallback(async () => {
// const invalidFields = isFormValid({
// amountIn: amountInAsDecimal.toNumber(),
// hasBorrows: hasBorrowToRepay,
// triggerPrice,
// })
// if (Object.keys(invalidFields).length) {
// return
// }
// 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
// if (!mangoAccount || !group || !inputBank || !outputBank || !triggerPrice)
// return
// setSubmitting(true)
// const inputMint = inputBank.mint
// const outputMint = outputBank.mint
// const amountIn = amountInAsDecimal.toNumber()
// try {
// const tx = await client.tokenConditionalSwapStopLoss(
// group,
// mangoAccount,
// inputMint,
// parseFloat(triggerPrice),
// outputMint,
// null,
// amountIn,
// 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)
// }
// }, [
// hasBorrowToRepay,
// flipPrices,
// limitPrice,
// triggerPrice,
// amountInAsDecimal,
// amountOutFormValue,
// ])
2023-08-02 06:20:09 -07:00
const orderDescription = useMemo(() => {
if (
!amountInFormValue ||
!amountOutFormValue ||
!inputBankName ||
!outputBankName ||
2023-08-02 06:20:09 -07:00
!triggerPrice
)
return
2023-08-03 19:06:09 -07:00
2023-08-02 06:20:09 -07:00
const quoteString = !flipPrices
? `${inputBankName} per ${outputBankName}`
: `${outputBankName} per ${inputBankName}`
2023-08-03 19:06:09 -07:00
const orderTypeString =
orderType === OrderTypes.STOP_LOSS
? t('trade:falls-to')
: t('trade:rises-to')
if (orderType === OrderTypes.REPAY_BORROW) {
return t('trade:repay-borrow-order-desc', {
amount: floorToDecimal(amountOutFormValue, outputBankDecimals),
2023-08-03 19:06:09 -07:00
priceUnit: quoteString,
symbol: outputBankName,
triggerPrice: floorToDecimal(triggerPrice, inputBankDecimals),
2023-08-03 19:06:09 -07:00
})
} else if (inputBankName === 'USDC') {
2023-08-03 19:06:09 -07:00
return t('trade:trigger-order-desc', {
amount: floorToDecimal(amountOutFormValue, outputBankDecimals),
2023-08-03 19:06:09 -07:00
orderType: orderTypeString,
2023-08-02 06:20:09 -07:00
priceUnit: quoteString,
symbol: outputBankName,
triggerPrice: floorToDecimal(triggerPrice, inputBankDecimals),
2023-08-01 22:32:20 -07:00
})
} else {
2023-08-02 06:20:09 -07:00
return t('trade:trigger-order-desc', {
amount: floorToDecimal(amountInFormValue, inputBankDecimals),
2023-08-03 19:06:09 -07:00
orderType: orderTypeString,
2023-08-02 06:20:09 -07:00
priceUnit: quoteString,
symbol: inputBankName,
triggerPrice: floorToDecimal(triggerPrice, inputBankDecimals),
2023-08-01 22:32:20 -07:00
})
}
2023-08-02 06:20:09 -07:00
}, [
amountInFormValue,
amountOutFormValue,
flipPrices,
inputBankDecimals,
inputBankName,
2023-08-03 19:06:09 -07:00
orderType,
outputBankDecimals,
outputBankName,
2023-08-02 06:20:09 -07:00
triggerPrice,
])
const triggerPriceSuffix = useMemo(() => {
if (!inputBankName || !outputBankName) return
2023-08-02 06:20:09 -07:00
if (!flipPrices) {
return `${inputBankName} per ${outputBankName}`
2023-08-03 03:34:57 -07:00
} else {
return `${outputBankName} per ${inputBankName}`
2023-08-02 06:20:09 -07:00
}
}, [flipPrices, inputBankName, outputBankName])
2023-08-01 22:32:20 -07:00
2023-08-03 03:34:57 -07:00
const toggleFlipPrices = useCallback(
2023-08-01 22:32:20 -07:00
(flip: boolean) => {
if (!inputBankName || !outputBankName) return
2023-08-03 23:14:41 -07:00
setFormErrors({})
2023-08-03 03:34:57 -07:00
handleFlipPrices(
flip,
inputBankName,
outputBankName,
2023-08-03 03:34:57 -07:00
swapChartSettings,
setSwapChartSettings,
)
2023-08-01 22:32:20 -07:00
},
2023-08-03 03:34:57 -07:00
[
inputBankName,
outputBankName,
setFormErrors,
2023-08-03 03:34:57 -07:00
setSwapChartSettings,
swapChartSettings,
2023-08-03 03:34:57 -07:00
],
2023-08-01 22:32:20 -07:00
)
2023-08-03 06:44:23 -07:00
const handleOrderTypeChange = useCallback(
(type: string) => {
2023-08-03 23:14:41 -07:00
setFormErrors({})
2023-08-03 06:44:23 -07:00
const newType = type as OrderTypes
setOrderType(newType)
2023-08-03 23:14:41 -07:00
const triggerMultiplier = getOrderTypeMultiplier(newType, flipPrices)
2023-08-03 06:44:23 -07:00
setOrderTypeMultiplier(triggerMultiplier)
const trigger = (quotePrice * triggerMultiplier).toString()
setTriggerPrice(trigger)
2023-08-03 19:06:09 -07:00
if (amountInAsDecimal.gt(0)) {
const amountOut = getAmountOut(
amountInAsDecimal.toString(),
trigger,
).toString()
setAmountOutFormValue(amountOut)
}
2023-08-03 06:44:23 -07:00
},
[flipPrices, quotePrice, setFormErrors, setOrderTypeMultiplier],
2023-08-03 06:44:23 -07:00
)
2023-08-03 23:14:41 -07:00
// const disablePlaceOrder =
// (orderType === OrderTypes.REPAY_BORROW && !hasBorrowToRepay) ||
// (orderType === OrderTypes.STOP_LOSS &&
// parseFloat(triggerPrice) > quotePrice) ||
// (orderType === OrderTypes.TAKE_PROFIT &&
// parseFloat(triggerPrice) < quotePrice) ||
// amountInAsDecimal.gt(sellTokenBalance)
2023-08-03 06:44:23 -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-08-03 06:44:23 -07:00
isTriggerOrder
2023-07-25 19:48:13 -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`}
id="swap-step-two"
>
2023-08-03 06:44:23 -07:00
<div className="col-span-1">
<p className="mb-2 text-th-fgd-2">{t('trade:order-type')}</p>
<Select
value={t(orderType)}
onChange={(type) => handleOrderTypeChange(type)}
className="w-full"
buttonClassName="ring-transparent rounded-t-lg rounded-b-lg focus:outline-none md:hover:bg-th-bkg-1 md:hover:ring-transparent focus-visible:bg-th-bkg-3 whitespace-nowrap"
>
{ORDER_TYPES.map((type) => (
<Select.Option key={type} value={type}>
{t(type)}
</Select.Option>
))}
</Select>
</div>
<div className="col-span-1">
2023-08-04 05:03:19 -07:00
<div className="mb-2 flex items-end justify-between">
<p className="text-th-fgd-2">{t('trade:trigger-price')}</p>
<p
className={`font-mono text-xs ${
triggerPriceDifference >= 0 ? 'text-th-up' : 'text-th-down'
}`}
>
{triggerPriceDifference
? triggerPriceDifference.toFixed(2)
: '0.00'}
%
</p>
2023-08-02 06:20:09 -07:00
</div>
<div className="flex items-center">
<div className="relative w-full">
<NumberFormat
inputMode="decimal"
thousandSeparator=","
allowNegative={false}
isNumericString={true}
2023-08-03 03:34:57 -07:00
decimalScale={
!flipPrices ? inputBankDecimals : outputBankDecimals || 6
2023-08-03 03:34:57 -07:00
}
2023-08-02 06:20:09 -07:00
name="triggerPrice"
id="triggerPrice"
2023-08-03 06:44:23 -07:00
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"
2023-08-02 06:20:09 -07:00
placeholder="0.00"
value={triggerPrice}
onValueChange={handleTriggerPrice}
isAllowed={withValueLimit}
/>
<div className="absolute top-1/2 -translate-y-1/2 left-2">
<TokenLogo
2023-08-03 03:34:57 -07:00
bank={flipPrices ? outputBank : inputBank}
2023-08-02 06:20:09 -07:00
size={16}
/>
</div>
2023-08-01 22:32:20 -07:00
</div>
2023-08-03 06:44:23 -07:00
</div>
2023-08-04 05:03:19 -07:00
<div className="flex justify-end">
<LinkButton
className="flex items-center font-normal text-xxs text-th-fgd-3"
onClick={() => toggleFlipPrices(!flipPrices)}
2023-08-03 06:44:23 -07:00
>
2023-08-04 05:03:19 -07:00
<span className="mr-1">{triggerPriceSuffix}</span>
<ArrowsRightLeftIcon className="h-3.5 w-3.5" />
</LinkButton>
</div>
2023-08-01 22:32:20 -07:00
</div>
2023-08-03 23:14:41 -07:00
{formErrors.triggerPrice ? (
<div className="col-span-2 flex justify-center">
<InlineNotification
type="error"
desc={formErrors.triggerPrice}
hideBorder
hidePadding
/>
</div>
) : null}
</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
2023-08-03 23:14:41 -07:00
error={formErrors.hasBorrows}
2023-07-25 19:48:13 -07:00
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
/>
{swapFormSizeUi === 'slider' ? (
<SwapSlider
2023-08-03 19:06:09 -07:00
useMargin={false}
amount={amountInAsDecimal.toNumber()}
2023-06-17 05:22:05 -07:00
onChange={(v) => handleAmountInUi(v)}
step={1 / 10 ** (inputBankDecimals || 6)}
/>
) : (
<PercentageSelectButtons
amountIn={amountInAsDecimal.toString()}
2023-06-17 05:22:05 -07:00
setAmountIn={(v) => handleAmountInUi(v)}
2023-08-03 19:06:09 -07:00
useMargin={false}
/>
)}
2023-08-03 23:14:41 -07:00
{orderType === OrderTypes.REPAY_BORROW &&
!hasBorrowToRepay ? null : orderDescription ? (
2023-08-02 06:20:09 -07:00
<div className="mt-4">
<InlineNotification
desc={
<>
2023-08-03 19:06:09 -07:00
{orderType !== OrderTypes.REPAY_BORROW ? (
inputBankName === 'USDC' ? (
2023-08-03 19:06:09 -07:00
<span className="text-th-up">{t('buy')}</span>
) : (
<span className="text-th-down">{t('sell')}</span>
)
) : null}{' '}
2023-08-02 06:20:09 -07:00
{orderDescription}
</>
}
type="info"
/>
</div>
) : null}
2023-08-03 19:06:09 -07:00
{ipAllowed ? (
<Button
2023-08-07 17:09:05 -07:00
// onClick={handlePlaceStopLoss}
2023-08-03 19:06:09 -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>
) : (
<Button
disabled
className="mt-6 mb-4 w-full leading-tight"
size="large"
>
{t('country-not-allowed', {
country: ipCountry ? `(${ipCountry})` : '',
})}
</Button>
)}
</>
)
}
export default LimitSwapForm