import BigNumber from 'bignumber.js' import { useCallback, useEffect, useMemo, useState } from 'react' import AccountSummary from 'components/Account/AccountSummary' import AssetImage from 'components/Asset/AssetImage' import Button from 'components/Button' import Card from 'components/Card' import DisplayCurrency from 'components/DisplayCurrency' import Divider from 'components/Divider' import { FormattedNumber } from 'components/FormattedNumber' import { ArrowRight, InfoCircle } from 'components/Icons' import Modal from 'components/Modal' import Switch from 'components/Switch' import Text from 'components/Text' import TitleAndSubCell from 'components/TitleAndSubCell' import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider' import { BN_ZERO } from 'constants/math' import useAutoLend from 'hooks/useAutoLend' import useCurrentAccount from 'hooks/useCurrentAccount' import useHealthComputer from 'hooks/useHealthComputer' import useToggle from 'hooks/useToggle' import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' import { byDenom } from 'utils/array' import { formatPercent } from 'utils/formatters' import { BN } from 'utils/helpers' import { getDebtAmountWithInterest } from 'utils/tokens' interface Props { account: Account modal: BorrowModal } function getDebtAmount(modal: BorrowModal) { return BN((modal.marketData as BorrowMarketTableData)?.debt ?? 0).toString() } function getAssetLogo(modal: BorrowModal) { if (!modal.asset) return null return } function RepayNotAvailable(props: { asset: Asset }) { return ( No funds for repay {`Unfortunately you don't have any ${props.asset.symbol} in your Credit Account to repay the debt.`} ) } export default function BorrowModalController() { const account = useCurrentAccount() const modal = useStore((s) => s.borrowModal) if (account && modal) { return } return null } function BorrowModal(props: Props) { const { modal, account } = props const [amount, setAmount] = useState(BN_ZERO) const [borrowToWallet, setBorrowToWallet] = useToggle() const borrow = useStore((s) => s.borrow) const repay = useStore((s) => s.repay) const asset = modal.asset const isRepay = modal.isRepay ?? false const [max, setMax] = useState(BN_ZERO) const { simulateBorrow, simulateRepay } = useUpdatedAccount(account) const { autoLendEnabledAccountIds } = useAutoLend() const apr = modal.marketData?.borrowRate ?? '0' const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const { computeMaxBorrowAmount } = useHealthComputer(account) const totalDebt = BN(getDebtAmount(modal)) const [depositBalance, lendBalance] = useMemo( () => [ account.deposits.find(byDenom(asset.denom))?.amount ?? BN_ZERO, account.lends.find(byDenom(asset.denom))?.amount ?? BN_ZERO, ], [account, asset.denom], ) const totalDebtRepayAmount = useMemo( () => getDebtAmountWithInterest(totalDebt, Number(apr)), [totalDebt, apr], ) const maxRepayAmount = useMemo(() => { const maxBalance = depositBalance.plus(lendBalance) return isRepay ? BigNumber.min(maxBalance, totalDebtRepayAmount) : BN_ZERO }, [depositBalance, lendBalance, isRepay, totalDebtRepayAmount]) function resetState() { setAmount(BN_ZERO) } function onConfirmClick() { if (!asset) return const { lend } = getDepositAndLendCoinsToSpend( BNCoin.fromDenomAndBigNumber(asset.denom, amount), account, ) if (isRepay) { repay({ accountId: account.id, coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount), accountBalance: amount.isEqualTo(totalDebtRepayAmount), lend, }) } else { borrow({ accountId: account.id, coin: BNCoin.fromDenomAndBigNumber(asset.denom, amount), borrowToWallet, }) } resetState() useStore.setState({ borrowModal: null }) } function onClose() { resetState() useStore.setState({ borrowModal: null }) } const handleChange = useCallback( (newAmount: BigNumber) => { const coin = BNCoin.fromDenomAndBigNumber(asset.denom, newAmount) if (!amount.isEqualTo(newAmount)) setAmount(newAmount) if (!isRepay) return const repayCoin = coin.amount.isGreaterThan(totalDebt) ? BNCoin.fromDenomAndBigNumber(asset.denom, totalDebt) : coin simulateRepay(repayCoin) }, [amount, asset.denom, isRepay, simulateRepay, totalDebt], ) const maxBorrow = useMemo(() => { const maxBorrowAmount = isRepay ? BN_ZERO : computeMaxBorrowAmount(asset.denom, borrowToWallet ? 'wallet' : 'deposit') return BigNumber.min(maxBorrowAmount, modal.marketData?.liquidity?.amount || 0) }, [asset.denom, borrowToWallet, computeMaxBorrowAmount, isRepay, modal.marketData]) useEffect(() => { if (!account || isRepay) return if (maxBorrow.isEqualTo(max)) return setMax(maxBorrow) }, [account, isRepay, maxBorrow, max]) useEffect(() => { if (!isRepay) return if (maxRepayAmount.isEqualTo(max)) return setMax(maxRepayAmount) }, [isRepay, max, maxRepayAmount]) useEffect(() => { if (amount.isLessThanOrEqualTo(max)) return handleChange(max) setAmount(max) }, [amount, max, handleChange]) useEffect(() => { if (isRepay) return const coin = BNCoin.fromDenomAndBigNumber(asset.denom, amount.isGreaterThan(max) ? max : amount) const target = borrowToWallet ? 'wallet' : isAutoLendEnabled ? 'lend' : 'deposit' simulateBorrow(target, coin) }, [isRepay, borrowToWallet, isAutoLendEnabled, simulateBorrow, asset, amount, max]) if (!modal || !asset) return null return ( {getAssetLogo(modal)} {isRepay ? 'Repay' : 'Borrow'} {asset.symbol} } headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b' contentClassName='flex flex-col' > {totalDebt.isGreaterThan(0) && ( <> Borrowed > )} Liquidity available {isRepay && maxRepayAmount.isZero() && } {!isRepay && ( <> Receive funds to Wallet Your borrowed funds will directly go to your wallet > )} } /> ) }