simplify form
This commit is contained in:
parent
02301bedcc
commit
934e55ec93
|
@ -6,7 +6,7 @@ import {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
SetStateAction,
|
SetStateAction,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import { ArrowDownIcon } from '@heroicons/react/20/solid'
|
import { ArrowDownIcon, ArrowsRightLeftIcon } from '@heroicons/react/20/solid'
|
||||||
import NumberFormat, {
|
import NumberFormat, {
|
||||||
NumberFormatValues,
|
NumberFormatValues,
|
||||||
SourceInfo,
|
SourceInfo,
|
||||||
|
@ -18,7 +18,6 @@ import { SIZE_INPUT_UI_KEY } from '../../utils/constants'
|
||||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||||
import SwapSlider from './SwapSlider'
|
import SwapSlider from './SwapSlider'
|
||||||
import PercentageSelectButtons from './PercentageSelectButtons'
|
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||||
import Select from '@components/forms/Select'
|
|
||||||
import { floorToDecimal } from 'utils/numbers'
|
import { floorToDecimal } from 'utils/numbers'
|
||||||
import { withValueLimit } from './MarketSwapForm'
|
import { withValueLimit } from './MarketSwapForm'
|
||||||
import SellTokenInput from './SellTokenInput'
|
import SellTokenInput from './SellTokenInput'
|
||||||
|
@ -26,36 +25,32 @@ import BuyTokenInput from './BuyTokenInput'
|
||||||
import { notify } from 'utils/notifications'
|
import { notify } from 'utils/notifications'
|
||||||
import * as sentry from '@sentry/nextjs'
|
import * as sentry from '@sentry/nextjs'
|
||||||
import { isMangoError } from 'types'
|
import { isMangoError } from 'types'
|
||||||
import Button from '@components/shared/Button'
|
import Button, { IconButton } from '@components/shared/Button'
|
||||||
import { useWallet } from '@solana/wallet-adapter-react'
|
|
||||||
import Loading from '@components/shared/Loading'
|
import Loading from '@components/shared/Loading'
|
||||||
import TokenLogo from '@components/shared/TokenLogo'
|
import TokenLogo from '@components/shared/TokenLogo'
|
||||||
import InlineNotification from '@components/shared/InlineNotification'
|
import InlineNotification from '@components/shared/InlineNotification'
|
||||||
|
|
||||||
type LimitSwapFormProps = {
|
type LimitSwapFormProps = {
|
||||||
|
showTokenSelect: 'input' | 'output' | undefined
|
||||||
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
||||||
}
|
}
|
||||||
|
|
||||||
type LimitSwapForm = {
|
type LimitSwapForm = {
|
||||||
limitPrice: string | undefined
|
amountIn: number
|
||||||
triggerPrice: string
|
triggerPrice: string
|
||||||
}
|
}
|
||||||
type FormErrors = Partial<Record<keyof LimitSwapForm, string>>
|
type FormErrors = Partial<Record<keyof LimitSwapForm, string>>
|
||||||
|
|
||||||
const ORDER_TYPES = [
|
|
||||||
// 'trade:limit',
|
|
||||||
'trade:stop-market',
|
|
||||||
'trade:stop-limit',
|
|
||||||
]
|
|
||||||
|
|
||||||
const set = mangoStore.getState().set
|
const set = mangoStore.getState().set
|
||||||
|
|
||||||
const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
const LimitSwapForm = ({
|
||||||
|
showTokenSelect,
|
||||||
|
setShowTokenSelect,
|
||||||
|
}: LimitSwapFormProps) => {
|
||||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||||
const { connected } = useWallet()
|
|
||||||
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
|
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
|
||||||
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
|
|
||||||
const [triggerPrice, setTriggerPrice] = useState('')
|
const [triggerPrice, setTriggerPrice] = useState('')
|
||||||
|
const [flipPrices, setFlipPrices] = useState(false)
|
||||||
const [submitting, setSubmitting] = useState(false)
|
const [submitting, setSubmitting] = useState(false)
|
||||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||||
|
@ -75,16 +70,6 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
: new Decimal(0)
|
: new Decimal(0)
|
||||||
}, [amountInFormValue])
|
}, [amountInFormValue])
|
||||||
|
|
||||||
const [baseBank, quoteBank] = useMemo(() => {
|
|
||||||
if (inputBank && inputBank.name === 'USDC') {
|
|
||||||
return [outputBank, inputBank]
|
|
||||||
} else if (outputBank && outputBank.name === 'USDC') {
|
|
||||||
return [inputBank, outputBank]
|
|
||||||
} else if (inputBank && inputBank.name === 'SOL') {
|
|
||||||
return [outputBank, inputBank]
|
|
||||||
} else return [inputBank, outputBank]
|
|
||||||
}, [inputBank, outputBank])
|
|
||||||
|
|
||||||
const setAmountInFormValue = useCallback((amountIn: string) => {
|
const setAmountInFormValue = useCallback((amountIn: string) => {
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.swap.amountIn = amountIn
|
s.swap.amountIn = amountIn
|
||||||
|
@ -103,88 +88,57 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const setLimitPrice = useCallback((price: string) => {
|
const [quotePrice, flippedQuotePrice] = useMemo(() => {
|
||||||
set((s) => {
|
if (!inputBank || !outputBank) return [0, 0]
|
||||||
s.swap.limitPrice = price
|
const quote = floorToDecimal(
|
||||||
})
|
inputBank.uiPrice / outputBank.uiPrice,
|
||||||
}, [])
|
outputBank.mintDecimals,
|
||||||
|
).toNumber()
|
||||||
const initialQuotePrice = useMemo(() => {
|
const flipped = floorToDecimal(
|
||||||
if (!baseBank || !quoteBank) return
|
outputBank.uiPrice / inputBank.uiPrice,
|
||||||
return floorToDecimal(
|
inputBank.mintDecimals,
|
||||||
baseBank.uiPrice / quoteBank.uiPrice,
|
).toNumber()
|
||||||
quoteBank.mintDecimals,
|
return [quote, flipped]
|
||||||
)
|
}, [inputBank, outputBank])
|
||||||
}, [baseBank, quoteBank])
|
|
||||||
|
|
||||||
// set default limit and trigger price
|
// set default limit and trigger price
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!initialQuotePrice) return
|
if (!quotePrice) return
|
||||||
if (!triggerPrice) {
|
if (!triggerPrice && !showTokenSelect) {
|
||||||
setTriggerPrice(
|
setTriggerPrice(quotePrice.toFixed(outputBank?.mintDecimals))
|
||||||
initialQuotePrice.mul(0.9).toFixed(quoteBank?.mintDecimals),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
if (!limitPrice) {
|
}, [quotePrice, outputBank, showTokenSelect, triggerPrice])
|
||||||
set((s) => {
|
|
||||||
s.swap.limitPrice = initialQuotePrice
|
|
||||||
.mul(0.8)
|
|
||||||
.toFixed(quoteBank?.mintDecimals)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [initialQuotePrice, limitPrice, quoteBank, triggerPrice])
|
|
||||||
|
|
||||||
const [limitPriceDifference, triggerPriceDifference] = useMemo(() => {
|
const triggerPriceDifference = useMemo(() => {
|
||||||
if (!initialQuotePrice) return [0, 0]
|
if ((!flipPrices && !quotePrice) || (flipPrices && !flippedQuotePrice))
|
||||||
const initialPrice = initialQuotePrice.toNumber()
|
return 0
|
||||||
const limitDifference = limitPrice
|
const oraclePrice = !flipPrices ? quotePrice : flippedQuotePrice
|
||||||
? ((parseFloat(limitPrice) - initialPrice) / initialPrice) * 100
|
|
||||||
: 0
|
|
||||||
const triggerDifference = triggerPrice
|
const triggerDifference = triggerPrice
|
||||||
? ((parseFloat(triggerPrice) - initialPrice) / initialPrice) * 100
|
? ((parseFloat(triggerPrice) - oraclePrice) / oraclePrice) * 100
|
||||||
: 0
|
: 0
|
||||||
return [limitDifference, triggerDifference]
|
return triggerDifference
|
||||||
}, [initialQuotePrice, limitPrice, triggerPrice])
|
}, [flippedQuotePrice, quotePrice, triggerPrice])
|
||||||
|
|
||||||
// const isFormValid = useCallback(
|
const handleTokenSelect = (type: 'input' | 'output') => {
|
||||||
// (form: LimitSwapForm) => {
|
setShowTokenSelect(type)
|
||||||
// const invalidFields: FormErrors = {}
|
setTriggerPrice('')
|
||||||
// setFormErrors({})
|
}
|
||||||
// const triggerPriceNumber = parseFloat(form.triggerPrice)
|
|
||||||
// const requiredFields: (keyof LimitSwapForm)[] = [
|
const isFormValid = useCallback((form: LimitSwapForm) => {
|
||||||
// 'limitPrice',
|
const invalidFields: FormErrors = {}
|
||||||
// 'triggerPrice',
|
setFormErrors({})
|
||||||
// ]
|
const requiredFields: (keyof LimitSwapForm)[] = ['amountIn', 'triggerPrice']
|
||||||
// for (const key of requiredFields) {
|
for (const key of requiredFields) {
|
||||||
// const value = form[key] as string
|
const value = form[key] as string
|
||||||
// if (!value) {
|
if (!value) {
|
||||||
// if (orderType === 'trade:stop-market') {
|
invalidFields[key] = t('settings:error-required-field')
|
||||||
// if (key !== 'limitPrice') {
|
}
|
||||||
// invalidFields[key] = t('settings:error-required-field')
|
}
|
||||||
// }
|
if (Object.keys(invalidFields).length) {
|
||||||
// } else {
|
setFormErrors(invalidFields)
|
||||||
// invalidFields[key] = t('settings:error-required-field')
|
}
|
||||||
// }
|
return invalidFields
|
||||||
// }
|
}, [])
|
||||||
// }
|
|
||||||
// if (
|
|
||||||
// orderType.includes('stop') &&
|
|
||||||
// initialQuotePrice &&
|
|
||||||
// triggerPriceNumber > initialQuotePrice.toNumber()
|
|
||||||
// ) {
|
|
||||||
// invalidFields.triggerPrice =
|
|
||||||
// 'Trigger price must be less than current price'
|
|
||||||
// }
|
|
||||||
// if (form.limitPrice && form.limitPrice > form.triggerPrice) {
|
|
||||||
// invalidFields.limitPrice = 'Limit price must be less than trigger price'
|
|
||||||
// }
|
|
||||||
// if (Object.keys(invalidFields).length) {
|
|
||||||
// setFormErrors(invalidFields)
|
|
||||||
// }
|
|
||||||
// return invalidFields
|
|
||||||
// },
|
|
||||||
// [initialQuotePrice, orderType],
|
|
||||||
// )
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If the use margin setting is toggled, clear the form values
|
If the use margin setting is toggled, clear the form values
|
||||||
|
@ -194,115 +148,97 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
setAmountOutFormValue('')
|
setAmountOutFormValue('')
|
||||||
}, [useMargin, setAmountInFormValue, setAmountOutFormValue])
|
}, [useMargin, setAmountInFormValue, setAmountOutFormValue])
|
||||||
|
|
||||||
// the price to use for calculating the opposing side's size
|
|
||||||
const sizePrice = useMemo(() => {
|
|
||||||
return orderType === 'trade:stop-market' ? triggerPrice : limitPrice
|
|
||||||
}, [limitPrice, orderType, triggerPrice])
|
|
||||||
|
|
||||||
// get the out amount from the in amount and trigger or limit price
|
// get the out amount from the in amount and trigger or limit price
|
||||||
const getAmountOut = useCallback(
|
const getAmountOut = useCallback(
|
||||||
(amountIn: string, price: string) => {
|
(amountIn: string, price: string) => {
|
||||||
const amountOut =
|
const amountOut = !flipPrices
|
||||||
outputBank?.name === quoteBank?.name
|
? floorToDecimal(
|
||||||
? floorToDecimal(
|
parseFloat(amountIn) * parseFloat(price),
|
||||||
parseFloat(amountIn) * parseFloat(price),
|
outputBank?.mintDecimals || 0,
|
||||||
outputBank?.mintDecimals || 0,
|
)
|
||||||
)
|
: floorToDecimal(
|
||||||
: floorToDecimal(
|
parseFloat(amountIn) / parseFloat(price),
|
||||||
parseFloat(amountIn) / parseFloat(price),
|
outputBank?.mintDecimals || 0,
|
||||||
outputBank?.mintDecimals || 0,
|
)
|
||||||
)
|
|
||||||
return amountOut
|
return amountOut
|
||||||
},
|
},
|
||||||
[outputBank, quoteBank],
|
[outputBank, flipPrices],
|
||||||
)
|
)
|
||||||
|
|
||||||
// get the in amount from the out amount and trigger or limit price
|
// get the in amount from the out amount and trigger or limit price
|
||||||
const getAmountIn = useCallback(
|
const getAmountIn = useCallback(
|
||||||
(amountOut: string, price: string) => {
|
(amountOut: string, price: string) => {
|
||||||
const amountIn =
|
const amountIn = !flipPrices
|
||||||
outputBank?.name === quoteBank?.name
|
? floorToDecimal(
|
||||||
? floorToDecimal(
|
parseFloat(amountOut) / parseFloat(price),
|
||||||
parseFloat(amountOut) / parseFloat(price),
|
inputBank?.mintDecimals || 0,
|
||||||
inputBank?.mintDecimals || 0,
|
)
|
||||||
)
|
: floorToDecimal(
|
||||||
: floorToDecimal(
|
parseFloat(amountOut) * parseFloat(price),
|
||||||
parseFloat(amountOut) * parseFloat(price),
|
inputBank?.mintDecimals || 0,
|
||||||
inputBank?.mintDecimals || 0,
|
)
|
||||||
)
|
|
||||||
return amountIn
|
return amountIn
|
||||||
},
|
},
|
||||||
[inputBank, outputBank, quoteBank],
|
[inputBank, outputBank, flipPrices],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleMax = useCallback(
|
const handleMax = useCallback(
|
||||||
(amountIn: string) => {
|
(amountIn: string) => {
|
||||||
setAmountInFormValue(amountIn)
|
setAmountInFormValue(amountIn)
|
||||||
if (parseFloat(amountIn) > 0 && sizePrice) {
|
if (parseFloat(amountIn) > 0 && triggerPrice) {
|
||||||
const amountOut = getAmountOut(amountIn, sizePrice)
|
const amountOut = getAmountOut(amountIn, triggerPrice)
|
||||||
setAmountOutFormValue(amountOut.toString())
|
setAmountOutFormValue(amountOut.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, sizePrice],
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleRepay = useCallback(
|
const handleRepay = useCallback(
|
||||||
(amountOut: string) => {
|
(amountOut: string) => {
|
||||||
setAmountOutFormValue(amountOut)
|
setAmountOutFormValue(amountOut)
|
||||||
if (parseFloat(amountOut) > 0 && sizePrice) {
|
if (parseFloat(amountOut) > 0 && triggerPrice) {
|
||||||
const amountIn = getAmountIn(amountOut, sizePrice)
|
const amountIn = getAmountIn(amountOut, triggerPrice)
|
||||||
setAmountInFormValue(amountIn.toString())
|
setAmountInFormValue(amountIn.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, sizePrice],
|
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleAmountInChange = useCallback(
|
const handleAmountInChange = useCallback(
|
||||||
(e: NumberFormatValues, info: SourceInfo) => {
|
(e: NumberFormatValues, info: SourceInfo) => {
|
||||||
if (info.source !== 'event') return
|
if (info.source !== 'event') return
|
||||||
|
setFormErrors({})
|
||||||
setAmountInFormValue(e.value)
|
setAmountInFormValue(e.value)
|
||||||
if (parseFloat(e.value) > 0 && sizePrice) {
|
if (parseFloat(e.value) > 0 && triggerPrice) {
|
||||||
const amountOut = getAmountOut(e.value, sizePrice)
|
const amountOut = getAmountOut(e.value, triggerPrice)
|
||||||
setAmountOutFormValue(amountOut.toString())
|
setAmountOutFormValue(amountOut.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, sizePrice],
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleAmountOutChange = useCallback(
|
const handleAmountOutChange = useCallback(
|
||||||
(e: NumberFormatValues, info: SourceInfo) => {
|
(e: NumberFormatValues, info: SourceInfo) => {
|
||||||
if (info.source !== 'event') return
|
if (info.source !== 'event') return
|
||||||
setAmountOutFormValue(e.value)
|
setAmountOutFormValue(e.value)
|
||||||
if (parseFloat(e.value) > 0 && sizePrice) {
|
if (parseFloat(e.value) > 0 && triggerPrice) {
|
||||||
const amountIn = getAmountIn(e.value, sizePrice)
|
const amountIn = getAmountIn(e.value, triggerPrice)
|
||||||
setAmountInFormValue(amountIn.toString())
|
setAmountInFormValue(amountIn.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, sizePrice],
|
[getAmountIn, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleAmountInUi = useCallback(
|
const handleAmountInUi = useCallback(
|
||||||
(amountIn: string) => {
|
(amountIn: string) => {
|
||||||
setAmountInFormValue(amountIn)
|
setAmountInFormValue(amountIn)
|
||||||
if (sizePrice) {
|
|
||||||
const amountOut = getAmountOut(amountIn, sizePrice)
|
|
||||||
setAmountOutFormValue(amountOut.toString())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, sizePrice],
|
|
||||||
)
|
|
||||||
|
|
||||||
const handleLimitPrice = useCallback(
|
|
||||||
(e: NumberFormatValues, info: SourceInfo) => {
|
|
||||||
if (info.source !== 'event') return
|
|
||||||
setFormErrors({})
|
setFormErrors({})
|
||||||
setLimitPrice(e.value)
|
if (triggerPrice) {
|
||||||
if (parseFloat(e.value) > 0 && parseFloat(amountInFormValue) > 0) {
|
const amountOut = getAmountOut(amountIn, triggerPrice)
|
||||||
const amountOut = getAmountOut(amountInFormValue, e.value)
|
|
||||||
setAmountOutFormValue(amountOut.toString())
|
setAmountOutFormValue(amountOut.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[amountInFormValue, setLimitPrice],
|
[getAmountOut, setAmountInFormValue, setAmountOutFormValue, triggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleTriggerPrice = useCallback(
|
const handleTriggerPrice = useCallback(
|
||||||
|
@ -310,49 +246,48 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
if (info.source !== 'event') return
|
if (info.source !== 'event') return
|
||||||
setFormErrors({})
|
setFormErrors({})
|
||||||
setTriggerPrice(e.value)
|
setTriggerPrice(e.value)
|
||||||
if (
|
if (parseFloat(e.value) > 0 && parseFloat(amountInFormValue) > 0) {
|
||||||
parseFloat(e.value) > 0 &&
|
|
||||||
parseFloat(amountInFormValue) > 0 &&
|
|
||||||
orderType === 'trade:stop-market'
|
|
||||||
) {
|
|
||||||
const amountOut = getAmountOut(amountInFormValue, e.value)
|
const amountOut = getAmountOut(amountInFormValue, e.value)
|
||||||
setAmountOutFormValue(amountOut.toString())
|
setAmountOutFormValue(amountOut.toString())
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[amountInFormValue, orderType, setTriggerPrice],
|
[amountInFormValue, flipPrices, setTriggerPrice],
|
||||||
)
|
)
|
||||||
|
|
||||||
const handleSwitchTokens = useCallback(() => {
|
const handleSwitchTokens = useCallback(() => {
|
||||||
if (amountInAsDecimal?.gt(0) && sizePrice) {
|
if (amountInAsDecimal?.gt(0) && triggerPrice) {
|
||||||
const amountOut =
|
const amountOut = amountInAsDecimal.div(triggerPrice)
|
||||||
outputBank?.name !== quoteBank?.name
|
|
||||||
? amountInAsDecimal.mul(sizePrice)
|
|
||||||
: amountInAsDecimal.div(sizePrice)
|
|
||||||
setAmountOutFormValue(amountOut.toString())
|
setAmountOutFormValue(amountOut.toString())
|
||||||
}
|
}
|
||||||
set((s) => {
|
set((s) => {
|
||||||
s.swap.inputBank = outputBank
|
s.swap.inputBank = outputBank
|
||||||
s.swap.outputBank = inputBank
|
s.swap.outputBank = inputBank
|
||||||
// s.swap.limitPrice = ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (flippedQuotePrice) {
|
||||||
|
setTriggerPrice(flippedQuotePrice.toFixed(inputBank?.mintDecimals))
|
||||||
|
}
|
||||||
setAnimateSwitchArrow(
|
setAnimateSwitchArrow(
|
||||||
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
|
(prevanimateSwitchArrow) => prevanimateSwitchArrow + 1,
|
||||||
)
|
)
|
||||||
}, [
|
}, [
|
||||||
setAmountInFormValue,
|
setAmountInFormValue,
|
||||||
amountInAsDecimal,
|
amountInAsDecimal,
|
||||||
|
flipPrices,
|
||||||
|
flippedQuotePrice,
|
||||||
inputBank,
|
inputBank,
|
||||||
orderType,
|
|
||||||
outputBank,
|
outputBank,
|
||||||
quoteBank,
|
triggerPrice,
|
||||||
sizePrice,
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const handlePlaceStopLoss = useCallback(async () => {
|
const handlePlaceStopLoss = useCallback(async () => {
|
||||||
// const invalidFields = isFormValid({ limitPrice, triggerPrice })
|
const invalidFields = isFormValid({
|
||||||
// if (Object.keys(invalidFields).length) {
|
amountIn: amountInAsDecimal.toNumber(),
|
||||||
// return
|
triggerPrice,
|
||||||
// }
|
})
|
||||||
|
if (Object.keys(invalidFields).length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const client = mangoStore.getState().client
|
const client = mangoStore.getState().client
|
||||||
const group = mangoStore.getState().group
|
const group = mangoStore.getState().group
|
||||||
|
@ -361,34 +296,25 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
const inputBank = mangoStore.getState().swap.inputBank
|
const inputBank = mangoStore.getState().swap.inputBank
|
||||||
const outputBank = mangoStore.getState().swap.outputBank
|
const outputBank = mangoStore.getState().swap.outputBank
|
||||||
|
|
||||||
if (
|
if (!mangoAccount || !group || !inputBank || !outputBank || !triggerPrice)
|
||||||
!mangoAccount ||
|
|
||||||
!group ||
|
|
||||||
!inputBank ||
|
|
||||||
!outputBank ||
|
|
||||||
(!triggerPrice && orderType !== 'trade:limit') ||
|
|
||||||
(!limitPrice && orderType !== 'trade:stop-market')
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
setSubmitting(true)
|
setSubmitting(true)
|
||||||
|
|
||||||
const orderPrice =
|
const inputMint = !flipPrices ? inputBank.mint : outputBank.mint
|
||||||
orderType === 'trade:limit'
|
const outputMint = !flipPrices ? outputBank.mint : inputBank.mint
|
||||||
? parseFloat(limitPrice!)
|
const amountIn = !flipPrices
|
||||||
: parseFloat(triggerPrice)
|
? amountInAsDecimal.toNumber()
|
||||||
|
: parseFloat(amountOutFormValue)
|
||||||
const stopLimitPrice =
|
|
||||||
orderType !== 'trade:stop-market' ? parseFloat(limitPrice!) : 0
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const tx = await client.tokenConditionalSwapStopLoss(
|
const tx = await client.tokenConditionalSwapStopLoss(
|
||||||
group,
|
group,
|
||||||
mangoAccount,
|
mangoAccount,
|
||||||
inputBank.mint,
|
inputMint,
|
||||||
orderPrice,
|
parseFloat(triggerPrice),
|
||||||
outputBank.mint,
|
outputMint,
|
||||||
stopLimitPrice,
|
null,
|
||||||
amountInAsDecimal.toNumber(),
|
amountIn,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
)
|
)
|
||||||
|
@ -417,122 +343,98 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
} finally {
|
} finally {
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
}
|
}
|
||||||
}, [orderType, limitPrice, triggerPrice, amountInAsDecimal])
|
}, [
|
||||||
|
flipPrices,
|
||||||
|
limitPrice,
|
||||||
|
triggerPrice,
|
||||||
|
amountInAsDecimal,
|
||||||
|
amountOutFormValue,
|
||||||
|
])
|
||||||
|
|
||||||
const limitOrderDisabled =
|
const triggerPriceLabel = useMemo(() => {
|
||||||
!connected || !amountInFormValue || !amountOutFormValue
|
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],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<SellTokenInput
|
<SellTokenInput
|
||||||
className="rounded-b-none"
|
className="rounded-b-none"
|
||||||
|
error={formErrors.amountIn}
|
||||||
handleAmountInChange={handleAmountInChange}
|
handleAmountInChange={handleAmountInChange}
|
||||||
setShowTokenSelect={setShowTokenSelect}
|
setShowTokenSelect={() => handleTokenSelect('input')}
|
||||||
handleMax={handleMax}
|
handleMax={handleMax}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
className={`grid ${
|
className={`grid grid-cols-2 gap-2 rounded-b-xl bg-th-bkg-2 p-3 pt-1`}
|
||||||
orderType === 'trade:stop-limit' ? 'grid-cols-3' : 'grid-cols-2'
|
|
||||||
} gap-2 rounded-b-xl bg-th-bkg-2 p-3 pt-1`}
|
|
||||||
id="swap-step-two"
|
id="swap-step-two"
|
||||||
>
|
>
|
||||||
<div className="col-span-1">
|
<div className="col-span-2">
|
||||||
<p className="mb-2 text-th-fgd-2">{t('trade:order-type')}</p>
|
<p className="mb-2 text-th-fgd-2">
|
||||||
<Select
|
{triggerPriceLabel}{' '}
|
||||||
value={t(orderType)}
|
<span className="text-xs text-th-fgd-3">
|
||||||
onChange={(type) => setOrderType(type)}
|
{triggerPriceDifference
|
||||||
className="w-full"
|
? `(${triggerPriceDifference.toFixed(2)}%)`
|
||||||
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"
|
: ''}
|
||||||
>
|
</span>
|
||||||
{ORDER_TYPES.map((type) => (
|
</p>
|
||||||
<Select.Option key={type} value={type}>
|
<div className="relative">
|
||||||
{t(type)}
|
<NumberFormat
|
||||||
</Select.Option>
|
inputMode="decimal"
|
||||||
))}
|
thousandSeparator=","
|
||||||
</Select>
|
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>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{formErrors.triggerPrice ? (
|
||||||
|
<div className="mt-1">
|
||||||
|
<InlineNotification
|
||||||
|
type="error"
|
||||||
|
desc={formErrors.triggerPrice}
|
||||||
|
hideBorder
|
||||||
|
hidePadding
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
{orderType !== 'trade:limit' ? (
|
|
||||||
<div className="col-span-1">
|
|
||||||
<p className="mb-2 text-th-fgd-2">
|
|
||||||
{t('trade:trigger-price')}{' '}
|
|
||||||
<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={quoteBank} size={16} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{formErrors.triggerPrice ? (
|
|
||||||
<div className="mt-1">
|
|
||||||
<InlineNotification
|
|
||||||
type="error"
|
|
||||||
desc={formErrors.triggerPrice}
|
|
||||||
hideBorder
|
|
||||||
hidePadding
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
{orderType !== 'trade:stop-market' ? (
|
|
||||||
<div className="col-span-1">
|
|
||||||
<p className="mb-2 text-th-fgd-2">
|
|
||||||
{t('trade:limit-price')}{' '}
|
|
||||||
<span className="text-xs text-th-fgd-3">
|
|
||||||
{limitPriceDifference
|
|
||||||
? `(${limitPriceDifference.toFixed(2)}%)`
|
|
||||||
: ''}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
<div className="relative">
|
|
||||||
<NumberFormat
|
|
||||||
inputMode="decimal"
|
|
||||||
thousandSeparator=","
|
|
||||||
allowNegative={false}
|
|
||||||
isNumericString={true}
|
|
||||||
decimalScale={outputBank?.mintDecimals || 6}
|
|
||||||
name="limitPrice"
|
|
||||||
id="limitPrice"
|
|
||||||
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={limitPrice}
|
|
||||||
onValueChange={handleLimitPrice}
|
|
||||||
isAllowed={withValueLimit}
|
|
||||||
/>
|
|
||||||
<div className="absolute top-1/2 -translate-y-1/2 left-2">
|
|
||||||
<TokenLogo bank={quoteBank} size={16} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{formErrors.limitPrice ? (
|
|
||||||
<div className="mt-1">
|
|
||||||
<InlineNotification
|
|
||||||
type="error"
|
|
||||||
desc={formErrors.limitPrice}
|
|
||||||
hideBorder
|
|
||||||
hidePadding
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="my-2 flex justify-center">
|
<div className="my-2 flex justify-center">
|
||||||
<button
|
<button
|
||||||
|
@ -551,7 +453,7 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
</div>
|
</div>
|
||||||
<BuyTokenInput
|
<BuyTokenInput
|
||||||
handleAmountOutChange={handleAmountOutChange}
|
handleAmountOutChange={handleAmountOutChange}
|
||||||
setShowTokenSelect={setShowTokenSelect}
|
setShowTokenSelect={() => handleTokenSelect('output')}
|
||||||
handleRepay={handleRepay}
|
handleRepay={handleRepay}
|
||||||
/>
|
/>
|
||||||
{swapFormSizeUi === 'slider' ? (
|
{swapFormSizeUi === 'slider' ? (
|
||||||
|
@ -571,7 +473,6 @@ const LimitSwapForm = ({ setShowTokenSelect }: LimitSwapFormProps) => {
|
||||||
<Button
|
<Button
|
||||||
onClick={handlePlaceStopLoss}
|
onClick={handlePlaceStopLoss}
|
||||||
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
||||||
disabled={limitOrderDisabled}
|
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
{submitting ? <Loading /> : t('swap:place-limit-order')}
|
{submitting ? <Loading /> : t('swap:place-limit-order')}
|
||||||
|
|
|
@ -12,17 +12,20 @@ import { INPUT_TOKEN_DEFAULT } from 'utils/constants'
|
||||||
import { NUMBER_FORMAT_CLASSNAMES, withValueLimit } from './MarketSwapForm'
|
import { NUMBER_FORMAT_CLASSNAMES, withValueLimit } from './MarketSwapForm'
|
||||||
import MaxSwapAmount from './MaxSwapAmount'
|
import MaxSwapAmount from './MaxSwapAmount'
|
||||||
import useUnownedAccount from 'hooks/useUnownedAccount'
|
import useUnownedAccount from 'hooks/useUnownedAccount'
|
||||||
|
import InlineNotification from '@components/shared/InlineNotification'
|
||||||
|
|
||||||
const SellTokenInput = ({
|
const SellTokenInput = ({
|
||||||
handleAmountInChange,
|
handleAmountInChange,
|
||||||
setShowTokenSelect,
|
setShowTokenSelect,
|
||||||
handleMax,
|
handleMax,
|
||||||
className,
|
className,
|
||||||
|
error,
|
||||||
}: {
|
}: {
|
||||||
handleAmountInChange: (e: NumberFormatValues, info: SourceInfo) => void
|
handleAmountInChange: (e: NumberFormatValues, info: SourceInfo) => void
|
||||||
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
||||||
handleMax: (amountIn: string) => void
|
handleMax: (amountIn: string) => void
|
||||||
className?: string
|
className?: string
|
||||||
|
error?: string
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation('common')
|
const { t } = useTranslation('common')
|
||||||
const { group } = useMangoGroup()
|
const { group } = useMangoGroup()
|
||||||
|
@ -74,6 +77,16 @@ const SellTokenInput = ({
|
||||||
: '–'}
|
: '–'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
{error ? (
|
||||||
|
<div className="col-span-2 mt-1 flex justify-end">
|
||||||
|
<InlineNotification
|
||||||
|
type="error"
|
||||||
|
desc={error}
|
||||||
|
hideBorder
|
||||||
|
hidePadding
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,7 @@ const SwapForm = () => {
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<TabUnderline
|
<TabUnderline
|
||||||
activeValue={swapOrLimit}
|
activeValue={swapOrLimit}
|
||||||
values={['swap', 'trade:stop-loss']}
|
values={['swap', 'trade:trigger-order']}
|
||||||
onChange={(v) => handleSwapOrLimit(v)}
|
onChange={(v) => handleSwapOrLimit(v)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -254,7 +254,10 @@ const SwapForm = () => {
|
||||||
setShowTokenSelect={setShowTokenSelect}
|
setShowTokenSelect={setShowTokenSelect}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LimitSwapForm setShowTokenSelect={setShowTokenSelect} />
|
<LimitSwapForm
|
||||||
|
showTokenSelect={showTokenSelect}
|
||||||
|
setShowTokenSelect={setShowTokenSelect}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
{ipAllowed ? (
|
{ipAllowed ? (
|
||||||
swapOrLimit === 'swap' ? (
|
swapOrLimit === 'swap' ? (
|
||||||
|
|
|
@ -20,7 +20,7 @@ const SwapInfoTabs = () => {
|
||||||
?.length || 0
|
?.length || 0
|
||||||
return [
|
return [
|
||||||
['balances', 0],
|
['balances', 0],
|
||||||
['trade:stop-orders', stopOrdersCount],
|
['trade:trigger-orders', stopOrdersCount],
|
||||||
['swap:swap-history', 0],
|
['swap:swap-history', 0],
|
||||||
]
|
]
|
||||||
}, [mangoAccount])
|
}, [mangoAccount])
|
||||||
|
@ -41,7 +41,7 @@ const SwapInfoTabs = () => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{selectedTab === 'balances' ? <SwapTradeBalances /> : null}
|
{selectedTab === 'balances' ? <SwapTradeBalances /> : null}
|
||||||
{selectedTab === 'trade:stop-orders' ? <SwapOrders /> : null}
|
{selectedTab === 'trade:trigger-orders' ? <SwapOrders /> : null}
|
||||||
{selectedTab === 'swap:swap-history' ? <SwapHistoryTable /> : null}
|
{selectedTab === 'swap:swap-history' ? <SwapHistoryTable /> : null}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -51,23 +51,21 @@ const SwapOrders = () => {
|
||||||
sellBank.mintDecimals,
|
sellBank.mintDecimals,
|
||||||
).toNumber()
|
).toNumber()
|
||||||
const triggerPrice = order.getPriceLowerLimitUi(group)
|
const triggerPrice = order.getPriceLowerLimitUi(group)
|
||||||
const limitPrice = order.getPriceUpperLimitUi(group)
|
|
||||||
const pricePremium = order.getPricePremium()
|
const pricePremium = order.getPricePremium()
|
||||||
const filled = order.getSoldUi(group)
|
const filled = order.getSoldUi(group)
|
||||||
|
const currentPrice = (sellBank.uiPrice / buyBank.uiPrice).toFixed(
|
||||||
const orderType =
|
buyBank.mintDecimals,
|
||||||
limitPrice === 0 ? 'trade:stop-market' : 'trade:stop-limit'
|
)
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
...order,
|
...order,
|
||||||
buyBank,
|
buyBank,
|
||||||
|
currentPrice,
|
||||||
sellBank,
|
sellBank,
|
||||||
pair,
|
pair,
|
||||||
size,
|
size,
|
||||||
filled,
|
filled,
|
||||||
triggerPrice,
|
triggerPrice,
|
||||||
limitPrice,
|
|
||||||
orderType,
|
|
||||||
fee: pricePremium,
|
fee: pricePremium,
|
||||||
}
|
}
|
||||||
formatted.push(data)
|
formatted.push(data)
|
||||||
|
@ -136,16 +134,6 @@ const SwapOrders = () => {
|
||||||
title={t('swap:pair')}
|
title={t('swap:pair')}
|
||||||
/>
|
/>
|
||||||
</Th>
|
</Th>
|
||||||
<Th>
|
|
||||||
<div className="flex justify-end">
|
|
||||||
<SortableColumnHeader
|
|
||||||
sortKey="orderType"
|
|
||||||
sort={() => requestSort('orderType')}
|
|
||||||
sortConfig={sortConfig}
|
|
||||||
title={t('trade:order-type')}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</Th>
|
|
||||||
<Th>
|
<Th>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<SortableColumnHeader
|
<SortableColumnHeader
|
||||||
|
@ -169,20 +157,20 @@ const SwapOrders = () => {
|
||||||
<Th>
|
<Th>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<SortableColumnHeader
|
<SortableColumnHeader
|
||||||
sortKey="triggerPrice"
|
sortKey="currentPrice"
|
||||||
sort={() => requestSort('triggerPrice')}
|
sort={() => requestSort('currentPrice')}
|
||||||
sortConfig={sortConfig}
|
sortConfig={sortConfig}
|
||||||
title={t('trade:trigger-price')}
|
title={t('trade:current-price')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Th>
|
</Th>
|
||||||
<Th>
|
<Th>
|
||||||
<div className="flex justify-end">
|
<div className="flex justify-end">
|
||||||
<SortableColumnHeader
|
<SortableColumnHeader
|
||||||
sortKey="limitPrice"
|
sortKey="triggerPrice"
|
||||||
sort={() => requestSort('limitPrice')}
|
sort={() => requestSort('triggerPrice')}
|
||||||
sortConfig={sortConfig}
|
sortConfig={sortConfig}
|
||||||
title={t('trade:limit-price')}
|
title={t('trade:trigger-price')}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Th>
|
</Th>
|
||||||
|
@ -203,10 +191,9 @@ const SwapOrders = () => {
|
||||||
{tableData.map((data, i) => {
|
{tableData.map((data, i) => {
|
||||||
const {
|
const {
|
||||||
buyBank,
|
buyBank,
|
||||||
|
currentPrice,
|
||||||
fee,
|
fee,
|
||||||
pair,
|
pair,
|
||||||
orderType,
|
|
||||||
limitPrice,
|
|
||||||
sellBank,
|
sellBank,
|
||||||
size,
|
size,
|
||||||
filled,
|
filled,
|
||||||
|
@ -215,9 +202,6 @@ const SwapOrders = () => {
|
||||||
return (
|
return (
|
||||||
<TrBody key={i} className="text-sm">
|
<TrBody key={i} className="text-sm">
|
||||||
<Td>{pair}</Td>
|
<Td>{pair}</Td>
|
||||||
<Td>
|
|
||||||
<p className="text-right font-body">{t(orderType)}</p>
|
|
||||||
</Td>
|
|
||||||
<Td>
|
<Td>
|
||||||
<p className="text-right">
|
<p className="text-right">
|
||||||
{size}
|
{size}
|
||||||
|
@ -237,36 +221,32 @@ const SwapOrders = () => {
|
||||||
</p>
|
</p>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{triggerPrice ? (
|
<p className="text-right">
|
||||||
<p className="text-right">
|
{currentPrice}
|
||||||
{triggerPrice}
|
<span className="text-th-fgd-3 font-body">
|
||||||
<span className="text-th-fgd-3 font-body">
|
{' '}
|
||||||
{' '}
|
{buyBank.name}
|
||||||
{buyBank.name}
|
</span>
|
||||||
</span>
|
</p>
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-right">–</p>
|
|
||||||
)}
|
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
{limitPrice ? (
|
<p className="text-right">
|
||||||
<p className="text-right">
|
{triggerPrice}
|
||||||
{limitPrice}
|
<span className="text-th-fgd-3 font-body">
|
||||||
<span className="text-th-fgd-3 font-body">
|
{' '}
|
||||||
{' '}
|
{buyBank.name}
|
||||||
{buyBank.name}
|
</span>
|
||||||
</span>
|
</p>
|
||||||
</p>
|
|
||||||
) : (
|
|
||||||
<p className="text-right">–</p>
|
|
||||||
)}
|
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
<p className="text-right">{fee.toFixed(2)}%</p>
|
<p className="text-right">{fee.toFixed(2)}%</p>
|
||||||
</Td>
|
</Td>
|
||||||
<Td className="flex justify-end">
|
<Td className="flex justify-end">
|
||||||
<IconButton onClick={() => handleCancel(data.id)} size="small">
|
<IconButton
|
||||||
|
disabled={cancelId === data.id.toString()}
|
||||||
|
onClick={() => handleCancel(data.id)}
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
{cancelId === data.id.toString() ? (
|
{cancelId === data.id.toString() ? (
|
||||||
<Loading />
|
<Loading />
|
||||||
) : (
|
) : (
|
||||||
|
|
|
@ -34,10 +34,10 @@ const SwapPage = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="grid grid-cols-12">
|
<div className="grid grid-cols-12">
|
||||||
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-7 2xl:col-span-8">
|
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-7 xl:col-span-8">
|
||||||
<SwapTokenChart />
|
<SwapTokenChart />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-5 2xl:col-span-4">
|
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-5 xl:col-span-4">
|
||||||
<SwapForm />
|
<SwapForm />
|
||||||
</div>
|
</div>
|
||||||
<div className="col-span-12">
|
<div className="col-span-12">
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
"stop-limit": "Stop Limit",
|
"stop-limit": "Stop Limit",
|
||||||
"stop-loss": "Stop-loss",
|
"stop-loss": "Stop-loss",
|
||||||
"stop-market": "Stop Market",
|
"stop-market": "Stop Market",
|
||||||
"stop-orders": "Stop Orders",
|
|
||||||
"taker": "Taker",
|
"taker": "Taker",
|
||||||
"taker-fee": "Taker Fee",
|
"taker-fee": "Taker Fee",
|
||||||
"tick-size": "Tick Size",
|
"tick-size": "Tick Size",
|
||||||
|
@ -104,6 +103,9 @@
|
||||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||||
"trades": "Trades",
|
"trades": "Trades",
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
|
"trigger-order": "Trigger Order",
|
||||||
|
"trigger-order-rate": "Trigger {{side}} {{symbol}} price",
|
||||||
|
"trigger-orders": "Trigger Orders",
|
||||||
"tweet-position": "Tweet",
|
"tweet-position": "Tweet",
|
||||||
"unrealized-pnl": "Unrealized PnL",
|
"unrealized-pnl": "Unrealized PnL",
|
||||||
"unsettled": "Unsettled",
|
"unsettled": "Unsettled",
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
"stop-limit": "Stop Limit",
|
"stop-limit": "Stop Limit",
|
||||||
"stop-loss": "Stop-loss",
|
"stop-loss": "Stop-loss",
|
||||||
"stop-market": "Stop Market",
|
"stop-market": "Stop Market",
|
||||||
"stop-orders": "Stop Orders",
|
|
||||||
"taker": "Taker",
|
"taker": "Taker",
|
||||||
"taker-fee": "Taker Fee",
|
"taker-fee": "Taker Fee",
|
||||||
"tick-size": "Tick Size",
|
"tick-size": "Tick Size",
|
||||||
|
@ -104,6 +103,9 @@
|
||||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||||
"trades": "Trades",
|
"trades": "Trades",
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
|
"trigger-order": "Trigger Order",
|
||||||
|
"trigger-order-rate": "Trigger {{side}} {{symbol}} price",
|
||||||
|
"trigger-orders": "Trigger Orders",
|
||||||
"tweet-position": "Tweet",
|
"tweet-position": "Tweet",
|
||||||
"unrealized-pnl": "Unrealized PnL",
|
"unrealized-pnl": "Unrealized PnL",
|
||||||
"unsettled": "Unsettled",
|
"unsettled": "Unsettled",
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
"stop-limit": "Stop Limit",
|
"stop-limit": "Stop Limit",
|
||||||
"stop-loss": "Stop-loss",
|
"stop-loss": "Stop-loss",
|
||||||
"stop-market": "Stop Market",
|
"stop-market": "Stop Market",
|
||||||
"stop-orders": "Stop Orders",
|
|
||||||
"taker": "Taker",
|
"taker": "Taker",
|
||||||
"taker-fee": "Taker Fee",
|
"taker-fee": "Taker Fee",
|
||||||
"tick-size": "Tick Size",
|
"tick-size": "Tick Size",
|
||||||
|
@ -104,6 +103,9 @@
|
||||||
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
"trade-sounds-tooltip": "Play a sound alert for every new trade",
|
||||||
"trades": "Trades",
|
"trades": "Trades",
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
|
"trigger-order": "Trigger Order",
|
||||||
|
"trigger-order-rate": "Trigger {{side}} {{symbol}} price",
|
||||||
|
"trigger-orders": "Trigger Orders",
|
||||||
"tweet-position": "Tweet",
|
"tweet-position": "Tweet",
|
||||||
"unrealized-pnl": "Unrealized PnL",
|
"unrealized-pnl": "Unrealized PnL",
|
||||||
"unsettled": "Unsettled",
|
"unsettled": "Unsettled",
|
||||||
|
|
|
@ -85,7 +85,6 @@
|
||||||
"stop-limit": "Stop Limit",
|
"stop-limit": "Stop Limit",
|
||||||
"stop-loss": "Stop-loss",
|
"stop-loss": "Stop-loss",
|
||||||
"stop-market": "Stop Market",
|
"stop-market": "Stop Market",
|
||||||
"stop-orders": "Stop Orders",
|
|
||||||
"taker": "吃單者",
|
"taker": "吃單者",
|
||||||
"tick-size": "波動單位",
|
"tick-size": "波動單位",
|
||||||
"taker-fee": "Taker Fee",
|
"taker-fee": "Taker Fee",
|
||||||
|
@ -102,6 +101,9 @@
|
||||||
"trades": "交易",
|
"trades": "交易",
|
||||||
"tweet-position": "分享至Twitter",
|
"tweet-position": "分享至Twitter",
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
|
"trigger-order": "Trigger Order",
|
||||||
|
"trigger-order-rate": "Trigger {{side}} {{symbol}} price",
|
||||||
|
"trigger-orders": "Trigger Orders",
|
||||||
"unsettled": "未結清",
|
"unsettled": "未結清",
|
||||||
"volume-alert": "交易量警報",
|
"volume-alert": "交易量警報",
|
||||||
"volume-alert-desc": "交易量超過警報設定時播放聲音"
|
"volume-alert-desc": "交易量超過警報設定時播放聲音"
|
||||||
|
|
|
@ -86,7 +86,6 @@
|
||||||
"stop-limit": "Stop Limit",
|
"stop-limit": "Stop Limit",
|
||||||
"stop-loss": "Stop-loss",
|
"stop-loss": "Stop-loss",
|
||||||
"stop-market": "Stop Market",
|
"stop-market": "Stop Market",
|
||||||
"stop-orders": "Stop Orders",
|
|
||||||
"taker": "吃單者",
|
"taker": "吃單者",
|
||||||
"taker-fee": "吃單者費用",
|
"taker-fee": "吃單者費用",
|
||||||
"tick-size": "波動單位",
|
"tick-size": "波動單位",
|
||||||
|
@ -104,6 +103,9 @@
|
||||||
"trade-sounds-tooltip": "為每筆新交易播放警報聲音",
|
"trade-sounds-tooltip": "為每筆新交易播放警報聲音",
|
||||||
"trades": "交易",
|
"trades": "交易",
|
||||||
"trigger-price": "Trigger Price",
|
"trigger-price": "Trigger Price",
|
||||||
|
"trigger-order": "Trigger Order",
|
||||||
|
"trigger-order-rate": "Trigger {{side}} {{symbol}} price",
|
||||||
|
"trigger-orders": "Trigger Orders",
|
||||||
"tweet-position": "分享至Twitter",
|
"tweet-position": "分享至Twitter",
|
||||||
"unrealized-pnl": "未實現盈虧",
|
"unrealized-pnl": "未實現盈虧",
|
||||||
"unsettled": "未結清",
|
"unsettled": "未結清",
|
||||||
|
|
Loading…
Reference in New Issue