From 56bfea8ed0e5ed3c5746216a2813e7054c92911b Mon Sep 17 00:00:00 2001 From: Gustavo Mauricio Date: Wed, 9 Nov 2022 12:47:36 +0000 Subject: [PATCH] Update account stats on pending action (#51) * feat: update account stats on pending action * lift repay amount with buffer calculation to repay modal * refactor navigation component to properly format account stats * update repay funds hook using credit manager client * consider market liquidity when calculating max withdraw amount * refactor useMaxBorrowAmount hook to be consistent with withdraw * format * remove duplicated import statements --- components/BorrowModal.tsx | 187 +++++++++++++++--- components/CreditManager/index.tsx | 20 +- components/Navigation.tsx | 10 +- components/RepayModal.tsx | 5 + components/WithdrawModal.tsx | 50 +++-- hooks/mutations/useRepayFunds.tsx | 56 ++---- hooks/useAccountStats.tsx | 253 ++++++++++++++++++++---- hooks/useCalculateMaxBorrowAmount.tsx | 29 ++- hooks/useCalculateMaxWithdrawAmount.tsx | 16 +- 9 files changed, 487 insertions(+), 139 deletions(-) diff --git a/components/BorrowModal.tsx b/components/BorrowModal.tsx index ed40acc0..26c1af36 100644 --- a/components/BorrowModal.tsx +++ b/components/BorrowModal.tsx @@ -12,12 +12,18 @@ import useCalculateMaxBorrowAmount from 'hooks/useCalculateMaxBorrowAmount' import useMarkets from 'hooks/useMarkets' import useTokenPrices from 'hooks/useTokenPrices' import { formatCurrency } from 'utils/formatters' +import useCreditAccountPositions from 'hooks/useCreditAccountPositions' +import useCreditManagerStore from 'stores/useCreditManagerStore' +import useAccountStats, { AccountStatsAction } from 'hooks/useAccountStats' +import { chain } from 'utils/chains' import { getTokenDecimals, getTokenSymbol } from 'utils/tokens' -import Button from './Button' +import ProgressBar from './ProgressBar' +import SemiCircleProgress from './SemiCircleProgress' import ContainerSecondary from './ContainerSecondary' import Spinner from './Spinner' import Tooltip from './Tooltip' +import Button from './Button' type Props = { show: boolean @@ -29,21 +35,46 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => { const [amount, setAmount] = useState(0) const [isBorrowToCreditAccount, setIsBorrowToCreditAccount] = useState(false) + const selectedAccount = useCreditManagerStore((s) => s.selectedAccount) + const { data: positionsData, isLoading: isLoadingPositions } = useCreditAccountPositions( + selectedAccount ?? '', + ) + + const { actions, borrowAmount } = useMemo(() => { + const borrowAmount = BigNumber(amount) + .times(10 ** getTokenDecimals(tokenDenom)) + .toNumber() + + const withdrawAmount = isBorrowToCreditAccount ? 0 : borrowAmount + + return { + borrowAmount, + withdrawAmount, + actions: [ + { + type: 'borrow', + amount: borrowAmount, + denom: tokenDenom, + }, + { + type: 'withdraw', + amount: withdrawAmount, + denom: tokenDenom, + }, + ] as AccountStatsAction[], + } + }, [amount, isBorrowToCreditAccount, tokenDenom]) + + const accountStats = useAccountStats(actions) + const tokenSymbol = getTokenSymbol(tokenDenom) - const { mutate, isLoading } = useBorrowFunds( - BigNumber(amount) - .times(10 ** getTokenDecimals(tokenDenom)) - .toNumber(), - tokenDenom, - !isBorrowToCreditAccount, - { - onSuccess: () => { - onClose() - toast.success(`${amount} ${tokenSymbol} successfully Borrowed`) - }, + const { mutate, isLoading } = useBorrowFunds(borrowAmount, tokenDenom, !isBorrowToCreditAccount, { + onSuccess: () => { + onClose() + toast.success(`${amount} ${tokenSymbol} successfully Borrowed`) }, - ) + }) const { data: tokenPrices } = useTokenPrices() const { data: balancesData } = useAllBalances() @@ -94,6 +125,17 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => { setAmount(0) } + const getTokenTotalUSDValue = (amount: string, denom: string) => { + // early return if prices are not fetched yet + if (!tokenPrices) return 0 + + return ( + BigNumber(amount) + .div(10 ** getTokenDecimals(denom)) + .toNumber() * tokenPrices[denom] + ) + } + return ( @@ -120,25 +162,13 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => { leaveFrom='opacity-100 scale-100' leaveTo='opacity-0 scale-95' > - + {isLoading && (
)} -
-
-

