diff --git a/apps/main/src/app/crvusd/client.tsx b/apps/main/src/app/crvusd/client.tsx index 58c27dbf7..0a7e959fa 100644 --- a/apps/main/src/app/crvusd/client.tsx +++ b/apps/main/src/app/crvusd/client.tsx @@ -30,7 +30,6 @@ export function CrvUsdClientLayout({ children, serverData }: { children: ReactNo const chainId = networksIdMapper[networkId] const { llamaApi: curve = null } = useConnection() const isPageVisible = useLayoutStore((state) => state.isPageVisible) - const fetchAllStoredUsdRates = useStore((state) => state.usdRates.fetchAllStoredUsdRates) const fetchGasInfo = useStore((state) => state.gas.fetchGasInfo) const hydrate = useStore((s) => s.hydrate) @@ -39,7 +38,6 @@ export function CrvUsdClientLayout({ children, serverData }: { children: ReactNo usePageVisibleInterval( () => { if (isPageVisible && curve) { - void fetchAllStoredUsdRates(curve) void fetchGasInfo(curve) } }, diff --git a/apps/main/src/app/dao/layout.tsx b/apps/main/src/app/dao/layout.tsx index 25ef06856..1efa7e87d 100644 --- a/apps/main/src/app/dao/layout.tsx +++ b/apps/main/src/app/dao/layout.tsx @@ -5,7 +5,6 @@ import { type ReactNode } from 'react' import networks, { networksIdMapper } from '@/dao/networks' import useStore from '@/dao/store/useStore' import { type UrlParams } from '@/dao/types/dao.types' -import { useConnection } from '@ui-kit/features/connect-wallet' import { useLayoutStore } from '@ui-kit/features/layout' import { useHydration } from '@ui-kit/hooks/useHydration' import usePageVisibleInterval from '@ui-kit/hooks/usePageVisibleInterval' @@ -13,13 +12,11 @@ import { useRedirectToEth } from '@ui-kit/hooks/useRedirectToEth' import { REFRESH_INTERVAL } from '@ui-kit/lib/model' const useAutoRefresh = (isHydrated: boolean) => { - const { curveApi } = useConnection() - const fetchAllStoredUsdRates = useStore((state) => state.usdRates.fetchAllStoredUsdRates) const isPageVisible = useLayoutStore((state) => state.isPageVisible) const getGauges = useStore((state) => state.gauges.getGauges) const getGaugesData = useStore((state) => state.gauges.getGaugesData) usePageVisibleInterval( - () => Promise.all([curveApi && fetchAllStoredUsdRates(curveApi), getGauges(), getGaugesData()]), + () => Promise.all([getGauges(), getGaugesData()]), REFRESH_INTERVAL['5m'], isPageVisible && isHydrated, ) diff --git a/apps/main/src/app/dex/layout.tsx b/apps/main/src/app/dex/layout.tsx index bbedae5e0..7ba04e673 100644 --- a/apps/main/src/app/dex/layout.tsx +++ b/apps/main/src/app/dex/layout.tsx @@ -26,7 +26,6 @@ const useAutoRefresh = (networkDef: NetworkDef) => { const fetchPoolsTvl = useStore((state) => state.pools.fetchPoolsTvl) const setTokensMapper = useStore((state) => state.tokens.setTokensMapper) const fetchGasInfo = useStore((state) => state.gas.fetchGasInfo) - const fetchAllStoredUsdRates = useStore((state) => state.usdRates.fetchAllStoredUsdRates) const fetchAllStoredBalances = useStore((state) => state.userBalances.fetchAllStoredBalances) const network = useStore((state) => state.networks.networks[networkDef.chainId]) @@ -50,7 +49,6 @@ const useAutoRefresh = (networkDef: NetworkDef) => { () => { if (curveApi) { void fetchGasInfo(curveApi) - void fetchAllStoredUsdRates(curveApi) void fetchPoolsVolumeTvl(curveApi) if (curveApi.signerAddress) { diff --git a/apps/main/src/dao/components/DetailInfoEstGas.tsx b/apps/main/src/dao/components/DetailInfoEstGas.tsx index b77249008..ae708189f 100644 --- a/apps/main/src/dao/components/DetailInfoEstGas.tsx +++ b/apps/main/src/dao/components/DetailInfoEstGas.tsx @@ -10,6 +10,7 @@ import DetailInfo from '@ui/DetailInfo' import IconTooltip from '@ui/Tooltip/TooltipIcon' import { FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { gweiToEther, weiToGwei } from '@ui-kit/utils' export type StepProgress = { @@ -34,7 +35,7 @@ const DetailInfoEstGas = ({ stepProgress?: StepProgress | null }) => { const { gasPricesDefault } = networks[chainId] - const chainTokenUsdRate = useStore((state) => state.usdRates.usdRatesMapper[ethAddress]) + const { data: chainTokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: ethAddress }) const gasInfo = useStore((state) => state.gas.gasInfo) const basePlusPriority = useStore((state) => state.gas.gasInfo?.basePlusPriority?.[gasPricesDefault]) diff --git a/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx b/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx index 2169818db..9fc80dde3 100644 --- a/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx +++ b/apps/main/src/dao/components/PageAnalytics/CrvStats/index.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components' import MetricsComp, { MetricsColumnData } from '@/dao/components/MetricsComp' +import { CONTRACT_CRV } from '@/dao/constants' import { useStatsVecrvQuery } from '@/dao/entities/stats-vecrv' import useStore from '@/dao/store/useStore' import Box from '@ui/Box' @@ -7,6 +8,7 @@ import Tooltip from '@ui/Tooltip' import { formatNumber } from '@ui/utils' import { useConnection, useWallet } from '@ui-kit/features/connect-wallet' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { Chain } from '@ui-kit/utils/network' const CrvStats = () => { @@ -15,15 +17,13 @@ const CrvStats = () => { const { curveApi: { chainId } = {} } = useConnection() const veCrvFees = useStore((state) => state.analytics.veCrvFees) const veCrvHolders = useStore((state) => state.analytics.veCrvHolders) - const usdRatesLoading = useStore((state) => state.usdRates.loading) - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) - const crv = usdRatesMapper.crv + const { data: crv, isFetching: isLoadingCrv } = useTokenUsdRate({ chainId, tokenAddress: CONTRACT_CRV }) // protect against trying to load data on non-mainnet networks const notMainnet = chainId !== Chain.Ethereum const noProvider = !provider || notMainnet const veCrvFeesLoading = veCrvFees.fetchStatus === 'LOADING' - const aprLoading = statsLoading || veCrvFeesLoading || usdRatesLoading || !crv + const aprLoading = statsLoading || veCrvFeesLoading || isLoadingCrv || crv == null const veCrvApr = aprLoading || notMainnet || !statsSuccess diff --git a/apps/main/src/dao/hooks/useEstimateGasConversion.tsx b/apps/main/src/dao/hooks/useEstimateGasConversion.tsx index ac8f8a175..a8a5df268 100644 --- a/apps/main/src/dao/hooks/useEstimateGasConversion.tsx +++ b/apps/main/src/dao/hooks/useEstimateGasConversion.tsx @@ -1,14 +1,16 @@ import { useMemo } from 'react' +import { ethAddress } from 'viem' +import { useChainId } from 'wagmi' import networks from '@/dao/networks' import useStore from '@/dao/store/useStore' import { BN, formatNumber } from '@ui/utils' -import { requireLib } from '@ui-kit/features/connect-wallet' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { gweiToEther, weiToGwei } from '@ui-kit/utils' const useEstimateGasConversion = (gas: number | null | undefined) => { - const curve = requireLib('curveApi') - const chainId = curve?.chainId - const chainTokenUsdRate = useStore().usdRates.usdRatesMapper['0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'] + const chainId = useChainId() + const { data: chainTokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: ethAddress }) + const gasPricesDefault = chainId && networks[chainId].gasPricesDefault const basePlusPriorities = useStore().gas.gasInfo?.basePlusPriority @@ -16,8 +18,6 @@ const useEstimateGasConversion = (gas: number | null | undefined) => { const basePlusPriority = basePlusPriorities && typeof gasPricesDefault !== 'undefined' && basePlusPriorities[gasPricesDefault] - if (!curve || !chainId) return { estGasCost: undefined, estGasCostUsd: undefined, tooltip: undefined } - if (!basePlusPriority || !gas) return { estGasCost: undefined, estGasCostUsd: undefined, tooltip: undefined } const { symbol, gasPricesUnit } = networks[chainId] @@ -30,7 +30,7 @@ const useEstimateGasConversion = (gas: number | null | undefined) => { const tooltip = `${formatNumber(estGasCost.toString())} ${symbol} at ${gasAmountUnit} ${gasPricesUnit}` return { estGasCost: estGasCost.toString(), estGasCostUsd, tooltip } } - }, [gas, curve, chainId, chainTokenUsdRate, gasPricesDefault, basePlusPriorities]) + }, [gas, chainId, chainTokenUsdRate, gasPricesDefault, basePlusPriorities]) } export default useEstimateGasConversion diff --git a/apps/main/src/dao/lib/curvejs.ts b/apps/main/src/dao/lib/curvejs.ts index 47ad9bdfc..3be621a1a 100644 --- a/apps/main/src/dao/lib/curvejs.ts +++ b/apps/main/src/dao/lib/curvejs.ts @@ -1,8 +1,7 @@ import type { FormType as LockFormType } from '@/dao/components/PageVeCrv/types' -import { CurveApi, Provider, EstimatedGas, UsdRatesMapper, ClaimButtonsKey } from '@/dao/types/dao.types' +import { CurveApi, Provider, EstimatedGas, ClaimButtonsKey } from '@/dao/types/dao.types' import { getErrorMessage } from '@/dao/utils' import type { DateValue } from '@internationalized/date' -import PromisePool from '@supercharge/promise-pool/dist' import { log } from '@ui-kit/lib' import dayjs from '@ui-kit/lib/dayjs' import { waitForTransaction, waitForTransactions } from '@ui-kit/lib/ethers' @@ -10,21 +9,6 @@ import { waitForTransaction, waitForTransactions } from '@ui-kit/lib/ethers' export const helpers = { waitForTransaction, waitForTransactions, - fetchUsdRates: async (curve: CurveApi, tokenAddresses: string[]) => { - log('fetchUsdRates', tokenAddresses.length) - const results: UsdRatesMapper = {} - - await PromisePool.for(tokenAddresses) - .withConcurrency(5) - .handleError((error, tokenAddress) => { - console.error(`Unable to get usd rate for ${tokenAddress}`, error) - results[tokenAddress] = NaN - }) - .process(async (tokenAddress) => { - results[tokenAddress] = await curve.getUsdRate(tokenAddress) - }) - return results - }, } const lockCrv = { diff --git a/apps/main/src/dao/store/createAppSlice.ts b/apps/main/src/dao/store/createAppSlice.ts index 8b54669d5..d47204c07 100644 --- a/apps/main/src/dao/store/createAppSlice.ts +++ b/apps/main/src/dao/store/createAppSlice.ts @@ -45,12 +45,11 @@ const createAppSlice = (set: SetState, get: GetState): AppSlice => isNetworkSwitched, }) - const { usdRates, user, gas, gauges } = get() + const { user, gas, gauges } = get() if (isNetworkSwitched) gas.resetState() await Promise.all([ api && isNetworkSwitched && gas.fetchGasInfo(api), api && wallet?.provider && user.updateUserData(api, wallet), - api && usdRates.fetchAllStoredUsdRates(api), gauges.getGauges(), gauges.getGaugesData(), ]) diff --git a/apps/main/src/dao/store/createUsdRatesSlice.ts b/apps/main/src/dao/store/createUsdRatesSlice.ts deleted file mode 100644 index c22b0ade1..000000000 --- a/apps/main/src/dao/store/createUsdRatesSlice.ts +++ /dev/null @@ -1,92 +0,0 @@ -import cloneDeep from 'lodash/cloneDeep' -import { ethAddress } from 'viem' -import type { GetState, SetState } from 'zustand' -import curvejsApi from '@/dao/lib/curvejs' -import type { State } from '@/dao/store/useStore' -import { CurveApi, UsdRatesMapper } from '@/dao/types/dao.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - usdRatesMapper: UsdRatesMapper - loading: boolean -} - -const sliceKey = 'usdRates' - -// prettier-ignore -export type UsdRatesSlice = { - [sliceKey]: SliceState & { - fetchUsdRateByToken(curve: CurveApi | null, tokenAddress: string): Promise - fetchUsdRateByTokens(curve: CurveApi | null, tokenAddresses: string[], shouldRefetch?: boolean): Promise - fetchAllStoredUsdRates(curve: CurveApi): Promise - - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - usdRatesMapper: { - [ethAddress]: 0, - crv: 0, - }, - loading: true, -} - -const createUsdRatesSlice = (set: SetState, get: GetState): UsdRatesSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - - fetchUsdRateByToken: async (curve, tokenAddress) => { - if (!curve) return undefined - - const resp = await get()[sliceKey].fetchUsdRateByTokens(curve, [tokenAddress], true) - return resp[tokenAddress] - }, - fetchUsdRateByTokens: async (curve, tokenAddresses, shouldRefetch) => { - const state = get() - const sliceState = state[sliceKey] - - const usdRatesMapper = sliceState.usdRatesMapper - - if (!curve) return usdRatesMapper - - const missing = shouldRefetch - ? tokenAddresses - : tokenAddresses.filter((t) => typeof usdRatesMapper[t] === 'undefined') - - if (missing.length > 0) { - sliceState.setStateByKey('loading', true) - const fetchedUsdRatesMapper = await curvejsApi.helpers.fetchUsdRates(curve, missing) - sliceState.setStateByKeys({ - usdRatesMapper: { ...usdRatesMapper, ...fetchedUsdRatesMapper }, - loading: false, - }) - } - - return get()[sliceKey].usdRatesMapper - }, - fetchAllStoredUsdRates: async (curve) => { - const tokenAddresses = Object.keys(get().usdRates.usdRatesMapper) - await get().usdRates.fetchUsdRateByTokens(curve, tokenAddresses, true) - }, - - setStateByActiveKey: (key: StateKey, activeKey: string, value: T) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key: StateKey, value: T) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState: Partial) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createUsdRatesSlice diff --git a/apps/main/src/dao/store/useStore.ts b/apps/main/src/dao/store/useStore.ts index 1e59b11f1..9e24d326f 100644 --- a/apps/main/src/dao/store/useStore.ts +++ b/apps/main/src/dao/store/useStore.ts @@ -8,16 +8,8 @@ import createGaugesSlice, { GaugesSlice } from '@/dao/store/createGaugesSlice' import createProposalsSlice, { ProposalsSlice } from '@/dao/store/createProposalsSlice' import createUserSlice, { UserSlice } from '@/dao/store/createUserSlice' import createLockedCrvSlice, { LockedCrvSlice } from './createLockedCrvSlice' -import createUsdRatesSlice, { UsdRatesSlice } from './createUsdRatesSlice' -export type State = AppSlice & - GasSlice & - ProposalsSlice & - UserSlice & - GaugesSlice & - AnalyticsSlice & - LockedCrvSlice & - UsdRatesSlice +export type State = AppSlice & GasSlice & ProposalsSlice & UserSlice & GaugesSlice & AnalyticsSlice & LockedCrvSlice const store = (set: SetState, get: GetState): State => ({ ...createAppSlice(set, get), @@ -27,7 +19,6 @@ const store = (set: SetState, get: GetState): State => ({ ...createUserSlice(set, get), ...createAnalyticsSlice(set, get), ...createLockedCrvSlice(set, get), - ...createUsdRatesSlice(set, get), }) const useStore = process.env.NODE_ENV === 'development' ? create(devtools(store)) : create(store) diff --git a/apps/main/src/dao/types/dao.types.ts b/apps/main/src/dao/types/dao.types.ts index 1fcfc6826..82355c1d5 100644 --- a/apps/main/src/dao/types/dao.types.ts +++ b/apps/main/src/dao/types/dao.types.ts @@ -34,7 +34,6 @@ export type GasInfo = { l1GasPriceWei?: number l2GasPriceWei?: number } -export type UsdRatesMapper = { [tokenAddress: string]: number | undefined } export type CurveJsProposalType = 'PARAMETER' | 'OWNERSHIP' export type PricesGaugeOverviewData = { diff --git a/apps/main/src/dex/components/ChipToken.tsx b/apps/main/src/dex/components/ChipToken.tsx index 370ca9899..801c4dfb0 100644 --- a/apps/main/src/dex/components/ChipToken.tsx +++ b/apps/main/src/dex/components/ChipToken.tsx @@ -1,12 +1,12 @@ -import { useMemo, useRef } from 'react' +import { useMemo, useRef, useState } from 'react' import type { AriaButtonProps } from 'react-aria' import { useButton } from 'react-aria' import styled from 'styled-components' -import useStore from '@/dex/store/useStore' +import { useChainId } from 'wagmi' import Icon from '@ui/Icon' import Spinner from '@ui/Spinner' import { formatNumberUsdRate } from '@ui/utils' -import { useConnection } from '@ui-kit/features/connect-wallet' +import { fetchTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { copyToClipboard } from '@ui-kit/utils' interface ButtonProps extends AriaButtonProps { @@ -46,14 +46,13 @@ interface ChipTokenProps extends AriaButtonProps { } const ChipToken = ({ className, isHighlight, tokenName, tokenAddress, ...props }: ChipTokenProps) => { - const { curveApi } = useConnection() - const usdRate = useStore((state) => state.usdRates.usdRatesMapper[tokenAddress]) - const fetchUsdRateByToken = useStore((state) => state.usdRates.fetchUsdRateByToken) + const chainId = useChainId() + const [usdRate, setUsdRate] = useState(undefined) const parsedUsdRate = formatNumberUsdRate(usdRate) const handleMouseEnter = (foundUsdRate?: string) => { - if (!foundUsdRate && curveApi) { - void fetchUsdRateByToken(curveApi, tokenAddress) + if (!foundUsdRate) { + void fetchTokenUsdRate({ chainId, tokenAddress }).then(setUsdRate) } } diff --git a/apps/main/src/dex/components/DetailInfoEstGas.tsx b/apps/main/src/dex/components/DetailInfoEstGas.tsx index 842063385..1709e25dc 100644 --- a/apps/main/src/dex/components/DetailInfoEstGas.tsx +++ b/apps/main/src/dex/components/DetailInfoEstGas.tsx @@ -10,6 +10,7 @@ import IconTooltip from '@ui/Tooltip/TooltipIcon' import { FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { useConnection } from '@ui-kit/features/connect-wallet' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { Chain, gweiToEther, weiToGwei } from '@ui-kit/utils' export type StepProgress = { @@ -34,7 +35,7 @@ const DetailInfoEstGas = ({ const { curveApi } = useConnection() const networks = useStore((state) => state.networks.networks) const { gasPricesDefault } = networks[chainId] - const chainTokenUsdRate = useStore((state) => state.usdRates.usdRatesMapper[ethAddress]) + const { data: chainTokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: ethAddress }) const gasInfo = useStore((state) => state.gas.gasInfo) const basePlusPriority = useStore((state) => state.gas.gasInfo?.basePlusPriority?.[gasPricesDefault]) diff --git a/apps/main/src/dex/components/PagePool/Swap/index.tsx b/apps/main/src/dex/components/PagePool/Swap/index.tsx index a3910cd46..c7b63472d 100644 --- a/apps/main/src/dex/components/PagePool/Swap/index.tsx +++ b/apps/main/src/dex/components/PagePool/Swap/index.tsx @@ -40,6 +40,7 @@ import { TokenSelector } from '@ui-kit/features/select-token' import usePageVisibleInterval from '@ui-kit/hooks/usePageVisibleInterval' import { t } from '@ui-kit/lib/i18n' import { REFRESH_INTERVAL } from '@ui-kit/lib/model' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' const Swap = ({ chainIdPoolId, @@ -74,9 +75,7 @@ const Swap = ({ const hasRouter = useStore((state) => state.hasRouter) const isMaxLoading = useStore((state) => state.poolSwap.isMaxLoading) const isPageVisible = useLayoutStore((state) => state.isPageVisible) - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) const fetchUserPoolInfo = useStore((state) => state.user.fetchUserPoolInfo) - const fetchUsdRateByTokens = useStore((state) => state.usdRates.fetchUsdRateByTokens) const fetchStepApprove = useStore((state) => state.poolSwap.fetchStepApprove) const fetchStepSwap = useStore((state) => state.poolSwap.fetchStepSwap) const resetState = useStore((state) => state.poolSwap.resetState) @@ -96,8 +95,12 @@ const Swap = ({ const userFromBalance = userPoolBalances?.[formValues.fromAddress] const userToBalance = userPoolBalances?.[formValues.toAddress] - const fromUsdRate = usdRatesMapper[formValues.fromAddress] - const toUsdRate = usdRatesMapper[formValues.toAddress] + const { data: fromUsdRate } = useTokenUsdRate( + { chainId, tokenAddress: formValues.fromAddress }, + !!formValues.fromAddress, + ) + + const { data: toUsdRate } = useTokenUsdRate({ chainId, tokenAddress: formValues.toAddress }, !!formValues.toAddress) const { selectList, swapTokensMapper } = useMemo(() => { const { selectList, swapTokensMapper } = getSwapTokens(tokensMapper, poolDataCacheOrApi) @@ -272,19 +275,6 @@ const Swap = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [chainId, poolId, haveSigner, userFromBalance, userToBalance]) - // get usdRates - useEffect(() => { - if (formValues.fromAddress || formValues.toAddress) { - if (formValues.fromAddress && isUndefined(fromUsdRate)) { - void fetchUsdRateByTokens(curve, [formValues.fromAddress]) - } - if (formValues.toAddress && isUndefined(toUsdRate)) { - void fetchUsdRateByTokens(curve, [formValues.toAddress]) - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [curve, formValues, fromUsdRate, toUsdRate]) - // curve state change useEffect(() => { if (chainId && poolId) { diff --git a/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx b/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx index 8ebfc1456..f47a6bf18 100644 --- a/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx +++ b/apps/main/src/dex/components/PagePool/Withdraw/components/FormWithdraw.tsx @@ -33,6 +33,7 @@ import { formatNumber } from '@ui/utils' import { mediaQueries } from '@ui/utils/responsive' import { notify } from '@ui-kit/features/connect-wallet' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' const FormWithdraw = ({ chainIdPoolId, @@ -55,7 +56,6 @@ const FormWithdraw = ({ const formStatus = useStore((state) => state.poolWithdraw.formStatus) const formValues = useStore((state) => state.poolWithdraw.formValues) const slippage = useStore((state) => state.poolWithdraw.slippage[activeKey] ?? DEFAULT_SLIPPAGE) - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) const fetchStepApprove = useStore((state) => state.poolWithdraw.fetchStepApprove) const fetchStepWithdraw = useStore((state) => state.poolWithdraw.fetchStepWithdraw) const setFormValues = useStore((state) => state.poolWithdraw.setFormValues) @@ -256,14 +256,17 @@ const FormWithdraw = ({ seed.isSeed, ]) + const tokenAddresses = useMemo(() => formValues.amounts.map((a) => a.tokenAddress), [formValues.amounts]) + const { data: usdRates } = useTokenUsdRates({ chainId, tokenAddresses }) + // usd amount for slippage warning const estUsdAmountTotalReceive = useMemo(() => { if (formValues.selected === 'token') { const foundCoinWithAmount = formValues.amounts.find((a) => Number(a.value) > 0) - if (foundCoinWithAmount && !isUndefined(usdRatesMapper[foundCoinWithAmount.tokenAddress])) { + if (foundCoinWithAmount && !isUndefined(usdRates[foundCoinWithAmount.tokenAddress])) { const { value, tokenAddress } = foundCoinWithAmount - const usdRate = usdRatesMapper[tokenAddress] + const usdRate = usdRates[tokenAddress] if (usdRate && !isNaN(usdRate)) { return (Number(usdRate) * Number(value)).toString() } @@ -273,7 +276,7 @@ const FormWithdraw = ({ let usdAmountTotal = 0 amounts.forEach((a) => { - const usdRate = usdRatesMapper[a.tokenAddress] + const usdRate = usdRates[a.tokenAddress] if (usdRate && !isNaN(usdRate)) { usdAmountTotal += Number(a.value) * Number(usdRate) } @@ -282,7 +285,7 @@ const FormWithdraw = ({ } return '' - }, [formValues, usdRatesMapper]) + }, [formValues, usdRates]) const haveSlippage = formValues.selected !== 'lpToken' const activeStep = haveSigner ? getActiveStep(steps) : null diff --git a/apps/main/src/dex/components/PageRouterSwap/components/RouterSwapAlerts.tsx b/apps/main/src/dex/components/PageRouterSwap/components/RouterSwapAlerts.tsx index f78eebc7a..b59baee91 100644 --- a/apps/main/src/dex/components/PageRouterSwap/components/RouterSwapAlerts.tsx +++ b/apps/main/src/dex/components/PageRouterSwap/components/RouterSwapAlerts.tsx @@ -1,12 +1,13 @@ import isNaN from 'lodash/isNaN' import isUndefined from 'lodash/isUndefined' import { useMemo } from 'react' +import { useChainId } from 'wagmi' import AlertFormError from '@/dex/components/AlertFormError' import AlertSlippage from '@/dex/components/AlertSlippage' import type { FormStatus, FormValues, SearchedParams } from '@/dex/components/PageRouterSwap/types' -import useStore from '@/dex/store/useStore' import AlertBox from '@ui/AlertBox' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' const RouterSwapAlerts = ({ formStatus, @@ -34,12 +35,11 @@ const RouterSwapAlerts = ({ isFullReset?: boolean, ) => void }) => { - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) - const { error, swapError } = formStatus const { toAddress } = searchedParams - const toUsdRate = usdRatesMapper[toAddress] + const chainId = useChainId() + const { data: toUsdRate } = useTokenUsdRate({ chainId, tokenAddress: toAddress }) const usdToAmount = useMemo( () => diff --git a/apps/main/src/dex/components/PageRouterSwap/index.tsx b/apps/main/src/dex/components/PageRouterSwap/index.tsx index e7dc66fb5..0ceb95c1a 100644 --- a/apps/main/src/dex/components/PageRouterSwap/index.tsx +++ b/apps/main/src/dex/components/PageRouterSwap/index.tsx @@ -40,6 +40,7 @@ import { useUserProfileStore } from '@ui-kit/features/user-profile' import usePageVisibleInterval from '@ui-kit/hooks/usePageVisibleInterval' import { t } from '@ui-kit/lib/i18n' import { REFRESH_INTERVAL } from '@ui-kit/lib/model' +import { useTokenUsdRate, useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' const QuickSwap = ({ pageLoaded, @@ -73,7 +74,6 @@ const QuickSwap = ({ const isMaxLoading = useStore((state) => state.quickSwap.isMaxLoading) const userBalancesMapper = useStore((state) => state.userBalances.userBalancesMapper) const userBalancesLoading = useStore((state) => state.userBalances.loading) - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) const fetchStepApprove = useStore((state) => state.quickSwap.fetchStepApprove) const fetchStepSwap = useStore((state) => state.quickSwap.fetchStepSwap) const resetFormErrors = useStore((state) => state.quickSwap.resetFormErrors) @@ -101,8 +101,13 @@ const QuickSwap = ({ const userFromBalance = userBalancesMapper[fromAddress] const userToBalance = userBalancesMapper[toAddress] - const fromUsdRate = usdRatesMapper[fromAddress] - const toUsdRate = usdRatesMapper[toAddress] + const { data: fromUsdRate } = useTokenUsdRate({ chainId, tokenAddress: fromAddress }, !!fromAddress) + const { data: toUsdRate } = useTokenUsdRate({ chainId, tokenAddress: toAddress }, !!toAddress) + + const userTokens = Object.entries(userBalancesMapper) + .filter(([, balance]) => parseFloat(balance ?? '0') > 0) + .map(([address]) => address) + const { data: usdRatesMapper } = useTokenUsdRates({ chainId, tokenAddresses: userTokens }) const tokens = useMemo(() => { if (isEmpty(tokenList) || isEmpty(tokensMapper)) return [] diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx index bd21e5296..8371c71e3 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/AmountTokenInput.tsx @@ -24,6 +24,7 @@ import { FlexContainer } from '@ui/styled-containers' import { formatNumber } from '@ui/utils' import { type TokenOption, TokenSelector } from '@ui-kit/features/select-token' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId: string }) => { const { setValue, getValues, formState, watch } = useFormContext() @@ -37,7 +38,10 @@ export const AmountTokenInput = ({ chainId, poolId }: { chainId: ChainId; poolId const { networkId } = useStore((state) => state.networks.networks[chainId]) const userBalancesMapper = useStore((state) => state.userBalances.userBalancesMapper) - const tokenPrices = useStore((state) => state.usdRates.usdRatesMapper) + const userTokens = Object.entries(userBalancesMapper) + .filter(([, balance]) => parseFloat(balance ?? '0') > 0) + .map(([address]) => address) + const { data: tokenPrices } = useTokenUsdRates({ chainId, tokenAddresses: userTokens }) const { tokensMapper } = useTokensMapper(chainId) diff --git a/apps/main/src/dex/features/deposit-gauge-reward/ui/HelperFields.tsx b/apps/main/src/dex/features/deposit-gauge-reward/ui/HelperFields.tsx index c99d5a6e5..5ae4d9367 100644 --- a/apps/main/src/dex/features/deposit-gauge-reward/ui/HelperFields.tsx +++ b/apps/main/src/dex/features/deposit-gauge-reward/ui/HelperFields.tsx @@ -1,24 +1,17 @@ -import { useMemo } from 'react' import { useFormContext } from 'react-hook-form' +import { useChainId } from 'wagmi' import FieldHelperUsdRate from '@/dex/components/FieldHelperUsdRate' import { type DepositRewardFormValues } from '@/dex/features/deposit-gauge-reward/types' -import useStore from '@/dex/store/useStore' import { FlexContainer } from '@ui/styled-containers' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' export const HelperFields = () => { const { watch } = useFormContext() const rewardTokenId = watch('rewardTokenId') const amount = watch('amount') - const usdRatesMapper = useStore((state) => state.usdRates.usdRatesMapper) - const tokens = [rewardTokenId] - const tokensKey = JSON.stringify(tokens) - - const [tokenUsdRate] = useMemo( - () => tokens.map((token) => (token ? usdRatesMapper[token] : undefined)), - // eslint-disable-next-line react-hooks/exhaustive-deps - [tokensKey, usdRatesMapper], - ) + const chainId = useChainId() + const { data: tokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: rewardTokenId }, !!rewardTokenId) return ( diff --git a/apps/main/src/dex/lib/curvejs.ts b/apps/main/src/dex/lib/curvejs.ts index b662aa8be..28162bd9f 100644 --- a/apps/main/src/dex/lib/curvejs.ts +++ b/apps/main/src/dex/lib/curvejs.ts @@ -19,7 +19,6 @@ import { RewardCrv, RewardOther, RewardsApy, - UsdRatesMapper, UserBalancesMapper, } from '@/dex/types/main.types' import { fulfilledValue, getErrorMessage, isValidAddress } from '@/dex/utils' @@ -86,24 +85,6 @@ const helpers = { return resp } }, - fetchUsdRates: async (curve: CurveApi, tokenAddresses: string[]) => { - log('fetchUsdRates', tokenAddresses.length) - const results: UsdRatesMapper = {} - - await PromisePool.for(tokenAddresses) - .withConcurrency(5) - .process(async (tokenAddress) => { - try { - results[tokenAddress] = await curve.getUsdRate(tokenAddress) - } catch (error) { - if (!curve.getIsLiteChain()) { - console.error(`Unable to get usd rate for ${tokenAddress}`, error) - } - results[tokenAddress] = NaN - } - }) - return results - }, waitForTransaction, waitForTransactions, } diff --git a/apps/main/src/dex/lib/networks.ts b/apps/main/src/dex/lib/networks.ts index eb5f67474..a5d180796 100644 --- a/apps/main/src/dex/lib/networks.ts +++ b/apps/main/src/dex/lib/networks.ts @@ -1,4 +1,5 @@ import memoize from 'memoizee' +import { ethAddress } from 'viem' import { DEFAULT_NETWORK_CONFIG } from '@/dex/constants' import { ChainId, NetworkConfig, type NetworkEnum, NetworkUrlParams } from '@/dex/types/main.types' import curve from '@curvefi/api' @@ -30,7 +31,7 @@ export const defaultNetworks = Object.entries({ ], swap: { fromAddress: '0xdac17f958d2ee523a2206206994597c13d831ec7', - toAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + toAddress: ethAddress, }, excludePoolsMapper: { 'weth-llamma': true, @@ -248,7 +249,7 @@ export const defaultNetworks = Object.entries({ }, hideSmallPoolsTvl: 5000, swap: { - fromAddress: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + fromAddress: ethAddress, toAddress: '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913', }, createQuickList: [ diff --git a/apps/main/src/dex/store/createCreatePoolSlice.ts b/apps/main/src/dex/store/createCreatePoolSlice.ts index 7e384d160..69fe7ca6a 100644 --- a/apps/main/src/dex/store/createCreatePoolSlice.ts +++ b/apps/main/src/dex/store/createCreatePoolSlice.ts @@ -21,6 +21,7 @@ import type { State } from '@/dex/store/useStore' import { ChainId, CurveApi } from '@/dex/types/main.types' import { notify } from '@ui-kit/features/connect-wallet' import { t } from '@ui-kit/lib/i18n' +import { fetchTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' type SliceState = { navigationIndex: number @@ -431,37 +432,39 @@ const createCreatePoolSlice = (set: SetState, get: GetState): Crea ...get().createPool.initialPrice, } + const { chainId } = curve + // set token prices if (tokenA.address !== '') { - const tokenAPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenA.address) + const tokenAPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenA.address }) initialPriceUpdates.tokenA = Number(tokenAPriceRaw) } if (tokenB.address !== '') { - const tokenBPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenB.address) + const tokenBPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenB.address }) initialPriceUpdates.tokenB = Number(tokenBPriceRaw) } if (tokenC.address !== '') { - const tokenCPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenC.address) + const tokenCPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenC.address }) initialPriceUpdates.tokenC = Number(tokenCPriceRaw) } if (tokenD.address !== '') { - const tokenDPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenD.address) + const tokenDPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenD.address }) initialPriceUpdates.tokenD = Number(tokenDPriceRaw) } if (tokenE.address !== '') { - const tokenEPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenE.address) + const tokenEPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenE.address }) initialPriceUpdates.tokenE = Number(tokenEPriceRaw) } if (tokenF.address !== '') { - const tokenFPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenF.address) + const tokenFPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenF.address }) initialPriceUpdates.tokenF = Number(tokenFPriceRaw) } if (tokenG.address !== '') { - const tokenGPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenG.address) + const tokenGPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenG.address }) initialPriceUpdates.tokenG = Number(tokenGPriceRaw) } if (tokenH.address !== '') { - const tokenHPriceRaw = await get().usdRates.fetchUsdRateByToken(curve, tokenH.address) + const tokenHPriceRaw = await fetchTokenUsdRate({ chainId, tokenAddress: tokenH.address }) initialPriceUpdates.tokenH = Number(tokenHPriceRaw) } @@ -577,18 +580,20 @@ const createCreatePoolSlice = (set: SetState, get: GetState): Crea } }, refreshInitialPrice: async (curve: CurveApi) => { - const tokenAPriceRaw = await get().usdRates.fetchUsdRateByToken( - curve, - get().createPool.tokensInPool[TOKEN_A].address, - ) - const tokenBPriceRaw = await get().usdRates.fetchUsdRateByToken( - curve, - get().createPool.tokensInPool[TOKEN_B].address, - ) - const tokenCPriceRaw = await get().usdRates.fetchUsdRateByToken( - curve, - get().createPool.tokensInPool[TOKEN_C].address, - ) + const { chainId } = curve + + const tokenAPriceRaw = await fetchTokenUsdRate({ + chainId, + tokenAddress: get().createPool.tokensInPool[TOKEN_A].address, + }) + const tokenBPriceRaw = await fetchTokenUsdRate({ + chainId, + tokenAddress: get().createPool.tokensInPool[TOKEN_B].address, + }) + const tokenCPriceRaw = await fetchTokenUsdRate({ + chainId, + tokenAddress: get().createPool.tokensInPool[TOKEN_C].address, + }) const tokenAPrice = Number(tokenAPriceRaw) const tokenBPrice = Number(tokenBPriceRaw) const tokenCPrice = Number(tokenCPriceRaw) diff --git a/apps/main/src/dex/store/createGlobalSlice.ts b/apps/main/src/dex/store/createGlobalSlice.ts index 57bd5959a..07bb4eb07 100644 --- a/apps/main/src/dex/store/createGlobalSlice.ts +++ b/apps/main/src/dex/store/createGlobalSlice.ts @@ -81,7 +81,6 @@ const createGlobalSlice = (set: SetState, get: GetState): GlobalSl state.pools.resetState() state.quickSwap.resetState() state.tokens.resetState() - state.usdRates.resetState() state.userBalances.resetState() state.user.resetState() state.userBalances.resetState() @@ -125,9 +124,6 @@ const createGlobalSlice = (set: SetState, get: GetState): GlobalSl void state.gas.fetchGasInfo(curveApi) void state.pools.fetchPricesApiPools(chainId) void state.pools.fetchBasePools(curveApi) - - // pull all api calls before isLoadingApi if it is not needed for initial load - void state.usdRates.fetchAllStoredUsdRates(curveApi) } if (curveApi.signerAddress) { diff --git a/apps/main/src/dex/store/createPoolsSlice.ts b/apps/main/src/dex/store/createPoolsSlice.ts index f9b1ed28f..bdc929073 100644 --- a/apps/main/src/dex/store/createPoolsSlice.ts +++ b/apps/main/src/dex/store/createPoolsSlice.ts @@ -45,6 +45,7 @@ import type { import { convertToLocaleTimestamp } from '@ui/Chart/utils' import { requireLib } from '@ui-kit/features/connect-wallet' import { log } from '@ui-kit/lib/logging' +import { fetchTokenUsdRate, getTokenUsdRateQueryData } from '@ui-kit/lib/model/entities/token-usd-rate' import { getPools } from '../lib/pools' type StateKey = keyof typeof DEFAULT_STATE @@ -342,14 +343,14 @@ const createPoolsSlice = (set: SetState, get: GetState): PoolsSlic } }, fetchPoolCurrenciesReserves: async (curve, poolData) => { - const { usdRates } = get() const { ...sliceState } = get()[sliceKey] const { chainId } = curve const { pool, isWrapped, tokens, tokenAddresses } = poolData - const [balancesResp, usdRatesMapper] = await Promise.all([ + const [balancesResp] = await Promise.all([ curvejsApi.pool.poolBalances(pool, isWrapped), - usdRates.fetchUsdRateByTokens(curve, tokenAddresses, true), + // Fetching the token prices now, used later with getTokenUsdRateQueryData + ...tokenAddresses.map((tokenAddress) => fetchTokenUsdRate({ chainId, tokenAddress })), ]) const { balances } = balancesResp @@ -360,7 +361,7 @@ const createPoolsSlice = (set: SetState, get: GetState): PoolsSlic for (const idx in tokenAddresses) { const tokenAddress = tokenAddresses[idx] - const usdRate = usdRatesMapper[tokenAddress] ?? 0 + const usdRate = getTokenUsdRateQueryData({ chainId, tokenAddress }) ?? 0 const usdRateError = isNaN(usdRate) const balance = Number(balances?.[idx]) const balanceUsd = !isEmpty && +usdRate > 0 && !usdRateError ? balance * usdRate : 0 diff --git a/apps/main/src/dex/store/createQuickSwapSlice.ts b/apps/main/src/dex/store/createQuickSwapSlice.ts index 52773491f..2c8d2d2cc 100644 --- a/apps/main/src/dex/store/createQuickSwapSlice.ts +++ b/apps/main/src/dex/store/createQuickSwapSlice.ts @@ -39,7 +39,6 @@ export type QuickSwapSlice = { fromAddress: string, toAddress: string, ): Promise<{ fromAmount: string; toAmount: string }> - fetchUsdRates(curve: CurveApi, searchedParams: SearchedParams): Promise fetchMaxAmount(curve: CurveApi, searchedParams: SearchedParams, maxSlippage: string | undefined): Promise fetchRoutesAndOutput(curve: CurveApi, searchedParams: SearchedParams, maxSlippage: string): Promise fetchEstGasApproval(curve: CurveApi, searchedParams: SearchedParams): Promise @@ -109,13 +108,6 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick toAmount: get().userBalances.userBalancesMapper[toAddress] ?? '0', } }, - fetchUsdRates: async (curve, { fromAddress, toAddress }) => { - const usdRateMapper = get().usdRates.usdRatesMapper - - if (typeof usdRateMapper[toAddress] === 'undefined' || typeof usdRateMapper[fromAddress] === 'undefined') { - await get().usdRates.fetchUsdRateByTokens(curve, [fromAddress, toAddress]) - } - }, fetchMaxAmount: async (curve, searchedParams, maxSlippage) => { const state = get() const sliceState = state[sliceKey] @@ -321,7 +313,7 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick if (!curve || !storedUserBalancesMapper || !searchedParams.fromAddress || !searchedParams.toAddress) return - const { signerAddress } = curve + const { signerAddress, chainId } = curve // set loading const storedRoutesAndOutput = sliceState.routesAndOutput[activeKey] @@ -335,9 +327,6 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick // get max if MAX button is clicked if (isGetMaxFrom) await sliceState.fetchMaxAmount(curve, searchedParams, maxSlippage) - // get usdRates - await sliceState.fetchUsdRates(curve, searchedParams) - // api calls await sliceState.fetchRoutesAndOutput(curve, searchedParams, maxSlippage) void sliceState.fetchEstGasApproval(curve, searchedParams) @@ -365,13 +354,6 @@ const createQuickSwapSlice = (set: SetState, get: GetState): Quick // Get user balances await state.userBalances.fetchUserBalancesByTokens(curve, tokens) - const userBalancesMapper = get().userBalances.userBalancesMapper - const filteredUserBalancesList = Object.keys(userBalancesMapper).filter( - (k) => +(userBalancesMapper[k] ?? '0') > 0, - ) - - // Get prices of user balance tokens - await state.usdRates.fetchUsdRateByTokens(curve, [...filteredUserBalancesList, ethAddress]) }, // steps diff --git a/apps/main/src/dex/store/createUsdRatesSlice.ts b/apps/main/src/dex/store/createUsdRatesSlice.ts deleted file mode 100644 index 7cee59eee..000000000 --- a/apps/main/src/dex/store/createUsdRatesSlice.ts +++ /dev/null @@ -1,88 +0,0 @@ -import cloneDeep from 'lodash/cloneDeep' -import type { GetState, SetState } from 'zustand' -import curvejsApi from '@/dex/lib/curvejs' -import type { State } from '@/dex/store/useStore' -import { CurveApi, UsdRatesMapper } from '@/dex/types/main.types' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - usdRatesMapper: UsdRatesMapper - loading: boolean -} - -const sliceKey = 'usdRates' - -// prettier-ignore -export type UsdRatesSlice = { - [sliceKey]: SliceState & { - fetchUsdRateByToken(curve: CurveApi | null, tokenAddress: string): Promise - fetchUsdRateByTokens(curve: CurveApi | null, tokenAddresses: string[], shouldRefetch?: boolean): Promise - fetchAllStoredUsdRates(curve: CurveApi): Promise - - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - usdRatesMapper: {}, - loading: true, -} - -const createUsdRatesSlice = (set: SetState, get: GetState): UsdRatesSlice => ({ - [sliceKey]: { - ...DEFAULT_STATE, - - fetchUsdRateByToken: async (curve, tokenAddress) => { - if (!curve) return undefined - - const resp = await get()[sliceKey].fetchUsdRateByTokens(curve, [tokenAddress], true) - return resp[tokenAddress] - }, - fetchUsdRateByTokens: async (curve, tokenAddresses, shouldRefetch) => { - const state = get() - const sliceState = state[sliceKey] - - const usdRatesMapper = sliceState.usdRatesMapper - - if (!curve) return usdRatesMapper - - const missing = shouldRefetch - ? tokenAddresses - : tokenAddresses.filter((t) => typeof usdRatesMapper[t] === 'undefined') - - if (missing.length > 0) { - sliceState.setStateByKey('loading', true) - const fetchedUsdRatesMapper = await curvejsApi.helpers.fetchUsdRates(curve, missing) - sliceState.setStateByKeys({ - usdRatesMapper: { ...usdRatesMapper, ...fetchedUsdRatesMapper }, - loading: false, - }) - } - - return get()[sliceKey].usdRatesMapper - }, - fetchAllStoredUsdRates: async (curve) => { - const tokenAddresses = Object.keys(get().usdRates.usdRatesMapper) - await get().usdRates.fetchUsdRateByTokens(curve, tokenAddresses, true) - }, - - setStateByActiveKey: (key: StateKey, activeKey: string, value: T) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key: StateKey, value: T) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState: Partial) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createUsdRatesSlice diff --git a/apps/main/src/dex/store/useStore.ts b/apps/main/src/dex/store/useStore.ts index ba6584895..81f60e658 100644 --- a/apps/main/src/dex/store/useStore.ts +++ b/apps/main/src/dex/store/useStore.ts @@ -20,7 +20,6 @@ import createPoolSwapSlice, { PoolSwapSlice } from '@/dex/store/createPoolSwapSl import createPoolWithdrawSlice, { PoolWithdrawSlice } from '@/dex/store/createPoolWithdrawSlice' import createQuickSwapSlice, { QuickSwapSlice } from '@/dex/store/createQuickSwapSlice' import createTokensSlice, { TokensSlice } from '@/dex/store/createTokensSlice' -import createUsdRatesSlice, { UsdRatesSlice } from '@/dex/store/createUsdRatesSlice' import createUserBalancesSlice, { UserBalancesSlice } from '@/dex/store/createUserBalancesSlice' import createUserSlice, { UserSlice } from '@/dex/store/createUserSlice' @@ -38,7 +37,6 @@ export type State = GlobalSlice & UserBalancesSlice & DashboardSlice & TokensSlice & - UsdRatesSlice & LockedCrvSlice & CreatePoolSlice & IntegrationsSlice & @@ -60,7 +58,6 @@ const store = (set: SetState, get: GetState): State => ({ ...createQuickSwapSlice(set, get), ...createUserBalancesSlice(set, get), ...createTokensSlice(set, get), - ...createUsdRatesSlice(set, get), ...createLockedCrvSlice(set, get), ...createCreatePoolSlice(set, get), ...createIntegrationsSlice(set, get), diff --git a/apps/main/src/dex/types/main.types.ts b/apps/main/src/dex/types/main.types.ts index c9edfa152..0b2b0f407 100644 --- a/apps/main/src/dex/types/main.types.ts +++ b/apps/main/src/dex/types/main.types.ts @@ -258,7 +258,6 @@ export type Tvl = { } export type TvlMapper = { [poolId: string]: Tvl } export type ValueMapperCached = { [poolId: string]: { value: string } } -export type UsdRatesMapper = { [tokenAddress: string]: number | undefined } export type UserPoolListMapper = { [poolId: string]: boolean } export type Volume = { poolId: string diff --git a/apps/main/src/dex/utils/utilsSwap.ts b/apps/main/src/dex/utils/utilsSwap.ts index ef2a48e3b..cc9972e08 100644 --- a/apps/main/src/dex/utils/utilsSwap.ts +++ b/apps/main/src/dex/utils/utilsSwap.ts @@ -1,4 +1,5 @@ import BigNumber from 'bignumber.js' +import { ethAddress } from 'viem' import type { Route } from '@/dex/components/PageRouterSwap/types' import { parseRouterRoutes } from '@/dex/components/PageRouterSwap/utils' import { CurveApi, PoolData } from '@/dex/types/main.types' @@ -23,9 +24,9 @@ export function excludeLowExchangeRateCheck(fromAddress: string, toAddress: stri const exclusionPairs = { '0xae78736cd615f374d3085123a210448e74fc6393-0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0': true, // rETH, wstETH - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-0x5979d7b546e38e414f7e9822514be443a4800529': true, - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee-0xe95a203b1a91a908f9b9ce46459d101078c2c3cb': true, // ETH, ankrETH - '0xe95a203b1a91a908f9b9ce46459d101078c2c3cb-0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': true, + [`${ethAddress}-0x5979d7b546e38e414f7e9822514be443a4800529`]: true, + [`${ethAddress}-0xe95a203b1a91a908f9b9ce46459d101078c2c3cb`]: true, // ETH, ankrETH + [`0xe95a203b1a91a908f9b9ce46459d101078c2c3cb-${ethAddress}`]: true, } // @ts-ignore return exclusionPairs[pair1] || exclusionPairs[pair2] diff --git a/apps/main/src/lend/components/DetailInfoEstimateGas.tsx b/apps/main/src/lend/components/DetailInfoEstimateGas.tsx index af6925951..a65eb4edc 100644 --- a/apps/main/src/lend/components/DetailInfoEstimateGas.tsx +++ b/apps/main/src/lend/components/DetailInfoEstimateGas.tsx @@ -1,7 +1,6 @@ import { useMemo } from 'react' import styled from 'styled-components' import { ethAddress } from 'viem' -import { useTokenUsdRate } from '@/lend/entities/token' import networks from '@/lend/networks' import useStore from '@/lend/store/useStore' import { ChainId } from '@/lend/types/lend.types' @@ -9,6 +8,7 @@ import DetailInfo from '@ui/DetailInfo' import IconTooltip from '@ui/Tooltip/TooltipIcon' import { BN, FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { gweiToEther, weiToGwei } from '@ui-kit/utils' export type StepProgress = { diff --git a/apps/main/src/lend/components/DetailInfoLTV.tsx b/apps/main/src/lend/components/DetailInfoLTV.tsx index 902af82bb..e45d38eed 100644 --- a/apps/main/src/lend/components/DetailInfoLTV.tsx +++ b/apps/main/src/lend/components/DetailInfoLTV.tsx @@ -1,9 +1,9 @@ import { useMemo } from 'react' import { useChainId } from '@/lend/entities/chain' -import { useTokenUsdRate, useTokenUsdRates } from '@/lend/entities/token' import DetailInfo from '@ui/DetailInfo' import { FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate, useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' type Amount = { amount: string; address: string } diff --git a/apps/main/src/lend/components/InpChipUsdRate.tsx b/apps/main/src/lend/components/InpChipUsdRate.tsx index 31f57dfde..c7c250872 100644 --- a/apps/main/src/lend/components/InpChipUsdRate.tsx +++ b/apps/main/src/lend/components/InpChipUsdRate.tsx @@ -1,7 +1,7 @@ import { useChainId } from '@/lend/entities/chain' -import { useTokenUsdRate } from '@/lend/entities/token' import InpChipUsdRateComp from '@ui/InpChipUsdRate' import type { InpChipUsdRateProps } from '@ui/InpChipUsdRate/InpChipUsdRate' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' const InpChipUsdRate = ({ address: tokenAddress, diff --git a/apps/main/src/lend/entities/chain/chain-tvl.ts b/apps/main/src/lend/entities/chain/chain-tvl.ts index 08faff2a0..ef614f3ef 100644 --- a/apps/main/src/lend/entities/chain/chain-tvl.ts +++ b/apps/main/src/lend/entities/chain/chain-tvl.ts @@ -1,7 +1,7 @@ import { useMemo } from 'react' -import { useTokenUsdRates } from '@/lend/entities/token/lib' import useStore from '@/lend/store/useStore' import { ChainId } from '@/lend/types/lend.types' +import { useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' import { FETCHING, PartialQueryResult, READY } from '@ui-kit/lib/queries' import { useOneWayMarketMapping } from './chain-hooks' import { calculateChainTvl } from './tvl' diff --git a/apps/main/src/lend/entities/token/index.ts b/apps/main/src/lend/entities/token/index.ts deleted file mode 100644 index 5b0e4e09e..000000000 --- a/apps/main/src/lend/entities/token/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './lib' -export * from './types' -export * from './token-query' diff --git a/apps/main/src/lend/entities/token/lib.ts b/apps/main/src/lend/entities/token/lib.ts deleted file mode 100644 index 0a343521c..000000000 --- a/apps/main/src/lend/entities/token/lib.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ChainParams } from '@ui-kit/lib/model/query' -import { useQueryMapping } from '@ui-kit/lib/queries' -import { getTokenUsdRateQueryOptions } from './token-query' - -export const useTokenUsdRates = ({ chainId, tokenAddresses = [] }: ChainParams & { tokenAddresses?: string[] }) => { - const uniqueAddresses = Array.from(new Set(tokenAddresses)) - return useQueryMapping( - uniqueAddresses.map((tokenAddress) => getTokenUsdRateQueryOptions({ chainId, tokenAddress })), - uniqueAddresses, - ) -} diff --git a/apps/main/src/lend/entities/token/token-query.ts b/apps/main/src/lend/entities/token/token-query.ts deleted file mode 100644 index f488c0856..000000000 --- a/apps/main/src/lend/entities/token/token-query.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { TokenParams, TokenQuery } from '@/lend/entities/token/index' -import { requireLib } from '@ui-kit/features/connect-wallet' -import { queryFactory, rootKeys } from '@ui-kit/lib/model/query' -import { tokenValidationSuite } from './validation' - -const root = ({ chainId, tokenAddress }: TokenParams) => - [...rootKeys.chain({ chainId }), 'token', { tokenAddress }] as const - -export const { - getQueryData: getTokenUsdRateQueryData, - useQuery: useTokenUsdRate, - getQueryOptions: getTokenUsdRateQueryOptions, -} = queryFactory({ - queryKey: (params: TokenParams) => [...root(params), 'usdRate'] as const, - queryFn: ({ tokenAddress }: TokenQuery): Promise => requireLib('llamaApi').getUsdRate(tokenAddress), - staleTime: '5m', - refetchInterval: '1m', - validationSuite: tokenValidationSuite, -}) diff --git a/apps/main/src/lend/entities/token/types.ts b/apps/main/src/lend/entities/token/types.ts deleted file mode 100644 index 432721f51..000000000 --- a/apps/main/src/lend/entities/token/types.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ChainQuery } from '@ui-kit/lib/model/query' -import { FieldsOf } from '@ui-kit/lib/validation' - -export type TokenQuery = ChainQuery & { tokenAddress: string } -export type TokenParams = FieldsOf diff --git a/apps/main/src/lend/hooks/useVaultShares.ts b/apps/main/src/lend/hooks/useVaultShares.ts index 7b98cc79a..75824f159 100644 --- a/apps/main/src/lend/hooks/useVaultShares.ts +++ b/apps/main/src/lend/hooks/useVaultShares.ts @@ -1,9 +1,9 @@ import { useEffect, useMemo } from 'react' import { useOneWayMarket } from '@/lend/entities/chain' -import { useTokenUsdRate } from '@/lend/entities/token' import useStore from '@/lend/store/useStore' import { ChainId } from '@/lend/types/lend.types' import { FORMAT_OPTIONS, formatNumber, formatNumberWithPrecision } from '@ui/utils' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' function useVaultShares(rChainId: ChainId, rOwmId: string, vaultShares: string | number | undefined = '0') { const market = useOneWayMarket(rChainId, rOwmId).data diff --git a/apps/main/src/lend/store/createMarketListSlice.ts b/apps/main/src/lend/store/createMarketListSlice.ts index 9ccc0dbf0..82b04b611 100644 --- a/apps/main/src/lend/store/createMarketListSlice.ts +++ b/apps/main/src/lend/store/createMarketListSlice.ts @@ -13,7 +13,6 @@ import type { } from '@/lend/components/PageMarketList/types' import { _getMarketList, DEFAULT_FORM_STATUS, parseSearchTermResults } from '@/lend/components/PageMarketList/utils' import { TITLE } from '@/lend/constants' -import { getTokenUsdRateQueryData } from '@/lend/entities/token' import { SEARCH_TERM } from '@/lend/hooks/useSearchTermMapper' import { helpers } from '@/lend/lib/apiLending' import networks from '@/lend/networks' @@ -33,6 +32,7 @@ import { sleep } from '@/lend/utils/helpers' import { getTotalApr } from '@/lend/utils/utilsRewards' import { IDict } from '@curvefi/llamalend-api/lib/interfaces' import { logQuery, logSuccess } from '@ui-kit/lib' +import { getTokenUsdRateQueryData } from '@ui-kit/lib/model/entities/token-usd-rate' import { searchByText } from '@ui-kit/utils' type StateKey = keyof typeof DEFAULT_STATE diff --git a/apps/main/src/loan/components/DetailInfoEstimateGas.tsx b/apps/main/src/loan/components/DetailInfoEstimateGas.tsx index 89adbb13d..2d293719a 100644 --- a/apps/main/src/loan/components/DetailInfoEstimateGas.tsx +++ b/apps/main/src/loan/components/DetailInfoEstimateGas.tsx @@ -1,5 +1,6 @@ import { useMemo } from 'react' import styled from 'styled-components' +import { ethAddress } from 'viem' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' import { ChainId } from '@/loan/types/loan.types' @@ -7,6 +8,7 @@ import DetailInfo from '@ui/DetailInfo' import IconTooltip from '@ui/Tooltip/TooltipIcon' import { BN, FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { gweiToEther, weiToGwei } from '@ui-kit/utils' export type StepProgress = { @@ -24,7 +26,7 @@ interface Props { } const DetailInfoEstimateGas = ({ chainId, isDivider = false, loading, estimatedGas, stepProgress }: Props) => { - const chainTokenUsdRate = useStore((state) => state.usdRates.tokens['0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee']) + const { data: chainTokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: ethAddress }) const gasPricesDefault = chainId && networks[chainId].gasPricesDefault // TODO: allow gas prices priority adjustment const basePlusPriorities = useStore((state) => state.gas.gasInfo?.basePlusPriority) @@ -36,14 +38,10 @@ const DetailInfoEstimateGas = ({ chainId, isDivider = false, loading, estimatedG const { symbol, gasPricesUnit } = networks[chainId] const estGasCost = new BN(gweiToEther(weiToGwei(basePlusPriority) * estimatedGas)) - if (chainTokenUsdRate === 'NaN') { - return { estGasCost: estGasCost.toString(), estGasCostUsd: 'NaN', tooltip: '' } - } else { - const estGasCostUsd = estGasCost.multipliedBy(chainTokenUsdRate).toString() - const gasAmountUnit = formatNumber(weiToGwei(basePlusPriority), { maximumFractionDigits: 2 }) - const tooltip = `${formatNumber(estGasCost.toString())} ${symbol} at ${gasAmountUnit} ${gasPricesUnit}` - return { estGasCost: estGasCost.toString(), estGasCostUsd, tooltip } - } + const estGasCostUsd = estGasCost.multipliedBy(chainTokenUsdRate).toString() + const gasAmountUnit = formatNumber(weiToGwei(basePlusPriority), { maximumFractionDigits: 2 }) + const tooltip = `${formatNumber(estGasCost.toString())} ${symbol} at ${gasAmountUnit} ${gasPricesUnit}` + return { estGasCost: estGasCost.toString(), estGasCostUsd, tooltip } } return { estGasCost: 0, estGasCostUsd: 0, tooltip: '' } }, [chainTokenUsdRate, basePlusPriority, chainId, estimatedGas]) diff --git a/apps/main/src/loan/components/InpChipUsdRate.tsx b/apps/main/src/loan/components/InpChipUsdRate.tsx index cf3fbb7d6..bad879073 100644 --- a/apps/main/src/loan/components/InpChipUsdRate.tsx +++ b/apps/main/src/loan/components/InpChipUsdRate.tsx @@ -1,12 +1,14 @@ -import useStore from '@/loan/store/useStore' +import { useChainId } from 'wagmi' import InpChipUsdRateComp from '@ui/InpChipUsdRate' import type { InpChipUsdRateProps } from '@ui/InpChipUsdRate/InpChipUsdRate' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' const InpChipUsdRate = ({ address = '', ...props }: Omit & { address: string | undefined }) => { - const usdRate = useStore((state) => state.usdRates.tokens[address]) + const chainId = useChainId() + const { data: usdRate } = useTokenUsdRate({ chainId, tokenAddress: address }) return } diff --git a/apps/main/src/loan/components/PageCrvUsdStaking/UserPosition/index.tsx b/apps/main/src/loan/components/PageCrvUsdStaking/UserPosition/index.tsx index 12ab7e677..18c6fa2af 100644 --- a/apps/main/src/loan/components/PageCrvUsdStaking/UserPosition/index.tsx +++ b/apps/main/src/loan/components/PageCrvUsdStaking/UserPosition/index.tsx @@ -23,7 +23,7 @@ const UserPosition = ({ chartExpanded = false }: { chartExpanded?: boolean }) => } = useTheme() const { data: statisticsData, isLoading: isStatisticsLoading } = useScrvUsdStatistics({}) const { data: userBalance, isLoading: userBalanceLoading } = useScrvUsdUserBalances({ userAddress: address }) - const usdRateLoading = useStore((state) => state.usdRates.loading) + const usdRateLoading = useStore((state) => state.scrvusd.scrvUsdExchangeRate.fetchStatus === 'loading') const scrvUsdExchangeRateFetchStatus = useStore((state) => state.scrvusd.scrvUsdExchangeRate.fetchStatus) const scrvUsdRate = useStore((state) => state.scrvusd.scrvUsdExchangeRate.value) diff --git a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfo.tsx b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfo.tsx index bea29d269..e91db6f69 100644 --- a/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfo.tsx +++ b/apps/main/src/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfo.tsx @@ -1,4 +1,5 @@ import { useMemo } from 'react' +import { useChainId } from 'wagmi' import DetailInfoLeverage from '@/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfoLeverage' import DetailInfoNonLeverage from '@/loan/components/PageLoanCreate/LoanFormCreate/components/DetailInfoNonLeverage' import type { FormDetailInfo, FormDetailInfoSharedProps } from '@/loan/components/PageLoanCreate/types' @@ -7,11 +8,14 @@ import DetailInfo from '@ui/DetailInfo' import { getActiveStep } from '@ui/Stepper/helpers' import { FORMAT_OPTIONS, formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' const DetailInfoComp = (props: FormDetailInfo) => { const { activeKeyLiqRange, formValues, isLeverage, isReady, llamma, haveSigner, steps, updateFormValues } = props - const collateralUsdRate = useStore((state) => state.usdRates.tokens[llamma?.collateral ?? '']) + const chainId = useChainId() + const { data: collateralUsdRate } = useTokenUsdRate({ chainId, tokenAddress: llamma?.collateral ?? '' }) + const isEditLiqRange = useStore((state) => state.loanCreate.isEditLiqRange) const liqRangesMapper = useStore((state) => state.loanCreate.liqRangesMapper[activeKeyLiqRange]) const setStateByKey = useStore((state) => state.loanCreate.setStateByKey) diff --git a/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperStatistics.tsx b/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperStatistics.tsx index a402ba25a..c221ef616 100644 --- a/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperStatistics.tsx +++ b/apps/main/src/loan/components/PagePegKeepers/components/PegKeeperStatistics.tsx @@ -1,7 +1,7 @@ +import { useChainId } from 'wagmi' import { useAppStatsTotalCrvusdSupply } from '@/loan/entities/appstats-total-crvusd-supply' import type { ChainId } from '@/loan/types/loan.types' import { CardHeader, Box } from '@mui/material' -import { useConnection } from '@ui-kit/features/connect-wallet' import { Metric } from '@ui-kit/shared/ui/Metric' import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces' @@ -9,7 +9,7 @@ const { Spacing } = SizesAndSpaces const CRVUSD_OPTION = { symbol: 'crvUSD', position: 'suffix' as const, abbreviate: true } export const PegKeeperStatistics = () => { - const chainId = useConnection().llamaApi?.chainId as ChainId | undefined + const chainId = useChainId() as ChainId const { data: crvusdTotalSupply, isLoading } = useAppStatsTotalCrvusdSupply({ chainId }) const { total, minted, pegKeepersDebt } = crvusdTotalSupply ?? {} diff --git a/apps/main/src/loan/components/SharedCells/TableCellMarketsTotalDebt.tsx b/apps/main/src/loan/components/SharedCells/TableCellMarketsTotalDebt.tsx index d0b728c45..fe6d4064c 100644 --- a/apps/main/src/loan/components/SharedCells/TableCellMarketsTotalDebt.tsx +++ b/apps/main/src/loan/components/SharedCells/TableCellMarketsTotalDebt.tsx @@ -1,12 +1,12 @@ import styled from 'styled-components' +import { useChainId } from 'wagmi' import { useAppStatsTotalCrvusdSupply } from '@/loan/entities/appstats-total-crvusd-supply' import type { ChainId } from '@/loan/types/loan.types' import TextCaption from '@ui/TextCaption' import { FORMAT_OPTIONS, formatNumber } from '@ui/utils' -import { useConnection } from '@ui-kit/features/connect-wallet' const TableCellMarketsTotalDebt = () => { - const chainId = useConnection().llamaApi?.chainId as ChainId + const chainId = useChainId() as ChainId const { data: crvusdTotalSupply } = useAppStatsTotalCrvusdSupply({ chainId }) const { total, minted, pegKeepersDebt, error } = crvusdTotalSupply ?? {} diff --git a/apps/main/src/loan/components/SharedCells/TableCellTotalCollateral.tsx b/apps/main/src/loan/components/SharedCells/TableCellTotalCollateral.tsx index 16a66fd0e..f342969f8 100644 --- a/apps/main/src/loan/components/SharedCells/TableCellTotalCollateral.tsx +++ b/apps/main/src/loan/components/SharedCells/TableCellTotalCollateral.tsx @@ -1,6 +1,7 @@ import isUndefined from 'lodash/isUndefined' import { useMemo } from 'react' import styled from 'styled-components' +import { useChainId } from 'wagmi' import useStore from '@/loan/store/useStore' import { ChainId } from '@/loan/types/loan.types' import { getTokenName } from '@/loan/utils/utilsLoan' @@ -10,6 +11,7 @@ import { Chip } from '@ui/Typography' import { formatNumber, type NumberFormatOptions } from '@ui/utils' import { useUserProfileStore } from '@ui-kit/features/user-profile' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' type Props = { rChainId: ChainId @@ -19,7 +21,9 @@ type Props = { const TableCellTotalCollateral = ({ rChainId, collateralId }: Props) => { const loanDetails = useStore((state) => state.loans.detailsMapper[collateralId]) const llamma = useStore((state) => state.collaterals.collateralDatasMapper[rChainId]?.[collateralId]?.llamma) - const collateralUsdRate = useStore((state) => state.usdRates.tokens[llamma?.collateral ?? '']) + + const chainId = useChainId() + const { data: collateralUsdRate } = useTokenUsdRate({ chainId, tokenAddress: llamma?.collateral ?? '' }) const isAdvancedMode = useUserProfileStore((state) => state.isAdvancedMode) @@ -43,7 +47,7 @@ const TableCellTotalCollateral = ({ rChainId, collateralId }: Props) => { return <> } - if (collateralUsdRate === 'NaN' || +collateralUsdRate === 0) { + if (collateralUsdRate == null || +collateralUsdRate === 0) { return ( ? diff --git a/apps/main/src/loan/hooks/useEstimateGasConversion.tsx b/apps/main/src/loan/hooks/useEstimateGasConversion.tsx index 184da64eb..906b75f0a 100644 --- a/apps/main/src/loan/hooks/useEstimateGasConversion.tsx +++ b/apps/main/src/loan/hooks/useEstimateGasConversion.tsx @@ -1,15 +1,16 @@ import { useMemo } from 'react' +import { ethAddress } from 'viem' +import { useChainId } from 'wagmi' import networks from '@/loan/networks' import useStore from '@/loan/store/useStore' import type { ChainId } from '@/loan/types/loan.types' import { BN, formatNumber } from '@ui/utils' -import { useConnection } from '@ui-kit/features/connect-wallet' +import { useTokenUsdRate } from '@ui-kit/lib/model/entities/token-usd-rate' import { gweiToEther, weiToGwei } from '@ui-kit/utils' const useEstimateGasConversion = (gas: number) => { - const { llamaApi: curve } = useConnection() - const chainId = curve?.chainId as ChainId | undefined - const chainTokenUsdRate = useStore().usdRates.tokens['0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee'] + const chainId = useChainId() as ChainId + const { data: chainTokenUsdRate } = useTokenUsdRate({ chainId, tokenAddress: ethAddress }) const gasPricesDefault = chainId && networks[chainId].gasPricesDefault const basePlusPriorities = useStore().gas.gasInfo?.basePlusPriority @@ -17,13 +18,11 @@ const useEstimateGasConversion = (gas: number) => { const basePlusPriority = basePlusPriorities && typeof gasPricesDefault !== 'undefined' && basePlusPriorities[gasPricesDefault] - if (!curve || !chainId) return { estGasCost: 0, estGasCostUsd: 0, tooltip: '' } - if (!basePlusPriority) return { estGasCost: 0, estGasCostUsd: 0, tooltip: '' } const { symbol, gasPricesUnit } = networks[chainId] const estGasCost = new BN(gweiToEther(weiToGwei(basePlusPriority) * gas)) - if (chainTokenUsdRate === 'NaN') { + if (chainTokenUsdRate == null) { return { estGasCost: estGasCost.toString(), estGasCostUsd: 'NaN', tooltip: '' } } else { const estGasCostUsd = estGasCost.multipliedBy(chainTokenUsdRate).toString() @@ -31,7 +30,7 @@ const useEstimateGasConversion = (gas: number) => { const tooltip = `${formatNumber(estGasCost.toString())} ${symbol} at ${gasAmountUnit} ${gasPricesUnit}` return { estGasCost: estGasCost.toString(), estGasCostUsd, tooltip } } - }, [gas, curve, chainId, chainTokenUsdRate, gasPricesDefault, basePlusPriorities]) + }, [gas, chainId, chainTokenUsdRate, gasPricesDefault, basePlusPriorities]) } export default useEstimateGasConversion diff --git a/apps/main/src/loan/hooks/useLoanAppStats.tsx b/apps/main/src/loan/hooks/useLoanAppStats.tsx index 41bdae760..0cf5a632b 100644 --- a/apps/main/src/loan/hooks/useLoanAppStats.tsx +++ b/apps/main/src/loan/hooks/useLoanAppStats.tsx @@ -6,6 +6,7 @@ import useStore from '@/loan/store/useStore' import type { ChainId } from '@/loan/types/loan.types' import { formatNumber } from '@ui/utils' import { t } from '@ui-kit/lib/i18n' +import { useTokenUsdRate, useTokenUsdRates } from '@ui-kit/lib/model/entities/token-usd-rate' const hasKeys = (obj: Record | undefined | null): obj is Record => !!obj && Object.keys(obj).length > 0 @@ -13,9 +14,19 @@ const hasKeys = (obj: Record | undefined | null): function useTvl(chainId: ChainId | undefined) { const collateralDatasMapper = useStore((state) => chainId && state.collaterals.collateralDatasMapper[chainId]) const loansDetailsMapper = useStore((state) => state.loans.detailsMapper) - const usdRatesMapper = useStore((state) => state.usdRates.tokens) + + const collateralTokenAddresses = useMemo( + () => + Object.values(collateralDatasMapper || {}) + .map((data) => data?.llamma?.collateral) + .filter(Boolean) as string[], + [collateralDatasMapper], + ) + + const { data: usdRates } = useTokenUsdRates({ chainId, tokenAddresses: collateralTokenAddresses }) + return useMemo(() => { - if (!hasKeys(collateralDatasMapper) || !hasKeys(loansDetailsMapper) || !hasKeys(usdRatesMapper)) { + if (!hasKeys(collateralDatasMapper) || !hasKeys(loansDetailsMapper) || Object.keys(usdRates).length === 0) { return '-' } let sum = 0 @@ -27,19 +38,15 @@ function useTvl(chainId: ChainId | undefined) { } const { totalCollateral, totalStablecoin } = loanDetails - const usdRate = usdRatesMapper[collateralData.llamma.collateral] - if (usdRate === 'NaN') { - return '?' - } - const totalCollateralUsd = +(totalCollateral ?? '0') * +(usdRate ?? '0') + const totalCollateralUsd = +(totalCollateral ?? '0') * usdRates[collateralData.llamma.collateral] sum += totalCollateralUsd + +(totalStablecoin ?? '0') } return sum > 0 ? formatNumber(sum, { currency: 'USD', notation: 'compact' }) : '-' - }, [collateralDatasMapper, loansDetailsMapper, usdRatesMapper]) + }, [collateralDatasMapper, loansDetailsMapper, usdRates]) } export function useLoanAppStats(chainId: ChainId | undefined) { - const crvusdPrice = useStore((state) => state.usdRates.tokens[CRVUSD_ADDRESS]) + const { data: crvusdPrice } = useTokenUsdRate({ chainId, tokenAddress: CRVUSD_ADDRESS }) const { data: dailyVolume } = useAppStatsDailyVolume({}) const { data: crvusdTotalSupply } = useAppStatsTotalCrvusdSupply({ chainId }) return [ diff --git a/apps/main/src/loan/store/createAppSlice.ts b/apps/main/src/loan/store/createAppSlice.ts index 5b73616a2..ddfb871a4 100644 --- a/apps/main/src/loan/store/createAppSlice.ts +++ b/apps/main/src/loan/store/createAppSlice.ts @@ -60,7 +60,7 @@ const createAppSlice = (set: SetState, get: GetState): AppSlice => hydrate: async (curveApi, prevCurveApi, wallet) => { if (!curveApi) return - const { loans, usdRates, campaigns, collaterals } = get() + const { loans, campaigns, collaterals } = get() const isNetworkSwitched = !!prevCurveApi?.chainId && prevCurveApi.chainId !== curveApi.chainId const isUserSwitched = !!prevCurveApi?.signerAddress && prevCurveApi.signerAddress !== curveApi.signerAddress @@ -82,7 +82,6 @@ const createAppSlice = (set: SetState, get: GetState): AppSlice => if (!prevCurveApi || isNetworkSwitched) { campaigns.initCampaignRewards(curveApi.chainId as ChainId) - void usdRates.fetchAllStoredUsdRates(curveApi) } log('Hydrate crvUSD - Complete') diff --git a/apps/main/src/loan/store/createCollateralListSlice.ts b/apps/main/src/loan/store/createCollateralListSlice.ts index f6f58d77e..541e23454 100644 --- a/apps/main/src/loan/store/createCollateralListSlice.ts +++ b/apps/main/src/loan/store/createCollateralListSlice.ts @@ -9,6 +9,7 @@ import { SEARCH_TERM } from '@/loan/hooks/useSearchTermMapper' import type { State } from '@/loan/store/useStore' import { ChainId, LlamaApi, CollateralData, TitleKey } from '@/loan/types/loan.types' import { sleep } from '@/loan/utils/helpers' +import { getTokenUsdRateQueryData } from '@ui-kit/lib/model/entities/token-usd-rate' import { searchByText } from '@ui-kit/utils' type StateKey = keyof typeof DEFAULT_STATE @@ -103,7 +104,7 @@ const createCollateralListSlice = (set: SetState, get: GetState): collateralDatas, ({ llamma }) => { const { totalCollateral, totalStablecoin } = loanMapper[llamma.id] - const collateralUsdRate = get().usdRates.tokens[llamma.collateral] + const collateralUsdRate = getTokenUsdRateQueryData({ chainId: rChainId, tokenAddress: llamma.collateral }) const totalCollateralUsd = Number(totalCollateral) * Number(collateralUsdRate) return totalCollateralUsd + Number(totalStablecoin ?? 0) }, diff --git a/apps/main/src/loan/store/createCollateralsSlice.ts b/apps/main/src/loan/store/createCollateralsSlice.ts index 47b0d2706..5db224730 100644 --- a/apps/main/src/loan/store/createCollateralsSlice.ts +++ b/apps/main/src/loan/store/createCollateralsSlice.ts @@ -64,9 +64,6 @@ const createCollateralsSlice = (set: SetState, get: GetState) => ( get()[sliceKey].setStateByActiveKey('collateralDatas', chainId.toString(), collateralDatas) get()[sliceKey].setStateByActiveKey('collateralDatasMapper', chainId.toString(), collateralDatasMapper) - // fetch collaterals USD rates - void get().usdRates.fetchUsdRateByTokens(curve, collateralAddresses) - // add to cache void get().storeCache.setStateByActiveKey('collateralDatasMapper', chainId.toString(), collateralDatasCacheMapper) diff --git a/apps/main/src/loan/store/createUsdRatesSlice.ts b/apps/main/src/loan/store/createUsdRatesSlice.ts deleted file mode 100644 index cd768031e..000000000 --- a/apps/main/src/loan/store/createUsdRatesSlice.ts +++ /dev/null @@ -1,92 +0,0 @@ -import cloneDeep from 'lodash/cloneDeep' -import type { GetState, SetState } from 'zustand' -import { CRVUSD_ADDRESS } from '@/loan/constants' -import networks from '@/loan/networks' -import type { State } from '@/loan/store/useStore' -import { type ChainId, LlamaApi, UsdRate } from '@/loan/types/loan.types' -import { PromisePool } from '@supercharge/promise-pool' -import { log } from '@ui-kit/lib/logging' - -type StateKey = keyof typeof DEFAULT_STATE - -type SliceState = { - tokens: UsdRate - loading: boolean -} - -const sliceKey = 'usdRates' - -export type UsdRatesSlice = { - [sliceKey]: SliceState & { - fetchUsdRateByToken(curve: LlamaApi, tokenAddress: string): Promise - fetchUsdRateByTokens(curve: LlamaApi, tokenAddresses: string[]): Promise - fetchAllStoredUsdRates(curve: LlamaApi): Promise - - // steps helper - setStateByActiveKey(key: StateKey, activeKey: string, value: T): void - setStateByKey(key: StateKey, value: T): void - setStateByKeys(SliceState: Partial): void - resetState(): void - } -} - -const DEFAULT_STATE: SliceState = { - tokens: { - [CRVUSD_ADDRESS]: '', - '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee': '', - }, - loading: true, -} - -const createUsdRatesSlice = (set: SetState, get: GetState) => ({ - [sliceKey]: { - ...DEFAULT_STATE, - - fetchUsdRateByToken: async (curve: LlamaApi, tokenAddress: string) => { - log('fetchUsdRateByToken', curve.chainId, tokenAddress) - const chainId = curve.chainId as ChainId - const resp = await networks[chainId].api.helpers.getUsdRate(curve, tokenAddress) - get()[sliceKey].setStateByActiveKey('tokens', tokenAddress, resp.usdRate) - }, - fetchUsdRateByTokens: async (curve: LlamaApi, tokenAddresses: string[]) => { - log('fetchUsdRateByTokens', curve.chainId, tokenAddresses.join(',')) - get().usdRates.setStateByKey('loading', true) - - const chainId = curve.chainId as ChainId - const { results } = await PromisePool.for(tokenAddresses) - .withConcurrency(5) - .process(async (tokenAddress) => { - const resp = await networks[chainId].api.helpers.getUsdRate(curve, tokenAddress) - return { tokenAddress, usdRate: resp.usdRate } - }) - - const usdRatesTokens: UsdRate = cloneDeep(get()[sliceKey].tokens) - for (const idx in results) { - const { tokenAddress, usdRate } = results[idx] - usdRatesTokens[tokenAddress] = usdRate - } - get()[sliceKey].setStateByKey('tokens', usdRatesTokens) - get().usdRates.setStateByKey('loading', false) - }, - fetchAllStoredUsdRates: async (curve: LlamaApi) => { - const tokenAddresses = Object.keys(get().usdRates.tokens) - void get()[sliceKey].fetchUsdRateByTokens(curve, tokenAddresses) - }, - - // slice helpers - setStateByActiveKey: (key: StateKey, activeKey: string, value: T) => { - get().setAppStateByActiveKey(sliceKey, key, activeKey, value) - }, - setStateByKey: (key: StateKey, value: T) => { - get().setAppStateByKey(sliceKey, key, value) - }, - setStateByKeys: (sliceState: Partial) => { - get().setAppStateByKeys(sliceKey, sliceState) - }, - resetState: () => { - get().resetAppState(sliceKey, cloneDeep(DEFAULT_STATE)) - }, - }, -}) - -export default createUsdRatesSlice diff --git a/apps/main/src/loan/store/useStore.ts b/apps/main/src/loan/store/useStore.ts index c46359a9d..40269577a 100644 --- a/apps/main/src/loan/store/useStore.ts +++ b/apps/main/src/loan/store/useStore.ts @@ -27,12 +27,10 @@ import createLoanSwap, { LoanSwapSlice } from '@/loan/store/createLoanSwap' import createOhlcChartSlice, { OhlcChartSlice } from '@/loan/store/createOhlcChartSlice' import createPegKeepersSlice, { PegKeepersSlice } from '@/loan/store/createPegKeepersSlice' import createScrvUsdSlice, { ScrvUsdSlice } from '@/loan/store/createScrvUsdSlice' -import createUsdRatesSlice, { UsdRatesSlice } from '@/loan/store/createUsdRatesSlice' export type State = CacheSlice & AppSlice & GasSlice & - UsdRatesSlice & ChartBandsSlice & CollateralsSlice & LoansSlice & @@ -55,7 +53,6 @@ const store = (set: SetState, get: GetState): State => ({ ...createCacheSlice(set, get), ...createAppSlice(set, get), ...createGasSlice(set, get), - ...createUsdRatesSlice(set, get), ...createChartBandsSlice(set, get), ...createCollateralsSlice(set, get), ...createLoansSlice(set, get), diff --git a/packages/curve-ui-kit/src/features/manage-soft-liquidation/ManageSoftLiquidation.stories.tsx b/packages/curve-ui-kit/src/features/manage-soft-liquidation/ManageSoftLiquidation.stories.tsx index c60f00fa1..c051b3ed6 100644 --- a/packages/curve-ui-kit/src/features/manage-soft-liquidation/ManageSoftLiquidation.stories.tsx +++ b/packages/curve-ui-kit/src/features/manage-soft-liquidation/ManageSoftLiquidation.stories.tsx @@ -1,4 +1,5 @@ import { useState } from 'react' +import { ethAddress } from 'viem' import type { Meta, StoryObj } from '@storybook/react' import { fn } from '@storybook/test' import type { Address } from '@ui-kit/utils' @@ -16,7 +17,7 @@ const debtToken: Token = { const collateralToRecover = [ { symbol: 'ETH', - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' as Address, + address: ethAddress, amount: 26539422, usd: 638000, }, diff --git a/packages/curve-ui-kit/src/features/select-token/SelectToken.stories.tsx b/packages/curve-ui-kit/src/features/select-token/SelectToken.stories.tsx index 0019bec30..bbeb32fcd 100644 --- a/packages/curve-ui-kit/src/features/select-token/SelectToken.stories.tsx +++ b/packages/curve-ui-kit/src/features/select-token/SelectToken.stories.tsx @@ -1,4 +1,5 @@ import { useState } from 'react' +import { ethAddress } from 'viem' import { Button, Stack, Typography } from '@mui/material' import { action } from '@storybook/addon-actions' import type { Meta, StoryObj } from '@storybook/react' @@ -12,7 +13,7 @@ const { Spacing } = SizesAndSpaces const defaultTokens: TokenOption[] = [ { chain: 'ethereum', - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + address: ethAddress, symbol: 'ETH', label: 'Ethereum', volume: 17, diff --git a/packages/curve-ui-kit/src/lib/model/entities/token-usd-rate.ts b/packages/curve-ui-kit/src/lib/model/entities/token-usd-rate.ts new file mode 100644 index 000000000..a276900bb --- /dev/null +++ b/packages/curve-ui-kit/src/lib/model/entities/token-usd-rate.ts @@ -0,0 +1,27 @@ +import { useQueries } from '@tanstack/react-query' +import { getLib, requireLib } from '@ui-kit/features/connect-wallet' +import { combineQueriesToObject } from '@ui-kit/lib' +import { queryFactory, rootKeys, type ChainParams, type TokenParams, type TokenQuery } from '@ui-kit/lib/model/query' +import { tokenValidationSuite } from '@ui-kit/lib/model/query/token-validation' + +const QUERY_KEY_IDENTIFIER = 'usdRate' as const + +export const { + getQueryData: getTokenUsdRateQueryData, + useQuery: useTokenUsdRate, + fetchQuery: fetchTokenUsdRate, + getQueryOptions: getTokenUsdRateQueryOptions, +} = queryFactory({ + queryKey: (params: TokenParams) => [...rootKeys.token(params), QUERY_KEY_IDENTIFIER] as const, + queryFn: ({ tokenAddress }: TokenQuery): Promise => + getLib('curveApi')?.getUsdRate(tokenAddress) ?? requireLib('llamaApi').getUsdRate(tokenAddress), + staleTime: '5m', + refetchInterval: '1m', + validationSuite: tokenValidationSuite, +}) + +export const useTokenUsdRates = ({ chainId, tokenAddresses = [] }: ChainParams & { tokenAddresses?: string[] }) => + useQueries({ + queries: tokenAddresses.map((tokenAddress) => getTokenUsdRateQueryOptions({ chainId, tokenAddress })), + combine: (results) => combineQueriesToObject(results, tokenAddresses), + }) diff --git a/packages/curve-ui-kit/src/lib/model/query/root-keys.ts b/packages/curve-ui-kit/src/lib/model/query/root-keys.ts index 47338d721..b67390af5 100644 --- a/packages/curve-ui-kit/src/lib/model/query/root-keys.ts +++ b/packages/curve-ui-kit/src/lib/model/query/root-keys.ts @@ -8,6 +8,7 @@ export type ChainNameQuery = { blockchainId: Chain } export type ContractQuery = ChainNameQuery & { contractAddress: Address } export type PoolQuery = ChainQuery & { poolId: string } export type GaugeQuery = PoolQuery +export type TokenQuery = ChainQuery & { tokenAddress: string } export type ChainParams = FieldsOf> export type UserParams = FieldsOf> @@ -16,6 +17,7 @@ export type ChainNameParams = FieldsOf export type ContractParams = FieldsOf export type PoolParams = FieldsOf> export type GaugeParams = FieldsOf> +export type TokenParams = FieldsOf export const rootKeys = { chain: ({ chainId }: ChainParams) => ['chain', { chainId }] as const, @@ -25,4 +27,6 @@ export const rootKeys = { contract: ({ blockchainId, contractAddress }: ContractParams) => [...rootKeys.chainName({ blockchainId }), 'contract', { contractAddress }] as const, gauge: ({ chainId, poolId }: GaugeParams) => [...rootKeys.pool({ chainId, poolId }), 'gauge'] as const, + token: ({ chainId, tokenAddress }: TokenParams) => + [...rootKeys.chain({ chainId }), 'token', { tokenAddress }] as const, } as const diff --git a/apps/main/src/lend/entities/token/validation.ts b/packages/curve-ui-kit/src/lib/model/query/token-validation.ts similarity index 90% rename from apps/main/src/lend/entities/token/validation.ts rename to packages/curve-ui-kit/src/lib/model/query/token-validation.ts index 19d23d3f9..978395058 100644 --- a/apps/main/src/lend/entities/token/validation.ts +++ b/packages/curve-ui-kit/src/lib/model/query/token-validation.ts @@ -1,7 +1,7 @@ import { enforce, group, test } from 'vest' -import type { TokenParams } from '@/lend/entities/token/types' import { chainValidationGroup } from '@ui-kit/lib/model/query/chain-validation' import { createValidationSuite } from '@ui-kit/lib/validation' +import type { TokenParams } from './root-keys' export const tokenValidationGroup = ({ chainId, tokenAddress }: TokenParams) => group('tokenValidation', () => { diff --git a/packages/curve-ui-kit/src/lib/queries/combine.ts b/packages/curve-ui-kit/src/lib/queries/combine.ts index 749d3ec4f..f00ef8d39 100644 --- a/packages/curve-ui-kit/src/lib/queries/combine.ts +++ b/packages/curve-ui-kit/src/lib/queries/combine.ts @@ -1,18 +1,8 @@ -import { useCallback } from 'react' -import { useQueries } from '@tanstack/react-query' -import { QueriesOptions, QueriesResults } from '@tanstack/react-query/build/legacy/useQueries' -import { - CombinedQueriesResult, - CombinedQueryMappingResult, - ExtractDataType, - QueryOptionsArray, - QueryResultsArray, -} from './types' +import type { UseQueryOptions } from '@tanstack/react-query' +import { QueryOptionsArray, QueryResultsArray } from './types' /** Combines the metadata of multiple queries into a single object. */ -export const combineQueriesMeta = ( - results: QueryResultsArray, -): Omit, 'data'> => ({ +export const combineQueriesMeta = (results: QueryResultsArray) => ({ isLoading: results.some((result) => result.isLoading), isPending: results.some((result) => result.isPending), isError: results.some((result) => result.isError), @@ -20,29 +10,11 @@ export const combineQueriesMeta = ( }) /** Combines the data and metadata of multiple queries into a single object. */ -const combineQueriesToObject = ( - results: QueryResultsArray, +export const combineQueriesToObject = ( + results: QueryResultsArray[]>, keys: K, -): CombinedQueryMappingResult => ({ - data: Object.fromEntries((results || []).map(({ data }, index) => [keys[index], data])) as Record< - K[number], - ExtractDataType - >, +) => ({ + // Using flatMap instead of map + filter(Boolean), because it's not correctly erasing | undefined from the Record value type + data: Object.fromEntries(results.flatMap(({ data }, index) => (data !== undefined ? [[keys[index], data]] : []))), ...combineQueriesMeta(results), }) - -/** - * Combines multiple queries into a single object with keys for each query - * @param queries The query options to combine - * @param keys The keys to use for each query - * @returns The combined queries in an object - */ -export const useQueryMapping = , TKey extends string[]>( - queries: readonly [...QueriesOptions], - keys: [...TKey], -) => - useQueries({ - // todo: figure out why the type has broken in react-query, related to https://github.com/TanStack/query/pull/8624 - queries: queries as Parameters[0]['queries'], - combine: useCallback((results: QueriesResults) => combineQueriesToObject(results, keys), [keys]), - }) diff --git a/packages/curve-ui-kit/src/lib/queries/types.ts b/packages/curve-ui-kit/src/lib/queries/types.ts index a7fa279dd..70e5a3dd3 100644 --- a/packages/curve-ui-kit/src/lib/queries/types.ts +++ b/packages/curve-ui-kit/src/lib/queries/types.ts @@ -2,10 +2,6 @@ import { UseQueryOptions, UseQueryResult } from '@tanstack/react-query' export type QueryOptionsArray = readonly UseQueryOptions[] -export type ExtractDataType = T extends UseQueryOptions ? TData : unknown - -export type CombinedDataType = ExtractDataType[] - export type QueryResultsArray = { [K in keyof T]: T[K] extends UseQueryOptions ? UseQueryResult : never } @@ -14,9 +10,3 @@ export type PartialQueryResult = Pick< UseQueryResult, 'data' | 'isLoading' | 'isPending' | 'isError' | 'isFetching' > - -export type CombinedQueriesResult = PartialQueryResult> - -export type CombinedQueryMappingResult = PartialQueryResult< - Record[number]> ->