add token positions warning to trigger swap

This commit is contained in:
saml33 2023-09-10 23:26:52 +10:00
parent 3957e93fb4
commit 634a017b1e
8 changed files with 82 additions and 57 deletions

View File

@ -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>

View File

@ -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

View File

@ -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,

View File

@ -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}
/>

View File

@ -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'

View File

@ -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

View File

@ -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]

View File

@ -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
}