import { Bank, HealthType } from '@blockworks-foundation/mango-v4' import { ArrowLeftIcon, ArrowUpTrayIcon, ChevronDownIcon, ExclamationCircleIcon, LinkIcon, } from '@heroicons/react/20/solid' import Decimal from 'decimal.js' import { useTranslation } from 'next-i18next' import Image from 'next/legacy/image' import { useCallback, useMemo, useState } from 'react' import NumberFormat, { NumberFormatValues } from 'react-number-format' import mangoStore from '@store/mangoStore' import { ACCOUNT_ACTION_MODAL_INNER_HEIGHT, INPUT_TOKEN_DEFAULT, } from './../utils/constants' import { notify } from './../utils/notifications' import { floorToDecimal, formatDecimal, formatFixedDecimals, } from './../utils/numbers' import ActionTokenList from './account/ActionTokenList' import ButtonGroup from './forms/ButtonGroup' import Label from './forms/Label' import Button from './shared/Button' import InlineNotification from './shared/InlineNotification' import Loading from './shared/Loading' import { EnterBottomExitBottom, FadeInFadeOut } from './shared/Transitions' import { withValueLimit } from './swap/SwapForm' import { getMaxWithdrawForBank } from './swap/useTokenMax' import MaxAmountButton from '@components/shared/MaxAmountButton' import HealthImpactTokenChange from '@components/HealthImpactTokenChange' import useMangoAccount from 'hooks/useMangoAccount' import useJupiterMints from 'hooks/useJupiterMints' import useMangoGroup from 'hooks/useMangoGroup' import TokenVaultWarnings from '@components/shared/TokenVaultWarnings' import { useWallet } from '@solana/wallet-adapter-react' import { useEnhancedWallet } from './wallet/EnhancedWalletProvider' import AmountWithValue from './shared/AmountWithValue' interface WithdrawFormProps { onSuccess: () => void token?: string } function WithdrawForm({ onSuccess, token }: WithdrawFormProps) { const { t } = useTranslation(['common', 'trade']) const { group } = useMangoGroup() const [inputAmount, setInputAmount] = useState('') const [submitting, setSubmitting] = useState(false) const [selectedToken, setSelectedToken] = useState( token || INPUT_TOKEN_DEFAULT ) const [showTokenList, setShowTokenList] = useState(false) const [sizePercentage, setSizePercentage] = useState('') const { mangoTokens } = useJupiterMints() const { mangoAccount } = useMangoAccount() const { connected } = useWallet() const { handleConnect } = useEnhancedWallet() const bank = useMemo(() => { const group = mangoStore.getState().group return group?.banksMapByName.get(selectedToken)?.[0] }, [selectedToken]) const logoUri = useMemo(() => { let logoURI if (mangoTokens?.length) { logoURI = mangoTokens.find( (t) => t.address === bank?.mint.toString() )?.logoURI } return logoURI }, [bank?.mint, mangoTokens]) const tokenMax = useMemo(() => { if (!bank || !mangoAccount || !group) return new Decimal(0) const amount = getMaxWithdrawForBank(group, bank, mangoAccount) return amount && amount.gt(0) ? floorToDecimal(amount, bank.mintDecimals) : new Decimal(0) }, [mangoAccount, bank, group]) const handleSizePercentage = useCallback( (percentage: string) => { if (!bank) return setSizePercentage(percentage) const amount = tokenMax.mul(Number(percentage) / 100) setInputAmount(floorToDecimal(amount, bank.mintDecimals).toFixed()) }, [bank, tokenMax] ) const handleWithdraw = useCallback(async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group const mangoAccount = mangoStore.getState().mangoAccount.current const actions = mangoStore.getState().actions if (!mangoAccount || !group || !bank) return setSubmitting(true) try { const tx = await client.tokenWithdraw( group, mangoAccount, bank.mint, parseFloat(inputAmount), false ) notify({ title: 'Transaction confirmed', type: 'success', txid: tx, }) await actions.reloadMangoAccount() setSubmitting(false) onSuccess() } catch (e: any) { console.error(e) notify({ title: 'Transaction failed', description: e.message, txid: e?.txid, type: 'error', }) setSubmitting(false) } }, [bank, inputAmount]) const handleSelectToken = useCallback((token: string) => { setSelectedToken(token) setShowTokenList(false) }, []) const withdrawBanks = useMemo(() => { if (mangoAccount) { const banks = group?.banksMapByName ? Array.from(group?.banksMapByName, ([key, value]) => { const bank: Bank = value[0] const accountBalance = getMaxWithdrawForBank( group, bank, mangoAccount ) return { key, value, accountBalance: accountBalance ? floorToDecimal(accountBalance, bank.mintDecimals).toNumber() : 0, accountBalanceValue: accountBalance && bank.uiPrice ? accountBalance.toNumber() * bank.uiPrice : 0, } }) : [] return banks } return [] }, [mangoAccount, group]) const initHealth = useMemo(() => { return group && mangoAccount ? mangoAccount.getHealthRatioUi(group, HealthType.init) : 100 }, [mangoAccount]) const showInsufficientBalance = Number(inputAmount) ? tokenMax.lt(inputAmount) : false return ( <> setShowTokenList(false)} className={`absolute left-4 top-4 z-40 w-6 text-th-fgd-4 focus:outline-none md:right-2 md:top-2 md:hover:text-th-active`} > {t('select-withdraw-token')} {t('token')} {t('available-balance')} {initHealth <= 0 ? ( ) : null} {bank ? : null} {bank ? ( handleSizePercentage('100')} value={floorToDecimal( Number(tokenMax), bank.mintDecimals ).toFixed()} /> ) : null} setShowTokenList(true)} className="default-transition flex h-full w-full items-center rounded-lg rounded-r-none py-2 px-3 text-th-fgd-2 hover:cursor-pointer hover:bg-th-bkg-2 hover:text-th-fgd-1" > {selectedToken} setInputAmount( !Number.isNaN(Number(e.value)) ? e.value : '' ) } isAllowed={withValueLimit} /> handleSizePercentage(p)} values={['10', '25', '50', '75', '100']} unit="%" /> {bank ? ( {t('withdraw-amount')} {inputAmount ? ( ) : ( )} ) : null} {!connected ? ( {t('connect')} ) : submitting ? ( ) : showInsufficientBalance ? ( {t('swap:insufficient-balance', { symbol: selectedToken, })} ) : ( {t('withdraw')} )} > ) } export default WithdrawForm
{t('token')}
{t('available-balance')}
{t('withdraw-amount')}