diff --git a/hooks/mutations/useBorrowFunds.tsx b/hooks/mutations/useBorrowFunds.tsx index e6755756..8a277a51 100644 --- a/hooks/mutations/useBorrowFunds.tsx +++ b/hooks/mutations/useBorrowFunds.tsx @@ -3,7 +3,6 @@ import { useMemo } from 'react' import { toast } from 'react-toastify' import useWalletStore from 'stores/useWalletStore' -import { contractAddresses } from 'config/contracts' import { hardcodedFee } from 'utils/contants' import useCreditManagerStore from 'stores/useCreditManagerStore' import { queryKeys } from 'types/query-keys-factory' @@ -14,61 +13,49 @@ const useBorrowFunds = ( withdraw = false, 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 executeMsg = useMemo(() => { + const actions = useMemo(() => { if (!withdraw) { - return { - update_credit_account: { - account_id: selectedAccount, - actions: [ - { - borrow: { - denom: denom, - amount: String(amount), - }, - }, - ], + return [ + { + borrow: { + denom: denom, + amount: String(amount), + }, }, - } + ] } - return { - update_credit_account: { - account_id: selectedAccount, - actions: [ - { - borrow: { - denom: denom, - amount: String(amount), - }, - }, - { - withdraw: { - denom: denom, - amount: String(amount), - }, - }, - ], + return [ + { + borrow: { + denom: denom, + amount: String(amount), + }, }, - } - }, [withdraw, selectedAccount, denom, amount]) + { + withdraw: { + denom: denom, + amount: String(amount), + }, + }, + ] + }, [withdraw, denom, amount]) return useMutation( async () => - await signingClient?.execute( - address, - contractAddresses.creditManager, - executeMsg, + await creditManagerClient?.updateCreditAccount( + { accountId: selectedAccount, actions }, hardcodedFee ), { onSettled: () => { - queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount ?? '')) + queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount)) queryClient.invalidateQueries(queryKeys.redbankBalances()) // if withdrawing to wallet, need to explicility invalidate balances queries diff --git a/hooks/useCalculateMaxBorrowAmount.tsx b/hooks/useCalculateMaxBorrowAmount.tsx index ec3e226b..6fe4cc1e 100644 --- a/hooks/useCalculateMaxBorrowAmount.tsx +++ b/hooks/useCalculateMaxBorrowAmount.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import BigNumber from 'bignumber.js' import useCreditManagerStore from 'stores/useCreditManagerStore' @@ -8,6 +8,12 @@ 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) + + return hourlyAPY.times(amount).toNumber() +} + const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boolean) => { const selectedAccount = useCreditManagerStore((s) => s.selectedAccount) @@ -16,33 +22,40 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole const { data: tokenPrices } = useTokenPrices() const { data: redbankBalances } = useRedbankBalances() + const getTokenValue = useCallback( + (amount: string, denom: string) => { + if (!tokenPrices) return 0 + + return BigNumber(amount).times(tokenPrices[denom]).toNumber() + }, + [tokenPrices] + ) + return useMemo(() => { if (!marketsData || !tokenPrices || !positionsData || !redbankBalances) return 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)) - .times(tokenPrices[denom]) - .toNumber() - } - // max ltv adjusted collateral const totalWeightedPositions = positionsData?.coins.reduce((acc, coin) => { - const tokenWeightedValue = BigNumber(getTokenTotalUSDValue(coin.amount, coin.denom)).times( + const tokenWeightedValue = BigNumber(getTokenValue(coin.amount, coin.denom)).times( Number(marketsData[coin.denom].max_loan_to_value) ) return tokenWeightedValue.plus(acc).toNumber() }, 0) - // total debt value + // approximate debt value in an hour timespan to avoid throwing on smart contract level + // due to debt interest being applied const totalLiabilitiesValue = positionsData?.debts.reduce((acc, coin) => { - const tokenUSDValue = BigNumber(getTokenTotalUSDValue(coin.amount, coin.denom)) + const estimatedInterestAmount = getApproximateHourlyInterest( + coin.amount, + marketsData[coin.denom].borrow_rate + ) - return tokenUSDValue.plus(acc).toNumber() + const tokenDebtValue = BigNumber(getTokenValue(coin.amount, coin.denom)).plus( + estimatedInterestAmount + ) + + return tokenDebtValue.plus(acc).toNumber() }, 0) const borrowTokenPrice = tokenPrices[denom] @@ -54,6 +67,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole maxValue = BigNumber(totalLiabilitiesValue) .minus(totalWeightedPositions) .div(borrowTokenPrice * Number(marketsData[denom].max_loan_to_value) - borrowTokenPrice) + .div(10 ** tokenDecimals) .decimalPlaces(tokenDecimals) .toNumber() } else { @@ -61,6 +75,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole maxValue = BigNumber(totalWeightedPositions) .minus(totalLiabilitiesValue) .div(borrowTokenPrice) + .div(10 ** tokenDecimals) .decimalPlaces(tokenDecimals) .toNumber() } @@ -72,7 +87,15 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole if (marketLiquidity < maxValue) return marketLiquidity return maxValue > 0 ? maxValue : 0 - }, [denom, isUnderCollateralized, marketsData, positionsData, redbankBalances, tokenPrices]) + }, [ + denom, + getTokenValue, + isUnderCollateralized, + marketsData, + positionsData, + redbankBalances, + tokenPrices, + ]) } export default useCalculateMaxBorrowAmount diff --git a/hooks/useCalculateMaxWithdrawAmount.tsx b/hooks/useCalculateMaxWithdrawAmount.tsx index 44a97e4e..4c8dc8fb 100644 --- a/hooks/useCalculateMaxWithdrawAmount.tsx +++ b/hooks/useCalculateMaxWithdrawAmount.tsx @@ -7,6 +7,12 @@ import useCreditManagerStore from 'stores/useCreditManagerStore' import useTokenPrices from './useTokenPrices' import useMarkets from './useMarkets' +const getApproximateHourlyInterest = (amount: string, borrowAPY: string) => { + const hourlyAPY = BigNumber(borrowAPY).div(24 * 365) + + return hourlyAPY.times(amount).toNumber() +} + const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { const selectedAccount = useCreditManagerStore((s) => s.selectedAccount) @@ -37,10 +43,18 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { const borrowTokenPrice = tokenPrices[denom] const borrowTokenMaxLTV = Number(marketsData[denom].max_loan_to_value) + // approximate debt value in an hour timespan to avoid throwing on smart contract level + // due to debt interest being applied const totalLiabilitiesValue = positionsData?.debts.reduce((acc, coin) => { - const tokenUSDValue = BigNumber(getTokenValue(coin.amount, coin.denom)) + const estimatedInterestAmount = getApproximateHourlyInterest( + coin.amount, + marketsData[coin.denom].borrow_rate + ) + const tokenDebtValue = BigNumber(getTokenValue(coin.amount, coin.denom)).plus( + estimatedInterestAmount + ) - return tokenUSDValue.plus(acc).toNumber() + return tokenDebtValue.plus(acc).toNumber() }, 0) const positionsWeightedAverageWithoutAsset = positionsData?.coins.reduce((acc, coin) => { @@ -77,6 +91,10 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => { .minus(requiredCollateral) .decimalPlaces(tokenDecimals) .toNumber() + + // required collateral might be greater than the amount in the credit account which will result + // in a negative max amount capacity + maxAmountCapacity = maxAmountCapacity < 0 ? 0 : maxAmountCapacity } const isCapacityHigherThanBalance = BigNumber(maxAmountCapacity).gt(tokenAmountInCreditAccount)