vault: separate aprs (#777)

This commit is contained in:
Bob van der Helm 2024-02-07 10:59:54 +01:00 committed by GitHub
parent ef9c353b6b
commit b2b8f93bc3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 136 additions and 68 deletions

View File

@ -1,31 +0,0 @@
import { aprsCache, aprsCacheResponse, cacheFn } from 'api/cache'
export default async function getAprs(chainConfig: ChainConfig) {
if (!chainConfig.farm) return []
try {
const response = await cacheFn(
() => fetch(chainConfig.endpoints.aprs.vaults),
aprsCacheResponse,
`${chainConfig.id}/aprsResponse`,
60,
)
if (response.ok) {
const data: AprResponse = await cacheFn(
() => response.json(),
aprsCache,
`${chainConfig.id}/aprs`,
60,
)
return data.vaults.map((aprData) => {
const finalApr = aprData.apr.projected_apr * 100
return { address: aprData.address, apr: finalApr } as Apr
})
}
return []
} catch {
return []
}
}

View File

@ -1,24 +1,20 @@
import getAssetParams from 'api/params/getAssetParams'
import getAprs from 'api/vaults/getVaultAprs'
import { getVaultConfigs } from 'api/vaults/getVaultConfigs'
import { getVaultUtilizations } from 'api/vaults/getVaultUtilizations'
import { BN } from 'utils/helpers'
import { convertAprToApy } from 'utils/parsers'
import { resolveHLSStrategies } from 'utils/resolvers'
export default async function getVaults(chainConfig: ChainConfig): Promise<Vault[]> {
const assetParams = await getAssetParams(chainConfig)
const vaultConfigs = await getVaultConfigs(chainConfig)
const $vaultUtilizations = getVaultUtilizations(chainConfig, vaultConfigs)
const $aprs = getAprs(chainConfig)
const vaultMetaDatas = chainConfig.vaults
const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls)
const hlsStrategies = resolveHLSStrategies('vault', HLSAssets)
const vaults: Vault[] = []
await Promise.all([$vaultUtilizations, $aprs]).then(([vaultUtilizations, aprs]) => {
await $vaultUtilizations.then((vaultUtilizations) => {
return vaultConfigs.map((vaultConfig) => {
const apr = aprs.find((apr) => apr.address === vaultConfig.addr)
const vaultMetaData = vaultMetaDatas.find(
(vaultMetaData) => vaultMetaData.address === vaultConfig.addr,
)
@ -36,8 +32,6 @@ export default async function getVaults(chainConfig: ChainConfig): Promise<Vault
)?.utilization.amount || 0,
),
},
apy: apr ? convertAprToApy(apr.apr, 365) : null,
apr: apr ? apr.apr : null,
ltv: {
max: Number(vaultConfig.max_loan_to_value),
liq: Number(vaultConfig.liquidation_threshold),

View File

@ -1,11 +1,13 @@
import classNames from 'classnames'
import AssetRate from 'components/common/assets/AssetRate'
import Loading from 'components/common/Loading'
import Text from 'components/common/Text'
export const APY_META = { accessorKey: 'apy', header: 'APY', meta: { className: 'w-30' } }
interface Props {
apy: number
apy?: number | null
markets: Market[]
denom: string
type: PositionType
@ -14,12 +16,16 @@ interface Props {
export default function Apr(props: Props) {
const { markets, type, denom, apy } = props
if (apy === undefined) return <Loading />
if (apy === null) return <Text size='xs'>N/A</Text>
if (apy === 0)
return (
<p className={classNames('w-full text-xs text-right number', type === 'vault' && 'pb-4')}>
&ndash;
</p>
)
const isEnabled =
markets.find((market) => market.asset.denom === props.denom)?.borrowEnabled ?? false

View File

@ -13,6 +13,7 @@ import { ORACLE_DENOM } from 'constants/oracle'
import useAllAssets from 'hooks/assets/useAllAssets'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { calculateAccountApr, getAccountPositionValues } from 'utils/accounts'
@ -37,6 +38,7 @@ export default function AccountComposition(props: Props) {
const hasChanged = !!updatedAccount
const { data: prices } = usePrices()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const assets = useAllAssets()
const data = useBorrowMarketAssetsTableData()
const borrowAssetsData = useMemo(() => data?.allAssets || [], [data])
@ -75,9 +77,19 @@ export default function AccountComposition(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
props.isHls,
),
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, props.isHls],
[
account,
assets,
borrowAssetsData,
hlsStrategies,
lendingAssetsData,
prices,
props.isHls,
vaultAprs,
],
)
const updatedApr = useMemo(
() =>
@ -89,6 +101,7 @@ export default function AccountComposition(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
props.isHls,
)
: BN_ZERO,
@ -99,6 +112,7 @@ export default function AccountComposition(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
props.isHls,
],
)

View File

@ -22,9 +22,10 @@ import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useAccountId from 'hooks/useAccountId'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import {
@ -38,6 +39,7 @@ export default function AccountDetailsController() {
const isHLS = useStore((s) => s.isHLS)
const { data: _, isLoading } = useAccounts('default', address)
const { data: accountIds } = useAccountIds(address, false, true)
const accountId = useAccountId()
const account = useCurrentAccount()
@ -59,6 +61,7 @@ function AccountDetails(props: Props) {
const { account } = props
const location = useLocation()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const [reduceMotion] = useLocalStorage<boolean>(
LocalStorageKeys.REDUCE_MOTION,
DEFAULT_SETTINGS.reduceMotion,
@ -107,9 +110,19 @@ function AccountDetails(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
account.kind === 'high_levered_strategy',
),
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, updatedAccount],
[
account,
assets,
borrowAssetsData,
hlsStrategies,
lendingAssetsData,
prices,
updatedAccount,
vaultAprs,
],
)
const isFullWidth =
location.pathname.includes('trade') ||
@ -128,7 +141,7 @@ function AccountDetails(props: Props) {
data-testid='account-details'
className={classNames(
accountDetailsExpanded ? 'right-4' : '-right-74',
'w-94 flex items-start gap-4 absolute top-6',
'w-94 flex items-start gap-4 absolute top-6 z-2',
!reduceMotion && 'transition-all duration-500',
)}
>

View File

@ -12,6 +12,7 @@ import useAllAssets from 'hooks/assets/useAllAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
import { calculateAccountApr, calculateAccountBalanceValue } from 'utils/accounts'
@ -27,6 +28,7 @@ export default function AccountStats(props: Props) {
const { data: account } = useAccount(accountId)
const { data: prices } = usePrices()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const positionBalance = useMemo(
() => (!account ? null : calculateAccountBalanceValue(account, prices, assets)),
@ -52,9 +54,10 @@ export default function AccountStats(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
account.kind === 'high_levered_strategy',
),
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices],
[account, assets, borrowAssetsData, hlsStrategies, lendingAssetsData, prices, vaultAprs],
)
const deleteAccountHandler = useCallback(() => {

View File

@ -4,8 +4,8 @@ import { byDenom } from 'utils/array'
export function getVaultAccountStrategiesRow(
vault: DepositedVault,
apy: number,
prices: BNCoin[],
apy?: number | null,
prev?: DepositedVault,
): AccountStrategyRow {
const { name } = vault
@ -17,7 +17,7 @@ export function getVaultAccountStrategiesRow(
.plus(previous.values.unlocked)
.plus(previous.values.unlocking)
if (totalLockedValue.isLessThan(totalValue)) {
if (totalLockedValue.isLessThan(totalValue) && apy) {
apy = totalLockedValue.dividedBy(totalValue).times(apy).toNumber()
}

View File

@ -2,6 +2,7 @@ import { useMemo } from 'react'
import { getVaultAccountStrategiesRow } from 'components/account/AccountStrategiesTable/functions'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
interface Props {
account: Account
@ -11,17 +12,18 @@ interface Props {
export default function useAccountStategiesData(props: Props) {
const { account, updatedAccount } = props
const { data: prices } = usePrices()
const { data: vaultAprs } = useVaultAprs()
return useMemo<AccountStrategyRow[]>(() => {
const usedAccount = updatedAccount ?? account
const accountVaults = usedAccount?.vaults ?? []
return accountVaults.map((vault) => {
const apy = vault.apy ?? 0
const apy = vaultAprs.find((vaultApr) => vaultApr.address === vault.address)?.apy
const prevVault = updatedAccount
? account?.vaults.find((position) => position.name === vault.name)
: vault
return getVaultAccountStrategiesRow(vault, apy, prices, prevVault)
return getVaultAccountStrategiesRow(vault, prices, apy, prevVault)
})
}, [account, updatedAccount, prices])
}, [updatedAccount, account, vaultAprs, prices])
}

View File

@ -13,9 +13,10 @@ import { LocalStorageKeys } from 'constants/localStorageKeys'
import { BN_ZERO } from 'constants/math'
import useAllAssets from 'hooks/assets/useAllAssets'
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
import { calculateAccountApr, calculateAccountLeverage } from 'utils/accounts'
@ -36,6 +37,7 @@ export default function AccountSummary(props: Props) {
storageKey,
defaultSetting,
)
const { data: vaultAprs } = useVaultAprs()
const { data: prices } = usePrices()
const assets = useAllAssets()
const updatedAccount = useStore((s) => s.updatedAccount)
@ -79,16 +81,18 @@ export default function AccountSummary(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
props.account.kind === 'high_levered_strategy',
),
[
updatedAccount,
props.account,
assets,
borrowAssetsData,
hlsStrategies,
lendingAssetsData,
prices,
updatedAccount,
hlsStrategies,
assets,
vaultAprs,
],
)

View File

@ -1,5 +1,6 @@
import { FormattedNumber } from 'components/common/FormattedNumber'
import Loading from 'components/common/Loading'
import Text from 'components/common/Text'
export const APY_META = { accessorKey: 'apy', header: 'APY' }
@ -10,7 +11,9 @@ interface Props {
export default function Apy(props: Props) {
const { vault } = props
if (vault.apy === null) return <Loading />
if (vault.apy === undefined) return <Loading />
if (vault.apy === null) return <Text size='xs'>N/A</Text>
return (
<FormattedNumber
amount={vault.apy ?? 0}

View File

@ -8,12 +8,14 @@ import useAccountId from 'hooks/useAccountId'
import useChainConfig from 'hooks/useChainConfig'
import useDepositedVaults from 'hooks/useDepositedVaults'
import useVaults from 'hooks/useVaults'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import { VaultStatus } from 'types/enums/vault'
function Content() {
const accountId = useAccountId()
const { data: vaults } = useVaults()
const { data: depositedVaults } = useDepositedVaults(accountId || '')
const { data: vaultAprs } = useVaultAprs()
const chainConfig = useChainConfig()
const vaultMetaData = chainConfig.vaults
@ -23,18 +25,19 @@ function Content() {
if (!vaults) return prev
const vault = vaults.find((vault) => vault.address === curr.address)
const depositedVault = depositedVaults?.find((vault) => vault.address === curr.address)
const apr = vaultAprs.find((vaultApr) => vaultApr.address === curr.address)!
if (depositedVault) {
prev.deposited.push(depositedVault)
prev.deposited.push({ ...depositedVault, ...apr })
} else if (vault) {
prev.available.push(vault)
prev.available.push({ ...vault, ...apr })
}
return prev
},
{ deposited: [], available: [] },
)
}, [vaults, depositedVaults, vaultMetaData])
}, [vaultMetaData, vaults, depositedVaults, vaultAprs])
const unlockedVaults: DepositedVault[] = []

View File

@ -2,6 +2,7 @@ import React from 'react'
import { FormattedNumber } from 'components/common/FormattedNumber'
import Loading from 'components/common/Loading'
import Text from 'components/common/Text'
import useMarket from 'hooks/markets/useMarket'
export const APY_META = { accessorKey: 'apy', header: 'APY Range' }
@ -14,7 +15,8 @@ export default function Apy(props: Props) {
const { vault } = props
const borrowRate = useMarket(vault.hls?.borrowDenom || '')?.apy.borrow
if (vault.apy === null || borrowRate === null) return <Loading />
if (vault.apy === undefined || borrowRate === null) return <Loading />
if (vault.apy === null) return <Text size='xs'>N/A</Text>
const APYs = [vault.apy, vault.apy * (vault.hls?.maxLeverage || 1) - (borrowRate || 0) * 100]

View File

@ -11,6 +11,7 @@ import useAllAssets from 'hooks/assets/useAllAssets'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import { getAccountSummaryStats } from 'utils/accounts'
import { DEFAULT_PORTFOLIO_STATS } from 'utils/constants'
@ -21,6 +22,7 @@ interface Props {
function Content(props: Props) {
const { data: account } = useAccount(props.accountId, true)
const { data: prices } = usePrices()
const { data: vaultAprs } = useVaultAprs()
const { health, healthFactor } = useHealthComputer(account)
const data = useBorrowMarketAssetsTableData()
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
@ -37,6 +39,7 @@ function Content(props: Props) {
lendingAssets,
hlsStrategies,
assets,
vaultAprs,
account.kind === 'high_levered_strategy',
)
@ -78,7 +81,7 @@ function Content(props: Props) {
sub: DEFAULT_PORTFOLIO_STATS[4].sub,
},
]
}, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices])
}, [account, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs])
return (
<Skeleton

View File

@ -17,6 +17,7 @@ import useAccountId from 'hooks/useAccountId'
import useHealthComputer from 'hooks/useHealthComputer'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import {
calculateAccountApr,
calculateAccountLeverage,
@ -37,6 +38,7 @@ export default function PortfolioCard(props: Props) {
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
const data = useBorrowMarketAssetsTableData()
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const [searchParams] = useSearchParams()
const assets = useAllAssets()
const borrowAssets = useMemo(() => data?.allAssets || [], [data])
@ -64,9 +66,10 @@ export default function PortfolioCard(props: Props) {
prices,
hlsStrategies,
assets,
vaultAprs,
account.kind === 'high_levered_strategy',
)
}, [lendingAssets, borrowAssets, prices, account, hlsStrategies, assets])
}, [lendingAssets, borrowAssets, prices, account, hlsStrategies, assets, vaultAprs])
const stats: { title: ReactNode; sub: string }[] = useMemo(() => {
const isLoaded = account && prices.length && apr !== null

View File

@ -11,6 +11,7 @@ import useAccounts from 'hooks/accounts/useAccounts'
import useAllAssets from 'hooks/assets/useAllAssets'
import useHLSStakingAssets from 'hooks/useHLSStakingAssets'
import usePrices from 'hooks/usePrices'
import useVaultAprs from 'hooks/vaults/useVaultAprs'
import useStore from 'store'
import { getAccountSummaryStats } from 'utils/accounts'
import { DEFAULT_PORTFOLIO_STATS } from 'utils/constants'
@ -24,6 +25,7 @@ export default function PortfolioSummary() {
const { allAssets: lendingAssets } = useLendingMarketAssetsTableData()
const { data: accounts } = useAccounts('default', urlAddress || walletAddress)
const { data: hlsStrategies } = useHLSStakingAssets()
const { data: vaultAprs } = useVaultAprs()
const assets = useAllAssets()
const stats = useMemo(() => {
if (!accounts?.length) return
@ -53,6 +55,7 @@ export default function PortfolioSummary() {
lendingAssets,
hlsStrategies,
assets,
vaultAprs,
)
return [
@ -93,7 +96,7 @@ export default function PortfolioSummary() {
sub: 'Combined leverage',
},
]
}, [accounts, assets, borrowAssets, hlsStrategies, lendingAssets, prices])
}, [accounts, assets, borrowAssets, hlsStrategies, lendingAssets, prices, vaultAprs])
if (!walletAddress && !urlAddress) return null

View File

@ -0,0 +1,40 @@
import useSWR from 'swr'
import useChainConfig from 'hooks/useChainConfig'
import { convertAprToApy } from 'utils/parsers'
export default function useVaultAprs() {
const chainConfig = useChainConfig()
return useSWR(`chains/${chainConfig.id}/vaults/aprs`, () => getAprs(chainConfig), {
fallbackData: getEmptyAprData(chainConfig),
})
}
async function getAprs(chainConfig: ChainConfig) {
if (!chainConfig.farm) return []
try {
const response = await fetch(chainConfig.endpoints.aprs.vaults)
if (response.ok) {
const data: AprResponse = await response.json()
return data.vaults.map((aprData) => {
const finalApr = aprData.apr.projected_apr * 100
return {
address: aprData.address,
apr: finalApr,
apy: convertAprToApy(finalApr, 365),
} as Apr
})
}
return getEmptyAprData(chainConfig, null)
} catch {
return getEmptyAprData(chainConfig, null)
}
}
function getEmptyAprData(chainConfig: ChainConfig, apr?: null) {
return chainConfig.vaults.map((vault) => ({ address: vault.address, apr: apr, apy: apr }) as Apr)
}

View File

@ -20,7 +20,7 @@ interface AccountChange extends Account {
interface AccountBalanceRow {
amount: BigNumber
apy: number
apy?: number | null
denom: string
size: number
symbol: string
@ -30,7 +30,7 @@ interface AccountBalanceRow {
}
interface AccountStrategyRow {
apy: number
apy?: number | null
name: string
denom: string
amount: BNCoin[]

View File

@ -39,8 +39,8 @@ interface Vault extends VaultConfig {
maxLeverage: number
borrowDenom: string
}
apy: number | null
apr: number | null
apr?: number | null
apy?: number | null
}
interface VaultValuesAndAmounts {
@ -120,5 +120,6 @@ interface AprBreakdown {
interface Apr {
address: string
apr: number
apr?: number | null
apy?: number | null
}

View File

@ -72,6 +72,7 @@ export const calculateAccountApr = (
prices: BNCoin[],
hlsStrategies: HLSStrategy[],
assets: Asset[],
vaultAprs: Apr[],
isHls?: boolean,
): BigNumber => {
const depositValue = calculateAccountValue('deposits', account, prices, assets)
@ -125,8 +126,10 @@ export const calculateAccountApr = (
})
vaults?.forEach((vault) => {
const apr = vaultAprs.find((vaultApr) => vaultApr.address === vault.address)?.apr
if (!apr) return
const lockedValue = vault.values.primary.plus(vault.values.secondary)
const positionInterest = lockedValue.multipliedBy(vault?.apr ?? 0).dividedBy(100)
const positionInterest = lockedValue.multipliedBy(apr).dividedBy(100)
totalVaultsInterestValue = totalVaultsInterestValue.plus(positionInterest)
})
@ -307,6 +310,7 @@ export function getAccountSummaryStats(
lendingAssets: LendingMarketTableData[],
hlsStrategies: HLSStrategy[],
assets: Asset[],
vaultAprs: Apr[],
isHls?: boolean,
) {
const [deposits, lends, debts, vaults] = getAccountPositionValues(account, prices, assets)
@ -318,6 +322,7 @@ export function getAccountSummaryStats(
prices,
hlsStrategies,
assets,
vaultAprs,
isHls,
)
const leverage = calculateAccountLeverage(account, prices, assets)

View File

@ -5,7 +5,7 @@ export const debugSWR: Middleware = (useSWRNext: SWRHook) => (key, fetcher, conf
const startTime = Date.now()
const res = await fetcher!(...args)
process.env.NODE_ENV !== 'production' &&
console.log('⬇️ GET: ', key, ' in ', Date.now() - startTime, 'ms')
console.log('⬇️ GET: ', key, ' in ', Date.now() - startTime, 'ms', 'data: ', res)
return res
}
// ...