Mp 3214 calculating account apr (#391)

This commit is contained in:
Linkie Link 2023-08-23 18:43:40 +02:00 committed by GitHub
parent 160f8da6aa
commit 997171a185
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 317 additions and 118 deletions

View File

@ -1,4 +1,3 @@
import { render } from '@testing-library/react'
import classNames from 'classnames'
import { ChevronDown, ChevronRight } from 'components/Icons'
@ -38,7 +37,7 @@ export default function AccordionContent(props: Props) {
</Text>
</div>
<div className='block pr-1 group-[[open]]:hidden'>
{isOpen ? <ChevronDown /> : <ChevronRight />}
{isOpen ? <ChevronDown className='h-1.5' /> : <ChevronRight className='w-1.5' />}
</div>
</div>
{isOpen && <div className='bg-white/5 transition-[padding]'>{renderContent()}</div>}

View File

@ -118,7 +118,7 @@ export default function AccountBalancesTable(props: Props) {
denom: row.original.denom,
amount: row.original.amount.toString(),
})
return <DisplayCurrency coin={coin} className='text-right text-xs' />
return <DisplayCurrency coin={coin} className='text-xs text-right' />
},
},
{
@ -132,7 +132,7 @@ export default function AccountBalancesTable(props: Props) {
)
return (
<FormattedNumber
className='text-right text-xs'
className='text-xs text-right'
amount={Number(BN(amount).abs())}
options={{ maxDecimals: 2, abbreviated: true }}
animate
@ -145,6 +145,8 @@ export default function AccountBalancesTable(props: Props) {
accessorKey: 'apy',
header: 'APY',
cell: ({ row }) => {
if (row.original.apy === 0)
return <span className='w-full text-xs text-center'>&ndash;</span>
return (
<FormattedNumber
className='text-xs'
@ -193,7 +195,7 @@ export default function AccountBalancesTable(props: Props) {
'align-center',
)}
>
<span className='h-6 w-6 text-white'>
<span className='w-6 h-6 text-white'>
{header.column.getCanSort()
? {
asc: <SortAsc />,

View File

@ -8,13 +8,13 @@ import { ArrowRight } from 'components/Icons'
import Text from 'components/Text'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import {
calculateAccountApr,
calculateAccountBalanceValue,
calculateAccountBorrowRate,
calculateAccountPnL,
getAccountPositionValues,
} from 'utils/accounts'
@ -35,6 +35,18 @@ interface ItemProps {
export default function AccountComposition(props: Props) {
const { account, change } = props
const { data: prices } = usePrices()
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const [depositsBalance, lendsBalance, debtsBalance] = useMemo(
() => getAccountPositionValues(account, prices),
@ -52,16 +64,28 @@ export default function AccountComposition(props: Props) {
() => (change ? calculateAccountBalanceValue(change, prices) : BN_ZERO),
[change, prices],
)
const balance = depositsBalance.plus(lendsBalance)
const balanceChange = depositsBalanceChange.plus(lendsBalanceChange)
const apr = calculateAccountApr(account, prices)
const aprChange = change ? calculateAccountPnL(change, prices) : BN_ZERO
const borrowRate = calculateAccountBorrowRate(account, prices)
const borrowRateChange = change ? calculateAccountPnL(change, prices) : BN_ZERO
const apr = useMemo(
() => calculateAccountApr(account, totalBalance, borrowAssetsData, lendingAssetsData, prices),
[account, totalBalance, borrowAssetsData, lendingAssetsData, prices],
)
const aprChange = useMemo(
() =>
change
? calculateAccountApr(
change,
totalBalance.plus(totalBalanceChange),
borrowAssetsData,
lendingAssetsData,
prices,
)
: BN_ZERO,
[change, totalBalance, totalBalanceChange, borrowAssetsData, lendingAssetsData, prices],
)
return (
<div className='w-full flex-wrap p-4'>
<div className='flex-wrap w-full p-4'>
<Item
title='Total Position Value'
current={balance}
@ -79,16 +103,9 @@ export default function AccountComposition(props: Props) {
title='Total Balance'
current={totalBalance}
change={totalBalance.plus(totalBalanceChange)}
className='border border-transparent border-y-white/20 py-3 font-bold'
className='py-3 font-bold border border-transparent border-y-white/20'
/>
<Item title='APR' current={apr} change={apr.plus(aprChange)} className='py-3' isPercentage />
<Item
title='Borrow Rate'
current={borrowRate}
change={borrowRate.plus(borrowRateChange)}
isPercentage
isDecrease
/>
</div>
)
}
@ -99,12 +116,12 @@ function Item(props: ItemProps) {
return (
<div className={classNames('flex w-full flex-nowrap', props.className)}>
<div className='flex flex-shrink items-center'>
<div className='flex items-center flex-shrink'>
<Text size='sm' className='text-white/60'>
{props.title}
</Text>
</div>
<div className='flex flex-1 items-center justify-end gap-2'>
<div className='flex items-center justify-end flex-1 gap-2'>
{props.isPercentage ? (
<FormattedNumber
amount={current.toNumber()}

View File

@ -7,13 +7,18 @@ import { Gauge } from 'components/Gauge'
import { ArrowRight, Heart } from 'components/Icons'
import Text from 'components/Text'
import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useHealthComputer from 'hooks/useHealthComputer'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountBalanceValue, calculateAccountLeverage } from 'utils/accounts'
import { formatLeverage } from 'utils/formatters'
import {
calculateAccountApr,
calculateAccountBalanceValue,
calculateAccountLeverage,
} from 'utils/accounts'
export default function AccountDetailsController() {
const account = useCurrentAccount()
@ -34,22 +39,44 @@ function AccountDetails(props: Props) {
const { health } = useHealthComputer(props.account)
const { health: updatedHealth } = useHealthComputer(updatedAccount)
const { data: prices } = usePrices()
const accountBalanceValue = calculateAccountBalanceValue(
updatedAccount ? updatedAccount : props.account,
prices,
const accountBalanceValue = useMemo(
() => calculateAccountBalanceValue(updatedAccount ? updatedAccount : props.account, prices),
[updatedAccount, props.account, prices],
)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalanceValue)
const leverage = useMemo(
() => calculateAccountLeverage(updatedAccount ? updatedAccount : props.account, prices),
[props.account, updatedAccount, prices],
)
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const apr = useMemo(
() =>
calculateAccountApr(
props.account,
accountBalanceValue,
borrowAssetsData,
lendingAssetsData,
prices,
),
[props.account, accountBalanceValue, borrowAssetsData, lendingAssetsData, prices],
)
return (
<div
data-testid='account-details'
className='w-16 rounded-base border border-white/20 bg-white/5 backdrop-blur-sticky'
className='w-16 border rounded-base border-white/20 bg-white/5 backdrop-blur-sticky'
>
<div className='flex w-full flex-wrap justify-center py-4'>
<div className='flex flex-wrap justify-center w-full py-4'>
<Gauge tooltip='Health Factor' percentage={health} icon={<Heart />} />
<Text size='2xs' className='mb-0.5 mt-1 w-full text-center text-white/50'>
Health
@ -77,19 +104,35 @@ function AccountDetails(props: Props) {
)}
</div>
</div>
<div className='w-full border-t border-white/20 py-4'>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Leverage
</Text>
<Text size='xs' className='text-center'>
{formatLeverage(leverage.toNumber())}
<Text size='2xs' className='text-center'>
<FormattedNumber
className={'w-full text-center text-2xs'}
amount={leverage.toNumber()}
options={{ maxDecimals: 2, minDecimals: 2, suffix: 'x' }}
animate
/>
</Text>
</div>
<div className='w-full border-t border-white/20 py-4'>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
Net worth
</Text>
<DisplayCurrency coin={coin} className='w-full truncate text-center text-2xs ' />
<DisplayCurrency coin={coin} className='w-full text-center truncate text-2xs ' />
</div>
<div className='w-full py-4 border-t border-white/20'>
<Text size='2xs' className='mb-0.5 w-full text-center text-white/50'>
APR
</Text>
<FormattedNumber
className={'w-full text-center text-2xs'}
amount={apr.toNumber()}
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
animate
/>
</div>
</div>
)

View File

@ -2,6 +2,7 @@ import classNames from 'classnames'
import { Heart } from 'components/Icons'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
import { REDUCE_MOTION_KEY } from 'constants/localStore'
import useLocalStorage from 'hooks/useLocalStorage'
@ -18,7 +19,7 @@ export default function AccountHealth(props: Props) {
return (
<div className={classNames('flex items-center gap-1.5', props.classNames)}>
<div className='flex h-4 w-3 flex-auto'>
<div className='flex flex-auto w-3 h-4'>
<Heart />
</div>
{props.hasLabel && (
@ -27,32 +28,34 @@ export default function AccountHealth(props: Props) {
</Text>
)}
<div className='flex flex-shrink'>
<svg width='100%' viewBox='0 0 53 4' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='53' height='4' rx='2' fill='white' fillOpacity='0.1' />
<rect
width={healthBarWidth}
height='4'
rx='2'
fill='url(#bar)'
style={{
transition: reduceMotion ? 'none' : 'width 1s ease',
}}
/>
<defs>
<linearGradient
id='bar'
x1='0%'
y1='0%'
x2='100%'
y2='0%'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#FF645F' />
<stop offset='0.5' stopColor='#FFB1AE' />
<stop offset='1' stopColor='#FFD5D3' />
</linearGradient>
</defs>
</svg>
<Tooltip content={`Account Health: ${props.health}%`} type='info'>
<svg width='100%' viewBox='0 0 53 4' fill='none' xmlns='http://www.w3.org/2000/svg'>
<rect width='53' height='4' rx='2' fill='white' fillOpacity='0.1' />
<rect
width={healthBarWidth}
height='4'
rx='2'
fill='url(#bar)'
style={{
transition: reduceMotion ? 'none' : 'width 1s ease',
}}
/>
<defs>
<linearGradient
id='bar'
x1='0%'
y1='0%'
x2='100%'
y2='0%'
gradientUnits='userSpaceOnUse'
>
<stop stopColor='#FF645F' />
<stop offset='0.5' stopColor='#FFB1AE' />
<stop offset='1' stopColor='#FFD5D3' />
</linearGradient>
</defs>
</svg>
</Tooltip>
</div>
</div>
)

View File

@ -13,7 +13,7 @@ import Text from 'components/Text'
import useCurrentAccount from 'hooks/useCurrentAccount'
import usePrices from 'hooks/usePrices'
import useStore from 'store'
import { calculateAccountValue } from 'utils/accounts'
import { calculateAccountBalanceValue } from 'utils/accounts'
import { getPage, getRoute } from 'utils/route'
interface Props {
@ -52,7 +52,7 @@ export default function AccountList(props: Props) {
return (
<div className='flex flex-wrap w-full p-4'>
{props.accounts.map((account) => {
const positionBalance = calculateAccountValue('deposits', account, prices)
const positionBalance = calculateAccountBalanceValue(account, prices)
const isActive = accountId === account.id
return (

View File

@ -1,5 +1,5 @@
import classNames from 'classnames'
import { Suspense } from 'react'
import { Suspense, useMemo } from 'react'
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
@ -18,13 +18,19 @@ function Content() {
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = [...borrowAvailableAssets, ...accountBorrowedAssets]
const lendingAssetsData = [...lendingAvailableAssets, ...accountLentAssets]
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
if (!address) {
return (
<Card
className='h-fit w-full justify-center bg-white/5'
className='justify-center w-full h-fit bg-white/5'
title='Portfolio'
contentClassName='px-4 py-6'
>
@ -40,9 +46,9 @@ function Content() {
className={classNames('grid w-full grid-cols-1 gap-4', 'md:grid-cols-2', 'lg:grid-cols-3')}
>
{account.map((account: Account, index: number) => (
<Card className='h-fit w-full bg-white/5' title={`Account ${account.id}`} key={index}>
<Card className='w-full h-fit bg-white/5' title={`Account ${account.id}`} key={index}>
<AccountComposition account={account} />
<Text className='mt-3 w-full bg-white/10 px-4 py-2 text-white/40'>Balances</Text>
<Text className='w-full px-4 py-2 mt-3 bg-white/10 text-white/40'>Balances</Text>
<AccountBalancesTable
account={account}
borrowingData={borrowAssetsData}
@ -61,12 +67,12 @@ function Fallback() {
className={classNames('grid w-full grid-cols-1 gap-4', 'md:grid-cols-2', 'lg:grid-cols-3')}
>
{Array.from({ length: cardCount }, (_, i) => (
<Card key={i} className='h-fit w-full bg-white/5' title='Account' contentClassName='py-6'>
<Card key={i} className='w-full h-fit bg-white/5' title='Account' contentClassName='py-6'>
<div className='p-4'>
<Loading className='h-4 w-50' />
</div>
<Text className='mt-3 w-full bg-white/10 px-4 py-2 text-white/40'>Balances</Text>
<Loading className='h-4 w-full' />
<Text className='w-full px-4 py-2 mt-3 bg-white/10 text-white/40'>Balances</Text>
<Loading className='w-full h-4' />
</Card>
))}
</div>

View File

@ -1,12 +1,16 @@
import { useMemo } from 'react'
import AccountHealth from 'components/Account/AccountHealth'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import { ArrowChartLineUp } from 'components/Icons'
import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useHealthComputer from 'hooks/useHealthComputer'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountValue } from 'utils/accounts'
import { formatHealth } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { calculateAccountApr, calculateAccountBalanceValue } from 'utils/accounts'
interface Props {
account: Account
@ -14,17 +18,50 @@ interface Props {
export default function AccountStats(props: Props) {
const { data: prices } = usePrices()
const positionBalance = calculateAccountValue('deposits', props.account, prices)
const positionBalance = useMemo(
() => calculateAccountBalanceValue(props.account, prices),
[props.account, prices],
)
const { health } = useHealthComputer(props.account)
const healthFactor = BN(100).minus(formatHealth(health)).toNumber()
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const apr = useMemo(
() =>
calculateAccountApr(
props.account,
positionBalance,
borrowAssetsData,
lendingAssetsData,
prices,
),
[props.account, positionBalance, borrowAssetsData, lendingAssetsData, prices],
)
return (
<div className='w-full flex-wrap'>
<div className='flex-wrap w-full'>
<DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionBalance)}
className='w-full text-xl'
/>
<div className='mt-1 flex w-full items-center'>
<AccountHealth health={healthFactor} classNames='w-[140px]' hasLabel />
<div className='flex items-center justify-between w-full mt-1'>
<span className='flex flex-wrap'>
<ArrowChartLineUp className='w-4 mr-1' />
<FormattedNumber
className='text-xs text-white/70'
amount={apr.toNumber()}
options={{ prefix: 'APR: ', suffix: '%', minDecimals: 2, maxDecimals: 2 }}
/>
</span>
<AccountHealth health={health} classNames='w-[140px]' hasLabel />
</div>
</div>
)

View File

@ -1,9 +1,13 @@
import { useMemo } from 'react'
import Accordion from 'components/Accordion'
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
import AccountComposition from 'components/Account/AccountComposition'
import AccountHealth from 'components/Account/AccountHealth'
import Card from 'components/Card'
import DisplayCurrency from 'components/DisplayCurrency'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import { BN_ZERO } from 'constants/math'
import { ORACLE_DENOM } from 'constants/oracle'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
@ -12,9 +16,7 @@ import useIsOpenArray from 'hooks/useIsOpenArray'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
import usePrices from 'hooks/usePrices'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountValue } from 'utils/accounts'
import { formatHealth } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { calculateAccountBalanceValue, calculateAccountLeverage } from 'utils/accounts'
interface Props {
account?: Account
@ -24,31 +26,49 @@ interface Props {
export default function AccountSummary(props: Props) {
const [isOpen, toggleOpen] = useIsOpenArray(2, true)
const { data: prices } = usePrices()
const accountBalance = props.account
? calculateAccountValue('deposits', props.account, prices)
: BN_ZERO
const accountBalance = useMemo(
() => (props.account ? calculateAccountBalanceValue(props.account, prices) : BN_ZERO),
[props.account, prices],
)
const { availableAssets: borrowAvailableAssets, accountBorrowedAssets } =
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = [...borrowAvailableAssets, ...accountBorrowedAssets]
const lendingAssetsData = [...lendingAvailableAssets, ...accountLentAssets]
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const { health } = useHealthComputer(props.account)
const healthFactor = BN(100).minus(formatHealth(health)).toNumber()
const leverage = useMemo(
() => (props.account ? calculateAccountLeverage(props.account, prices) : BN_ZERO),
[props.account, prices],
)
if (!props.account) return null
return (
<div className='h-[546px] min-w-[345px] basis-[345px] overflow-y-scroll scrollbar-hide'>
<Card className='mb-4 h-min min-w-fit bg-white/10' contentClassName='flex'>
<Item>
<Item title='Networth'>
<DisplayCurrency
coin={BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, accountBalance)}
className='text-sm'
className='text-xs'
/>
</Item>
<Item>
<AccountHealth health={healthFactor} />
<Item title='Leverage'>
<FormattedNumber
className='text-xs'
amount={leverage.toNumber()}
options={{ minDecimals: 2, maxDecimals: 2, suffix: 'x' }}
animate
/>
</Item>
<Item title='Account Health'>
<AccountHealth health={health} />
</Item>
</Card>
<Accordion
@ -87,9 +107,12 @@ export default function AccountSummary(props: Props) {
function Item(props: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
className='flex items-center justify-center flex-1 gap-1 px-2 py-2 border-r border-r-white/10'
className='flex flex-wrap items-center gap-1 px-4 py-2 border-r border-r-white/10'
{...props}
>
<Text size='xs' className='w-full text-white/50'>
{props.title}
</Text>
{props.children}
</div>
)

View File

@ -35,6 +35,7 @@ export const FormattedNumber = React.memo(
if (
(prevAmountRef.current === props.amount && props.amount === 0) ||
!props.animate ||
prevAmountRef.current === 0 ||
reduceMotion
)
return (

View File

@ -10,8 +10,8 @@ import classNames from 'classnames'
import { useEffect, useMemo, useState } from 'react'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text'
import useAssetTableColumns from 'components/Modals/AssetsSelect/useAssetTableColumns'
import Text from 'components/Text'
import useStore from 'store'
import { byDenom } from 'utils/array'
@ -97,7 +97,7 @@ export default function AssetSelectTable(props: Props) {
'align-center',
)}
>
<span className='h-6 w-6 text-white'>
<span className='w-6 h-6 text-white'>
{header.column.getCanSort()
? {
asc: <SortAsc />,

View File

@ -38,13 +38,13 @@ export default function useAssetTableColumns() {
header: (data) => {
const tableData = data.table.options.data as AssetTableRow[]
const assetData = tableData.length && (tableData[0].asset as BorrowAsset)
if (assetData && assetData.borrowRate !== null) return 'Borrow Rate'
if (assetData && assetData?.borrowRate) return 'Borrow Rate'
return 'Balance'
},
cell: ({ row }) => {
const asset = row.original.asset as BorrowAsset
const balance = row.original.balance
if (asset.borrowRate !== null)
if (asset?.borrowRate)
return (
<Text size='sm' className='mb-0.5 text-white'>
{formatPercent(asset.borrowRate ?? 0)}

View File

@ -7,6 +7,7 @@ import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
import WalletBridges from 'components/Wallet/WalletBridges'
import { BN_ZERO } from 'constants/math'
import useAutoLendEnabledAccountIds from 'hooks/useAutoLendEnabledAccountIds'
import useToggle from 'hooks/useToggle'
import useWalletBalances from 'hooks/useWalletBalances'
import useStore from 'store'
@ -34,6 +35,8 @@ export default function FundAccount(props: Props) {
const hasAssetSelected = fundingAssets.length > 0
const hasFundingAssets =
fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0')
const { autoLendEnabledAccountIds } = useAutoLendEnabledAccountIds()
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId)
const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
@ -60,6 +63,7 @@ export default function FundAccount(props: Props) {
walletAssetsModal: {
isOpen: true,
selectedDenoms,
isBorrow: false,
},
})
}, [selectedDenoms])
@ -87,13 +91,17 @@ export default function FundAccount(props: Props) {
if (assetToUpdateIdx > -1) {
prevAssets[assetToUpdateIdx].amount = amount
}
setChange({ deposits: prevAssets })
setChange({ [isAutoLendEnabled ? "lends" : "deposits"]: prevAssets })
return prevAssets
})
},
[setChange],
[setChange, isAutoLendEnabled],
)
useEffect(() => {
setChange({ [isAutoLendEnabled ? "lends" : "deposits"]: fundingAssets })
}, [isAutoLendEnabled, fundingAssets, setChange])
useEffect(() => {
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
useStore.setState({ focusComponent: { component: <WalletBridges /> } })

View File

@ -15,9 +15,9 @@ export default function FundWithdrawModalContent(props: Props) {
const [change, setChange] = useState<AccountChange | undefined>()
return (
<div className='flex flex-1 items-start gap-6 p-6'>
<div className='flex items-start flex-1 gap-6 p-6'>
<Card
className='flex flex-1 bg-white/5 p-4'
className='flex flex-1 p-4 bg-white/5'
contentClassName='gap-6 flex flex-col justify-between h-full min-h-[380px]'
>
{isFunding ? (

View File

@ -67,7 +67,7 @@ export default function TokenInputWithSlider(props: Props) {
accountId={props.accountId}
/>
<Slider
value={percentage}
value={percentage || 0}
onChange={(value) => onChangeSlider(value)}
disabled={props.disabled}
/>

View File

@ -1,7 +1,9 @@
import Card from 'components/Card'
import useCurrentAccount from 'hooks/useCurrentAccount'
import { useMemo } from 'react'
import AccountBalancesTable from 'components/Account/AccountBalancesTable'
import Card from 'components/Card'
import useBorrowMarketAssetsTableData from 'hooks/useBorrowMarketAssetsTableData'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
export default function AccountDetailsCard() {
@ -10,8 +12,14 @@ export default function AccountDetailsCard() {
useBorrowMarketAssetsTableData()
const { availableAssets: lendingAvailableAssets, accountLentAssets } =
useLendingMarketAssetsTableData()
const borrowAssetsData = [...borrowAvailableAssets, ...accountBorrowedAssets]
const lendingAssetsData = [...lendingAvailableAssets, ...accountLentAssets]
const borrowAssetsData = useMemo(
() => [...borrowAvailableAssets, ...accountBorrowedAssets],
[borrowAvailableAssets, accountBorrowedAssets],
)
const lendingAssetsData = useMemo(
() => [...lendingAvailableAssets, ...accountLentAssets],
[lendingAvailableAssets, accountLentAssets],
)
const tabs = (
<div className={className.tabWrapper}>

View File

@ -57,4 +57,5 @@ interface UnlockModal {
interface WalletAssetModal {
isOpen?: boolean
selectedDenoms: string[]
isBorrow?: boolean
}

View File

@ -8,6 +8,7 @@ import {
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { getAssetByDenom } from 'utils/assets'
import { BN } from 'utils/helpers'
import { convertApyToApr } from 'utils/parsers'
export const calculateAccountBalanceValue = (
account: Account | AccountChange,
@ -34,7 +35,7 @@ export const calculateAccountValue = (
account: Account | AccountChange,
prices: BNCoin[],
): BigNumber => {
if (!account[type]) return BN_ZERO
if (!account[type] || !prices) return BN_ZERO
if (type === 'vaults') {
return (
@ -56,18 +57,62 @@ export const calculateAccountValue = (
}, BN_ZERO)
}
export const calculateAccountPnL = (
account: Account | AccountChange,
prices: BNCoin[],
): BigNumber => {
return BN_ZERO
}
export const calculateAccountApr = (
account: Account | AccountChange,
totalValue: BigNumber,
borrowAssetsData: BorrowMarketTableData[],
lendingAssetsData: LendingMarketTableData[],
prices: BNCoin[],
): BigNumber => {
return BN_ZERO
if (totalValue.isZero()) return BN_ZERO
const { vaults, lends, debts } = account
let totalLendsInterestValue = BN_ZERO
let totalVaultsInterestValue = BN_ZERO
let totalDeptsInterestValue = BN_ZERO
lends?.forEach((lend) => {
const asset = getAssetByDenom(lend.denom)
if (!asset) return BN_ZERO
const price = prices.find((price) => price.denom === lend.denom)?.amount ?? 0
const amount = BN(lend.amount).shiftedBy(-asset.decimals)
const apr =
lendingAssetsData.find((lendingAsset) => lendingAsset.asset.denom === lend.denom)
?.marketLiquidityRate ?? 0
const positionInterest = amount.multipliedBy(price).multipliedBy(apr)
totalLendsInterestValue = totalLendsInterestValue.plus(positionInterest)
})
vaults?.forEach((vault) => {
const asset = getAssetByDenom(vault.denoms.lp)
if (!asset) return BN_ZERO
const price = prices.find((price) => price.denom === vault.denoms.lp)?.amount ?? 0
const amount = BN(vault.amounts.locked).shiftedBy(-asset.decimals)
const positionInterest = amount
.multipliedBy(price)
.multipliedBy(convertApyToApr(vault?.apy ?? 0, 365))
totalVaultsInterestValue = totalVaultsInterestValue.plus(positionInterest)
})
debts?.forEach((debt) => {
const asset = getAssetByDenom(debt.denom)
if (!asset) return BN_ZERO
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
const amount = BN(debt.amount).shiftedBy(-asset.decimals)
const apr =
borrowAssetsData.find((borrowAsset) => borrowAsset.asset.denom === debt.denom)
?.marketLiquidityRate ?? 0
const positionInterest = amount.multipliedBy(price).multipliedBy(apr)
totalDeptsInterestValue = totalDeptsInterestValue.plus(positionInterest)
})
const totalPositiveInterestValue = totalLendsInterestValue
.plus(totalVaultsInterestValue)
.minus(totalDeptsInterestValue)
const totalApr = totalPositiveInterestValue.dividedBy(totalValue).times(100)
return totalApr
}
export const calculateAccountBorrowRate = (

View File

@ -16,6 +16,12 @@ export const convertAprToApy = (apr: number, numberOfCompoundingPeriods: number)
return ((1 + apr / 100 / numberOfCompoundingPeriods) ** numberOfCompoundingPeriods - 1) * 100
}
export const convertApyToApr = (apy: number, numberOfCompoundingPeriods: number): number => {
return (
(Math.pow(1 + apy / 100, numberOfCompoundingPeriods) - 1) * 100 * numberOfCompoundingPeriods
)
}
export const combineBNCoins = (coins: BNCoin[]): BNCoin[] => {
const combinedMap: { [key: string]: number } = {}