import { toUiDecimalsForQuote } from '@blockworks-foundation/mango-v4' import { ArrowDownTrayIcon, ChevronDownIcon, ExclamationCircleIcon, } from '@heroicons/react/20/solid' import { Wallet } from '@project-serum/anchor' import { useWallet } from '@solana/wallet-adapter-react' import Decimal from 'decimal.js' import { useTranslation } from 'next-i18next' import Image from 'next/image' import React, { useCallback, useEffect, useMemo, useState } from 'react' import NumberFormat, { NumberFormatValues } from 'react-number-format' import mangoStore from '@store/mangoStore' import { ModalProps } from '../../types/modal' import { ALPHA_DEPOSIT_LIMIT, INPUT_TOKEN_DEFAULT } from '../../utils/constants' import { notify } from '../../utils/notifications' import { floorToDecimal, formatFixedDecimals } from '../../utils/numbers' import { TokenAccount } from '../../utils/tokens' import ActionTokenList from '../account/ActionTokenList' import ButtonGroup from '../forms/ButtonGroup' import Label from '../forms/Label' import Button from '../shared/Button' import HealthImpact from '../shared/HealthImpact' import InfoTooltip from '../shared/InfoTooltip' import InlineNotification from '../shared/InlineNotification' import Loading from '../shared/Loading' import Modal from '../shared/Modal' import { EnterBottomExitBottom, FadeInFadeOut } from '../shared/Transitions' import { withValueLimit } from '../swap/SwapForm' import MaxAmountButton from '@components/shared/MaxAmountButton' import Tooltip from '@components/shared/Tooltip' interface DepositModalProps { token?: string } type ModalCombinedProps = DepositModalProps & ModalProps export const walletBalanceForToken = ( walletTokens: TokenAccount[], token: string ): { maxAmount: number; maxDecimals: number } => { const group = mangoStore.getState().group const bank = group?.banksMapByName.get(token)![0] let walletToken if (bank) { const tokenMint = bank?.mint walletToken = tokenMint ? walletTokens.find((t) => t.mint.toString() === tokenMint.toString()) : null } return { maxAmount: walletToken ? walletToken.uiAmount : 0, maxDecimals: bank?.mintDecimals || 6, } } function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) { const { t } = useTranslation('common') const group = mangoStore((s) => s.group) 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 [showMaxSolWarning, setShowMaxSolWarning] = useState(false) const jupiterTokens = mangoStore((s) => s.jupiterTokens) const bank = useMemo(() => { const group = mangoStore.getState().group return group?.banksMapByName.get(selectedToken)![0] }, [selectedToken]) const logoUri = useMemo(() => { let logoURI if (jupiterTokens.length) { logoURI = jupiterTokens.find( (t) => t.address === bank?.mint.toString() )!.logoURI } return logoURI }, [bank?.mint, jupiterTokens]) const { wallet } = useWallet() const walletTokens = mangoStore((s) => s.wallet.tokens) const tokenMax = useMemo(() => { return walletBalanceForToken(walletTokens, selectedToken) }, [walletTokens, selectedToken]) const setMax = useCallback(() => { const max = selectedToken === 'SOL' ? tokenMax.maxAmount - 0.05 : tokenMax.maxAmount setInputAmount(max.toString()) setSizePercentage('100') }, [tokenMax]) const handleSizePercentage = useCallback( (percentage: string) => { setSizePercentage(percentage) let amount = new Decimal(tokenMax.maxAmount).mul(percentage).div(100) if (percentage !== '100') { amount = floorToDecimal(amount, tokenMax.maxDecimals) } else { amount = selectedToken === 'SOL' ? amount.sub(0.05) : amount } setInputAmount(amount.toString()) }, [tokenMax] ) useEffect(() => { if (selectedToken === 'SOL' && sizePercentage === '100') { setShowMaxSolWarning(true) } else { setShowMaxSolWarning(false) } }, [selectedToken, sizePercentage]) const handleSelectToken = (token: string) => { setSelectedToken(token) setShowTokenList(false) } const handleDeposit = async () => { const client = mangoStore.getState().client const group = mangoStore.getState().group const actions = mangoStore.getState().actions const mangoAccount = mangoStore.getState().mangoAccount.current if (!mangoAccount || !group) return try { setSubmitting(true) const tx = await client.tokenDeposit( group, mangoAccount, bank!.mint, parseFloat(inputAmount) ) notify({ title: 'Transaction confirmed', type: 'success', txid: tx, }) await actions.reloadMangoAccount() actions.fetchWalletTokens(wallet!.adapter as unknown as Wallet) setSubmitting(false) } catch (e: any) { notify({ title: 'Transaction failed', description: e.message, txid: e?.signature, type: 'error', }) console.error('Error depositing:', e) } onClose() } // TODO extract into a shared hook for UserSetupModal.tsx const banks = useMemo(() => { const banks = group?.banksMapByName ? Array.from(group?.banksMapByName, ([key, value]) => { const walletBalance = walletBalanceForToken(walletTokens, key) return { key, value, walletBalance: floorToDecimal( walletBalance.maxAmount, walletBalance.maxDecimals ).toNumber(), walletBalanceValue: walletBalance.maxAmount * value[0].uiPrice!, } }) : [] return banks }, [group?.banksMapByName, walletTokens]) const exceedsAlphaMax = useMemo(() => { const mangoAccount = mangoStore.getState().mangoAccount.current if (!group || !mangoAccount) return if ( mangoAccount.owner.toString() === '8SSLjXBEVk9nesbhi9UMCA32uijbVBUqWoKPPQPTekzt' ) return false const accountValue = toUiDecimalsForQuote( mangoAccount.getEquity(group)!.toNumber() ) return ( parseFloat(inputAmount) > ALPHA_DEPOSIT_LIMIT || accountValue > ALPHA_DEPOSIT_LIMIT ) }, [inputAmount]) const showInsufficientBalance = tokenMax.maxAmount < Number(inputAmount) return (

{t('select-token')}

{t('token')}

{t('deposit-rate')}

{t('wallet-balance')}

{t('deposit')}

{showMaxSolWarning ? (
) : null}
setInputAmount(Number(e.value) ? e.value : '') } isAllowed={withValueLimit} />
handleSizePercentage(p)} values={['10', '25', '50', '75', '100']} unit="%" />

{t('deposit-value')}

{formatFixedDecimals( bank?.uiPrice! * Number(inputAmount), true )}

{t('asset-weight')}

{bank!.initAssetWeight.toFixed(2)}x

{t('collateral-value')}

{formatFixedDecimals( bank!.uiPrice! * Number(inputAmount) * Number(bank!.initAssetWeight), true )}

) } export default DepositModal