diff --git a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useGetInitialPrice.ts b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useGetInitialPrice.ts index 07d05ef4db..c15e1ba49b 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useGetInitialPrice.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useGetInitialPrice.ts @@ -1,87 +1,45 @@ import { useEffect, useState } from 'react' import { useIsWindowVisible } from '@cowprotocol/common-hooks' -import { getAddress, getIsNativeToken } from '@cowprotocol/common-utils' +import { getWrappedToken } from '@cowprotocol/common-utils' import { useWalletInfo } from '@cowprotocol/wallet' import { Currency, Fraction } from '@uniswap/sdk-core' -import * as Sentry from '@sentry/browser' import ms from 'ms.macro' import { useAsyncMemo } from 'use-async-memo' import { useLimitOrdersDerivedState } from 'modules/limitOrders/hooks/useLimitOrdersDerivedState' -import { parsePrice } from 'modules/limitOrders/utils/parsePrice' -import { getNativePrice } from 'api/cowProtocol' import { useSafeMemo } from 'common/hooks/useSafeMemo' -type PriceResult = number | Error | undefined +import { fetchCurrencyUsdPrice, usdcPriceLoader } from '../../usdAmount' const PRICE_UPDATE_INTERVAL = ms`10sec` -async function requestPriceForCurrency(chainId: number | undefined, currency: Currency | null): Promise { - const currencyAddress = getAddress(currency) - - if (!chainId || !currency) { - return - } - - try { - if (getIsNativeToken(currency) || !currencyAddress) { - return parsePrice(1, currency) - } - - const result = await getNativePrice(chainId, currencyAddress) - - if (!result) { - throw new Error('No result from native_price endpoint') - } - - const price = parsePrice(result.price || 0, currency) - if (!price) { - throw new Error("Couldn't parse native_price result") - } - - return price - } catch (error: any) { - console.warn('[requestPriceForCurrency] Error fetching native_price', error) - - const sentryError = Object.assign(error, { - message: error.message || 'Error fetching native_price ', - name: 'NativePriceFetchError', - }) - - const params = { - chainId, - tokenAddress: currencyAddress, - tokenName: currency?.name, - tokenSymbol: currency.symbol, - } - - Sentry.captureException(sentryError, { - contexts: { - params, - }, - }) - - return error - } -} - export async function requestPrice( chainId: number | undefined, inputCurrency: Currency | null, - outputCurrency: Currency | null + outputCurrency: Currency | null, ): Promise { + if (!chainId || !inputCurrency || !outputCurrency) { + return null + } + + const inputToken = getWrappedToken(inputCurrency) + const outputToken = getWrappedToken(outputCurrency) + + // Only needed for the fallback CoW price, which needs to know the USDC price + const getUsdPrice = usdcPriceLoader(chainId) + return Promise.all([ - requestPriceForCurrency(chainId, inputCurrency), - requestPriceForCurrency(chainId, outputCurrency), + fetchCurrencyUsdPrice(inputToken, getUsdPrice), + fetchCurrencyUsdPrice(outputToken, getUsdPrice), ]).then(([inputPrice, outputPrice]) => { - if (!inputPrice || !outputPrice || inputPrice instanceof Error || outputPrice instanceof Error) { + if (!inputPrice || !outputPrice) { return null } - const result = new Fraction(inputPrice, outputPrice) + const result = inputPrice.divide(outputPrice) console.debug('Updated limit orders initial price: ', result.toSignificant(18)) @@ -91,7 +49,6 @@ export async function requestPrice( // Fetches the INPUT and OUTPUT price and calculates initial Active rate // When return null it means we failed on price loading -// TODO: rename it to useNativeBasedPrice export function useGetInitialPrice(): { price: Fraction | null; isLoading: boolean } { const { chainId } = useWalletInfo() const { inputCurrency, outputCurrency } = useLimitOrdersDerivedState() @@ -100,16 +57,18 @@ export function useGetInitialPrice(): { price: Fraction | null; isLoading: boole const isWindowVisible = useIsWindowVisible() const price = useAsyncMemo( - () => { + async () => { setIsLoading(true) console.debug('[useGetInitialPrice] Fetching price') - return requestPrice(chainId, inputCurrency, outputCurrency).finally(() => { + try { + return await requestPrice(chainId, inputCurrency, outputCurrency) + } finally { setIsLoading(false) - }) + } }, [chainId, inputCurrency, outputCurrency, updateTimestamp], - null + null, ) // Update initial price every 10 seconds diff --git a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useUpdateActiveRate.ts b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useUpdateActiveRate.ts index ce86b30432..24a38f5c72 100644 --- a/apps/cowswap-frontend/src/modules/limitOrders/hooks/useUpdateActiveRate.ts +++ b/apps/cowswap-frontend/src/modules/limitOrders/hooks/useUpdateActiveRate.ts @@ -73,6 +73,6 @@ export function useUpdateActiveRate(): UpdateRateCallback { inputCurrencyAmount, outputCurrencyAmount, updateLimitOrdersState, - ] + ], ) } diff --git a/apps/cowswap-frontend/src/modules/usdAmount/index.ts b/apps/cowswap-frontend/src/modules/usdAmount/index.ts index faa962e9e2..e0d7593382 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/index.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/index.ts @@ -2,3 +2,5 @@ export { useTradeUsdAmounts } from './hooks/useTradeUsdAmounts' export { UsdPricesUpdater } from './updaters/UsdPricesUpdater' export { useUsdAmount } from './hooks/useUsdAmount' export { useUsdPrice } from './hooks/useUsdPrice' +export { usdcPriceLoader } from './utils/usdcPriceLoader' +export { fetchCurrencyUsdPrice } from './services/fetchCurrencyUsdPrice' diff --git a/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts b/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts index ebf048cdc0..b85d792e2b 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts @@ -1,16 +1,13 @@ import { useAtomValue, useSetAtom } from 'jotai' import { useEffect, useMemo } from 'react' -import { USDC } from '@cowprotocol/common-const' import { getWrappedToken } from '@cowprotocol/common-utils' -import { SupportedChainId } from '@cowprotocol/cow-sdk' import { useWalletInfo } from '@cowprotocol/wallet' import { Fraction, Token } from '@uniswap/sdk-core' import ms from 'ms.macro' import useSWR, { SWRConfiguration } from 'swr' -import { getCowProtocolNativePrice } from '../apis/getCowProtocolNativePrice' import { fetchCurrencyUsdPrice } from '../services/fetchCurrencyUsdPrice' import { currenciesUsdPriceQueueAtom, @@ -19,6 +16,7 @@ import { usdRawPricesAtom, UsdRawPriceState, } from '../state/usdRawPricesAtom' +import { usdcPriceLoader } from '../utils/usdcPriceLoader' const swrOptions: SWRConfiguration = { refreshInterval: ms`60s`, @@ -66,19 +64,6 @@ export function UsdPricesUpdater() { return null } -function usdcPriceLoader(chainId: SupportedChainId): () => Promise { - let usdcPricePromise: Promise | null = null - - return () => { - // Cache the result to avoid fetching it multiple times - if (!usdcPricePromise) { - usdcPricePromise = getCowProtocolNativePrice(USDC[chainId]) - } - - return usdcPricePromise - } -} - async function processQueue(queue: Token[], getUsdcPrice: () => Promise): Promise { const results = await Promise.all( queue.map(async (currency) => { diff --git a/apps/cowswap-frontend/src/modules/usdAmount/utils/usdcPriceLoader.ts b/apps/cowswap-frontend/src/modules/usdAmount/utils/usdcPriceLoader.ts new file mode 100644 index 0000000000..9a45b51f05 --- /dev/null +++ b/apps/cowswap-frontend/src/modules/usdAmount/utils/usdcPriceLoader.ts @@ -0,0 +1,9 @@ +import { USDC } from '@cowprotocol/common-const' +import { SupportedChainId } from '@cowprotocol/cow-sdk' +import { Fraction } from '@uniswap/sdk-core' + +import { getCowProtocolNativePrice } from '../apis/getCowProtocolNativePrice' + +export function usdcPriceLoader(chainId: SupportedChainId): () => Promise { + return () => getCowProtocolNativePrice(USDC[chainId]) +}