import { ArrowDownRightIcon } from '@heroicons/react/20/solid' import { useWallet } from '@solana/wallet-adapter-react' import Decimal from 'decimal.js' import { useTranslation } from 'next-i18next' import React, { useCallback, useEffect, useMemo, useState } from 'react' import NumberFormat from 'react-number-format' import mangoStore from '@store/mangoStore' import { notify } from './../utils/notifications' import { formatNumericValue } from './../utils/numbers' import ActionTokenList from './account/ActionTokenList' import ButtonGroup from './forms/ButtonGroup' import Label from './forms/Label' import Button from './shared/Button' import Loading from './shared/Loading' import { EnterBottomExitBottom, FadeInFadeOut } from './shared/Transitions' import { withValueLimit } from './swap/MarketSwapForm' import MaxAmountButton from '@components/shared/MaxAmountButton' import HealthImpactTokenChange from '@components/HealthImpactTokenChange' import SolBalanceWarnings from '@components/shared/SolBalanceWarnings' import useMangoAccount from 'hooks/useMangoAccount' import { BORROW_REPAY_MODAL_INNER_HEIGHT, INPUT_TOKEN_DEFAULT, } from 'utils/constants' import ConnectEmptyState from './shared/ConnectEmptyState' import BankAmountWithValue from './shared/BankAmountWithValue' import useBanksWithBalances from 'hooks/useBanksWithBalances' import { isMangoError } from 'types' import TokenListButton from './shared/TokenListButton' import { ACCOUNT_ACTIONS_NUMBER_FORMAT_CLASSES, BackButton } from './BorrowForm' import TokenLogo from './shared/TokenLogo' import InlineNotification from './shared/InlineNotification' import { handleInputChange } from 'utils/account' interface RepayFormProps { onSuccess: () => void token?: string } function RepayForm({ onSuccess, token }: RepayFormProps) { const { t } = useTranslation('common') const { mangoAccount } = useMangoAccount() const walletTokens = mangoStore((s) => s.wallet.tokens) 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 banks = useBanksWithBalances('borrowedAmount') // const { maxSolDeposit } = useSolBalance() const bank = useMemo(() => { const group = mangoStore.getState().group return group?.banksMapByName.get(selectedToken)?.[0] }, [selectedToken]) const { connected, publicKey } = useWallet() const borrowAmount = useMemo(() => { if (!mangoAccount || !bank) return new Decimal(0) const amount = new Decimal( mangoAccount.getTokenBorrowsUi(bank), ).toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP) return amount }, [bank, mangoAccount]) const walletBalance = useMemo(() => { if (!bank || !walletTokens?.length) return 0 const hasBorrowToken = walletTokens.find( (token) => token.mint.toString() === bank.mint.toString(), ) return hasBorrowToken?.uiAmount || 0 }, [bank, walletTokens]) const max = useMemo(() => { if (!walletBalance) return new Decimal(0) return walletBalance >= borrowAmount.toNumber() ? borrowAmount : new Decimal(walletBalance) }, [borrowAmount, walletBalance]) useEffect(() => { if (token && !borrowAmount.eq(0)) { setInputAmount(borrowAmount.toFixed()) } }, [token, borrowAmount]) const setMax = useCallback(() => { if (!bank) return const amount = max.toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP) setInputAmount(amount.toFixed()) setSizePercentage('100') }, [bank, max]) const handleSizePercentage = useCallback( (percentage: string) => { if (!bank) return setSizePercentage(percentage) const amount = max .mul(percentage) .div(100) .toDecimalPlaces(bank.mintDecimals, Decimal.ROUND_UP) setInputAmount(amount.toFixed()) }, [bank, max], ) const handleSelectToken = (token: string) => { setSelectedToken(token) setShowTokenList(false) } const handleDeposit = useCallback( async (amount: string) => { const mangoAccount = mangoStore.getState().mangoAccount.current const client = mangoStore.getState().client const group = mangoStore.getState().group const actions = mangoStore.getState().actions if (!mangoAccount || !group || !bank || !publicKey) return // we don't want to leave negative dust in the account if someone wants to repay the full amount const actualAmount = sizePercentage === '100' ? borrowAmount.toNumber() * 1.01 : parseFloat(amount) setSubmitting(true) try { const { signature: tx, slot } = await client.tokenDeposit( group, mangoAccount, bank.mint, actualAmount, true, ) notify({ title: 'Transaction confirmed', type: 'success', txid: tx, }) await actions.reloadMangoAccount(slot) actions.fetchWalletTokens(publicKey) setSubmitting(false) onSuccess() } catch (e) { console.error('Error repaying:', e) setSubmitting(false) if (!isMangoError(e)) return notify({ title: 'Transaction failed', description: e.message, txid: e?.txid, type: 'error', }) } }, [bank, borrowAmount, publicKey, onSuccess, sizePercentage], ) useEffect(() => { if (!selectedToken && !token && banks.length) { setSelectedToken(banks[0].bank.name) } }, [token, banks, selectedToken]) const outstandingAmount = inputAmount ? borrowAmount.toNumber() - parseFloat(inputAmount) : borrowAmount.toNumber() const isDeposit = parseFloat(inputAmount) > borrowAmount.toNumber() return banks.length ? ( <> setShowTokenList(false)} />

{t('select-repay-token')}

{t('token')}

{t('amount-owed')}

} setShowList={setShowTokenList} />
handleInputChange( values, source, setInputAmount, setSizePercentage, ) } isAllowed={withValueLimit} />
handleSizePercentage(p)} values={['10', '25', '50', '75', '100']} unit="%" />
{bank ? (

{t('repayment-amount')}

{isDeposit ? (

{t('deposit-amount')}

) : null}

{t('outstanding-borrow')}

{outstandingAmount > 0 ? formatNumericValue(outstandingAmount, bank.mintDecimals) : 0}{' '} {selectedToken}

) : null}
{(borrowAmount.toNumber() && max.eq(0)) || (inputAmount && new Decimal(inputAmount).gt(max)) ? (
) : null}
) : !connected ? (
) : (
😎

No borrows to repay...

) } export default RepayForm