import BigNumber from 'bignumber.js' import React, { useEffect, useMemo, useState } from 'react' import Button from 'components/Button' import DepositCapMessage from 'components/DepositCapMessage' import DisplayCurrency from 'components/DisplayCurrency' import Divider from 'components/Divider' import { ArrowRight, ExclamationMarkCircled } from 'components/Icons' import Slider from 'components/Slider' import Text from 'components/Text' import TokenInput from 'components/TokenInput' import { BN_ZERO } from 'constants/math' import { ORACLE_DENOM } from 'constants/oracle' import useHealthComputer from 'hooks/useHealthComputer' import useMarketAssets from 'hooks/useMarketAssets' import usePrices from 'hooks/usePrices' import useStore from 'store' import { BNCoin } from 'types/classes/BNCoin' import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import { byDenom } from 'utils/array' import { findCoinByDenom, getAssetByDenom } from 'utils/assets' import { formatPercent } from 'utils/formatters' import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers' export interface VaultBorrowingsProps { account: Account borrowings: BNCoin[] deposits: BNCoin[] primaryAsset: Asset secondaryAsset: Asset vault: Vault depositActions: Action[] onChangeBorrowings: (borrowings: BNCoin[]) => void displayCurrency: string depositCapReachedCoins: BNCoin[] } export default function VaultBorrowings(props: VaultBorrowingsProps) { const { borrowings, onChangeBorrowings } = props const { data: marketAssets } = useMarketAssets() const { data: prices } = usePrices() const vaultModal = useStore((s) => s.vaultModal) const depositIntoVault = useStore((s) => s.depositIntoVault) const updatedAccount = useStore((s) => s.updatedAccount) const { computeMaxBorrowAmount } = useHealthComputer(props.account) const [percentage, setPercentage] = useState(0) const calculateSliderPercentage = (maxBorrowAmounts: BNCoin[], borrowings: BNCoin[]) => { if (borrowings.length === 1) { const amount = borrowings[0].amount if (amount.isZero()) return 0 const max = maxBorrowAmounts.find(byDenom(borrowings[0].denom))?.amount || BN_ZERO return amount.times(100).dividedBy(max).toNumber() } return 0 } const maxBorrowAmountsRaw: BNCoin[] = useMemo(() => { return props.borrowings.map((borrowing) => { const maxAmount = computeMaxBorrowAmount(borrowing.denom, { vault: { address: props.vault.address }, }) return new BNCoin({ denom: borrowing.denom, amount: maxAmount.toString(), }) }) }, [props.borrowings, computeMaxBorrowAmount, props.vault.address]) const maxBorrowAmounts = useMemo(() => { const borrowPowerLeft = props.borrowings.reduce((capLeft, borrowing) => { const maxAmount = maxBorrowAmountsRaw.find((amount) => amount.denom === borrowing.denom) if (!maxAmount) return capLeft capLeft -= borrowing.amount.dividedBy(maxAmount.amount).toNumber() return capLeft }, 1) return maxBorrowAmountsRaw.map((maxAmount) => { return new BNCoin({ denom: maxAmount.denom, amount: maxAmount.amount.times(borrowPowerLeft).integerValue().toString(), }) }) }, [maxBorrowAmountsRaw, props.borrowings]) const totalValue = useMemo( () => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices), [props.borrowings, props.deposits, prices], ) useEffect(() => { const selectedBorrowDenoms = vaultModal?.selectedBorrowDenoms || [] if ( borrowings.length === selectedBorrowDenoms.length && borrowings.every((coin) => selectedBorrowDenoms.includes(coin.denom)) ) { return } const updatedBorrowings = selectedBorrowDenoms.map((denom) => { const amount = findCoinByDenom(denom, borrowings)?.amount || BN_ZERO return new BNCoin({ denom, amount: amount.toString(), }) }) setPercentage(calculateSliderPercentage(maxBorrowAmounts, updatedBorrowings)) onChangeBorrowings(updatedBorrowings) }, [vaultModal, maxBorrowAmounts, borrowings, onChangeBorrowings]) function onChangeSlider(value: number) { if (props.borrowings.length !== 1) return const denom = props.borrowings[0].denom const currentAmount = props.borrowings[0].amount const maxAmount = maxBorrowAmounts.find(byDenom(denom))?.amount ?? BN_ZERO const newBorrowings: BNCoin[] = [ new BNCoin({ denom, amount: ( maxAmount.plus(currentAmount).multipliedBy(value).dividedBy(100).decimalPlaces(0) || BN_ZERO ).toString(), }), ] props.onChangeBorrowings(newBorrowings) setPercentage(value) } function updateAssets(denom: string, amount: BigNumber) { const index = props.borrowings.findIndex((coin) => coin.denom === denom) props.borrowings[index].amount = amount props.onChangeBorrowings([...props.borrowings]) } function onDelete(denom: string) { const index = props.borrowings.findIndex((coin) => coin.denom === denom) props.borrowings.splice(index, 1) const newBorrowings = [...props.borrowings] props.onChangeBorrowings(newBorrowings) if (!vaultModal) return useStore.setState({ vaultModal: { ...vaultModal, selectedBorrowDenoms: props.borrowings.map((coin) => coin.denom), }, }) setPercentage(calculateSliderPercentage(maxBorrowAmounts, newBorrowings)) } function addAsset() { useStore.setState({ addVaultBorrowingsModal: { selectedDenoms: props.borrowings.map((coin) => coin.denom), }, }) setPercentage(calculateSliderPercentage(maxBorrowAmounts, props.borrowings)) } function onConfirm() { if (!updatedAccount || !vaultModal) return depositIntoVault({ accountId: updatedAccount.id, actions: props.depositActions, deposits: props.deposits, borrowings: props.borrowings, isCreate: vaultModal.isCreate, kind: 'default', }) useStore.setState({ vaultModal: null }) } return (
{props.borrowings.map((coin) => { const asset = getAssetByDenom(coin.denom) const maxAmount = maxBorrowAmounts.find(byDenom(coin.denom))?.amount if (!asset || !maxAmount) return return ( updateAssets(coin.denom, amount)} onDelete={() => onDelete(coin.denom)} /> ) })} {props.borrowings.length === 1 && } {props.borrowings.length === 0 && (
You have no borrowing assets selected. Click on select borrow assets if you would like to add assets to borrow.
)}
) }