209 lines
7.2 KiB
TypeScript
209 lines
7.2 KiB
TypeScript
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 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 useBaseAsset from 'hooks/assets/useBasetAsset'
|
|
import useMarkets from 'hooks/markets/useMarkets'
|
|
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
|
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
|
import useStore from 'store'
|
|
import { BNCoin } from 'types/classes/BNCoin'
|
|
import { formatPercent } from 'utils/formatters'
|
|
import { BN } from 'utils/helpers'
|
|
import { getDebtAmountWithInterest } from 'utils/tokens'
|
|
|
|
interface Props {
|
|
account: Account
|
|
}
|
|
|
|
function RepayNotAvailable(props: { asset: Asset }) {
|
|
return (
|
|
<Card className='w-full'>
|
|
<div className='flex items-start p-4'>
|
|
<InfoCircle className='w-6 mr-2 flex-0' />
|
|
<div className='flex flex-col flex-1 gap-1'>
|
|
<Text size='sm'>No funds for repay</Text>
|
|
<Text
|
|
size='xs'
|
|
className='text-white/40'
|
|
>{`Unfortunately you don't have any ${props.asset.symbol} in your Wallet to repay the debt.`}</Text>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
export default function Repay(props: Props) {
|
|
const { account } = props
|
|
const modal = useStore((s) => s.v1BorrowAndRepayModal)
|
|
const baseAsset = useBaseAsset()
|
|
const asset = modal?.data.asset ?? baseAsset
|
|
const [amount, setAmount] = useState(BN_ZERO)
|
|
const balance = useCurrentWalletBalance(asset.denom)
|
|
const v1Action = useStore((s) => s.v1Action)
|
|
const [max, setMax] = useState(BN_ZERO)
|
|
const { simulateRepay } = useUpdatedAccount(account)
|
|
const apy = modal?.data.apy.borrow ?? 0
|
|
const accountDebt = modal?.data.accountDebtAmount ?? BN_ZERO
|
|
const markets = useMarkets()
|
|
|
|
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 = BN(balance?.amount ?? 0)
|
|
return BigNumber.min(maxBalance, overpayExeedsCap ? accountDebt : accountDebtWithInterest)
|
|
}, [accountDebtWithInterest, overpayExeedsCap, accountDebt, balance?.amount])
|
|
|
|
const close = useCallback(() => {
|
|
setAmount(BN_ZERO)
|
|
useStore.setState({ v1BorrowAndRepayModal: null })
|
|
}, [setAmount])
|
|
|
|
const onConfirmClick = useCallback(() => {
|
|
v1Action('repay', BNCoin.fromDenomAndBigNumber(asset.denom, amount))
|
|
close()
|
|
}, [v1Action, asset, amount, close])
|
|
|
|
const handleChange = useCallback(
|
|
(newAmount: BigNumber) => {
|
|
if (!amount.isEqualTo(newAmount)) setAmount(newAmount)
|
|
},
|
|
[amount, setAmount],
|
|
)
|
|
|
|
const onDebounce = useCallback(() => {
|
|
const repayCoin = BNCoin.fromDenomAndBigNumber(
|
|
asset.denom,
|
|
amount.isGreaterThan(accountDebt) ? accountDebt : amount,
|
|
)
|
|
simulateRepay(repayCoin, true)
|
|
}, [amount, accountDebt, asset, simulateRepay])
|
|
|
|
useEffect(() => {
|
|
if (maxRepayAmount.isEqualTo(max)) return
|
|
setMax(maxRepayAmount)
|
|
}, [max, maxRepayAmount])
|
|
|
|
useEffect(() => {
|
|
if (amount.isLessThanOrEqualTo(max)) return
|
|
handleChange(max)
|
|
setAmount(max)
|
|
}, [amount, max, handleChange])
|
|
|
|
if (!modal) return null
|
|
|
|
return (
|
|
<Modal
|
|
onClose={close}
|
|
header={
|
|
<span className='flex items-center gap-4 px-4'>
|
|
<AssetImage asset={modal.data.asset} size={24} />
|
|
<Text>
|
|
{'Repay'} {asset.symbol}
|
|
</Text>
|
|
</span>
|
|
}
|
|
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
|
|
contentClassName='flex flex-col'
|
|
>
|
|
<div className='flex gap-3 px-6 py-4 border-b border-white/5 gradient-header'>
|
|
<TitleAndSubCell title={formatPercent(apy)} sub={'Borrow Rate APY'} />
|
|
|
|
<div className='h-100 w-[1px] bg-white/10' />
|
|
<div className='flex flex-col gap-0.5'>
|
|
<div className='flex gap-2'>
|
|
<FormattedNumber
|
|
className='text-xs'
|
|
amount={accountDebt.toNumber()}
|
|
options={{
|
|
decimals: asset.decimals,
|
|
abbreviated: false,
|
|
suffix: ` ${asset.symbol}`,
|
|
}}
|
|
/>
|
|
<DisplayCurrency
|
|
className='text-xs'
|
|
coin={BNCoin.fromDenomAndBigNumber(asset.denom, accountDebt)}
|
|
parentheses
|
|
/>
|
|
</div>
|
|
<Text size='xs' className='text-white/50' tag='span'>
|
|
Total Borrowed
|
|
</Text>
|
|
</div>
|
|
<div className='h-100 w-[1px] bg-white/10' />
|
|
<div className='flex flex-col gap-0.5'>
|
|
<div className='flex gap-2'>
|
|
<FormattedNumber
|
|
className='text-xs'
|
|
amount={modal.data?.liquidity.toNumber() ?? 0}
|
|
options={{ decimals: asset.decimals, abbreviated: true, suffix: ` ${asset.symbol}` }}
|
|
animate
|
|
/>
|
|
<DisplayCurrency
|
|
className='text-xs'
|
|
coin={BNCoin.fromDenomAndBigNumber(asset.denom, modal.data?.liquidity ?? BN_ZERO)}
|
|
parentheses
|
|
/>
|
|
</div>
|
|
<Text size='xs' className='text-white/50' tag='span'>
|
|
Liquidity available
|
|
</Text>
|
|
</div>
|
|
</div>
|
|
<div className='flex items-start flex-1 gap-6 p-6'>
|
|
<Card
|
|
className='flex flex-1 p-4 bg-white/5'
|
|
contentClassName='gap-6 flex flex-col justify-between h-full'
|
|
>
|
|
<TokenInputWithSlider
|
|
asset={asset}
|
|
onChange={handleChange}
|
|
onDebounce={onDebounce}
|
|
amount={amount}
|
|
max={max}
|
|
disabled={max.isZero()}
|
|
className='w-full'
|
|
maxText='Max'
|
|
warningMessages={[]}
|
|
/>
|
|
<Divider />
|
|
{maxRepayAmount.isZero() && <RepayNotAvailable asset={asset} />}
|
|
<Button
|
|
onClick={onConfirmClick}
|
|
className='w-full'
|
|
disabled={amount.isZero()}
|
|
text={`Repay ${asset.symbol}`}
|
|
rightIcon={<ArrowRight />}
|
|
/>
|
|
</Card>
|
|
<AccountSummaryInModal account={account} />
|
|
</div>
|
|
</Modal>
|
|
)
|
|
}
|