import classNames from 'classnames' import { AnimatedNumber, Apy, BorrowCapacity, DisplayCurrency, Loading, TextTooltip, TokenBalance, } from 'components/common' import { MARS_DECIMALS, MARS_SYMBOL, VAULT_DEPOSIT_BUFFER } from 'constants/appConstants' import { getLiqBorrowValue, getMaxBorrowValue } from 'functions/fields' import { useAsset, usePrice, useRedBankAsset } from 'hooks/data' import { formatCooldown, formatValue, lookup } from 'libs/parse' import React, { useMemo } from 'react' import { useTranslation } from 'react-i18next' import useStore from 'store' import styles from './BreakdownTable.module.scss' interface Props { vault: Vault prevPosition: Position newPosition: Position isRepay?: boolean setIsRepay?: React.Dispatch> isAfter?: boolean className?: string hideTitle?: boolean isSetUp?: boolean } export enum AmountType { NET_PRIMARY, NET_SECONDARY, DEBT, POSITION_PRIMARY, POSITION_SECONDARY, } export const BreakdownTable = (props: Props) => { const { t } = useTranslation() const primaryAsset = useAsset({ denom: props.vault.denoms.primary }) const secondaryAsset = useAsset({ denom: props.vault.denoms.secondary }) const convertToDisplayCurrency = useStore((s) => s.convertToDisplayCurrency) const convertToBaseCurrency = useStore((s) => s.convertToBaseCurrency) const baseCurrency = useStore((s) => s.baseCurrency) const primaryPrice = usePrice(props.vault.denoms.primary) const secondaryPrice = usePrice(props.vault.denoms.secondary) const primaryRedBankAsset = useRedBankAsset(props.vault.denoms.primary) const secondaryRedBankAsset = useRedBankAsset(props.vault.denoms.secondary) const primaryChange = props.newPosition.amounts.primary - props.prevPosition.amounts.primary const secondaryChange = props.newPosition.amounts.secondary - props.prevPosition.amounts.secondary const borrowKey = props.newPosition.borrowDenom === props.vault.denoms.primary ? 'borrowedPrimary' : 'borrowedSecondary' const borrowedChange = props.newPosition.amounts[borrowKey] - props.prevPosition.amounts[borrowKey] const containerClasses = classNames([ props.className, styles.container, props.isAfter && styles.isAfter, ]) const getTokenBalance = (amountType: AmountType) => { let denom = '' let amount = 0 switch (amountType) { case AmountType.NET_PRIMARY: amount = props.newPosition.amounts.primary denom = props.vault.denoms.primary break case AmountType.NET_SECONDARY: amount = props.newPosition.amounts.secondary denom = props.vault.denoms.secondary break case AmountType.DEBT: amount = props.newPosition.amounts[borrowKey] denom = props.vault.denoms.secondary break case AmountType.POSITION_PRIMARY: amount = props.newPosition.amounts.primary + props.newPosition.amounts.borrowedPrimary denom = props.vault.denoms.primary break case AmountType.POSITION_SECONDARY: amount = props.newPosition.amounts.secondary + props.newPosition.amounts.borrowedSecondary denom = props.vault.denoms.secondary break } return ( ) } const getChangeText = (amount: number, type: 'primary' | 'secondary', isBorrow?: boolean) => { const decimals = type === 'primary' ? primaryAsset?.decimals : secondaryAsset?.decimals const symbol = type === 'primary' ? primaryAsset?.symbol : secondaryAsset?.symbol return ( 0 && !isBorrow) || (amount < 0 && isBorrow) ? 'colorInfoProfit' : 'colorInfoLoss' } > {amount !== 0 ? formatValue( lookup(amount, symbol ?? MARS_SYMBOL, decimals ?? MARS_DECIMALS), 0, 4, true, amount > 0 ? '(+' : '(', ')', true, false, ) : ''} ) } const getValueText = ( type: 'primary' | 'secondary' | 'net' | 'borrowedPrimary' | 'borrowedSecondary' | 'total', ) => ( ) const maxBorrowValue = useMemo( () => getMaxBorrowValue(props.vault, props.newPosition), [props.vault, props.newPosition], ) const getWarningMessage = () => { const isReducingSupply = props.prevPosition.amounts.primary > props.newPosition.amounts.primary || props.prevPosition.amounts.secondary > props.newPosition.amounts.secondary if (isReducingSupply) { return (

{t('fields.messages.unlockWarning', { time: formatCooldown(props.vault.lockup) })}

) } const isReducingDebt = props.newPosition.amounts[borrowKey] < props.prevPosition.amounts[borrowKey] if (isReducingDebt) { return ( <>

{t('fields.messages.noReduce')}

props.setIsRepay && props.setIsRepay(true)}> {t('fields.messages.repayFromWallet')}

) } const additionalPositionValue = props.isSetUp ? props.newPosition.values.total : props.newPosition.values.total - props.prevPosition.values.total const vaultCapValue = convertToBaseCurrency({ amount: ((props.vault.vaultCap?.max || 0) * VAULT_DEPOSIT_BUFFER).toString(), denom: props.vault.vaultCap?.denom || '', }) const vaultCapUsedValue = convertToBaseCurrency({ amount: (props.vault.vaultCap?.used || 0).toString(), denom: props.vault.vaultCap?.denom || '', }) const isVaultCapReached = props.vault.vaultCap ? vaultCapUsedValue + additionalPositionValue > vaultCapValue : false if (isVaultCapReached && props.vault.vaultCap) { const leftoverCap = vaultCapValue - vaultCapUsedValue const maxPositionValue = convertToDisplayCurrency({ amount: ((props.isSetUp ? 0 : props.prevPosition.values.total) + leftoverCap).toString(), denom: baseCurrency.denom, }) if (maxPositionValue <= 0) { return

{t('fields.messages.vaultCapReached')}

} return (

{t('fields.messages.vaultCap', { amount: formatValue(maxPositionValue, 0, 2, true, '$'), })}

) } return null } /* APY CALCULATION */ const currentLeverage = props.newPosition.currentLeverage const borrowRate = Number( borrowKey === 'borrowedPrimary' ? primaryRedBankAsset?.borrowRate : secondaryRedBankAsset?.borrowRate, ) const trueBorrowRate = borrowRate * (Number(currentLeverage) - 1) const apy = (props.vault.apy || 0) * currentLeverage - trueBorrowRate const apyData = { total: props.vault.apy || 0, borrow: trueBorrowRate, } return (
{!props.hideTitle && (

{props.isAfter ? 'After' : 'Before'}

)}
{t('common.apy')}: {props.vault.apy !== null ? ( } tooltip={} /> ) : ( )}
{formatValue(1, 0, 0, false, false, ` ${primaryAsset?.symbol} ≈ `)}
{t('fields.supply')}
{t('fields.supply')}
{getTokenBalance(AmountType.NET_PRIMARY)}
{props.newPosition.amounts.secondary > 0 && (
{getTokenBalance(AmountType.NET_SECONDARY)}
)}
{primaryAsset?.symbol}
{props.newPosition.amounts.secondary > 0 &&
{secondaryAsset?.symbol}
}
{getChangeText(primaryChange, 'primary')}
{props.newPosition.amounts.secondary > 0 && (
{getChangeText(secondaryChange, 'secondary')}
)}
{getValueText('net')}
{t('common.debt')}
{t('common.debt')} {getTokenBalance(AmountType.DEBT)} {borrowKey === 'borrowedPrimary' ? primaryAsset?.symbol : secondaryAsset?.symbol} {getChangeText(borrowedChange, 'secondary', true)} {getValueText(borrowKey)}
{t('fields.positionValue')}
{t('fields.positionValue')}
{getTokenBalance(AmountType.POSITION_PRIMARY)}
{getTokenBalance(AmountType.POSITION_SECONDARY)}
{primaryAsset?.symbol}
{secondaryAsset?.symbol}
{getChangeText(primaryChange, 'primary')}
{getChangeText(secondaryChange + borrowedChange, 'secondary')}
{getValueText('total')}
{!props.isRepay &&
{getWarningMessage()}
}
) }