various fixes
This commit is contained in:
parent
a2f2c0fe03
commit
78da92f453
|
@ -23,7 +23,11 @@ const TokenVaultWarnings = ({
|
|||
|
||||
const [maxWithdraw, maxBorrow] = useMemo(() => {
|
||||
if (!mangoAccount || !group) return [0, 0]
|
||||
const maxWithdraw = getMaxWithdrawForBank(group, bank, mangoAccount)
|
||||
const maxWithdraw = getMaxWithdrawForBank(
|
||||
group,
|
||||
bank,
|
||||
mangoAccount,
|
||||
).toNumber()
|
||||
const maxBorrow = mangoAccount.getMaxWithdrawWithBorrowForTokenUi(
|
||||
group,
|
||||
bank.mint,
|
||||
|
|
|
@ -5,6 +5,7 @@ import {
|
|||
useMemo,
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useLayoutEffect,
|
||||
} from 'react'
|
||||
import { ArrowDownIcon, ArrowsRightLeftIcon } from '@heroicons/react/20/solid'
|
||||
import NumberFormat, {
|
||||
|
@ -34,6 +35,7 @@ import TokenLogo from '@components/shared/TokenLogo'
|
|||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import { handleFlipPrices } from './SwapTokenChart'
|
||||
import Select from '@components/forms/Select'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
|
||||
type LimitSwapFormProps = {
|
||||
showTokenSelect: 'input' | 'output' | undefined
|
||||
|
@ -65,10 +67,11 @@ const LimitSwapForm = ({
|
|||
setShowTokenSelect,
|
||||
}: LimitSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
|
||||
const [triggerPrice, setTriggerPrice] = useState('')
|
||||
const [orderType, setOrderType] = useState(ORDER_TYPES[0])
|
||||
const [orderTypeMultiplier, setOrderTypeMultiplier] = useState(0.9)
|
||||
const [orderTypeMultiplier, setOrderTypeMultiplier] = useState(1.1)
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||
|
@ -140,10 +143,18 @@ const LimitSwapForm = ({
|
|||
useEffect(() => {
|
||||
if (!quotePrice) return
|
||||
if (!triggerPrice && !showTokenSelect) {
|
||||
setTriggerPrice((quotePrice * 0.9).toFixed(inputBank?.mintDecimals))
|
||||
setTriggerPrice((quotePrice * 1.1).toFixed(inputBank?.mintDecimals))
|
||||
}
|
||||
}, [inputBank, quotePrice, showTokenSelect, triggerPrice])
|
||||
|
||||
// flip trigger price when chart direction is flipped
|
||||
useLayoutEffect(() => {
|
||||
if (!quotePrice) return
|
||||
setTriggerPrice(
|
||||
(quotePrice * orderTypeMultiplier).toFixed(inputBank?.mintDecimals),
|
||||
)
|
||||
}, [flipPrices, orderTypeMultiplier])
|
||||
|
||||
const triggerPriceDifference = useMemo(() => {
|
||||
if (!quotePrice) return 0
|
||||
const triggerDifference = triggerPrice
|
||||
|
@ -173,9 +184,7 @@ const LimitSwapForm = ({
|
|||
return invalidFields
|
||||
}, [])
|
||||
|
||||
/*
|
||||
If the use margin setting is toggled, clear the form values
|
||||
*/
|
||||
// If the use margin setting is toggled, clear the form values
|
||||
useEffect(() => {
|
||||
setAmountInFormValue('')
|
||||
setAmountOutFormValue('')
|
||||
|
@ -400,22 +409,38 @@ const LimitSwapForm = ({
|
|||
!triggerPrice
|
||||
)
|
||||
return
|
||||
|
||||
const quoteString = !flipPrices
|
||||
? `${inputBank.name} per ${outputBank.name}`
|
||||
: `${outputBank.name} per ${inputBank.name}`
|
||||
if (inputBank.name === 'USDC') {
|
||||
|
||||
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, outputBank.mintDecimals),
|
||||
priceUnit: quoteString,
|
||||
symbol: outputBank.name,
|
||||
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
|
||||
})
|
||||
} else if (inputBank.name === 'USDC') {
|
||||
return t('trade:trigger-order-desc', {
|
||||
amount: floorToDecimal(amountOutFormValue, outputBank.mintDecimals),
|
||||
symbol: outputBank.name,
|
||||
triggerPrice: triggerPrice,
|
||||
orderType: orderTypeString,
|
||||
priceUnit: quoteString,
|
||||
symbol: outputBank.name,
|
||||
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
|
||||
})
|
||||
} else {
|
||||
return t('trade:trigger-order-desc', {
|
||||
amount: floorToDecimal(amountInFormValue, inputBank.mintDecimals),
|
||||
symbol: inputBank.name,
|
||||
triggerPrice: triggerPrice,
|
||||
orderType: orderTypeString,
|
||||
priceUnit: quoteString,
|
||||
symbol: inputBank.name,
|
||||
triggerPrice: floorToDecimal(triggerPrice, inputBank.mintDecimals),
|
||||
})
|
||||
}
|
||||
}, [
|
||||
|
@ -423,6 +448,7 @@ const LimitSwapForm = ({
|
|||
amountOutFormValue,
|
||||
flipPrices,
|
||||
inputBank,
|
||||
orderType,
|
||||
outputBank,
|
||||
triggerPrice,
|
||||
])
|
||||
|
@ -489,13 +515,20 @@ const LimitSwapForm = ({
|
|||
setOrderType(newType)
|
||||
const triggerMultiplier =
|
||||
newType === OrderTypes.STOP_LOSS
|
||||
? 0.9
|
||||
: newType === OrderTypes.TAKE_PROFIT
|
||||
? 1.1
|
||||
: newType === OrderTypes.TAKE_PROFIT
|
||||
? 0.9
|
||||
: 1
|
||||
setOrderTypeMultiplier(triggerMultiplier)
|
||||
const trigger = (quotePrice * triggerMultiplier).toString()
|
||||
setTriggerPrice(trigger)
|
||||
if (amountInAsDecimal.gt(0)) {
|
||||
const amountOut = getAmountOut(
|
||||
amountInAsDecimal.toString(),
|
||||
trigger,
|
||||
).toString()
|
||||
setAmountOutFormValue(amountOut)
|
||||
}
|
||||
},
|
||||
[quotePrice, setOrderTypeMultiplier],
|
||||
)
|
||||
|
@ -619,7 +652,7 @@ const LimitSwapForm = ({
|
|||
/>
|
||||
{swapFormSizeUi === 'slider' ? (
|
||||
<SwapSlider
|
||||
useMargin={useMargin}
|
||||
useMargin={false}
|
||||
amount={amountInAsDecimal.toNumber()}
|
||||
onChange={(v) => handleAmountInUi(v)}
|
||||
step={1 / 10 ** (inputBank?.mintDecimals || 6)}
|
||||
|
@ -628,7 +661,7 @@ const LimitSwapForm = ({
|
|||
<PercentageSelectButtons
|
||||
amountIn={amountInAsDecimal.toString()}
|
||||
setAmountIn={(v) => handleAmountInUi(v)}
|
||||
useMargin={useMargin}
|
||||
useMargin={false}
|
||||
/>
|
||||
)}
|
||||
{orderDescription ? (
|
||||
|
@ -636,11 +669,13 @@ const LimitSwapForm = ({
|
|||
<InlineNotification
|
||||
desc={
|
||||
<>
|
||||
{inputBank?.name === 'USDC' ? (
|
||||
<span className="text-th-up">{t('buy')}</span>
|
||||
) : (
|
||||
<span className="text-th-down">{t('sell')}</span>
|
||||
)}{' '}
|
||||
{orderType !== OrderTypes.REPAY_BORROW ? (
|
||||
inputBank?.name === 'USDC' ? (
|
||||
<span className="text-th-up">{t('buy')}</span>
|
||||
) : (
|
||||
<span className="text-th-down">{t('sell')}</span>
|
||||
)
|
||||
) : null}{' '}
|
||||
{orderDescription}
|
||||
</>
|
||||
}
|
||||
|
@ -648,19 +683,31 @@ const LimitSwapForm = ({
|
|||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{orderType === 'repay-borrow' && !hasBorrowToRepay ? (
|
||||
{orderType === OrderTypes.REPAY_BORROW && !hasBorrowToRepay ? (
|
||||
<div className="mt-3">
|
||||
<InlineNotification desc={t('swap:no-borrow')} type="error" />
|
||||
</div>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={handlePlaceStopLoss}
|
||||
disabled={disablePlaceOrder}
|
||||
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{submitting ? <Loading /> : t('swap:place-limit-order')}
|
||||
</Button>
|
||||
{ipAllowed ? (
|
||||
<Button
|
||||
onClick={handlePlaceStopLoss}
|
||||
disabled={disablePlaceOrder}
|
||||
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>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,12 +6,16 @@ import {
|
|||
Dispatch,
|
||||
SetStateAction,
|
||||
} from 'react'
|
||||
import { ArrowDownIcon } from '@heroicons/react/20/solid'
|
||||
import {
|
||||
ArrowDownIcon,
|
||||
ExclamationCircleIcon,
|
||||
LinkIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import { NumberFormatValues, SourceInfo } from 'react-number-format'
|
||||
import Decimal from 'decimal.js'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useDebounce from '../shared/useDebounce'
|
||||
import { SIZE_INPUT_UI_KEY } from '../../utils/constants'
|
||||
import { MANGO_MINT, SIZE_INPUT_UI_KEY, USDC_MINT } from '../../utils/constants'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import { RouteInfo } from 'types/jupiter'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
|
@ -19,11 +23,17 @@ import SwapSlider from './SwapSlider'
|
|||
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||
import BuyTokenInput from './BuyTokenInput'
|
||||
import SellTokenInput from './SellTokenInput'
|
||||
import Button from '@components/shared/Button'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import { useTokenMax } from './useTokenMax'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
|
||||
type MarketSwapFormProps = {
|
||||
bestRoute: RouteInfo | undefined | null
|
||||
selectedRoute: RouteInfo | undefined | null
|
||||
setSelectedRoute: Dispatch<SetStateAction<RouteInfo | undefined | null>>
|
||||
setShowTokenSelect: Dispatch<SetStateAction<'input' | 'output' | undefined>>
|
||||
}
|
||||
|
||||
|
@ -39,17 +49,16 @@ export const NUMBER_FORMAT_CLASSNAMES =
|
|||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
const MarketSwapForm = ({
|
||||
bestRoute,
|
||||
selectedRoute,
|
||||
setSelectedRoute,
|
||||
setShowTokenSelect,
|
||||
}: MarketSwapFormProps) => {
|
||||
const MarketSwapForm = ({ setShowTokenSelect }: MarketSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] = useState<RouteInfo | null>()
|
||||
const [animateSwitchArrow, setAnimateSwitchArrow] = useState(0)
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
|
||||
const {
|
||||
margin: useMargin,
|
||||
slippage,
|
||||
inputBank,
|
||||
outputBank,
|
||||
amountIn: amountInFormValue,
|
||||
|
@ -58,7 +67,16 @@ const MarketSwapForm = ({
|
|||
} = mangoStore((s) => s.swap)
|
||||
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
||||
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
|
||||
const { connected } = useWallet()
|
||||
const { connected, publicKey } = useWallet()
|
||||
const { bestRoute, routes } = useQuoteRoutes({
|
||||
inputMint: inputBank?.mint.toString() || USDC_MINT,
|
||||
outputMint: outputBank?.mint.toString() || MANGO_MINT,
|
||||
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
|
||||
slippage,
|
||||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
})
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountIn)
|
||||
|
@ -197,6 +215,27 @@ const MarketSwapForm = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Transition
|
||||
className="absolute top-0 right-0 z-10 h-full w-full bg-th-bkg-1 pb-0"
|
||||
show={showConfirm}
|
||||
enter="transition ease-in duration-300"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-out duration-300"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
<SwapReviewRouteInfo
|
||||
onClose={() => setShowConfirm(false)}
|
||||
amountIn={amountInAsDecimal}
|
||||
slippage={slippage}
|
||||
routes={routes}
|
||||
selectedRoute={selectedRoute}
|
||||
setSelectedRoute={setSelectedRoute}
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
<SellTokenInput
|
||||
handleAmountInChange={handleAmountInChange}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
|
@ -237,8 +276,100 @@ const MarketSwapForm = ({
|
|||
useMargin={useMargin}
|
||||
/>
|
||||
)}
|
||||
{ipAllowed ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mt-6 mb-4 w-full leading-tight"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarketSwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
inputSymbol,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
useMargin,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected, connect } = useWallet()
|
||||
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
|
||||
const showInsufficientBalance = useMargin
|
||||
? amountWithBorrow.lt(amountIn)
|
||||
: tokenMax.lt(amountIn)
|
||||
|
||||
const disabled =
|
||||
connected &&
|
||||
(!amountIn.toNumber() ||
|
||||
showInsufficientBalance ||
|
||||
!amountOut ||
|
||||
!selectedRoute)
|
||||
|
||||
const onClick = connected ? () => setShowConfirm(true) : connect
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{connected ? (
|
||||
showInsufficientBalance ? (
|
||||
<div className="flex items-center">
|
||||
<ExclamationCircleIcon className="mr-2 h-5 w-5 flex-shrink-0" />
|
||||
{t('swap:insufficient-balance', {
|
||||
symbol: inputSymbol,
|
||||
})}
|
||||
</div>
|
||||
) : loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center">
|
||||
<LinkIcon className="mr-2 h-5 w-5" />
|
||||
{t('connect')}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
{selectedRoute === null && amountIn.gt(0) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,32 +1,16 @@
|
|||
import { useState, useCallback, useMemo, useEffect } from 'react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import {
|
||||
Cog8ToothIcon,
|
||||
ExclamationCircleIcon,
|
||||
LinkIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
import Decimal from 'decimal.js'
|
||||
import { Cog8ToothIcon } from '@heroicons/react/20/solid'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import ContentBox from '../shared/ContentBox'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import useDebounce from '../shared/useDebounce'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import SwapFormTokenList from './SwapFormTokenList'
|
||||
import { Transition } from '@headlessui/react'
|
||||
import Button, { IconButton, LinkButton } from '../shared/Button'
|
||||
import Loading from '../shared/Loading'
|
||||
import { IconButton, LinkButton } from '../shared/Button'
|
||||
import { EnterBottomExitBottom } from '../shared/Transitions'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import { HealthType } from '@blockworks-foundation/mango-v4'
|
||||
import { MANGO_MINT, SWAP_MARGIN_KEY, USDC_MINT } from '../../utils/constants'
|
||||
import { useTokenMax } from './useTokenMax'
|
||||
import { SWAP_MARGIN_KEY } from '../../utils/constants'
|
||||
import HealthImpact from '@components/shared/HealthImpact'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { RouteInfo } from 'types/jupiter'
|
||||
import useMangoGroup from 'hooks/useMangoGroup'
|
||||
import TokenVaultWarnings from '@components/shared/TokenVaultWarnings'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import SwapSettings from './SwapSettings'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
|
@ -40,18 +24,13 @@ const set = mangoStore.getState().set
|
|||
|
||||
const SwapForm = () => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] = useState<RouteInfo | null>()
|
||||
const [showTokenSelect, setShowTokenSelect] = useState<'input' | 'output'>()
|
||||
const [showSettings, setShowSettings] = useState(false)
|
||||
const [showConfirm, setShowConfirm] = useState(false)
|
||||
const [swapOrLimit, setSwapOrLimit] = useState('swap')
|
||||
const { group } = useMangoGroup()
|
||||
const [, setSavedSwapMargin] = useLocalStorageState<boolean>(
|
||||
SWAP_MARGIN_KEY,
|
||||
true,
|
||||
)
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const {
|
||||
margin: useMargin,
|
||||
|
@ -60,33 +39,7 @@ const SwapForm = () => {
|
|||
outputBank,
|
||||
amountIn: amountInFormValue,
|
||||
amountOut: amountOutFormValue,
|
||||
swapMode,
|
||||
} = mangoStore((s) => s.swap)
|
||||
const [debouncedAmountIn] = useDebounce(amountInFormValue, 300)
|
||||
const [debouncedAmountOut] = useDebounce(amountOutFormValue, 300)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const { connected, publicKey } = useWallet()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountIn)
|
||||
? new Decimal(debouncedAmountIn)
|
||||
: new Decimal(0)
|
||||
}, [debouncedAmountIn])
|
||||
|
||||
const amountOutAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountOut)
|
||||
? new Decimal(debouncedAmountOut)
|
||||
: new Decimal(0)
|
||||
}, [debouncedAmountOut])
|
||||
|
||||
const { bestRoute, routes } = useQuoteRoutes({
|
||||
inputMint: inputBank?.mint.toString() || USDC_MINT,
|
||||
outputMint: outputBank?.mint.toString() || MANGO_MINT,
|
||||
amount: swapMode === 'ExactIn' ? debouncedAmountIn : debouncedAmountOut,
|
||||
slippage,
|
||||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
})
|
||||
|
||||
const handleTokenInSelect = useCallback((mintAddress: string) => {
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -114,14 +67,15 @@ const SwapForm = () => {
|
|||
|
||||
const maintProjectedHealth = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (
|
||||
!inputBank ||
|
||||
!mangoAccount ||
|
||||
!outputBank ||
|
||||
!amountOutAsDecimal ||
|
||||
!amountOutFormValue ||
|
||||
!group
|
||||
)
|
||||
return 0
|
||||
return 100
|
||||
|
||||
const simulatedHealthRatio =
|
||||
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
|
||||
|
@ -129,11 +83,11 @@ const SwapForm = () => {
|
|||
[
|
||||
{
|
||||
mintPk: inputBank.mint,
|
||||
uiTokenAmount: amountInAsDecimal.toNumber() * -1,
|
||||
uiTokenAmount: parseFloat(amountInFormValue) * -1,
|
||||
},
|
||||
{
|
||||
mintPk: outputBank.mint,
|
||||
uiTokenAmount: amountOutAsDecimal.toNumber(),
|
||||
uiTokenAmount: parseFloat(amountOutFormValue),
|
||||
},
|
||||
],
|
||||
HealthType.maint,
|
||||
|
@ -143,28 +97,7 @@ const SwapForm = () => {
|
|||
: simulatedHealthRatio < 0
|
||||
? 0
|
||||
: Math.trunc(simulatedHealthRatio)
|
||||
}, [
|
||||
mangoAccount,
|
||||
inputBank,
|
||||
outputBank,
|
||||
amountInAsDecimal,
|
||||
amountOutAsDecimal,
|
||||
])
|
||||
|
||||
const loadingSwapDetails: boolean = useMemo(() => {
|
||||
return (
|
||||
!!(amountInAsDecimal.toNumber() || amountOutAsDecimal.toNumber()) &&
|
||||
swapOrLimit === 'swap' &&
|
||||
connected &&
|
||||
typeof selectedRoute === 'undefined'
|
||||
)
|
||||
}, [
|
||||
amountInAsDecimal,
|
||||
amountOutAsDecimal,
|
||||
connected,
|
||||
selectedRoute,
|
||||
swapOrLimit,
|
||||
])
|
||||
}, [inputBank, outputBank, amountInFormValue, amountOutFormValue])
|
||||
|
||||
const handleSwapOrLimit = useCallback(
|
||||
(orderType: string) => {
|
||||
|
@ -185,15 +118,15 @@ const SwapForm = () => {
|
|||
|
||||
const estSlippage = useMemo(() => {
|
||||
const { group } = mangoStore.getState()
|
||||
if (!group || !inputBank || !amountInAsDecimal.gt(0)) return 0
|
||||
const amountIn = amountInAsDecimal.toNumber()
|
||||
const amountIn = parseFloat(amountInFormValue) || 0
|
||||
if (!group || !inputBank || amountIn <= 0) return 0
|
||||
const value = amountIn * inputBank.uiPrice
|
||||
const slippage = group.getPriceImpactByTokenIndex(
|
||||
inputBank.tokenIndex,
|
||||
value,
|
||||
)
|
||||
return slippage
|
||||
}, [amountInAsDecimal, inputBank])
|
||||
}, [amountInFormValue, inputBank])
|
||||
|
||||
return (
|
||||
<ContentBox
|
||||
|
@ -201,25 +134,6 @@ const SwapForm = () => {
|
|||
className="relative overflow-hidden border-x-0 bg-th-bkg-1 md:border-l md:border-r-0 md:border-t-0 md:border-b-0"
|
||||
>
|
||||
<div>
|
||||
<Transition
|
||||
className="absolute top-0 right-0 z-10 h-full w-full bg-th-bkg-1 pb-0"
|
||||
show={showConfirm}
|
||||
enter="transition ease-in duration-300"
|
||||
enterFrom="-translate-x-full"
|
||||
enterTo="translate-x-0"
|
||||
leave="transition ease-out duration-300"
|
||||
leaveFrom="translate-x-0"
|
||||
leaveTo="-translate-x-full"
|
||||
>
|
||||
<SwapReviewRouteInfo
|
||||
onClose={() => setShowConfirm(false)}
|
||||
amountIn={amountInAsDecimal}
|
||||
slippage={slippage}
|
||||
routes={routes}
|
||||
selectedRoute={selectedRoute}
|
||||
setSelectedRoute={setSelectedRoute}
|
||||
/>
|
||||
</Transition>
|
||||
<EnterBottomExitBottom
|
||||
className="thin-scroll absolute bottom-0 left-0 z-10 h-full w-full overflow-auto bg-th-bkg-1 p-6 pb-0"
|
||||
show={!!showTokenSelect}
|
||||
|
@ -259,44 +173,14 @@ const SwapForm = () => {
|
|||
</IconButton>
|
||||
</div>
|
||||
{swapOrLimit === 'swap' ? (
|
||||
<MarketSwapForm
|
||||
bestRoute={bestRoute}
|
||||
selectedRoute={selectedRoute}
|
||||
setSelectedRoute={setSelectedRoute}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
<MarketSwapForm setShowTokenSelect={setShowTokenSelect} />
|
||||
) : (
|
||||
<LimitSwapForm
|
||||
showTokenSelect={showTokenSelect}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
)}
|
||||
{ipAllowed ? (
|
||||
swapOrLimit === 'swap' ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={
|
||||
selectedRoute ? amountOutAsDecimal.toNumber() : undefined
|
||||
}
|
||||
/>
|
||||
) : null
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mt-6 mb-4 w-full leading-tight"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
{group && inputBank ? (
|
||||
{inputBank ? (
|
||||
<TokenVaultWarnings bank={inputBank} type="swap" />
|
||||
) : null}
|
||||
{inputBank &&
|
||||
|
@ -355,7 +239,7 @@ const SwapForm = () => {
|
|||
</div>
|
||||
</>
|
||||
) : null}
|
||||
{estSlippage > 0 && swapOrLimit === 'trade:trigger-order' ? (
|
||||
{estSlippage ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-sm text-th-fgd-3">
|
||||
|
@ -392,74 +276,3 @@ const SwapForm = () => {
|
|||
}
|
||||
|
||||
export default SwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
inputSymbol,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
useMargin,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected, connect } = useWallet()
|
||||
const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
|
||||
const showInsufficientBalance = useMargin
|
||||
? amountWithBorrow.lt(amountIn)
|
||||
: tokenMax.lt(amountIn)
|
||||
|
||||
const disabled =
|
||||
connected &&
|
||||
(!amountIn.toNumber() ||
|
||||
showInsufficientBalance ||
|
||||
!amountOut ||
|
||||
!selectedRoute)
|
||||
|
||||
const onClick = connected ? () => setShowConfirm(true) : connect
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
className="mt-6 mb-4 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{connected ? (
|
||||
showInsufficientBalance ? (
|
||||
<div className="flex items-center">
|
||||
<ExclamationCircleIcon className="mr-2 h-5 w-5 flex-shrink-0" />
|
||||
{t('swap:insufficient-balance', {
|
||||
symbol: inputSymbol,
|
||||
})}
|
||||
</div>
|
||||
) : loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)
|
||||
) : (
|
||||
<div className="flex items-center">
|
||||
<LinkIcon className="mr-2 h-5 w-5" />
|
||||
{t('connect')}
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
{selectedRoute === null && amountIn.gt(0) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -249,26 +249,6 @@ const SwapTokenChart = () => {
|
|||
: `${inputSymbol}/${outputSymbol}`
|
||||
}, [flipPrices, inputBank, inputCoingeckoId, outputBank])
|
||||
|
||||
// const handleFlipPrices = useCallback(
|
||||
// (flip: boolean) => {
|
||||
// if (!flipPrices && flip) {
|
||||
// setSwapChartSettings([
|
||||
// ...swapChartSettings,
|
||||
// { pair: swapMarketName, flipPrices: true },
|
||||
// ])
|
||||
// } else {
|
||||
// setSwapChartSettings(
|
||||
// swapChartSettings.filter(
|
||||
// (chart: SwapChartSettings) =>
|
||||
// !chart.pair.includes(inputBank!.name) &&
|
||||
// !chart.pair.includes(outputBank!.name),
|
||||
// ),
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// [flipPrices, inputBank, outputBank, swapChartSettings, swapMarketName],
|
||||
// )
|
||||
|
||||
const handleSwapMouseEnter = useCallback(
|
||||
(
|
||||
swap: SwapHistoryItem | undefined,
|
||||
|
@ -460,6 +440,22 @@ const SwapTokenChart = () => {
|
|||
})
|
||||
}, [coingeckoData, chartSwapTimes])
|
||||
|
||||
const latestChartDataItem = useMemo(() => {
|
||||
if (!inputBank || !outputBank) return []
|
||||
const price = !flipPrices
|
||||
? outputBank.uiPrice / inputBank.uiPrice
|
||||
: inputBank.uiPrice / outputBank.uiPrice
|
||||
const item: ChartDataItem[] = [
|
||||
{
|
||||
price,
|
||||
time: Date.now(),
|
||||
inputTokenPrice: inputBank.uiPrice,
|
||||
outputTokenPrice: outputBank.uiPrice,
|
||||
},
|
||||
]
|
||||
return item
|
||||
}, [flipPrices, inputBank, outputBank])
|
||||
|
||||
const chartData = useMemo(() => {
|
||||
if (!coingeckoData || !coingeckoData.length || coingeckoData.length < 2)
|
||||
return []
|
||||
|
@ -469,7 +465,10 @@ const SwapTokenChart = () => {
|
|||
const swapPoints = swapHistoryPoints.filter(
|
||||
(point) => point.time >= minTime && point.time <= maxTime,
|
||||
)
|
||||
return coingeckoData.concat(swapPoints).sort((a, b) => a.time - b.time)
|
||||
return coingeckoData
|
||||
.concat(swapPoints)
|
||||
.sort((a, b) => a.time - b.time)
|
||||
.concat(latestChartDataItem)
|
||||
} else return coingeckoData
|
||||
}, [coingeckoData, swapHistoryPoints, showSwaps])
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
@ -104,7 +105,7 @@
|
|||
"trades": "Trades",
|
||||
"trigger-price": "Trigger Price",
|
||||
"trigger-order": "Trigger Order",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-orders": "Trigger Orders",
|
||||
"tweet-position": "Tweet",
|
||||
"unrealized-pnl": "Unrealized PnL",
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
@ -104,7 +105,7 @@
|
|||
"trades": "Trades",
|
||||
"trigger-price": "Trigger Price",
|
||||
"trigger-order": "Trigger Order",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-orders": "Trigger Orders",
|
||||
"tweet-position": "Tweet",
|
||||
"unrealized-pnl": "Unrealized PnL",
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"realized-pnl": "Realized PnL",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "Reduce Only",
|
||||
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
|
||||
"sells": "Sells",
|
||||
"settle-funds": "Settle Funds",
|
||||
"settle-funds-error": "Failed to settle funds",
|
||||
|
@ -104,7 +105,7 @@
|
|||
"trades": "Trades",
|
||||
"trigger-price": "Trigger Price",
|
||||
"trigger-order": "Trigger Order",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-orders": "Trigger Orders",
|
||||
"tweet-position": "Tweet",
|
||||
"unrealized-pnl": "Unrealized PnL",
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
"quote": "计价",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "限减少",
|
||||
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
|
||||
"sells": "卖单",
|
||||
"settle-funds": "借清资金",
|
||||
"settle-funds-error": "借清出错",
|
||||
|
@ -102,7 +103,7 @@
|
|||
"tweet-position": "分享至Twitter",
|
||||
"trigger-price": "Trigger Price",
|
||||
"trigger-order": "Trigger Order",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-orders": "Trigger Orders",
|
||||
"unsettled": "未結清",
|
||||
"volume-alert": "交易量警報",
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
"realized-pnl": "已實現的盈虧",
|
||||
"reduce": "Reduce",
|
||||
"reduce-only": "限減少",
|
||||
"repay-borrow-order-desc": "Repay {{amount}} {{symbol}} if the oracle price reaches {{triggerPrice}} {{priceUnit}}",
|
||||
"sells": "賣單",
|
||||
"settle-funds": "借清資金",
|
||||
"settle-funds-error": "借清出錯",
|
||||
|
@ -104,7 +105,7 @@
|
|||
"trades": "交易",
|
||||
"trigger-price": "Trigger Price",
|
||||
"trigger-order": "Trigger Order",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price is {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-order-desc": "{{amount}} {{symbol}} if the oracle price {{orderType}} {{triggerPrice}} {{priceUnit}}",
|
||||
"trigger-orders": "Trigger Orders",
|
||||
"tweet-position": "分享至Twitter",
|
||||
"unrealized-pnl": "未實現盈虧",
|
||||
|
|
Loading…
Reference in New Issue