Borrow improvements (#29)
* update token prices and market data to match smart contract * feat: redbank balances query and respective rendering on ui * query key for rb balances and respective invalidations * update contracts config * fix: avoid returning negative max borrow amounts * fix: added deposit action to repay execute message * add minus sign before apy on debt positions * consider market liquidity on max borrow calculation * hive url added to chain config * update hardcoded token decimals
This commit is contained in:
parent
5cb1da132b
commit
d22de166da
@ -128,7 +128,7 @@ const CreditManager = () => {
|
||||
})}
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
{(Number(marketsData?.[coin.denom].borrow_rate) * 100).toFixed(1)}%
|
||||
-{(Number(marketsData?.[coin.denom].borrow_rate) * 100).toFixed(1)}%
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
@ -1,8 +1,13 @@
|
||||
// https://github.com/mars-protocol/rover/blob/master/scripts/deploy/addresses/osmo-test-4.json
|
||||
export const contractAddresses = {
|
||||
export const roverContracts = {
|
||||
accountNft: 'osmo1dravtyd0425fkdmkysc3ns7zud05clf5uhj6qqsnkdtrpkewu73q9f3f02',
|
||||
mockVault: 'osmo1emcckulm2mkx36xeanhsn3z3zjeql6pgd8yf8a5cf03ccvy7a4dqjw9tl7',
|
||||
marsOracleAdapter: 'osmo1cw6pv97g7fmhqykrn0gc9ngrx5tnky75rmlwkzxuqhsk58u0n8asz036g0',
|
||||
swapper: 'osmo1w2552km2u9w4k2gjw4n8drmuz5yxw8x4qzy6dl3da824km5cjlys00x3qp',
|
||||
creditManager: 'osmo18dt5y0ecyd5qg8nqwzrgxuljfejglyh2fjd984s8cy7fcx8mxh9qfl3hwq',
|
||||
}
|
||||
|
||||
export const contractAddresses = {
|
||||
...roverContracts,
|
||||
redBank: 'osmo1w5rqrdhut890jplmsqnr8gj3uf0wq6lj5rfdnhrtl63lpf6e7v6qalrhhn',
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { contractAddresses } from 'config/contracts'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
||||
import { queryKeys } from 'types/query-keys-factory'
|
||||
import { getTokenDecimals } from 'utils/tokens'
|
||||
|
||||
const useBorrowFunds = (
|
||||
amount: string | number,
|
||||
@ -36,6 +37,8 @@ const useBorrowFunds = (
|
||||
}, [address])
|
||||
|
||||
const executeMsg = useMemo(() => {
|
||||
const tokenDecimals = getTokenDecimals(denom)
|
||||
|
||||
if (!withdraw) {
|
||||
return {
|
||||
update_credit_account: {
|
||||
@ -45,7 +48,7 @@ const useBorrowFunds = (
|
||||
borrow: {
|
||||
denom: denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** 6)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
},
|
||||
@ -62,7 +65,7 @@ const useBorrowFunds = (
|
||||
borrow: {
|
||||
denom: denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** 6)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
},
|
||||
@ -70,14 +73,14 @@ const useBorrowFunds = (
|
||||
withdraw: {
|
||||
denom: denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** 6)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}, [amount, denom, withdraw, selectedAccount])
|
||||
}, [withdraw, selectedAccount, denom, amount])
|
||||
|
||||
return useMutation(
|
||||
async () =>
|
||||
@ -90,6 +93,7 @@ const useBorrowFunds = (
|
||||
{
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount ?? ''))
|
||||
queryClient.invalidateQueries(queryKeys.redbankBalances())
|
||||
|
||||
// if withdrawing to wallet, need to explicility invalidate balances queries
|
||||
if (withdraw) {
|
||||
|
@ -6,6 +6,7 @@ import { getTokenDecimals } from 'utils/tokens'
|
||||
import useCreditAccountPositions from './useCreditAccountPositions'
|
||||
import useMarkets from './useMarkets'
|
||||
import useTokenPrices from './useTokenPrices'
|
||||
import useRedbankBalances from './useRedbankBalances'
|
||||
|
||||
const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boolean) => {
|
||||
const selectedAccount = useCreditManagerStore((s) => s.selectedAccount)
|
||||
@ -13,9 +14,10 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
||||
const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '')
|
||||
const { data: marketsData } = useMarkets()
|
||||
const { data: tokenPrices } = useTokenPrices()
|
||||
const { data: redbankBalances } = useRedbankBalances()
|
||||
|
||||
return useMemo(() => {
|
||||
if (!marketsData || !tokenPrices || !positionsData) return 0
|
||||
if (!marketsData || !tokenPrices || !positionsData || !redbankBalances) return 0
|
||||
|
||||
const getTokenTotalUSDValue = (amount: string, denom: string) => {
|
||||
// early return if prices are not fetched yet
|
||||
@ -27,6 +29,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
||||
.toNumber()
|
||||
}
|
||||
|
||||
// max ltv adjusted collateral
|
||||
const totalWeightedPositions = positionsData?.coins.reduce((acc, coin) => {
|
||||
const tokenWeightedValue = BigNumber(getTokenTotalUSDValue(coin.amount, coin.denom)).times(
|
||||
Number(marketsData[coin.denom].max_loan_to_value)
|
||||
@ -35,6 +38,7 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
||||
return tokenWeightedValue.plus(acc).toNumber()
|
||||
}, 0)
|
||||
|
||||
// total debt value
|
||||
const totalLiabilitiesValue = positionsData?.debts.reduce((acc, coin) => {
|
||||
const tokenUSDValue = BigNumber(getTokenTotalUSDValue(coin.amount, coin.denom))
|
||||
|
||||
@ -44,20 +48,31 @@ const useCalculateMaxBorrowAmount = (denom: string, isUnderCollateralized: boole
|
||||
const borrowTokenPrice = tokenPrices[denom]
|
||||
const tokenDecimals = getTokenDecimals(denom)
|
||||
|
||||
let maxValue
|
||||
if (isUnderCollateralized) {
|
||||
return BigNumber(totalLiabilitiesValue)
|
||||
// MAX TO CREDIT ACCOUNT
|
||||
maxValue = BigNumber(totalLiabilitiesValue)
|
||||
.minus(totalWeightedPositions)
|
||||
.div(borrowTokenPrice * Number(marketsData[denom].max_loan_to_value) - borrowTokenPrice)
|
||||
.decimalPlaces(tokenDecimals)
|
||||
.toNumber()
|
||||
} else {
|
||||
return BigNumber(totalWeightedPositions)
|
||||
// MAX TO WALLET
|
||||
maxValue = BigNumber(totalWeightedPositions)
|
||||
.minus(totalLiabilitiesValue)
|
||||
.div(borrowTokenPrice)
|
||||
.decimalPlaces(tokenDecimals)
|
||||
.toNumber()
|
||||
}
|
||||
}, [denom, isUnderCollateralized, marketsData, positionsData, tokenPrices])
|
||||
|
||||
const marketLiquidity = BigNumber(redbankBalances?.[denom] ?? '')
|
||||
.div(10 ** getTokenDecimals(denom))
|
||||
.toNumber()
|
||||
|
||||
if (marketLiquidity < maxValue) return marketLiquidity
|
||||
|
||||
return maxValue > 0 ? maxValue : 0
|
||||
}, [denom, isUnderCollateralized, marketsData, positionsData, redbankBalances, tokenPrices])
|
||||
}
|
||||
|
||||
export default useCalculateMaxBorrowAmount
|
||||
|
@ -38,8 +38,8 @@ const useMarkets = () => {
|
||||
wasm: {
|
||||
uosmo: {
|
||||
denom: 'uosmo',
|
||||
max_loan_to_value: '0.65',
|
||||
liquidation_threshold: '0.7',
|
||||
max_loan_to_value: '0.55',
|
||||
liquidation_threshold: '0.65',
|
||||
liquidation_bonus: '0.1',
|
||||
reserve_factor: '0.2',
|
||||
interest_rate_model: {
|
||||
@ -61,8 +61,8 @@ const useMarkets = () => {
|
||||
},
|
||||
'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2': {
|
||||
denom: 'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2',
|
||||
max_loan_to_value: '0.77',
|
||||
liquidation_threshold: '0.8',
|
||||
max_loan_to_value: '0.65',
|
||||
liquidation_threshold: '0.7',
|
||||
liquidation_bonus: '0.1',
|
||||
reserve_factor: '0.2',
|
||||
interest_rate_model: {
|
||||
|
59
hooks/useRedbankBalances.tsx
Normal file
59
hooks/useRedbankBalances.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { Coin } from '@cosmjs/stargate'
|
||||
|
||||
import { contractAddresses } from 'config/contracts'
|
||||
import { queryKeys } from 'types/query-keys-factory'
|
||||
import { chain } from 'utils/chains'
|
||||
|
||||
interface Result {
|
||||
data: {
|
||||
bank: {
|
||||
balance: Coin[]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fetchBalances = () => {
|
||||
return fetch(chain.hive, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
query: `
|
||||
query RedbankBalances {
|
||||
bank {
|
||||
balance(
|
||||
address: "${contractAddresses.redBank}"
|
||||
) {
|
||||
amount
|
||||
denom
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
}),
|
||||
}).then((res) => res.json())
|
||||
}
|
||||
|
||||
const useRedbankBalances = () => {
|
||||
const result = useQuery<Result>(queryKeys.redbankBalances(), fetchBalances)
|
||||
|
||||
return {
|
||||
...result,
|
||||
data: useMemo(() => {
|
||||
if (!result.data) return
|
||||
|
||||
return result.data?.data.bank.balance.reduce(
|
||||
(acc, coin) => ({
|
||||
...acc,
|
||||
[coin.denom]: coin.amount,
|
||||
}),
|
||||
{}
|
||||
) as { [key in string]: string }
|
||||
}, [result.data]),
|
||||
}
|
||||
}
|
||||
|
||||
export default useRedbankBalances
|
@ -10,6 +10,7 @@ import { contractAddresses } from 'config/contracts'
|
||||
import { hardcodedFee } from 'utils/contants'
|
||||
import useCreditManagerStore from 'stores/useCreditManagerStore'
|
||||
import { queryKeys } from 'types/query-keys-factory'
|
||||
import { getTokenDecimals } from 'utils/tokens'
|
||||
|
||||
const useRepayFunds = (
|
||||
amount: string | number,
|
||||
@ -34,23 +35,33 @@ const useRepayFunds = (
|
||||
})()
|
||||
}, [address])
|
||||
|
||||
const tokenDecimals = getTokenDecimals(denom)
|
||||
|
||||
const executeMsg = useMemo(() => {
|
||||
return {
|
||||
update_credit_account: {
|
||||
account_id: selectedAccount,
|
||||
actions: [
|
||||
{
|
||||
deposit: {
|
||||
denom: denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
},
|
||||
{
|
||||
repay: {
|
||||
denom: denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** 6)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
}, [amount, denom, selectedAccount])
|
||||
}, [amount, denom, selectedAccount, tokenDecimals])
|
||||
|
||||
return useMutation(
|
||||
async () =>
|
||||
@ -58,13 +69,23 @@ const useRepayFunds = (
|
||||
address,
|
||||
contractAddresses.creditManager,
|
||||
executeMsg,
|
||||
hardcodedFee
|
||||
hardcodedFee,
|
||||
undefined,
|
||||
[
|
||||
{
|
||||
denom,
|
||||
amount: BigNumber(amount)
|
||||
.times(10 ** tokenDecimals)
|
||||
.toString(),
|
||||
},
|
||||
]
|
||||
),
|
||||
{
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.creditAccountsPositions(selectedAccount ?? ''))
|
||||
queryClient.invalidateQueries(queryKeys.tokenBalance(address, denom))
|
||||
queryClient.invalidateQueries(queryKeys.allBalances(address))
|
||||
queryClient.invalidateQueries(queryKeys.redbankBalances())
|
||||
},
|
||||
onError: (err: Error) => {
|
||||
toast.error(err.message)
|
||||
|
@ -4,8 +4,8 @@ const useTokenPrices = () => {
|
||||
return useQuery<{ [key in string]: number }>(
|
||||
['tokenPrices'],
|
||||
() => ({
|
||||
uosmo: 1.1,
|
||||
'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2': 11,
|
||||
uosmo: 1,
|
||||
'ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2': 1.5,
|
||||
}),
|
||||
{
|
||||
staleTime: Infinity,
|
||||
|
@ -10,6 +10,7 @@ import useMarkets from 'hooks/useMarkets'
|
||||
import useTokenPrices from 'hooks/useTokenPrices'
|
||||
import { BorrowFunds, RepayFunds } from 'components/Borrow'
|
||||
import BorrowTable from 'components/Borrow/BorrowTable'
|
||||
import useRedbankBalances from 'hooks/useRedbankBalances'
|
||||
|
||||
type ModuleState =
|
||||
| {
|
||||
@ -35,6 +36,7 @@ const Borrow = () => {
|
||||
const { data: positionsData } = useCreditAccountPositions(selectedAccount ?? '')
|
||||
const { data: marketsData } = useMarkets()
|
||||
const { data: tokenPrices } = useTokenPrices()
|
||||
const { data: redbankBalances } = useRedbankBalances()
|
||||
|
||||
const borrowedAssetsMap = useMemo(() => {
|
||||
let borrowedAssetsMap: Map<string, string> = new Map()
|
||||
@ -54,7 +56,7 @@ const Borrow = () => {
|
||||
.map((denom) => {
|
||||
const { symbol, chain, icon } = getTokenInfo(denom)
|
||||
const borrowRate = Number(marketsData?.[denom].borrow_rate) || 0
|
||||
const marketLiquidity = BigNumber(marketsData?.[denom].deposit_cap ?? '')
|
||||
const marketLiquidity = BigNumber(redbankBalances?.[denom] ?? '')
|
||||
.div(10 ** getTokenDecimals(denom))
|
||||
.toNumber()
|
||||
|
||||
@ -84,7 +86,7 @@ const Borrow = () => {
|
||||
.map((denom) => {
|
||||
const { symbol, chain, icon } = getTokenInfo(denom)
|
||||
const borrowRate = Number(marketsData?.[denom].borrow_rate) || 0
|
||||
const marketLiquidity = BigNumber(marketsData?.[denom].deposit_cap ?? '')
|
||||
const marketLiquidity = BigNumber(redbankBalances?.[denom] ?? '')
|
||||
.div(10 ** getTokenDecimals(denom))
|
||||
.toNumber()
|
||||
|
||||
@ -101,7 +103,7 @@ const Borrow = () => {
|
||||
return rowData
|
||||
}) ?? [],
|
||||
}
|
||||
}, [allowedCoinsData, borrowedAssetsMap, marketsData, tokenPrices])
|
||||
}, [allowedCoinsData, borrowedAssetsMap, marketsData, redbankBalances, tokenPrices])
|
||||
|
||||
const handleBorrowClick = (denom: string) => {
|
||||
setModuleState({ show: 'borrow', data: { tokenDenom: denom } })
|
||||
|
@ -1,6 +1,7 @@
|
||||
export const queryKeys = {
|
||||
allBalances: (address: string) => ['allBalances', address],
|
||||
allowedCoins: () => ['allowedCoins'],
|
||||
redbankBalances: () => ['redbankBalances'],
|
||||
creditAccounts: (address: string) => ['creditAccounts', address],
|
||||
creditAccountsPositions: (accountId: string) => ['creditAccountPositions', accountId],
|
||||
tokenBalance: (address: string, denom: string) => ['tokenBalance', address, denom],
|
||||
|
@ -42,6 +42,7 @@ export const chainsInfo = {
|
||||
chainId: 'osmo-test-4',
|
||||
rpc: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-rpc',
|
||||
rest: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-lcd',
|
||||
hive: 'https://osmosis-delphi-testnet-1.simply-vc.com.mt/XF32UOOU55CX/osmosis-hive/graphql',
|
||||
stakeCurrency: {
|
||||
coinDenom: 'OSMO',
|
||||
coinMinimalDenom: 'uosmo',
|
||||
|
Loading…
Reference in New Issue
Block a user