About

-

- Bringing the next generation of video creation to the Metaverse. -
- Powered by deep-learning. -

-
- mars -
-
Borrow {tokenSymbol} @@ -219,6 +249,111 @@ const BorrowModal = ({ show, onClose, tokenDenom }: Props) => { Borrow
+ +
+

About

+

Subaccount {selectedAccount}

+
+ {accountStats && ( +
+

+ {formatCurrency( + BigNumber(accountStats.netWorth) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +

+ {/* TOOLTIP */} +
+ +
+ + +
+ )} +
+
+
+
Total Position:
+
+ {formatCurrency( + BigNumber(accountStats?.totalPosition ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +
+
+
+
Total Liabilities:
+
+ {formatCurrency( + BigNumber(accountStats?.totalDebt ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +
+
+
+
+

Balances

+ {isLoadingPositions ? ( +
Loading...
+ ) : ( + + + + + + + + + + + {positionsData?.coins.map((coin) => ( + + + + + + + ))} + {positionsData?.debts.map((coin) => ( + + + + + + + ))} + +
AssetValueSizeAPY
{getTokenSymbol(coin.denom)} + {formatCurrency(getTokenTotalUSDValue(coin.amount, coin.denom))} + + {BigNumber(coin.amount) + .div(10 ** getTokenDecimals(coin.denom)) + .toNumber() + .toLocaleString(undefined, { + maximumFractionDigits: getTokenDecimals(coin.denom), + })} + -
{getTokenSymbol(coin.denom)} + -{formatCurrency(getTokenTotalUSDValue(coin.amount, coin.denom))} + + - + {BigNumber(coin.amount) + .div(10 ** getTokenDecimals(coin.denom)) + .toNumber() + .toLocaleString(undefined, { + maximumFractionDigits: 6, + })} + + -{(Number(marketsData?.[coin.denom].borrow_rate) * 100).toFixed(1)}% +
+ )} +
+
diff --git a/components/CreditManager/index.tsx b/components/CreditManager/index.tsx index b086e60f..a4f35e90 100644 --- a/components/CreditManager/index.tsx +++ b/components/CreditManager/index.tsx @@ -12,8 +12,8 @@ import useCreditManagerStore from 'stores/useCreditManagerStore' import useWalletStore from 'stores/useWalletStore' import { formatCurrency } from 'utils/formatters' import { getTokenDecimals, getTokenSymbol } from 'utils/tokens' - -import Button from '../Button' +import { chain } from 'utils/chains' +import Button from 'components/Button' const CreditManager = () => { const [showFundWalletModal, setShowFundWalletModal] = useState(false) @@ -78,11 +78,23 @@ const CreditManager = () => {
Total Position:
-
{formatCurrency(accountStats?.totalPosition ?? 0)}
+
+ {formatCurrency( + BigNumber(accountStats?.totalPosition ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +
Total Liabilities:
-
{formatCurrency(accountStats?.totalDebt ?? 0)}
+
+ {formatCurrency( + BigNumber(accountStats?.totalDebt ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +
diff --git a/components/Navigation.tsx b/components/Navigation.tsx index 385c2356..9dc95964 100644 --- a/components/Navigation.tsx +++ b/components/Navigation.tsx @@ -4,6 +4,7 @@ import Image from 'next/image' import Link from 'next/link' import { useRouter } from 'next/router' import React, { useMemo } from 'react' +import BigNumber from 'bignumber.js' import ArrowRightLine from 'components/Icons/arrow-right-line.svg' import ProgressBar from 'components/ProgressBar' @@ -17,6 +18,7 @@ import useCreditAccounts from 'hooks/useCreditAccounts' import useCreditManagerStore from 'stores/useCreditManagerStore' import useWalletStore from 'stores/useWalletStore' import { formatCurrency } from 'utils/formatters' +import { chain } from 'utils/chains' import Button from './Button' import SemiCircleProgress from './SemiCircleProgress' @@ -81,7 +83,13 @@ const Navigation = () => {
{accountStats && ( <> -

{formatCurrency(accountStats.netWorth)}

+

+ {formatCurrency( + BigNumber(accountStats.netWorth) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +

{/* TOOLTIP */}
void @@ -37,6 +40,8 @@ const RepayModal = ({ show, onClose, tokenDenom }: Props) => { positionsData?.debts.find((coin) => coin.denom === tokenDenom)?.amount ?? 0 return BigNumber(tokenDebtAmount) + .times(REPAY_BUFFER) + .decimalPlaces(0) .div(10 ** getTokenDecimals(tokenDenom)) .toNumber() }, [positionsData, tokenDenom]) diff --git a/components/WithdrawModal.tsx b/components/WithdrawModal.tsx index 22d84d66..059923e1 100644 --- a/components/WithdrawModal.tsx +++ b/components/WithdrawModal.tsx @@ -6,7 +6,6 @@ import { toast } from 'react-toastify' import Slider from 'components/Slider' import useWithdrawFunds from 'hooks/mutations/useWithdrawFunds' -import useAccountStats from 'hooks/useAccountStats' import useAllBalances from 'hooks/useAllBalances' import useCalculateMaxWithdrawAmount from 'hooks/useCalculateMaxWithdrawAmount' import useCreditAccountPositions from 'hooks/useCreditAccountPositions' @@ -15,12 +14,14 @@ import useTokenPrices from 'hooks/useTokenPrices' import useCreditManagerStore from 'stores/useCreditManagerStore' import { formatCurrency } from 'utils/formatters' import { getTokenDecimals, getTokenSymbol } from 'utils/tokens' +import useAccountStats, { AccountStatsAction } from 'hooks/useAccountStats' +import { chain } from 'utils/chains' -import Button from './Button' -import ContainerSecondary from './ContainerSecondary' -import ProgressBar from './ProgressBar' -import SemiCircleProgress from './SemiCircleProgress' import Spinner from './Spinner' +import SemiCircleProgress from './SemiCircleProgress' +import ProgressBar from './ProgressBar' +import ContainerSecondary from './ContainerSecondary' +import Button from './Button' const WithdrawModal = ({ show, onClose }: any) => { const [amount, setAmount] = useState(0) @@ -35,7 +36,6 @@ const WithdrawModal = ({ show, onClose }: any) => { const { data: balancesData } = useAllBalances() const { data: tokenPrices } = useTokenPrices() const { data: marketsData } = useMarkets() - const accountStats = useAccountStats() const selectedTokenSymbol = getTokenSymbol(selectedToken) const selectedTokenDecimals = getTokenDecimals(selectedToken) @@ -46,7 +46,7 @@ const WithdrawModal = ({ show, onClose }: any) => { .toNumber() }, [positionsData, selectedTokenDecimals, selectedToken]) - const { borrowAmount, withdrawAmount } = useMemo(() => { + const { actions, borrowAmount, withdrawAmount } = useMemo(() => { const borrowAmount = amount > tokenAmountInCreditAccount ? BigNumber(amount) @@ -62,8 +62,22 @@ const WithdrawModal = ({ show, onClose }: any) => { return { borrowAmount, withdrawAmount, + actions: [ + { + type: 'borrow', + amount: borrowAmount, + denom: selectedToken, + }, + { + type: 'withdraw', + amount: withdrawAmount, + denom: selectedToken, + }, + ] as AccountStatsAction[], } - }, [amount, selectedTokenDecimals, tokenAmountInCreditAccount]) + }, [amount, selectedToken, selectedTokenDecimals, tokenAmountInCreditAccount]) + + const accountStats = useAccountStats(actions) const { mutate, isLoading } = useWithdrawFunds(withdrawAmount, borrowAmount, selectedToken, { onSuccess: () => { @@ -234,7 +248,13 @@ const WithdrawModal = ({ show, onClose }: any) => {
{accountStats && (
-

{formatCurrency(accountStats.netWorth)}

+

+ {formatCurrency( + BigNumber(accountStats.netWorth) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )} +

{/* TOOLTIP */}
{
Total Position:
- {formatCurrency(accountStats?.totalPosition ?? 0)} + {formatCurrency( + BigNumber(accountStats?.totalPosition ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )}
Total Liabilities:
- {formatCurrency(accountStats?.totalDebt ?? 0)} + {formatCurrency( + BigNumber(accountStats?.totalDebt ?? 0) + .dividedBy(10 ** chain.stakeCurrency.coinDecimals) + .toNumber(), + )}
diff --git a/hooks/mutations/useRepayFunds.tsx b/hooks/mutations/useRepayFunds.tsx index fb4ee003..5c6aad88 100644 --- a/hooks/mutations/useRepayFunds.tsx +++ b/hooks/mutations/useRepayFunds.tsx @@ -1,5 +1,4 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' -import BigNumber from 'bignumber.js' import { useMemo } from 'react' import { toast } from 'react-toastify' @@ -8,64 +7,51 @@ import useCreditManagerStore from 'stores/useCreditManagerStore' import useWalletStore from 'stores/useWalletStore' import { queryKeys } from 'types/query-keys-factory' import { hardcodedFee } from 'utils/contants' -import { getTokenDecimals } from 'utils/tokens' - -// 0.001% buffer / slippage to avoid repay action from not fully repaying the debt amount -const REPAY_BUFFER = 1.00001 const useRepayFunds = ( amount: number, denom: string, options: Omit, ) => { - const signingClient = useWalletStore((s) => s.signingClient) - const selectedAccount = useCreditManagerStore((s) => s.selectedAccount) + const creditManagerClient = useWalletStore((s) => s.clients.creditManager) + const selectedAccount = useCreditManagerStore((s) => s.selectedAccount ?? '') const address = useWalletStore((s) => s.address) const queryClient = useQueryClient() - const adjustedAmount = BigNumber(amount).times(REPAY_BUFFER).decimalPlaces(0).toString() - - const executeMsg = useMemo(() => { - return { - update_credit_account: { - account_id: selectedAccount, - actions: [ - { - deposit: { - denom: denom, - amount: adjustedAmount, - }, - }, - { - repay: { - denom: denom, - amount: adjustedAmount, - }, - }, - ], + const actions = useMemo(() => { + return [ + { + deposit: { + denom: denom, + amount: String(amount), + }, }, - } - }, [adjustedAmount, denom, selectedAccount]) + { + repay: { + denom: denom, + amount: String(amount), + }, + }, + ] + }, [amount, denom]) return useMutation( async () => - await signingClient?.execute( - address, - contractAddresses.creditManager, - executeMsg, + await creditManagerClient?.updateCreditAccount( + { accountId: selectedAccount, actions }, hardcodedFee, undefined, [ { denom, - amount: adjustedAmount, + amount: String(amount), }, ], ), { onSettled: () => { - queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount ?? '')) + queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount)) queryClient.invalidateQueries(queryKeys.tokenBalance(address, denom)) queryClient.invalidateQueries(queryKeys.allBalances(address)) queryClient.invalidateQueries(queryKeys.redbankBalances()) diff --git a/hooks/useAccountStats.tsx b/hooks/useAccountStats.tsx index 10dd0d73..77bb6211 100644 --- a/hooks/useAccountStats.tsx +++ b/hooks/useAccountStats.tsx @@ -2,7 +2,6 @@ import BigNumber from 'bignumber.js' import { useMemo } from 'react' import useCreditManagerStore from 'stores/useCreditManagerStore' -import { getTokenDecimals } from 'utils/tokens' import useCreditAccountPositions from './useCreditAccountPositions' import useMarkets from './useMarkets' @@ -18,59 +17,104 @@ const getRiskFromAverageLiquidationLTVs = (value: number) => { return 1 } -const useAccountStats = () => { +type Asset = { + amount: string + denom: string + liquidationThreshold: string + basePrice: number +} + +type Debt = { + amount: string + denom: string + basePrice: number +} + +const calculateStatsFromAccountPositions = (assets: Asset[], debts: Debt[]) => { + const totalPosition = assets.reduce((acc, asset) => { + const tokenTotalValue = BigNumber(asset.amount).times(asset.basePrice) + + return BigNumber(tokenTotalValue).plus(acc).toNumber() + }, 0) + + const totalDebt = debts.reduce((acc, debt) => { + const tokenTotalValue = BigNumber(debt.amount).times(debt.basePrice) + + return BigNumber(tokenTotalValue).plus(acc).toNumber() + }, 0) + + const totalWeightedPositions = assets.reduce((acc, asset) => { + const assetWeightedValue = BigNumber(asset.amount) + .times(asset.basePrice) + .times(asset.liquidationThreshold) + + return assetWeightedValue.plus(acc).toNumber() + }, 0) + + const netWorth = BigNumber(totalPosition).minus(totalDebt).toNumber() + + const liquidationLTVsWeightedAverage = BigNumber(totalWeightedPositions) + .div(totalPosition) + .toNumber() + + const maxLeverage = BigNumber(1) + .div(BigNumber(1).minus(liquidationLTVsWeightedAverage)) + .toNumber() + const currentLeverage = BigNumber(totalPosition).div(netWorth).toNumber() + const health = BigNumber(1).minus(BigNumber(currentLeverage).div(maxLeverage)).toNumber() || 1 + + const risk = liquidationLTVsWeightedAverage + ? getRiskFromAverageLiquidationLTVs(liquidationLTVsWeightedAverage) + : 0 + + return { + health, + maxLeverage, + currentLeverage, + netWorth, + risk, + totalPosition, + totalDebt, + } +} + +export type AccountStatsAction = { + type: 'borrow' | 'repay' | 'deposit' | 'withdraw' + amount: number + denom: string +} + +const useAccountStats = (actions?: AccountStatsAction[]) => { const selectedAccount = useCreditManagerStore((s) => s.selectedAccount) const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '') const { data: marketsData } = useMarkets() const { data: tokenPrices } = useTokenPrices() - return useMemo(() => { + const currentStats = useMemo(() => { if (!marketsData || !tokenPrices || !positionsData) return null - const getTokenTotalUSDValue = (amount: string, denom: string) => { - // early return if prices are not fetched yet - if (!tokenPrices) return 0 + const assets = positionsData.coins.map((coin) => { + const market = marketsData[coin.denom] - return BigNumber(amount) - .div(10 ** getTokenDecimals(denom)) - .times(tokenPrices[denom]) - .toNumber() - } + return { + amount: coin.amount, + denom: coin.denom, + liquidationThreshold: market.liquidation_threshold, + basePrice: tokenPrices[coin.denom], + } + }) - const totalPosition = positionsData.coins.reduce((acc, coin) => { - const tokenTotalValue = getTokenTotalUSDValue(coin.amount, coin.denom) - return BigNumber(tokenTotalValue).plus(acc).toNumber() - }, 0) + const debts = positionsData.debts.map((debt) => { + return { + amount: debt.amount, + denom: debt.denom, + basePrice: tokenPrices[debt.denom], + } + }) - const totalDebt = positionsData.debts.reduce((acc, coin) => { - const tokenTotalValue = getTokenTotalUSDValue(coin.amount, coin.denom) - return BigNumber(tokenTotalValue).plus(acc).toNumber() - }, 0) - - const totalWeightedPositions = positionsData.coins.reduce((acc, coin) => { - const tokenWeightedValue = BigNumber(getTokenTotalUSDValue(coin.amount, coin.denom)).times( - Number(marketsData[coin.denom].liquidation_threshold), - ) - - return tokenWeightedValue.plus(acc).toNumber() - }, 0) - - const netWorth = BigNumber(totalPosition).minus(totalDebt).toNumber() - - const liquidationLTVsWeightedAverage = BigNumber(totalWeightedPositions) - .div(totalPosition) - .toNumber() - - const maxLeverage = BigNumber(1) - .div(BigNumber(1).minus(liquidationLTVsWeightedAverage)) - .toNumber() - const currentLeverage = BigNumber(totalPosition).div(netWorth).toNumber() - const health = BigNumber(1).minus(BigNumber(currentLeverage).div(maxLeverage)).toNumber() || 1 - - const risk = liquidationLTVsWeightedAverage - ? getRiskFromAverageLiquidationLTVs(liquidationLTVsWeightedAverage) - : 0 + const { health, maxLeverage, currentLeverage, netWorth, risk, totalPosition, totalDebt } = + calculateStatsFromAccountPositions(assets, debts) return { health, @@ -82,6 +126,129 @@ const useAccountStats = () => { totalDebt, } }, [marketsData, positionsData, tokenPrices]) + + const actionResultStats = useMemo(() => { + if (!actions) return null + if (!tokenPrices || !marketsData || !positionsData) return null + + let resultPositionsCoins = positionsData.coins.map((coin) => ({ ...coin })) + let resultPositionsDebts = positionsData.debts.map((debt) => { + const { shares, ...otherProps } = debt + return { ...otherProps } + }) + + actions.forEach((action) => { + if (action.amount === 0) return + + if (action.type === 'deposit') { + const index = resultPositionsCoins.findIndex((coin) => coin.denom === action.denom) + + if (index !== -1) { + resultPositionsCoins[index].amount = BigNumber(resultPositionsCoins[index].amount) + .plus(action.amount) + .toFixed() + } else { + resultPositionsCoins.push({ + amount: action.amount.toString(), + denom: action.denom, + }) + } + + return + } + + if (action.type === 'withdraw') { + const index = resultPositionsCoins.findIndex((coin) => coin.denom === action.denom) + + if (index !== -1) { + resultPositionsCoins[index].amount = BigNumber(resultPositionsCoins[index].amount) + .minus(action.amount) + .toFixed() + } else { + throw new Error('Cannot withdraw more than you have') + } + + return + } + + if (action.type === 'borrow') { + const indexDebts = resultPositionsDebts.findIndex((coin) => coin.denom === action.denom) + const indexPositions = resultPositionsCoins.findIndex((coin) => coin.denom === action.denom) + + if (indexDebts !== -1) { + resultPositionsDebts[indexDebts].amount = BigNumber( + resultPositionsDebts[indexDebts].amount, + ) + .plus(action.amount) + .toFixed() + } else { + resultPositionsDebts.push({ + amount: action.amount.toString(), + denom: action.denom, + }) + } + + if (indexPositions !== -1) { + resultPositionsCoins[indexPositions].amount = BigNumber( + resultPositionsCoins[indexPositions].amount, + ) + .plus(action.amount) + .toFixed() + } else { + resultPositionsCoins.push({ + amount: action.amount.toString(), + denom: action.denom, + }) + } + + return + } + + // TODO: repay + if (action.type === 'repay') { + const index = resultPositionsDebts.findIndex((coin) => coin.denom === action.denom) + resultPositionsDebts[index].amount = BigNumber(resultPositionsDebts[index].amount) + .minus(action.amount) + .toFixed() + + return + } + }) + + const assets = resultPositionsCoins.map((coin) => { + const market = marketsData[coin.denom] + + return { + amount: coin.amount, + denom: coin.denom, + liquidationThreshold: market.liquidation_threshold, + basePrice: tokenPrices[coin.denom], + } + }) + + const debts = resultPositionsDebts.map((debt) => { + return { + amount: debt.amount, + denom: debt.denom, + basePrice: tokenPrices[debt.denom], + } + }) + + const { health, maxLeverage, currentLeverage, netWorth, risk, totalPosition, totalDebt } = + calculateStatsFromAccountPositions(assets, debts) + + return { + health, + maxLeverage, + currentLeverage, + netWorth, + risk, + totalPosition, + totalDebt, + } + }, [actions, marketsData, positionsData, tokenPrices]) + + return actionResultStats || currentStats } export default useAccountStats diff --git a/hooks/useCalculateMaxBorrowAmount.tsx b/hooks/useCalculateMaxBorrowAmount.tsx index ffcab6d6..16c70e94 100644 --- a/hooks/useCalculateMaxBorrowAmount.tsx +++ b/hooks/useCalculateMaxBorrowAmount.tsx @@ -62,32 +62,31 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole const borrowTokenPrice = tokenPrices[denom] const tokenDecimals = getTokenDecimals(denom) - let maxValue + let maxAmountCapacity if (isUnderCollateralized) { // MAX TO CREDIT ACCOUNT - maxValue = BigNumber(totalLiabilitiesValue) + maxAmountCapacity = BigNumber(totalLiabilitiesValue) .minus(totalWeightedPositions) .div(borrowTokenPrice * Number(marketsData[denom].max_loan_to_value) - borrowTokenPrice) - .div(10 ** tokenDecimals) - .decimalPlaces(tokenDecimals) - .toNumber() } else { // MAX TO WALLET - maxValue = BigNumber(totalWeightedPositions) + maxAmountCapacity = BigNumber(totalWeightedPositions) .minus(totalLiabilitiesValue) .div(borrowTokenPrice) - .div(10 ** tokenDecimals) - .decimalPlaces(tokenDecimals) - .toNumber() } - const marketLiquidity = BigNumber(redbankBalances?.[denom] ?? 0) - .div(10 ** getTokenDecimals(denom)) + const marketLiquidity = redbankBalances?.[denom] ?? 0 + + const maxBorrowAmount = maxAmountCapacity.gt(marketLiquidity) + ? marketLiquidity + : maxAmountCapacity.gt(0) + ? maxAmountCapacity + : 0 + + return BigNumber(maxBorrowAmount) + .dividedBy(10 ** tokenDecimals) + .decimalPlaces(tokenDecimals) .toNumber() - - if (marketLiquidity < maxValue) return marketLiquidity - - return maxValue > 0 ? maxValue : 0 }, [ denom, getTokenValue, diff --git a/hooks/useCalculateMaxWithdrawAmount.tsx b/hooks/useCalculateMaxWithdrawAmount.tsx index 722c91e6..bdbad82a 100644 --- a/hooks/useCalculateMaxWithdrawAmount.tsx +++ b/hooks/useCalculateMaxWithdrawAmount.tsx @@ -7,6 +7,7 @@ import { getTokenDecimals } from 'utils/tokens' import useCreditAccountPositions from './useCreditAccountPositions' import useMarkets from './useMarkets' import useTokenPrices from './useTokenPrices' +import useRedbankBalances from './useRedbankBalances' const getApproximateHourlyInterest = (amount: string, borrowAPY: string) => { const hourlyAPY = BigNumber(borrowAPY).div(24 * 365) @@ -20,6 +21,7 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '') const { data: marketsData } = useMarkets() const { data: tokenPrices } = useTokenPrices() + const { data: redbankBalances } = useRedbankBalances() const tokenDecimals = getTokenDecimals(denom) @@ -37,7 +39,7 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { }, [denom, positionsData]) const maxAmount = useMemo(() => { - if (!marketsData || !tokenPrices || !positionsData || !denom) return 0 + if (!marketsData || !tokenPrices || !positionsData || !redbankBalances || !denom) return 0 const hasDebt = positionsData.debts.length > 0 @@ -98,15 +100,20 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { maxAmountCapacity = maxAmountCapacity < 0 ? 0 : maxAmountCapacity } - const isCapacityHigherThanBalance = BigNumber(maxAmountCapacity).gt(tokenAmountInCreditAccount) + const marketLiquidity = redbankBalances?.[denom] ?? 0 - if (!borrow && isCapacityHigherThanBalance) { + const maxWithdrawAmount = BigNumber(maxAmountCapacity).gt(marketLiquidity) + ? marketLiquidity + : maxAmountCapacity + const isMaxAmountHigherThanBalance = BigNumber(maxWithdrawAmount).gt(tokenAmountInCreditAccount) + + if (!borrow && isMaxAmountHigherThanBalance) { return BigNumber(tokenAmountInCreditAccount) .div(10 ** tokenDecimals) .toNumber() } - return BigNumber(maxAmountCapacity) + return BigNumber(maxWithdrawAmount) .div(10 ** tokenDecimals) .decimalPlaces(tokenDecimals) .toNumber() @@ -116,6 +123,7 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { getTokenValue, marketsData, positionsData, + redbankBalances, tokenAmountInCreditAccount, tokenDecimals, tokenPrices,