swap form button component
This commit is contained in:
parent
e2abfb5bec
commit
b5941acf3e
|
@ -19,24 +19,14 @@ import SwapSlider from './SwapSlider'
|
|||
import PercentageSelectButtons from './PercentageSelectButtons'
|
||||
import BuyTokenInput from './BuyTokenInput'
|
||||
import SellTokenInput from './SellTokenInput'
|
||||
import Button from '@components/shared/Button'
|
||||
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'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import Link from 'next/link'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import useRemainingBorrowsInPeriod from 'hooks/useRemainingBorrowsInPeriod'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import { SwapFormTokenListType } from './SwapFormTokenList'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
import TopBarStore from '@store/topBarStore'
|
||||
import SwapFormSubmitButton from './SwapFormSubmitButton'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -61,7 +51,6 @@ const MarketSwapForm = ({
|
|||
setShowTokenSelect,
|
||||
onSuccess,
|
||||
}: MarketSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] =
|
||||
useState<JupiterV6RouteInfo | null>()
|
||||
|
@ -90,7 +79,6 @@ const MarketSwapForm = ({
|
|||
wallet: publicKey?.toBase58(),
|
||||
mangoAccount,
|
||||
})
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const amountInAsDecimal: Decimal | null = useMemo(() => {
|
||||
return Number(debouncedAmountIn)
|
||||
|
@ -276,131 +264,15 @@ const MarketSwapForm = ({
|
|||
setShowTokenSelect={setShowTokenSelect}
|
||||
handleRepay={handleRepay}
|
||||
/>
|
||||
{ipAllowed ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default MarketSwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { setShowSettingsModal } = TopBarStore()
|
||||
// const { amount: tokenMax, amountWithBorrow } = useTokenMax(useMargin)
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank, inputBank])
|
||||
|
||||
// check if the borrowed amount exceeds the net borrow limit in the current period
|
||||
const borrowExceedsLimitInPeriod = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (!mangoAccount || !inputBank || !remainingBorrowsInPeriod) return false
|
||||
|
||||
const balance = mangoAccount.getTokenDepositsUi(inputBank)
|
||||
const remainingBalance = balance - amountIn.toNumber()
|
||||
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
const borrowAmountNotional = borrowAmount * inputBank.uiPrice
|
||||
return borrowAmountNotional > remainingBorrowsInPeriod
|
||||
}, [amountIn, inputBank, mangoAccountAddress, remainingBorrowsInPeriod])
|
||||
|
||||
const disabled =
|
||||
!amountIn.toNumber() ||
|
||||
!amountOut ||
|
||||
!selectedRoute ||
|
||||
!!selectedRoute.error
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<div className="pb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href={''} onClick={() => setShowSettingsModal(true)}>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{borrowExceedsLimitInPeriod &&
|
||||
remainingBorrowsInPeriod &&
|
||||
timeToNextPeriod ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={t('error-borrow-exceeds-limit', {
|
||||
remaining: formatCurrencyValue(remainingBorrowsInPeriod),
|
||||
resetTime: dayjs().to(dayjs().add(timeToNextPeriod, 'second')),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import { formatCurrencyValue } from 'utils/numbers'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import Decimal from 'decimal.js'
|
||||
import { JupiterV6RouteInfo } from 'types/jupiter'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { useWallet } from '@solana/wallet-adapter-react'
|
||||
import TopBarStore from '@store/topBarStore'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import useRemainingBorrowsInPeriod from 'hooks/useRemainingBorrowsInPeriod'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
import { useMemo } from 'react'
|
||||
import Button from '@components/shared/Button'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import Link from 'next/link'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { connected } = useWallet()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
const { setShowSettingsModal } = TopBarStore()
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank, inputBank])
|
||||
|
||||
// check if the borrowed amount exceeds the net borrow limit in the current period
|
||||
const borrowExceedsLimitInPeriod = useMemo(() => {
|
||||
const mangoAccount = mangoStore.getState().mangoAccount.current
|
||||
if (!mangoAccount || !inputBank || !remainingBorrowsInPeriod) return false
|
||||
|
||||
const balance = mangoAccount.getTokenDepositsUi(inputBank)
|
||||
const remainingBalance = balance - amountIn.toNumber()
|
||||
const borrowAmount = remainingBalance < 0 ? Math.abs(remainingBalance) : 0
|
||||
const borrowAmountNotional = borrowAmount * inputBank.uiPrice
|
||||
return borrowAmountNotional > remainingBorrowsInPeriod
|
||||
}, [amountIn, inputBank, mangoAccountAddress, remainingBorrowsInPeriod])
|
||||
|
||||
const disabled =
|
||||
!amountIn.toNumber() ||
|
||||
!amountOut ||
|
||||
!selectedRoute ||
|
||||
!!selectedRoute.error
|
||||
|
||||
return ipAllowed ? (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<div className="pb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href={''} onClick={() => setShowSettingsModal(true)}>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{borrowExceedsLimitInPeriod &&
|
||||
remainingBorrowsInPeriod &&
|
||||
timeToNextPeriod ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={t('error-borrow-exceeds-limit', {
|
||||
remaining: formatCurrencyValue(remainingBorrowsInPeriod),
|
||||
resetTime: dayjs().to(dayjs().add(timeToNextPeriod, 'second')),
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwapFormSubmitButton
|
|
@ -17,14 +17,8 @@ import { JupiterV6RouteInfo } from 'types/jupiter'
|
|||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { DEFAULT_PERCENTAGE_VALUES } from './PercentageSelectButtons'
|
||||
import BuyTokenInput from './BuyTokenInput'
|
||||
import Button from '@components/shared/Button'
|
||||
import SwapReviewRouteInfo from './SwapReviewRouteInfo'
|
||||
import useIpAddress from 'hooks/useIpAddress'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useQuoteRoutes from './useQuoteRoutes'
|
||||
import Loading from '@components/shared/Loading'
|
||||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import SecondaryConnectButton from '@components/shared/SecondaryConnectButton'
|
||||
import dayjs from 'dayjs'
|
||||
import relativeTime from 'dayjs/plugin/relativeTime'
|
||||
import { floorToDecimal } from 'utils/numbers'
|
||||
|
@ -33,6 +27,7 @@ import WalletSellTokenInput from './WalletSellTokenInput'
|
|||
import { walletBalanceForToken } from '@components/DepositForm'
|
||||
import WalletSwapSlider from './WalletSwapSlider'
|
||||
import ButtonGroup from '@components/forms/ButtonGroup'
|
||||
import SwapFormSubmitButton from './SwapFormSubmitButton'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -43,7 +38,6 @@ type WalletSwapFormProps = {
|
|||
const set = mangoStore.getState().set
|
||||
|
||||
const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
//initial state is undefined null is returned on error
|
||||
const [selectedRoute, setSelectedRoute] =
|
||||
useState<JupiterV6RouteInfo | null>()
|
||||
|
@ -52,7 +46,6 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
const [sizePercentage, setSizePercentage] = useState('')
|
||||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
const {
|
||||
margin: useMargin,
|
||||
slippage,
|
||||
inputBank,
|
||||
outputBank,
|
||||
|
@ -73,7 +66,6 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
mangoAccount: undefined,
|
||||
mode: 'JUPITER',
|
||||
})
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
||||
|
@ -278,81 +270,15 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
loading={loadingSwapDetails}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
{ipAllowed ? (
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
useMargin={useMargin}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
inputSymbol={inputBank?.name}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
) : (
|
||||
<Button
|
||||
disabled
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
>
|
||||
{t('country-not-allowed', {
|
||||
country: ipCountry ? `(${ipCountry})` : '',
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
<SwapFormSubmitButton
|
||||
loadingSwapDetails={loadingSwapDetails}
|
||||
selectedRoute={selectedRoute}
|
||||
setShowConfirm={setShowConfirm}
|
||||
amountIn={amountInAsDecimal}
|
||||
amountOut={selectedRoute ? amountOutAsDecimal.toNumber() : undefined}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WalletSwapForm
|
||||
|
||||
const SwapFormSubmitButton = ({
|
||||
amountIn,
|
||||
amountOut,
|
||||
loadingSwapDetails,
|
||||
selectedRoute,
|
||||
setShowConfirm,
|
||||
}: {
|
||||
amountIn: Decimal
|
||||
amountOut: number | undefined
|
||||
inputSymbol: string | undefined
|
||||
loadingSwapDetails: boolean
|
||||
selectedRoute: JupiterV6RouteInfo | undefined | null
|
||||
setShowConfirm: (x: boolean) => void
|
||||
useMargin: boolean
|
||||
}) => {
|
||||
const { t } = useTranslation('common')
|
||||
const { connected } = useWallet()
|
||||
|
||||
const disabled =
|
||||
(connected && !amountIn.toNumber()) || !amountOut || !selectedRoute
|
||||
|
||||
return (
|
||||
<>
|
||||
{connected ? (
|
||||
<Button
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
disabled={disabled}
|
||||
size="large"
|
||||
>
|
||||
{loadingSwapDetails ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<span>{t('swap:review-swap')}</span>
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<SecondaryConnectButton
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center"
|
||||
isLarge
|
||||
/>
|
||||
)}
|
||||
{(selectedRoute === null && amountIn.gt(0)) ||
|
||||
(selectedRoute && !!selectedRoute.error) ? (
|
||||
<div className="mb-4">
|
||||
<InlineNotification type="error" desc={t('swap:no-swap-found')} />
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -46,8 +46,8 @@ const fetchIpGeolocation = async () => {
|
|||
}
|
||||
|
||||
export default function useIpAddress() {
|
||||
const [ipAllowed, setIpAllowed] = useState(false)
|
||||
const [spotAllowed, setSpotAllowed] = useState(false)
|
||||
const [ipAllowed, setIpAllowed] = useState(true)
|
||||
const [spotAllowed, setSpotAllowed] = useState(true)
|
||||
const [ipCountry, setIpCountry] = useState('')
|
||||
|
||||
const ipCountryCode = useQuery<string, Error>(
|
||||
|
|
Loading…
Reference in New Issue