import BigNumber from 'bignumber.js' import { useCallback, useEffect, useMemo, useState } from 'react' import Modal from 'components/Modals/Modal' import AccountSummaryInModal from 'components/account/AccountSummary/AccountSummaryInModal' import Button from 'components/common/Button' import Card from 'components/common/Card' import DisplayCurrency from 'components/common/DisplayCurrency' import Divider from 'components/common/Divider' import { FormattedNumber } from 'components/common/FormattedNumber' import { ArrowRight, InfoCircle } from 'components/common/Icons' import Switch from 'components/common/Switch' import Text from 'components/common/Text' import TitleAndSubCell from 'components/common/TitleAndSubCell' import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider' import AssetImage from 'components/common/assets/AssetImage' import { BN_ZERO } from 'constants/math' import useCurrentAccount from 'hooks/accounts/useCurrentAccount' import useMarkets from 'hooks/markets/useMarkets' import useAutoLend from 'hooks/useAutoLend' import useHealthComputer from 'hooks/useHealthComputer' import useToggle from 'hooks/useToggle' import { useUpdatedAccount } from 'hooks/useUpdatedAccount' import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions' import useWalletBalances from 'hooks/useWalletBalances' 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; repayFromWallet: boolean }) { return (
No funds for repay {`Unfortunately you don't have any ${ props.asset.symbol } in your ${ props.repayFromWallet ? 'Wallet' : '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 [repayFromWallet, setRepayFromWallet] = useToggle() const walletAddress = useStore((s) => s.address) const { data: walletBalances } = useWalletBalances(walletAddress) 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 apy = modal.marketData.apy.borrow const isAutoLendEnabled = autoLendEnabledAccountIds.includes(account.id) const { computeMaxBorrowAmount } = useHealthComputer(account) const totalDebt = BN(getDebtAmount(modal)) const accountDebt = account.debts.find(byDenom(asset.denom))?.amount ?? BN_ZERO const markets = useMarkets() 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 accountDebtWithInterest = useMemo( () => getDebtAmountWithInterest(accountDebt, apy), [accountDebt, apy], ) const overpayExeedsCap = useMemo(() => { const marketAsset = markets.find((market) => market.asset.denom === asset.denom) if (!marketAsset) return const overpayAmount = accountDebtWithInterest.minus(accountDebt) const marketCapAfterOverpay = marketAsset.cap.used.plus(overpayAmount) return marketAsset.cap.max.isLessThanOrEqualTo(marketCapAfterOverpay) }, [markets, asset.denom, accountDebt, accountDebtWithInterest]) const maxRepayAmount = useMemo(() => { const maxBalance = repayFromWallet ? BN(walletBalances.find(byDenom(asset.denom))?.amount ?? 0) : depositBalance.plus(lendBalance) return isRepay ? BigNumber.min(maxBalance, overpayExeedsCap ? accountDebt : accountDebtWithInterest) : BN_ZERO }, [ depositBalance, lendBalance, isRepay, accountDebtWithInterest, overpayExeedsCap, accountDebt, walletBalances, asset.denom, repayFromWallet, ]) 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(accountDebtWithInterest), lend: repayFromWallet ? BNCoin.fromDenomAndBigNumber(asset.denom, BN_ZERO) : lend, fromWallet: repayFromWallet, }) } 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) => { if (!amount.isEqualTo(newAmount)) setAmount(newAmount) }, [amount, setAmount], ) const onDebounce = useCallback(() => { if (isRepay) { const repayCoin = BNCoin.fromDenomAndBigNumber( asset.denom, amount.isGreaterThan(accountDebt) ? accountDebt : amount, ) simulateRepay(repayCoin, repayFromWallet) } else { const borrowCoin = BNCoin.fromDenomAndBigNumber( asset.denom, amount.isGreaterThan(max) ? max : amount, ) const target = borrowToWallet ? 'wallet' : isAutoLendEnabled ? 'lend' : 'deposit' simulateBorrow(target, borrowCoin) } }, [ amount, isRepay, repayFromWallet, maxRepayAmount, max, asset, borrowToWallet, isAutoLendEnabled, simulateBorrow, simulateRepay, ]) const maxBorrow = useMemo(() => { const maxBorrowAmount = isRepay ? BN_ZERO : computeMaxBorrowAmount(asset.denom, borrowToWallet ? 'wallet' : 'deposit') return BigNumber.min(maxBorrowAmount, modal.marketData?.liquidity || 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]) 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) && ( <>
Total Borrowed
)}
Liquidity available
{isRepay && maxRepayAmount.isZero() && ( )} {isRepay ? ( <>
Repay from Wallet Repay your debt directly from your wallet
) : ( <>
Receive funds to Wallet Your borrowed funds will directly go to your wallet
)}
) }