import classNames from 'classnames' import { AnimatedNumber, Apy, BorrowCapacity, DisplayCurrency, 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 primaryPrice = usePrice(props.vault.denoms.primary) const secondaryPrice = usePrice(props.vault.denoms.secondary) 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 borrowedChange = props.newPosition.amounts.borrowed - props.prevPosition.amounts.borrowed 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.borrowed denom = props.vault.denoms.secondary break case AmountType.POSITION_PRIMARY: amount = props.newPosition.amounts.primary denom = props.vault.denoms.primary break case AmountType.POSITION_SECONDARY: amount = props.newPosition.amounts.secondary + props.newPosition.amounts.borrowed 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: 'net' | 'borrowed' | '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.borrowed < props.prevPosition.amounts.borrowed 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 vaultCap = (props.vault.vaultCap?.max || 0) * VAULT_DEPOSIT_BUFFER const isVaultCapReached = props.vault.vaultCap ? props.vault.vaultCap.used + additionalPositionValue > vaultCap : false if (isVaultCapReached && props.vault.vaultCap) { const leftoverCap = vaultCap - props.vault.vaultCap.used const maxPositionValue = convertToDisplayCurrency({ amount: ((props.isSetUp ? 0 : props.prevPosition.values.total) + leftoverCap).toString(), denom: 'uosmo', }) 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 trueBorrowRate = (Number(secondaryRedBankAsset?.borrowRate ?? 0) / 2) * (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')}: } tooltip={} />
{formatValue(1, 0, 0, false, false, ' OSMO ≈ ')}
{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)} {secondaryAsset?.symbol} {getChangeText(borrowedChange, 'secondary', true)} {getValueText('borrowed')}
{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()}
}
) }