diff --git a/src/contexts/useYearn.tsx b/src/contexts/useYearn.tsx index 1a5c7464..195dbf9d 100755 --- a/src/contexts/useYearn.tsx +++ b/src/contexts/useYearn.tsx @@ -1,14 +1,14 @@ import {createContext, memo, useContext, useEffect} from 'react'; import {deserialize, serialize} from 'wagmi'; -import useWallet from '@builtbymom/web3/contexts/useWallet'; import {toAddress} from '@builtbymom/web3/utils'; import {useLocalStorageValue} from '@react-hookz/web'; -import {useYearnEarned} from '../hooks/useYearnEarned'; -import {useYearnPrices} from '../hooks/useYearnPrices'; -import {useYearnTokens} from '../hooks/useYearnTokens'; -import {useYearnVaults} from '../hooks/useYearnVaults'; +import {useFetchYearnEarnedForUser} from '../hooks/useFetchYearnEarnedForUser'; +import {useFetchYearnPrices} from '../hooks/useFetchYearnPrices'; +import {useFetchYearnTokens} from '../hooks/useFetchYearnTokens'; +import {useFetchYearnVaults} from '../hooks/useFetchYearnVaults'; import {Solver} from '../utils/schemas/yDaemonTokenListBalances'; +import {useYearnWallet} from './useYearnWallet'; import type {ReactElement} from 'react'; import type {KeyedMutator} from 'swr'; @@ -69,7 +69,7 @@ const YearnContext = createContext({ }); export const YearnContextApp = memo(function YearnContextApp({children}: {children: ReactElement}): ReactElement { - const {onRefresh} = useWallet(); + const {onRefresh} = useYearnWallet(); const {value: maxLoss, set: set_maxLoss} = useLocalStorageValue('yearn.fi/max-loss', { defaultValue: DEFAULT_MAX_LOSS, parse: (str: string, fallback: bigint): bigint => (str ? deserialize(str) : fallback), @@ -88,10 +88,10 @@ export const YearnContextApp = memo(function YearnContextApp({children}: {childr } ); - const prices = useYearnPrices(); - const tokens = useYearnTokens(); - const earned = useYearnEarned(); - const {vaults, vaultsMigrations, vaultsRetired, isLoading, mutate} = useYearnVaults(); + const prices = useFetchYearnPrices(); + const tokens = useFetchYearnTokens(); + const earned = useFetchYearnEarnedForUser(); + const {vaults, vaultsMigrations, vaultsRetired, isLoading, mutate} = useFetchYearnVaults(); useEffect(() => { const tokensToRefresh: TUseBalancesTokens[] = []; diff --git a/src/contexts/useYearnWallet.tsx b/src/contexts/useYearnWallet.tsx new file mode 100755 index 00000000..11cba4d9 --- /dev/null +++ b/src/contexts/useYearnWallet.tsx @@ -0,0 +1,308 @@ +import {createContext, memo, useCallback, useContext, useEffect, useMemo, useState} from 'react'; +import {useBalances} from '@builtbymom/web3/hooks/useBalances.multichains'; +import {toAddress, toNormalizedBN} from '@builtbymom/web3/utils'; +import {useDeepCompareMemo} from '@react-hookz/web'; +import {onLoadDone, onLoadStart} from '@yearn-finance/web-lib/contexts/useUI'; +import { + CRV_TOKEN_ADDRESS, + CVXCRV_TOKEN_ADDRESS, + ETH_TOKEN_ADDRESS, + LPYCRV_TOKEN_ADDRESS, + LPYCRV_V2_TOKEN_ADDRESS, + YCRV_CURVE_POOL_V2_ADDRESS, + YCRV_TOKEN_ADDRESS, + YVBOOST_TOKEN_ADDRESS, + YVECRV_TOKEN_ADDRESS +} from '@yearn-finance/web-lib/utils/constants'; + +import {useYearn} from './useYearn'; + +import type {ReactElement} from 'react'; +import type {TUseBalancesTokens} from '@builtbymom/web3/hooks/useBalances.multichains'; +import type {TAddress, TDict, TNormalizedBN} from '@builtbymom/web3/types'; +import type {TYChainTokens, TYToken} from '../types'; +import type {TYDaemonVault} from '../utils/schemas/yDaemonVaultsSchemas'; + +export type TWalletContext = { + getToken: ({address, chainID}: TTokenAndChain) => TYToken; + getBalance: ({address, chainID}: TTokenAndChain) => TNormalizedBN; + getPrice: ({address, chainID}: TTokenAndChain) => TNormalizedBN; + balances: TYChainTokens; + cumulatedValueInV2Vaults: number; + cumulatedValueInV3Vaults: number; + isLoading: boolean; + shouldUseForknetBalances: boolean; + onRefresh: (tokenList?: TUseBalancesTokens[]) => Promise; + triggerForknetBalances: () => void; +}; +type TTokenAndChain = {address: TAddress; chainID: number}; + +const defaultToken: TYToken = { + address: toAddress(''), + name: '', + symbol: '', + decimals: 18, + chainID: 1, + value: 0, + stakingValue: 0, + price: toNormalizedBN(0), + balance: toNormalizedBN(0), + supportedZaps: [] +}; + +const defaultProps = { + getToken: (): TYToken => defaultToken, + getBalance: (): TNormalizedBN => toNormalizedBN(0), + getPrice: (): TNormalizedBN => toNormalizedBN(0), + balances: {}, + cumulatedValueInV2Vaults: 0, + cumulatedValueInV3Vaults: 0, + isLoading: true, + shouldUseForknetBalances: false, + onRefresh: async (): Promise => ({}), + triggerForknetBalances: (): void => {} +}; + +function useYearnTokens({shouldUseForknetBalances}: {shouldUseForknetBalances: boolean}): TUseBalancesTokens[] { + const {vaults, vaultsMigrations, vaultsRetired, isLoadingVaultList} = useYearn(); + + const availableTokens = useMemo((): TUseBalancesTokens[] => { + if (isLoadingVaultList) { + return []; + } + const tokens: TUseBalancesTokens[] = []; + const tokensExists: TDict = {}; + const extraTokens: TUseBalancesTokens[] = []; + extraTokens.push( + ...[ + {chainID: 1, address: ETH_TOKEN_ADDRESS}, + {chainID: 10, address: ETH_TOKEN_ADDRESS}, + {chainID: 137, address: ETH_TOKEN_ADDRESS}, + {chainID: 250, address: ETH_TOKEN_ADDRESS}, + {chainID: 8453, address: ETH_TOKEN_ADDRESS}, + {chainID: 42161, address: ETH_TOKEN_ADDRESS}, + {chainID: 1, address: YCRV_TOKEN_ADDRESS}, + {chainID: 1, address: LPYCRV_TOKEN_ADDRESS}, + {chainID: 1, address: CRV_TOKEN_ADDRESS}, + {chainID: 1, address: YVBOOST_TOKEN_ADDRESS}, + {chainID: 1, address: YVECRV_TOKEN_ADDRESS}, + {chainID: 1, address: CVXCRV_TOKEN_ADDRESS}, + {chainID: 1, address: YCRV_CURVE_POOL_V2_ADDRESS}, + {chainID: 1, address: LPYCRV_V2_TOKEN_ADDRESS} + ] + ); + + for (const token of extraTokens) { + tokensExists[token.address] = true; + tokens.push(token); + } + + Object.values(vaults || {}).forEach((vault?: TYDaemonVault): void => { + if (!vault) { + return; + } + if (vault?.address && !tokensExists[toAddress(vault?.address)]) { + tokens.push({address: vault.address, chainID: vault.chainID}); + tokensExists[vault.address] = true; + } + if (vault?.token?.address && !tokensExists[toAddress(vault?.token?.address)]) { + tokens.push({address: vault.token.address, chainID: vault.chainID}); + tokensExists[vault.token.address] = true; + } + if (vault?.staking?.available && !tokensExists[toAddress(vault?.staking?.address)]) { + tokens.push({ + address: vault?.staking?.address, + chainID: vault.chainID, + symbol: vault.symbol, + decimals: vault.decimals, + name: vault.name + }); + tokensExists[vault?.staking?.address] = true; + } + }); + + return tokens; + }, [isLoadingVaultList, vaults]); + + //List all vaults with a possible migration + const migratableTokens = useMemo((): TUseBalancesTokens[] => { + const tokens: TUseBalancesTokens[] = []; + Object.values(vaultsMigrations || {}).forEach((vault?: TYDaemonVault): void => { + if (!vault) { + return; + } + tokens.push({address: vault.address, chainID: vault.chainID}); + }); + return tokens; + }, [vaultsMigrations]); + + const retiredTokens = useMemo((): TUseBalancesTokens[] => { + const tokens: TUseBalancesTokens[] = []; + Object.values(vaultsRetired || {}).forEach((vault?: TYDaemonVault): void => { + if (!vault) { + return; + } + tokens.push({address: vault.address, chainID: vault.chainID}); + }); + return tokens; + }, [vaultsRetired]); + + const allTokens = useMemo((): TUseBalancesTokens[] => { + const tokens = [...availableTokens, ...migratableTokens, ...retiredTokens]; + if (!shouldUseForknetBalances) { + return tokens; + } + for (const token of tokens) { + if (token.chainID === 1) { + //remove it + tokens.push({...token, chainID: 1337}); + } + } + return tokens; + }, [availableTokens, migratableTokens, retiredTokens, shouldUseForknetBalances]); + + return allTokens; +} + +function useYearnBalances({shouldUseForknetBalances}: {shouldUseForknetBalances: boolean}): { + tokens: TYChainTokens; + isLoading: boolean; + onRefresh: (tokenToUpdate?: TUseBalancesTokens[]) => Promise; +} { + const {prices} = useYearn(); + const allTokens = useYearnTokens({shouldUseForknetBalances}); + const {data: tokensRaw, onUpdate, onUpdateSome, isLoading} = useBalances({tokens: allTokens, prices}); + + const tokens = useDeepCompareMemo((): TYChainTokens => { + const _tokens = {...tokensRaw}; + if (shouldUseForknetBalances) { + _tokens[1] = _tokens[1337]; // eslint-disable-line prefer-destructuring + } + + return _tokens as TYChainTokens; + }, [tokensRaw, shouldUseForknetBalances]); + + const onRefresh = useCallback( + async (tokenToUpdate?: TUseBalancesTokens[]): Promise => { + if (tokenToUpdate) { + const updatedBalances = await onUpdateSome(tokenToUpdate); + return updatedBalances as TYChainTokens; + } + const updatedBalances = await onUpdate(); + return updatedBalances as TYChainTokens; + }, + [onUpdate, onUpdateSome] + ); + + useEffect((): void => { + if (isLoading) { + onLoadStart(); + } else { + onLoadDone(); + } + }, [isLoading]); + + return {tokens, isLoading, onRefresh}; +} + +/* 🔵 - Yearn Finance ********************************************************** + ** This context controls most of the user's wallet data we may need to + ** interact with our app, aka mostly the balances and the token prices. + ******************************************************************************/ +const YearnWalletContext = createContext(defaultProps); +export const YearnWalletContextApp = memo(function YearnWalletContextApp({ + children +}: { + children: ReactElement; +}): ReactElement { + const {vaults, prices, vaultsMigrations} = useYearn(); + const [shouldUseForknetBalances, set_shouldUseForknetBalances] = useState(false); + const {tokens, isLoading, onRefresh} = useYearnBalances({shouldUseForknetBalances}); + + const [cumulatedValueInV2Vaults, cumulatedValueInV3Vaults] = useMemo((): [number, number] => { + let cumulatedValueInV2Vaults = 0; + let cumulatedValueInV3Vaults = 0; + for (const [, perChain] of Object.entries(tokens)) { + for (const [tokenAddress, tokenData] of Object.entries(perChain)) { + if (tokenData.value + tokenData.stakingValue === 0) { + continue; + } + if (vaults?.[toAddress(tokenAddress)]) { + if (vaults[toAddress(tokenAddress)].version.split('.')?.[0] === '3') { + cumulatedValueInV3Vaults += tokenData.value + tokenData.stakingValue; + } else { + cumulatedValueInV2Vaults += tokenData.value + tokenData.stakingValue; + } + } else if (vaultsMigrations?.[toAddress(tokenAddress)]) { + if (vaultsMigrations[toAddress(tokenAddress)].version.split('.')?.[0] === '3') { + cumulatedValueInV3Vaults += tokenData.value + tokenData.stakingValue; + } else { + cumulatedValueInV2Vaults += tokenData.value + tokenData.stakingValue; + } + } + } + } + return [cumulatedValueInV2Vaults, cumulatedValueInV3Vaults]; + }, [vaults, vaultsMigrations, tokens]); + + const getToken = useCallback( + ({address, chainID}: TTokenAndChain): TYToken => tokens?.[chainID || 1]?.[address] || defaultToken, + [tokens] + ); + const getBalance = useCallback( + ({address, chainID}: TTokenAndChain): TNormalizedBN => + tokens?.[chainID || 1]?.[address]?.balance || toNormalizedBN(0), + [tokens] + ); + const getPrice = useCallback( + ({address, chainID}: TTokenAndChain): TNormalizedBN => { + const price = tokens?.[chainID || 1]?.[address]?.price; + if (!price) { + return toNormalizedBN(prices?.[chainID]?.[address] || 0, 6) || toNormalizedBN(0); + } + return price; + }, + [prices, tokens] + ); + + /* 🔵 - Yearn Finance ****************************************************** + ** Setup and render the Context provider to use in the app. + ***************************************************************************/ + const contextValue = useMemo( + (): TWalletContext => ({ + getToken, + getBalance, + getPrice, + balances: tokens, + cumulatedValueInV2Vaults, + cumulatedValueInV3Vaults, + isLoading: isLoading || false, + shouldUseForknetBalances, + onRefresh, + triggerForknetBalances: (): void => + set_shouldUseForknetBalances((s): boolean => { + const isEnabled = !s; + if (!(window as any).ethereum) { + (window as any).ethereum = {}; + } + (window as any).ethereum.useForknetForMainnet = isEnabled; + return isEnabled; + }) + }), + [ + getToken, + getBalance, + getPrice, + tokens, + cumulatedValueInV2Vaults, + cumulatedValueInV3Vaults, + isLoading, + shouldUseForknetBalances, + onRefresh + ] + ); + + return {children}; +}); + +export const useYearnWallet = (): TWalletContext => useContext(YearnWalletContext); diff --git a/src/hooks/useYearnEarned.ts b/src/hooks/useFetchYearnEarnedForUser.ts similarity index 62% rename from src/hooks/useYearnEarned.ts rename to src/hooks/useFetchYearnEarnedForUser.ts index fc98edc5..c1deb78a 100644 --- a/src/hooks/useYearnEarned.ts +++ b/src/hooks/useFetchYearnEarnedForUser.ts @@ -2,12 +2,17 @@ import {useWeb3} from '@builtbymom/web3/contexts/useWeb3'; import {useFetch} from '@builtbymom/web3/hooks/useFetch'; import {useDeepCompareMemo} from '@react-hookz/web'; -import {useYDaemonBaseURI} from '../hooks/useYDaemonBaseURI'; import {yDaemonEarnedSchema} from '../utils/schemas/yDaemonEarnedSchema'; +import {useYDaemonBaseURI} from './useYDaemonBaseURI'; import type {TYDaemonEarned} from '../utils/schemas/yDaemonEarnedSchema'; -function useYearnEarned(): TYDaemonEarned { +/****************************************************************************** + ** The useFetchYearnEarnedForUser hook is used to fetch an estimate of the + ** amount earned by the user. This estimate is calculated by the yDaemon API + ** based on the events emitted by the yearn contracts catched by the subgraph. + *****************************************************************************/ +function useFetchYearnEarnedForUser(): TYDaemonEarned { const {address} = useWeb3(); const {yDaemonBaseUri: yDaemonBaseUriWithoutChain} = useYDaemonBaseURI(); @@ -34,4 +39,4 @@ function useYearnEarned(): TYDaemonEarned { return memorizedEarned; } -export {useYearnEarned}; +export {useFetchYearnEarnedForUser}; diff --git a/src/hooks/useYearnPrices.ts b/src/hooks/useFetchYearnPrices.ts similarity index 58% rename from src/hooks/useYearnPrices.ts rename to src/hooks/useFetchYearnPrices.ts index 6b701e89..6c9ac93b 100644 --- a/src/hooks/useYearnPrices.ts +++ b/src/hooks/useFetchYearnPrices.ts @@ -1,12 +1,17 @@ import {useFetch} from '@builtbymom/web3/hooks/useFetch'; import {useDeepCompareMemo} from '@react-hookz/web'; -import {useYDaemonBaseURI} from '../hooks/useYDaemonBaseURI'; import {yDaemonPricesChainSchema} from '../utils/schemas/yDaemonPricesSchema'; +import {useYDaemonBaseURI} from './useYDaemonBaseURI'; import type {TYDaemonPricesChain} from '../utils/schemas/yDaemonPricesSchema'; -function useYearnPrices(): TYDaemonPricesChain { +/****************************************************************************** + ** The useFetchYearnPrices hook is used to fetch the prices of the tokens from + ** the yDaemon API. It returns an object with the prices of the tokens, + ** splitted by chain. + *****************************************************************************/ +function useFetchYearnPrices(): TYDaemonPricesChain { const {yDaemonBaseUri: yDaemonBaseUriWithoutChain} = useYDaemonBaseURI(); const {data: prices} = useFetch({ endpoint: `${yDaemonBaseUriWithoutChain}/prices/all`, @@ -23,4 +28,4 @@ function useYearnPrices(): TYDaemonPricesChain { return pricesUpdated; } -export {useYearnPrices}; +export {useFetchYearnPrices}; diff --git a/src/hooks/useYearnTokens.ts b/src/hooks/useFetchYearnTokens.ts similarity index 72% rename from src/hooks/useYearnTokens.ts rename to src/hooks/useFetchYearnTokens.ts index 74be8e04..3710c48f 100644 --- a/src/hooks/useYearnTokens.ts +++ b/src/hooks/useFetchYearnTokens.ts @@ -2,13 +2,17 @@ import {useFetch} from '@builtbymom/web3/hooks/useFetch'; import {toAddress} from '@builtbymom/web3/utils'; import {useDeepCompareMemo} from '@react-hookz/web'; -import {useYDaemonBaseURI} from '../hooks/useYDaemonBaseURI'; import {yDaemonTokensChainSchema} from '../utils/schemas/yDaemonTokensSchema'; +import {useYDaemonBaseURI} from './useYDaemonBaseURI'; import type {TNDict} from '@builtbymom/web3/types'; import type {TYDaemonTokens, TYDaemonTokensChain} from '../utils/schemas/yDaemonTokensSchema'; -function useYearnTokens(): TYDaemonTokens { +/****************************************************************************** + ** The useFetchYearnTokens hook is used to fetch the tokens from the yDaemon + ** API. It returns an object with the tokens, splitted by chain. + *****************************************************************************/ +function useFetchYearnTokens(): TYDaemonTokens { const {yDaemonBaseUri: yDaemonBaseUriWithoutChain} = useYDaemonBaseURI(); const {data: tokens} = useFetch({ endpoint: `${yDaemonBaseUriWithoutChain}/tokens/all`, @@ -37,4 +41,4 @@ function useYearnTokens(): TYDaemonTokens { return tokensUpdated; } -export {useYearnTokens}; +export {useFetchYearnTokens}; diff --git a/src/hooks/useYearnVaults.ts b/src/hooks/useFetchYearnVaults.ts similarity index 84% rename from src/hooks/useYearnVaults.ts rename to src/hooks/useFetchYearnVaults.ts index d28cd1b1..c9a7eb83 100644 --- a/src/hooks/useYearnVaults.ts +++ b/src/hooks/useFetchYearnVaults.ts @@ -2,14 +2,22 @@ import {useFetch} from '@builtbymom/web3/hooks/useFetch'; import {toAddress} from '@builtbymom/web3/utils'; import {useDeepCompareMemo} from '@react-hookz/web'; -import {useYDaemonBaseURI} from '../hooks/useYDaemonBaseURI'; import {yDaemonVaultsSchema} from '../utils/schemas/yDaemonVaultsSchemas'; +import {useYDaemonBaseURI} from './useYDaemonBaseURI'; import type {KeyedMutator} from 'swr'; import type {TDict} from '@builtbymom/web3/types'; import type {TYDaemonVault, TYDaemonVaults} from '../utils/schemas/yDaemonVaultsSchemas'; -function useYearnVaults(): { +/****************************************************************************** + ** The useFetchYearnVaults hook is used to fetch the vaults from the yDaemon + ** API. + ** It will fetch 3 kinds of vaults: + ** - The active vaults + ** - The vaults that are in the migration process + ** - The retired vaults + *****************************************************************************/ +function useFetchYearnVaults(): { vaults: TDict; vaultsMigrations: TDict; vaultsRetired: TDict; @@ -91,4 +99,4 @@ function useYearnVaults(): { }; } -export {useYearnVaults}; +export {useFetchYearnVaults}; diff --git a/src/hooks/useWhyDidYouUpdate.ts b/src/hooks/useWhyDidYouUpdate.ts deleted file mode 100644 index 209d76c1..00000000 --- a/src/hooks/useWhyDidYouUpdate.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import {useEffect, useRef} from 'react'; - -export function useWhyDidYouUpdate(name: string, props: any): void { - // Get a mutable ref object where we can store props ... - // ... for comparison next time this hook runs. - const previousProps = useRef(); - - useEffect((): void => { - if (previousProps.current) { - // Get all keys from previous and current props - const allKeys = Object.keys({...previousProps.current, ...props}); - // Use this object to keep track of changed props - const changesObj: any = {}; - // Iterate through keys - allKeys.forEach((key): void => { - // If previous is different from current - if (previousProps.current[key] !== props[key]) { - // Add to changesObj - changesObj[key] = { - from: previousProps.current[key], - to: props[key] - }; - } - }); - - // If changesObj not empty then output to console - if (Object.keys(changesObj).length) { - console.log('[why-did-you-update]', name, changesObj); - } - } - - // Finally update previousProps with current props for next hook call - previousProps.current = props; - }); -} diff --git a/src/hooks/useYDaemonStatus.ts b/src/hooks/useYDaemonStatus.ts new file mode 100644 index 00000000..d1caf40e --- /dev/null +++ b/src/hooks/useYDaemonStatus.ts @@ -0,0 +1,27 @@ +import useSWR from 'swr'; +import {baseFetcher} from '@yearn-finance/web-lib/utils/fetchers'; + +import {useYDaemonBaseURI} from './useYDaemonBaseURI'; + +import type {SWRResponse} from 'swr'; + +type TProps = { + chainID: number | string; +}; + +/****************************************************************************** + ** The useYDaemonStatus hook is used to fetch the status of the yDaemon API. + ** It returns an object with the status of the API. + *****************************************************************************/ +export function useYDaemonStatus({chainID}: TProps): SWRResponse | null { + const {yDaemonBaseUri} = useYDaemonBaseURI({chainID}); + const result = useSWR(`${yDaemonBaseUri}/status`, baseFetcher, { + revalidateOnFocus: false + }); + + if (!result.data || result.isLoading) { + return result; + } + + return result; +} diff --git a/src/hooks/useYearnBalance.ts b/src/hooks/useYearnBalance.ts new file mode 100644 index 00000000..fde30410 --- /dev/null +++ b/src/hooks/useYearnBalance.ts @@ -0,0 +1,22 @@ +import {toAddress} from '@builtbymom/web3/utils'; + +import {useYearnWallet} from '../contexts/useYearnWallet'; + +import type {TAddress, TDict, TNormalizedBN} from '@builtbymom/web3/types'; + +/****************************************************************************** + ** The useYearnBalance hook is used to retrieve the balance of a token from + ** the useYearnWallet context. + *****************************************************************************/ +export function useYearnBalance({ + address, + chainID +}: { + address: string | TAddress; + chainID: number; + source?: TDict; +}): TNormalizedBN { + const {getBalance} = useYearnWallet(); + + return getBalance({address: toAddress(address), chainID: chainID}); +} diff --git a/src/hooks/useYearnToken.ts b/src/hooks/useYearnToken.ts new file mode 100644 index 00000000..bdcdb969 --- /dev/null +++ b/src/hooks/useYearnToken.ts @@ -0,0 +1,21 @@ +import {useMemo} from 'react'; +import {toAddress} from '@builtbymom/web3/utils'; + +import {useYearnWallet} from '../contexts/useYearnWallet'; + +import type {TAddress} from '@builtbymom/web3/types'; +import type {TYToken} from '../types'; + +/****************************************************************************** + ** The useYearnToken hook is used to retrieve the token from the useWallet + ** context. The token is returned as a TYToken. + *****************************************************************************/ +export function useYearnToken({address, chainID}: {address: string | TAddress; chainID: number}): TYToken { + const {getToken} = useYearnWallet(); + + const balance = useMemo((): TYToken => { + return getToken({address: toAddress(address), chainID: chainID}); + }, [getToken, address, chainID]); + + return balance; +} diff --git a/src/hooks/useYearnTokenPrice.ts b/src/hooks/useYearnTokenPrice.ts new file mode 100644 index 00000000..99755416 --- /dev/null +++ b/src/hooks/useYearnTokenPrice.ts @@ -0,0 +1,22 @@ +import {useMemo} from 'react'; +import {toBigInt, toNormalizedValue} from '@builtbymom/web3/utils'; + +import {useYearn} from '../contexts/useYearn'; + +import type {TAddress} from '@builtbymom/web3/types'; + +/****************************************************************************** + ** The useYearnTokenPrice hook is used to retrieve the price of a token from + ** the useYearn context. The price is returned as a normalizedValue with a + ** fallback of 0. + *****************************************************************************/ +export function useYearnTokenPrice({address, chainID}: {address: TAddress; chainID: number}): number { + const {prices} = useYearn(); + + const tokenPrice = useMemo( + (): number => toNormalizedValue(toBigInt(prices?.[chainID]?.[address] || 0), 6), + [address, prices, chainID] + ); + + return tokenPrice; +} diff --git a/src/types/index.ts b/src/types/index.ts index ffbe7703..79b9c3d0 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,6 +1,6 @@ import type {ReactElement} from 'react'; -import type {TAddress} from '@builtbymom/web3/types'; -import type {TSolver} from '../utils/schemas/yDaemonTokenListBalances'; +import type {TAddress, TDict, TNDict, TToken} from '@builtbymom/web3/types'; +import type {TSolver, TSupportedZaps} from '../utils/schemas/yDaemonTokenListBalances'; export type TDropdownOption = { label: string; @@ -42,6 +42,7 @@ export type TDropdownGaugeOption = { APY: number; }; }; + export type TDropdownGaugeProps = { options: TDropdownGaugeOption[]; selected?: TDropdownGaugeOption; @@ -63,3 +64,9 @@ export type TMessariGraphData = { tvl: number; pps: number; }; + +export type TYToken = TToken & { + supportedZaps: TSupportedZaps[]; + stakingValue: number; +}; +export type TYChainTokens = TNDict>;