MP-1705: Borrow limit (#48)
* update borrow funds hooks using credit manager client * update calculate max borrow and withdraw hooks to consider future debt
This commit is contained in:
parent
9bc09c68af
commit
8bde02c2e9
@ -3,7 +3,6 @@ import { useMemo } from 'react'
|
|||||||
import { toast } from 'react-toastify'
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
import useWalletStore from 'stores/useWalletStore'
|
import useWalletStore from 'stores/useWalletStore'
|
||||||
import { contractAddresses } from 'config/contracts'
|
|
||||||
import { hardcodedFee } from 'utils/contants'
|
import { hardcodedFee } from 'utils/contants'
|
||||||
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
||||||
import { queryKeys } from 'types/query-keys-factory'
|
import { queryKeys } from 'types/query-keys-factory'
|
||||||
@ -14,33 +13,25 @@ const useBorrowFunds = (
|
|||||||
withdraw = false,
|
withdraw = false,
|
||||||
options: Omit<UseMutationOptions, 'onError'>
|
options: Omit<UseMutationOptions, 'onError'>
|
||||||
) => {
|
) => {
|
||||||
const signingClient = useWalletStore((s) => s.signingClient)
|
const creditManagerClient = useWalletStore((s) => s.clients.creditManager)
|
||||||
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount ?? '')
|
||||||
const address = useWalletStore((s) => s.address)
|
const address = useWalletStore((s) => s.address)
|
||||||
|
|
||||||
const queryClient = useQueryClient()
|
const queryClient = useQueryClient()
|
||||||
|
|
||||||
const executeMsg = useMemo(() => {
|
const actions = useMemo(() => {
|
||||||
if (!withdraw) {
|
if (!withdraw) {
|
||||||
return {
|
return [
|
||||||
update_credit_account: {
|
|
||||||
account_id: selectedAccount,
|
|
||||||
actions: [
|
|
||||||
{
|
{
|
||||||
borrow: {
|
borrow: {
|
||||||
denom: denom,
|
denom: denom,
|
||||||
amount: String(amount),
|
amount: String(amount),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return [
|
||||||
update_credit_account: {
|
|
||||||
account_id: selectedAccount,
|
|
||||||
actions: [
|
|
||||||
{
|
{
|
||||||
borrow: {
|
borrow: {
|
||||||
denom: denom,
|
denom: denom,
|
||||||
@ -53,22 +44,18 @@ const useBorrowFunds = (
|
|||||||
amount: String(amount),
|
amount: String(amount),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
]
|
||||||
},
|
}, [withdraw, denom, amount])
|
||||||
}
|
|
||||||
}, [withdraw, selectedAccount, denom, amount])
|
|
||||||
|
|
||||||
return useMutation(
|
return useMutation(
|
||||||
async () =>
|
async () =>
|
||||||
await signingClient?.execute(
|
await creditManagerClient?.updateCreditAccount(
|
||||||
address,
|
{ accountId: selectedAccount, actions },
|
||||||
contractAddresses.creditManager,
|
|
||||||
executeMsg,
|
|
||||||
hardcodedFee
|
hardcodedFee
|
||||||
),
|
),
|
||||||
{
|
{
|
||||||
onSettled: () => {
|
onSettled: () => {
|
||||||
queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount ?? ''))
|
queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount))
|
||||||
queryClient.invalidateQueries(queryKeys.redbankBalances())
|
queryClient.invalidateQueries(queryKeys.redbankBalances())
|
||||||
|
|
||||||
// if withdrawing to wallet, need to explicility invalidate balances queries
|
// if withdrawing to wallet, need to explicility invalidate balances queries
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
|
||||||
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
||||||
@ -8,6 +8,12 @@ import useMarkets from './useMarkets'
|
|||||||
import useTokenPrices from './useTokenPrices'
|
import useTokenPrices from './useTokenPrices'
|
||||||
import useRedbankBalances from './useRedbankBalances'
|
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 useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boolean) => {
|
||||||
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
||||||
|
|
||||||
@ -16,33 +22,40 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
|||||||
const { data: tokenPrices } = useTokenPrices()
|
const { data: tokenPrices } = useTokenPrices()
|
||||||
const { data: redbankBalances } = useRedbankBalances()
|
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(() => {
|
return useMemo(() => {
|
||||||
if (!marketsData || !tokenPrices || !positionsData || !redbankBalances) return 0
|
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
|
// max ltv adjusted collateral
|
||||||
const totalWeightedPositions = positionsData?.coins.reduce((acc, coin) => {
|
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)
|
Number(marketsData[coin.denom].max_loan_to_value)
|
||||||
)
|
)
|
||||||
|
|
||||||
return tokenWeightedValue.plus(acc).toNumber()
|
return tokenWeightedValue.plus(acc).toNumber()
|
||||||
}, 0)
|
}, 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 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)
|
}, 0)
|
||||||
|
|
||||||
const borrowTokenPrice = tokenPrices[denom]
|
const borrowTokenPrice = tokenPrices[denom]
|
||||||
@ -54,6 +67,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
|||||||
maxValue = BigNumber(totalLiabilitiesValue)
|
maxValue = BigNumber(totalLiabilitiesValue)
|
||||||
.minus(totalWeightedPositions)
|
.minus(totalWeightedPositions)
|
||||||
.div(borrowTokenPrice * Number(marketsData[denom].max_loan_to_value) - borrowTokenPrice)
|
.div(borrowTokenPrice * Number(marketsData[denom].max_loan_to_value) - borrowTokenPrice)
|
||||||
|
.div(10 ** tokenDecimals)
|
||||||
.decimalPlaces(tokenDecimals)
|
.decimalPlaces(tokenDecimals)
|
||||||
.toNumber()
|
.toNumber()
|
||||||
} else {
|
} else {
|
||||||
@ -61,6 +75,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
|||||||
maxValue = BigNumber(totalWeightedPositions)
|
maxValue = BigNumber(totalWeightedPositions)
|
||||||
.minus(totalLiabilitiesValue)
|
.minus(totalLiabilitiesValue)
|
||||||
.div(borrowTokenPrice)
|
.div(borrowTokenPrice)
|
||||||
|
.div(10 ** tokenDecimals)
|
||||||
.decimalPlaces(tokenDecimals)
|
.decimalPlaces(tokenDecimals)
|
||||||
.toNumber()
|
.toNumber()
|
||||||
}
|
}
|
||||||
@ -72,7 +87,15 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
|||||||
if (marketLiquidity < maxValue) return marketLiquidity
|
if (marketLiquidity < maxValue) return marketLiquidity
|
||||||
|
|
||||||
return maxValue > 0 ? maxValue : 0
|
return maxValue > 0 ? maxValue : 0
|
||||||
}, [denom, isUnderCollateralized, marketsData, positionsData, redbankBalances, tokenPrices])
|
}, [
|
||||||
|
denom,
|
||||||
|
getTokenValue,
|
||||||
|
isUnderCollateralized,
|
||||||
|
marketsData,
|
||||||
|
positionsData,
|
||||||
|
redbankBalances,
|
||||||
|
tokenPrices,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useCalculateMaxBorrowAmount
|
export default useCalculateMaxBorrowAmount
|
||||||
|
@ -7,6 +7,12 @@ import useCreditManagerStore from 'stores/useCreditManagerStore'
|
|||||||
import useTokenPrices from './useTokenPrices'
|
import useTokenPrices from './useTokenPrices'
|
||||||
import useMarkets from './useMarkets'
|
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 useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => {
|
||||||
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
||||||
|
|
||||||
@ -37,10 +43,18 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => {
|
|||||||
const borrowTokenPrice = tokenPrices[denom]
|
const borrowTokenPrice = tokenPrices[denom]
|
||||||
const borrowTokenMaxLTV = Number(marketsData[denom].max_loan_to_value)
|
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 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)
|
}, 0)
|
||||||
|
|
||||||
const positionsWeightedAverageWithoutAsset = positionsData?.coins.reduce((acc, coin) => {
|
const positionsWeightedAverageWithoutAsset = positionsData?.coins.reduce((acc, coin) => {
|
||||||
@ -77,6 +91,10 @@ const useCalculateMaxWithdrawAmount = (denom: string, borrow: boolean) => {
|
|||||||
.minus(requiredCollateral)
|
.minus(requiredCollateral)
|
||||||
.decimalPlaces(tokenDecimals)
|
.decimalPlaces(tokenDecimals)
|
||||||
.toNumber()
|
.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)
|
const isCapacityHigherThanBalance = BigNumber(maxAmountCapacity).gt(tokenAmountInCreditAccount)
|
||||||
|
Loading…
Reference in New Issue
Block a user