From 5392a4717c0e613182f774dca379a33397604075 Mon Sep 17 00:00:00 2001 From: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:45:57 +0200 Subject: [PATCH] Mp 3245 usehlsvaults hook (#541) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨ routing and pages for HLS * ✨ create hooks for fetching HLS vaults and Strategies * Share accounts (#539) * feat: do not redirect to wallet on portfolio page * fix: use connected wallet for AccountMenu * fix: fixed ghost AccountDetails * feat: created ShareBar and share functionality * fix: don’t show shareBar if no address is present * fix: stupid 'next/navigation' * tidy: format * fix: fixed tests * ✨ routing and pages for HLS (#538) * πŸ› use useAccountIds * fix: fixed the tests * fix: accountIds is now a suspense --------- Co-authored-by: Bob van der Helm <34470358+bobthebuidlr@users.noreply.github.com> * πŸ› fix build --------- Co-authored-by: Linkie Link --- src/api/hls/getHLSStakingAssets.ts | 28 ++++++++++++ src/api/hls/getHLSVaults.ts | 34 +++++++++++++++ .../HLS/AvailableHLSStakingAssets.tsx | 3 ++ src/components/HLS/AvailableHLSVaults.tsx | 3 ++ src/hooks/useHLSStakingAssets.ts | 10 +++++ src/hooks/useHLSVaults.ts | 10 +++++ src/pages/HLSFarmPage.tsx | 2 + src/pages/HLSStakingPage.tsx | 2 + src/types/interfaces/asset.d.ts | 14 ++++++ src/utils/helpers.ts | 4 ++ src/utils/resolvers.ts | 43 ++++++++++++++++++- 11 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/api/hls/getHLSStakingAssets.ts create mode 100644 src/api/hls/getHLSVaults.ts create mode 100644 src/components/HLS/AvailableHLSStakingAssets.tsx create mode 100644 src/components/HLS/AvailableHLSVaults.tsx create mode 100644 src/hooks/useHLSStakingAssets.ts create mode 100644 src/hooks/useHLSVaults.ts diff --git a/src/api/hls/getHLSStakingAssets.ts b/src/api/hls/getHLSStakingAssets.ts new file mode 100644 index 00000000..ea2a73b5 --- /dev/null +++ b/src/api/hls/getHLSStakingAssets.ts @@ -0,0 +1,28 @@ +import { getParamsQueryClient } from 'api/cosmwasm-client' +import getAssetParams from 'api/params/getAssetParams' +import { BN } from 'utils/helpers' +import { resolveHLSStrategies } from 'utils/resolvers' + +export default async function getHLSStakingAssets() { + const assetParams = await getAssetParams() + const client = await getParamsQueryClient() + const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls) + const strategies = resolveHLSStrategies('coin', HLSAssets) + + const depositCaps$ = strategies.map((strategy) => + client.totalDeposit({ denom: strategy.denoms.deposit }), + ) + + return Promise.all(depositCaps$).then((depositCaps) => { + return depositCaps.map((depositCap, index) => { + return { + ...strategies[index], + depositCap: { + denom: depositCap.denom, + used: BN(depositCap.amount), + max: BN(depositCap.cap), + }, + } as HLSStrategy + }) + }) +} diff --git a/src/api/hls/getHLSVaults.ts b/src/api/hls/getHLSVaults.ts new file mode 100644 index 00000000..2b09a9b3 --- /dev/null +++ b/src/api/hls/getHLSVaults.ts @@ -0,0 +1,34 @@ +import { getCreditManagerQueryClient } from 'api/cosmwasm-client' +import getAssetParams from 'api/params/getAssetParams' +import { getVaultConfigs } from 'api/vaults/getVaultConfigs' +import { BN } from 'utils/helpers' +import { resolveHLSStrategies } from 'utils/resolvers' + +export default async function getHLSVaults() { + const assetParams = await getAssetParams() + const client = await getCreditManagerQueryClient() + const vaultConfigs = await getVaultConfigs() + const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls) + const strategies = resolveHLSStrategies('vault', HLSAssets) + + const vaultUtilizations$ = strategies.map((strategy) => + client.vaultUtilization({ vault: { address: strategy.denoms.deposit } }), + ) + + return Promise.all(vaultUtilizations$).then((vaultUtilizations) => + vaultUtilizations.map( + (utilization, index) => + ({ + ...strategies[index], + depositCap: { + denom: utilization.vault.address, + used: BN(utilization.utilization.amount), + max: BN( + vaultConfigs.find((config) => config.addr === utilization.vault.address)?.deposit_cap + .amount || 0, + ), + }, + }) as HLSStrategy, + ), + ) +} diff --git a/src/components/HLS/AvailableHLSStakingAssets.tsx b/src/components/HLS/AvailableHLSStakingAssets.tsx new file mode 100644 index 00000000..f230a3fa --- /dev/null +++ b/src/components/HLS/AvailableHLSStakingAssets.tsx @@ -0,0 +1,3 @@ +export default function AvailableHlsStakingAssets() { + return null +} diff --git a/src/components/HLS/AvailableHLSVaults.tsx b/src/components/HLS/AvailableHLSVaults.tsx new file mode 100644 index 00000000..5b4fca4c --- /dev/null +++ b/src/components/HLS/AvailableHLSVaults.tsx @@ -0,0 +1,3 @@ +export default function AvailableHlsVaults() { + return null +} diff --git a/src/hooks/useHLSStakingAssets.ts b/src/hooks/useHLSStakingAssets.ts new file mode 100644 index 00000000..b7e55603 --- /dev/null +++ b/src/hooks/useHLSStakingAssets.ts @@ -0,0 +1,10 @@ +import useSWR from 'swr' + +import getHLSStakingAssets from 'api/hls/getHLSStakingAssets' + +export default function useHLSStakingAssets() { + return useSWR('hls-staking', getHLSStakingAssets, { + fallbackData: [], + revalidateOnFocus: false, + }) +} diff --git a/src/hooks/useHLSVaults.ts b/src/hooks/useHLSVaults.ts new file mode 100644 index 00000000..42106fc6 --- /dev/null +++ b/src/hooks/useHLSVaults.ts @@ -0,0 +1,10 @@ +import useSWR from 'swr' + +import getHLSVaults from 'api/hls/getHLSVaults' + +export default function useHLSVaults() { + return useSWR('hls-vaults', getHLSVaults, { + fallbackData: [], + revalidateOnFocus: false, + }) +} diff --git a/src/pages/HLSFarmPage.tsx b/src/pages/HLSFarmPage.tsx index d386ee30..9007be37 100644 --- a/src/pages/HLSFarmPage.tsx +++ b/src/pages/HLSFarmPage.tsx @@ -1,4 +1,5 @@ import Tab from 'components/Earn/Tab' +import AvailableHLSVaults from 'components/HLS/AvailableHLSVaults' import MigrationBanner from 'components/MigrationBanner' import { HLS_TABS } from 'constants/pages' @@ -7,6 +8,7 @@ export default function HLSFarmPage() {
+
) } diff --git a/src/pages/HLSStakingPage.tsx b/src/pages/HLSStakingPage.tsx index 1fc8e2d2..7721deda 100644 --- a/src/pages/HLSStakingPage.tsx +++ b/src/pages/HLSStakingPage.tsx @@ -1,4 +1,5 @@ import Tab from 'components/Earn/Tab' +import AvailableHlsStakingAssets from 'components/HLS/AvailableHLSStakingAssets' import MigrationBanner from 'components/MigrationBanner' import { HLS_TABS } from 'constants/pages' @@ -7,6 +8,7 @@ export default function HLSStakingPage() {
+
) } diff --git a/src/types/interfaces/asset.d.ts b/src/types/interfaces/asset.d.ts index ae3de69f..92924527 100644 --- a/src/types/interfaces/asset.d.ts +++ b/src/types/interfaces/asset.d.ts @@ -97,3 +97,17 @@ interface MarketTableData { marketLiquidityAmount: BigNumber marketLiquidationThreshold: number } + +interface HLSStrategy extends HLSStrategyNoCap { + depositCap: DepositCap +} + +interface HLSStrategyNoCap { + maxLTV: number + maxLeverage: number + apy: number | null + denoms: { + deposit: string + borrow: string + } +} diff --git a/src/utils/helpers.ts b/src/utils/helpers.ts index a6445eb3..a1434345 100644 --- a/src/utils/helpers.ts +++ b/src/utils/helpers.ts @@ -56,3 +56,7 @@ export function getValueFromBNCoins(coins: BNCoin[], prices: BNCoin[]): BigNumbe return totalValue } + +export function getLeverageFromLTV(ltv: number) { + return +(1 / (1 - ltv)).toPrecision(2) +} diff --git a/src/utils/resolvers.ts b/src/utils/resolvers.ts index cdb7e9c8..99c6698e 100644 --- a/src/utils/resolvers.ts +++ b/src/utils/resolvers.ts @@ -1,9 +1,10 @@ import { AssetParamsBaseForAddr as AssetParams, + AssetParamsBaseForAddr, TotalDepositResponse, } from 'types/generated/mars-params/MarsParams.types' import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types' -import { BN } from 'utils/helpers' +import { BN, getLeverageFromLTV } from 'utils/helpers' export function resolveMarketResponse( marketResponse: RedBankMarket, @@ -27,3 +28,43 @@ export function resolveMarketResponse( liquidationThreshold: Number(assetParamsResponse.liquidation_threshold), } } + +export function resolveHLSStrategies( + type: 'vault' | 'coin', + assets: AssetParamsBaseForAddr[], +): HLSStrategyNoCap[] { + const HLSStakingStrategies: HLSStrategyNoCap[] = [] + + assets.forEach((asset) => { + const correlations = asset.credit_manager.hls?.correlations.filter((correlation) => { + return type in correlation + }) + + let correlatedDenoms: string[] | undefined + + if (type === 'coin') { + correlatedDenoms = correlations + ?.map((correlation) => (correlation as { coin: { denom: string } }).coin.denom) + .filter((denoms) => !denoms.includes('gamm/pool/')) + } else { + correlatedDenoms = correlations?.map( + (correlation) => (correlation as { vault: { addr: string } }).vault.addr, + ) + } + + if (!correlatedDenoms?.length) return + + correlatedDenoms.forEach((correlatedDenom) => + HLSStakingStrategies.push({ + apy: null, + maxLeverage: getLeverageFromLTV(+asset.credit_manager.hls!.max_loan_to_value), + maxLTV: +asset.credit_manager.hls!.max_loan_to_value, + denoms: { + deposit: correlatedDenom, + borrow: asset.denom, + }, + }), + ) + }) + return HLSStakingStrategies +}