import { useCallback, useEffect, useMemo, useState } from 'react' import usePrices from 'hooks/usePrices' import useAssetParams from 'hooks/useAssetParams' import { AssetParamsBaseForAddr, HealthComputer, } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types' import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types' import useVaultConfigs from 'hooks/useVaultConfigs' import { BorrowTarget, compute_health_js, max_borrow_estimate_js, max_swap_estimate_js, max_withdraw_estimate_js, SwapKind, } from 'utils/health_computer' import { convertAccountToPositions } from 'utils/accounts' import { Positions, VaultPositionValue, } from 'types/generated/mars-credit-manager/MarsCreditManager.types' import useStore from 'store' import { BN_ZERO } from 'constants/math' import { BN } from 'utils/helpers' export default function useHealthComputer(account?: Account) { const { data: prices } = usePrices() const { data: assetParams } = useAssetParams() const { data: vaultConfigs } = useVaultConfigs() const baseCurrency = useStore((s) => s.baseCurrency) const [health, setHealth] = useState(0) const positions: Positions | null = useMemo(() => { if (!account) return null return convertAccountToPositions(account) }, [account]) const baseCurrencyPrice = useMemo( () => prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0, [prices, baseCurrency.denom], ) const vaultPositionValues = useMemo(() => { if (!account?.vaults) return null return account.vaults.reduce( (prev, curr) => { const baseCoinPrice = prices.find((price) => price.denom === curr.denoms.lp)?.amount || 0 prev[curr.address] = { base_coin: { amount: '0', // Not used by healthcomputer denom: curr.denoms.lp, value: curr.amounts.unlocking.times(baseCoinPrice).integerValue().toString(), }, vault_coin: { amount: '0', // Not used by healthcomputer denom: curr.denoms.vault, value: curr.values.primary .div(baseCurrencyPrice) .plus(curr.values.secondary.div(baseCurrencyPrice)) .integerValue() .toString(), }, } return prev }, {} as { [key: string]: VaultPositionValue }, ) }, [account?.vaults, prices, baseCurrencyPrice]) const priceData = useMemo(() => { const baseCurrencyPrice = prices.find((price) => price.denom === baseCurrency.denom)?.amount || 0 return prices.reduce( (prev, curr) => { prev[curr.denom] = curr.amount.div(baseCurrencyPrice).decimalPlaces(18).toString() return prev }, {} as { [key: string]: string }, ) }, [prices, baseCurrency.denom]) const denomsData = useMemo( () => assetParams.reduce( (prev, curr) => { prev[curr.denom] = curr return prev }, {} as { [key: string]: AssetParamsBaseForAddr }, ), [assetParams], ) const vaultConfigsData = useMemo(() => { if (!positions || !vaultConfigs.length) return null const vaultPositionDenoms = positions.vaults.map((vault) => vault.vault.address) return vaultConfigs .filter((config) => vaultPositionDenoms.includes(config.addr)) .reduce( (prev, curr) => { prev[curr.addr] = curr return prev }, {} as { [key: string]: VaultConfigBaseForString }, ) }, [vaultConfigs, positions]) const healthComputer: HealthComputer | null = useMemo(() => { if ( !positions || !vaultPositionValues || !vaultConfigsData || Object.keys(denomsData).length === 0 || Object.keys(priceData).length === 0 || positions.vaults.length !== Object.keys(vaultPositionValues).length ) return null return { denoms_data: { params: denomsData, prices: priceData }, vaults_data: { vault_configs: vaultConfigsData, vault_values: vaultPositionValues, }, positions: positions, kind: 'default', } }, [priceData, denomsData, vaultConfigsData, vaultPositionValues, positions]) useEffect(() => { if (!healthComputer) return setHealth(Number(compute_health_js(healthComputer).max_ltv_health_factor) || 0) }, [healthComputer]) const computeMaxBorrowAmount = useCallback( (denom: string, target: BorrowTarget) => { if (!healthComputer) return BN_ZERO return BN(max_borrow_estimate_js(healthComputer, denom, target)) }, [healthComputer], ) const computeMaxWithdrawAmount = useCallback( (denom: string) => { if (!healthComputer) return BN_ZERO return BN(max_withdraw_estimate_js(healthComputer, denom)) }, [healthComputer], ) const computeMaxSwapAmount = useCallback( (from: string, to: string, kind: SwapKind) => { if (!healthComputer) return BN_ZERO try { return BN(max_swap_estimate_js(healthComputer, from, to, kind)) } catch { return BN_ZERO } }, [healthComputer], ) return { health, computeMaxBorrowAmount, computeMaxWithdrawAmount, computeMaxSwapAmount } }