)
diff --git a/src/components/Account/AccountHealth.tsx b/src/components/Account/AccountHealth.tsx
index c1de567f..6ed7340f 100644
--- a/src/components/Account/AccountHealth.tsx
+++ b/src/components/Account/AccountHealth.tsx
@@ -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 (
-
+
{props.hasLabel && (
@@ -27,32 +28,34 @@ export default function AccountHealth(props: Props) {
)}
-
+
+
+
)
diff --git a/src/components/Account/AccountList.tsx b/src/components/Account/AccountList.tsx
index 9312af58..dadf37f6 100644
--- a/src/components/Account/AccountList.tsx
+++ b/src/components/Account/AccountList.tsx
@@ -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 (
{props.accounts.map((account) => {
- const positionBalance = calculateAccountValue('deposits', account, prices)
+ const positionBalance = calculateAccountBalanceValue(account, prices)
const isActive = accountId === account.id
return (
diff --git a/src/components/Account/AccountOverview.tsx b/src/components/Account/AccountOverview.tsx
index aff78838..77d7491f 100644
--- a/src/components/Account/AccountOverview.tsx
+++ b/src/components/Account/AccountOverview.tsx
@@ -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 (
@@ -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) => (
-
+
- Balances
+ Balances
{Array.from({ length: cardCount }, (_, i) => (
-
+
- Balances
-
+ Balances
+
))}
diff --git a/src/components/Account/AccountStats.tsx b/src/components/Account/AccountStats.tsx
index d02d9346..73e125fb 100644
--- a/src/components/Account/AccountStats.tsx
+++ b/src/components/Account/AccountStats.tsx
@@ -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 (
-
+
-
)
diff --git a/src/components/Account/AccountSummary.tsx b/src/components/Account/AccountSummary.tsx
index 85cb0c8b..f2f52129 100644
--- a/src/components/Account/AccountSummary.tsx
+++ b/src/components/Account/AccountSummary.tsx
@@ -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 (
- -
+
-
- -
-
+
-
+
+
+ -
+
) {
return (
+
+ {props.title}
+
{props.children}
)
diff --git a/src/components/FormattedNumber.tsx b/src/components/FormattedNumber.tsx
index bee2b963..282c6a8a 100644
--- a/src/components/FormattedNumber.tsx
+++ b/src/components/FormattedNumber.tsx
@@ -35,6 +35,7 @@ export const FormattedNumber = React.memo(
if (
(prevAmountRef.current === props.amount && props.amount === 0) ||
!props.animate ||
+ prevAmountRef.current === 0 ||
reduceMotion
)
return (
diff --git a/src/components/Modals/AssetsSelect/AssetSelectTable.tsx b/src/components/Modals/AssetsSelect/AssetSelectTable.tsx
index ac004205..9cfa332c 100644
--- a/src/components/Modals/AssetsSelect/AssetSelectTable.tsx
+++ b/src/components/Modals/AssetsSelect/AssetSelectTable.tsx
@@ -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',
)}
>
-
+
{header.column.getCanSort()
? {
asc: ,
diff --git a/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx b/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx
index e515d54d..d9f75a77 100644
--- a/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx
+++ b/src/components/Modals/AssetsSelect/useAssetTableColumns.tsx
@@ -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 (
{formatPercent(asset.borrowRate ?? 0)}
diff --git a/src/components/Modals/FundWithdraw/FundAccount.tsx b/src/components/Modals/FundWithdraw/FundAccount.tsx
index 9c1c681a..a028f22c 100644
--- a/src/components/Modals/FundWithdraw/FundAccount.tsx
+++ b/src/components/Modals/FundWithdraw/FundAccount.tsx
@@ -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: } })
diff --git a/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx b/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx
index f71a7bae..75da6f4a 100644
--- a/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx
+++ b/src/components/Modals/FundWithdraw/FundAndWithdrawModalContent.tsx
@@ -15,9 +15,9 @@ export default function FundWithdrawModalContent(props: Props) {
const [change, setChange] = useState()
return (
-
+
{isFunding ? (
diff --git a/src/components/TokenInput/TokenInputWithSlider.tsx b/src/components/TokenInput/TokenInputWithSlider.tsx
index 16d6820d..d74fe484 100644
--- a/src/components/TokenInput/TokenInputWithSlider.tsx
+++ b/src/components/TokenInput/TokenInputWithSlider.tsx
@@ -67,7 +67,7 @@ export default function TokenInputWithSlider(props: Props) {
accountId={props.accountId}
/>
onChangeSlider(value)}
disabled={props.disabled}
/>
diff --git a/src/components/Trade/AccountDetailsCard.tsx b/src/components/Trade/AccountDetailsCard.tsx
index 6099b98e..4a9caa03 100644
--- a/src/components/Trade/AccountDetailsCard.tsx
+++ b/src/components/Trade/AccountDetailsCard.tsx
@@ -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 = (
diff --git a/src/types/interfaces/store/modals.d.ts b/src/types/interfaces/store/modals.d.ts
index a7febb09..9fbb99ce 100644
--- a/src/types/interfaces/store/modals.d.ts
+++ b/src/types/interfaces/store/modals.d.ts
@@ -57,4 +57,5 @@ interface UnlockModal {
interface WalletAssetModal {
isOpen?: boolean
selectedDenoms: string[]
+ isBorrow?: boolean
}
diff --git a/src/utils/accounts.ts b/src/utils/accounts.ts
index 3a1579de..468d7446 100644
--- a/src/utils/accounts.ts
+++ b/src/utils/accounts.ts
@@ -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 = (
diff --git a/src/utils/parsers.ts b/src/utils/parsers.ts
index 6f015eaa..edadc171 100644
--- a/src/utils/parsers.ts
+++ b/src/utils/parsers.ts
@@ -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 } = {}