add token positions warning to trigger swap
This commit is contained in:
parent
3957e93fb4
commit
634a017b1e
|
@ -36,9 +36,9 @@ import TokenListButton from './shared/TokenListButton'
|
|||
import { ACCOUNT_ACTIONS_NUMBER_FORMAT_CLASSES, BackButton } from './BorrowForm'
|
||||
import TokenLogo from './shared/TokenLogo'
|
||||
import SecondaryConnectButton from './shared/SecondaryConnectButton'
|
||||
import useMangoAccountAccounts from 'hooks/useMangoAccountAccounts'
|
||||
import InlineNotification from './shared/InlineNotification'
|
||||
import Link from 'next/link'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
|
||||
interface DepositFormProps {
|
||||
onSuccess: () => void
|
||||
|
@ -78,20 +78,13 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
const [refreshingWalletTokens, setRefreshingWalletTokens] = useState(false)
|
||||
const { maxSolDeposit } = useSolBalance()
|
||||
const banks = useBanksWithBalances('walletBalance')
|
||||
const { usedTokens, totalTokens } = useMangoAccountAccounts()
|
||||
|
||||
const bank = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
return group?.banksMapByName.get(selectedToken)?.[0]
|
||||
}, [selectedToken])
|
||||
|
||||
const tokenPositionsFull = useMemo(() => {
|
||||
if (!bank || !usedTokens.length || !totalTokens.length) return false
|
||||
const hasTokenPosition = usedTokens.find(
|
||||
(token) => token.tokenIndex === bank.tokenIndex,
|
||||
)
|
||||
return hasTokenPosition ? false : usedTokens.length >= totalTokens.length
|
||||
}, [bank, usedTokens, totalTokens])
|
||||
const tokenPositionsFull = useTokenPositionsFull([bank])
|
||||
|
||||
const { connected, publicKey } = useWallet()
|
||||
const walletTokens = mangoStore((s) => s.wallet.tokens)
|
||||
|
@ -331,17 +324,19 @@ function DepositForm({ onSuccess, token }: DepositFormProps) {
|
|||
/>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href="/settings" onClick={() => onSuccess()} shallow>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<div className="mt-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href="/settings" onClick={() => onSuccess()} shallow>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</FadeInFadeOut>
|
||||
|
|
|
@ -342,7 +342,7 @@ const SwapFormSubmitButton = ({
|
|||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
const tokenPositionsFull = useTokenPositionsFull(outputBank, inputBank)
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank, inputBank])
|
||||
|
||||
const freeCollateral = useMemo(() => {
|
||||
const group = mangoStore.getState().group
|
||||
|
|
|
@ -14,7 +14,7 @@ import { NUMBER_FORMAT_CLASSNAMES } from './MarketSwapForm'
|
|||
import InlineNotification from '@components/shared/InlineNotification'
|
||||
import useMangoAccount from 'hooks/useMangoAccount'
|
||||
import { SwapFormTokenListType } from './SwapFormTokenList'
|
||||
import { getInputTokenBalance } from './LimitSwapForm'
|
||||
import { getInputTokenBalance } from './TriggerSwapForm'
|
||||
|
||||
const ReduceOutputTokenInput = ({
|
||||
error,
|
||||
|
|
|
@ -16,11 +16,11 @@ import InlineNotification from '@components/shared/InlineNotification'
|
|||
import Tooltip from '@components/shared/Tooltip'
|
||||
import TabUnderline from '@components/shared/TabUnderline'
|
||||
import MarketSwapForm from './MarketSwapForm'
|
||||
import LimitSwapForm from './LimitSwapForm'
|
||||
import Switch from '@components/forms/Switch'
|
||||
import useLocalStorageState from 'hooks/useLocalStorageState'
|
||||
import { SwapFormTokenListType } from './SwapFormTokenList'
|
||||
import { TriggerOrderTypes } from 'types'
|
||||
import TriggerSwapForm from './TriggerSwapForm'
|
||||
|
||||
const set = mangoStore.getState().set
|
||||
|
||||
|
@ -175,7 +175,7 @@ const SwapForm = () => {
|
|||
{swapOrTrigger === 'swap' ? (
|
||||
<MarketSwapForm setShowTokenSelect={setShowTokenSelect} />
|
||||
) : (
|
||||
<LimitSwapForm
|
||||
<TriggerSwapForm
|
||||
showTokenSelect={showTokenSelect}
|
||||
setShowTokenSelect={setShowTokenSelect}
|
||||
/>
|
||||
|
|
|
@ -14,7 +14,7 @@ import FormatNumericValue from '@components/shared/FormatNumericValue'
|
|||
import { formatTokenSymbol } from 'utils/tokens'
|
||||
import TokenLogo from '@components/shared/TokenLogo'
|
||||
import Input from '@components/forms/Input'
|
||||
import { getInputTokenBalance } from './LimitSwapForm'
|
||||
import { getInputTokenBalance } from './TriggerSwapForm'
|
||||
|
||||
export type SwapFormTokenListType =
|
||||
| 'input'
|
||||
|
|
|
@ -46,6 +46,8 @@ import relativeTime from 'dayjs/plugin/relativeTime'
|
|||
import { SwapFormTokenListType } from './SwapFormTokenList'
|
||||
import { formatTokenSymbol } from 'utils/tokens'
|
||||
import Tooltip from '@components/shared/Tooltip'
|
||||
import Link from 'next/link'
|
||||
import useTokenPositionsFull from 'hooks/useTokenPositionsFull'
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
|
@ -54,17 +56,17 @@ const priceToDisplayString = (price: number | Decimal | string): string => {
|
|||
return val.toFixed(val.dp())
|
||||
}
|
||||
|
||||
type LimitSwapFormProps = {
|
||||
type TriggerSwapFormProps = {
|
||||
showTokenSelect: SwapFormTokenListType
|
||||
setShowTokenSelect: Dispatch<SetStateAction<SwapFormTokenListType>>
|
||||
}
|
||||
|
||||
type LimitSwapForm = {
|
||||
type TriggerSwapForm = {
|
||||
amountIn: number
|
||||
triggerPrice: string
|
||||
}
|
||||
|
||||
type FormErrors = Partial<Record<keyof LimitSwapForm, string>>
|
||||
type FormErrors = Partial<Record<keyof TriggerSwapForm, string>>
|
||||
|
||||
enum OrderTypes {
|
||||
STOP_LOSS = 'trade:stop-loss',
|
||||
|
@ -103,10 +105,10 @@ const getOrderTypeMultiplier = (
|
|||
}
|
||||
}
|
||||
|
||||
const LimitSwapForm = ({
|
||||
const TriggerSwapForm = ({
|
||||
showTokenSelect,
|
||||
setShowTokenSelect,
|
||||
}: LimitSwapFormProps) => {
|
||||
}: TriggerSwapFormProps) => {
|
||||
const { t } = useTranslation(['common', 'swap', 'trade'])
|
||||
const { mangoAccountAddress } = useMangoAccount()
|
||||
const { ipAllowed, ipCountry } = useIpAddress()
|
||||
|
@ -116,7 +118,7 @@ const LimitSwapForm = ({
|
|||
const [swapFormSizeUi] = useLocalStorageState(SIZE_INPUT_UI_KEY, 'slider')
|
||||
const [formErrors, setFormErrors] = useState<FormErrors>({})
|
||||
const { remainingBorrowsInPeriod, timeToNextPeriod } =
|
||||
useRemainingBorrowsInPeriod(true)
|
||||
useRemainingBorrowsInPeriod(false, true)
|
||||
|
||||
const {
|
||||
inputBank,
|
||||
|
@ -127,6 +129,8 @@ const LimitSwapForm = ({
|
|||
triggerPrice,
|
||||
} = mangoStore((s) => s.swap)
|
||||
|
||||
const tokenPositionsFull = useTokenPositionsFull([outputBank])
|
||||
|
||||
const { connected, connect } = useWallet()
|
||||
|
||||
const [inputBankName, outputBankName, inputBankDecimals, outputBankDecimals] =
|
||||
|
@ -270,10 +274,10 @@ const LimitSwapForm = ({
|
|||
])
|
||||
|
||||
const isFormValid = useCallback(
|
||||
(form: LimitSwapForm) => {
|
||||
(form: TriggerSwapForm) => {
|
||||
const invalidFields: FormErrors = {}
|
||||
setFormErrors({})
|
||||
const requiredFields: (keyof LimitSwapForm)[] = [
|
||||
const requiredFields: (keyof TriggerSwapForm)[] = [
|
||||
'amountIn',
|
||||
'triggerPrice',
|
||||
]
|
||||
|
@ -850,7 +854,7 @@ const LimitSwapForm = ({
|
|||
) : null}
|
||||
{ipAllowed ? (
|
||||
<Button
|
||||
disabled={borrowExceedsLimitInPeriod}
|
||||
disabled={borrowExceedsLimitInPeriod || tokenPositionsFull}
|
||||
onClick={onClick}
|
||||
className="mb-4 mt-6 flex w-full items-center justify-center text-base"
|
||||
size="large"
|
||||
|
@ -879,6 +883,21 @@ const LimitSwapForm = ({
|
|||
})}
|
||||
</Button>
|
||||
)}
|
||||
{tokenPositionsFull ? (
|
||||
<div className="pb-4">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={
|
||||
<>
|
||||
{t('error-token-positions-full')}{' '}
|
||||
<Link href="/settings" shallow>
|
||||
{t('manage')}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
{borrowExceedsLimitInPeriod &&
|
||||
remainingBorrowsInPeriod &&
|
||||
timeToNextPeriod ? (
|
||||
|
@ -896,4 +915,4 @@ const LimitSwapForm = ({
|
|||
)
|
||||
}
|
||||
|
||||
export default LimitSwapForm
|
||||
export default TriggerSwapForm
|
|
@ -8,14 +8,19 @@ import {
|
|||
toUiDecimalsForQuote,
|
||||
} from '@blockworks-foundation/mango-v4'
|
||||
|
||||
export default function useRemainingBorrowsInPeriod(isSwap?: boolean) {
|
||||
export default function useRemainingBorrowsInPeriod(
|
||||
isSwap?: boolean,
|
||||
isSwapTrigger?: boolean,
|
||||
) {
|
||||
const { selectedMarket } = useSelectedMarket()
|
||||
const { inputBank } = mangoStore((s) => s.swap)
|
||||
const { inputBank, outputBank } = mangoStore((s) => s.swap)
|
||||
const { side } = mangoStore((s) => s.tradeForm)
|
||||
|
||||
const bank = useMemo(() => {
|
||||
if (isSwap && inputBank) {
|
||||
return inputBank
|
||||
} else if (isSwapTrigger && outputBank) {
|
||||
return outputBank
|
||||
} else {
|
||||
if (selectedMarket instanceof Serum3Market) {
|
||||
const group = mangoStore.getState().group
|
||||
|
@ -33,7 +38,7 @@ export default function useRemainingBorrowsInPeriod(isSwap?: boolean) {
|
|||
}
|
||||
}
|
||||
return
|
||||
}, [inputBank, isSwap, selectedMarket, side])
|
||||
}, [inputBank, isSwap, isSwapTrigger, outputBank, selectedMarket, side])
|
||||
|
||||
const [remainingBorrowsInPeriod, timeToNextPeriod] = useMemo(() => {
|
||||
if (!bank) return [undefined, undefined]
|
||||
|
|
|
@ -2,33 +2,39 @@ import { Bank } from '@blockworks-foundation/mango-v4'
|
|||
import useMangoAccountAccounts from './useMangoAccountAccounts'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
export default function useTokenPositionsFull(
|
||||
buyBank: Bank | undefined,
|
||||
sellBank: Bank | undefined,
|
||||
) {
|
||||
export default function useTokenPositionsFull(banks: Array<Bank | undefined>) {
|
||||
const { usedTokens, totalTokens } = useMangoAccountAccounts()
|
||||
const tokenPositionsFull = useMemo(() => {
|
||||
if (!buyBank || !sellBank || !usedTokens.length || !totalTokens.length)
|
||||
if (
|
||||
banks.every((bank) => bank === undefined) ||
|
||||
!usedTokens.length ||
|
||||
!totalTokens.length
|
||||
)
|
||||
return false
|
||||
const hasInputTokenPosition = usedTokens.find(
|
||||
(token) => token.tokenIndex === buyBank.tokenIndex,
|
||||
)
|
||||
const hasOutputTokenPosition = usedTokens.find(
|
||||
(token) => token.tokenIndex === sellBank.tokenIndex,
|
||||
)
|
||||
let alreadyHasPositionCount = 0
|
||||
for (const bank of banks) {
|
||||
const hasPosition = usedTokens.find(
|
||||
(token) => token.tokenIndex === bank?.tokenIndex,
|
||||
)
|
||||
if (hasPosition) {
|
||||
alreadyHasPositionCount += 1
|
||||
}
|
||||
}
|
||||
const availableTokenPositions = totalTokens.length - usedTokens.length
|
||||
if (
|
||||
(hasInputTokenPosition && hasOutputTokenPosition) ||
|
||||
availableTokenPositions >= 2
|
||||
banks.length === 2 &&
|
||||
(alreadyHasPositionCount === 2 || availableTokenPositions >= 2)
|
||||
) {
|
||||
return false
|
||||
} else if (
|
||||
(hasInputTokenPosition && !hasOutputTokenPosition) ||
|
||||
(!hasInputTokenPosition && hasOutputTokenPosition)
|
||||
}
|
||||
if (
|
||||
banks.length === 1 &&
|
||||
(alreadyHasPositionCount === 1 || availableTokenPositions >= 1)
|
||||
) {
|
||||
return availableTokenPositions >= 1 ? false : true
|
||||
} else return true
|
||||
}, [buyBank, sellBank, usedTokens, totalTokens])
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}, [banks, usedTokens, totalTokens])
|
||||
|
||||
return tokenPositionsFull
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue