From 3f5e116b86dab77ea6878911ddfe363be8ff40c4 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 7 Sep 2023 22:22:16 +0600 Subject: [PATCH 1/3] fix(usd-amount): debounce deletion currency from USD price queue --- .../usdAmount/state/usdRawPricesAtom.ts | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts b/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts index 66d35add69..039cd56ea3 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts @@ -2,6 +2,8 @@ import { atom } from 'jotai' import { Fraction, Token } from '@uniswap/sdk-core' +import ms from 'ms.macro' + import { deepEqual } from 'utils/deepEqual' export interface UsdRawPriceState { @@ -13,14 +15,21 @@ export interface UsdRawPriceState { export type UsdRawPrices = { [tokenAddress: string]: UsdRawPriceState } +const DELETION_FROM_QUEUE_DEBOUNCE = ms`5s` + export const currenciesUsdPriceQueueAtom = atom<{ [tokenAddress: string]: Token }>({}) export const usdRawPricesAtom = atom({}) +const currenciesToDeleteFromQueueAtom = atom<{ [tokenAddress: string]: Token | undefined }>({}) + export const addCurrencyToUsdPriceQueue = atom(null, (get, set, currency: Token) => { const currencyAddress = currency.address.toLowerCase() const currenciesToLoadUsdPrice = get(currenciesUsdPriceQueueAtom) + // Remove the currency from deletion queue + set(currenciesToDeleteFromQueueAtom, { ...get(currenciesToDeleteFromQueueAtom), [currencyAddress]: undefined }) + if (!currenciesToLoadUsdPrice[currencyAddress]) { set(currenciesUsdPriceQueueAtom, { ...currenciesToLoadUsdPrice, @@ -32,12 +41,27 @@ export const addCurrencyToUsdPriceQueue = atom(null, (get, set, currency: Token) export const removeCurrencyToUsdPriceFromQueue = atom(null, (get, set, currency: Token) => { const currencyAddress = currency.address.toLowerCase() const currenciesToLoadUsdPrice = get(currenciesUsdPriceQueueAtom) + const currenciesToDeleteFromQueue = get(currenciesToDeleteFromQueueAtom) + + const isCurrencyInUsdPriceQueue = !!currenciesToLoadUsdPrice[currencyAddress] + const isCurrencyInDeletionQueue = !!currenciesToDeleteFromQueue[currencyAddress] + + if (isCurrencyInUsdPriceQueue && !isCurrencyInDeletionQueue) { + // Add the currency from deletion queue + set(currenciesToDeleteFromQueueAtom, { ...currenciesToDeleteFromQueue, [currencyAddress]: currency }) + + setTimeout(() => { + const currenciesToLoadUsdPrice = { ...get(currenciesUsdPriceQueueAtom) } + + // Remove the currency from USD price queue only if it's not added again + if (get(currenciesToDeleteFromQueueAtom)[currencyAddress]) { + set(currenciesToDeleteFromQueueAtom, { ...get(currenciesToDeleteFromQueueAtom), [currencyAddress]: undefined }) - if (currenciesToLoadUsdPrice[currencyAddress]) { - const stateCopy = { ...currenciesToLoadUsdPrice } - delete stateCopy[currencyAddress] + delete currenciesToLoadUsdPrice[currencyAddress] - set(currenciesUsdPriceQueueAtom, stateCopy) + set(currenciesUsdPriceQueueAtom, currenciesToLoadUsdPrice) + } + }, DELETION_FROM_QUEUE_DEBOUNCE) } }) From 91f2e8737538f5745c0344624384bb06c35f29cb Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 7 Sep 2023 23:05:22 +0600 Subject: [PATCH 2/3] fix(usd-amount): delete token from queue only if there are no subscribers left --- .../usdAmount/state/usdRawPricesAtom.ts | 56 +++++++++---------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts b/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts index 039cd56ea3..9fe741c9d1 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/state/usdRawPricesAtom.ts @@ -2,8 +2,6 @@ import { atom } from 'jotai' import { Fraction, Token } from '@uniswap/sdk-core' -import ms from 'ms.macro' - import { deepEqual } from 'utils/deepEqual' export interface UsdRawPriceState { @@ -15,53 +13,51 @@ export interface UsdRawPriceState { export type UsdRawPrices = { [tokenAddress: string]: UsdRawPriceState } -const DELETION_FROM_QUEUE_DEBOUNCE = ms`5s` +const usdPriceQueueSubscribersCountAtom = atom<{ [tokenAddress: string]: number }>({}) export const currenciesUsdPriceQueueAtom = atom<{ [tokenAddress: string]: Token }>({}) export const usdRawPricesAtom = atom({}) -const currenciesToDeleteFromQueueAtom = atom<{ [tokenAddress: string]: Token | undefined }>({}) +export const addCurrencyToUsdPriceQueue = atom(null, (get, set, token: Token) => { + const currencyAddress = token.address.toLowerCase() + const usdPriceQueueSubscribersCount = get(usdPriceQueueSubscribersCountAtom) + const currenciesToLoadUsdPrice = { ...get(currenciesUsdPriceQueueAtom) } -export const addCurrencyToUsdPriceQueue = atom(null, (get, set, currency: Token) => { - const currencyAddress = currency.address.toLowerCase() - const currenciesToLoadUsdPrice = get(currenciesUsdPriceQueueAtom) + const subscribersCount = (usdPriceQueueSubscribersCount[currencyAddress] || 0) + 1 - // Remove the currency from deletion queue - set(currenciesToDeleteFromQueueAtom, { ...get(currenciesToDeleteFromQueueAtom), [currencyAddress]: undefined }) + // Increase the subscribers count + set(usdPriceQueueSubscribersCountAtom, { + ...usdPriceQueueSubscribersCount, + [currencyAddress]: subscribersCount, + }) if (!currenciesToLoadUsdPrice[currencyAddress]) { set(currenciesUsdPriceQueueAtom, { ...currenciesToLoadUsdPrice, - [currencyAddress]: currency, + [currencyAddress]: token, }) } }) -export const removeCurrencyToUsdPriceFromQueue = atom(null, (get, set, currency: Token) => { - const currencyAddress = currency.address.toLowerCase() - const currenciesToLoadUsdPrice = get(currenciesUsdPriceQueueAtom) - const currenciesToDeleteFromQueue = get(currenciesToDeleteFromQueueAtom) - - const isCurrencyInUsdPriceQueue = !!currenciesToLoadUsdPrice[currencyAddress] - const isCurrencyInDeletionQueue = !!currenciesToDeleteFromQueue[currencyAddress] - - if (isCurrencyInUsdPriceQueue && !isCurrencyInDeletionQueue) { - // Add the currency from deletion queue - set(currenciesToDeleteFromQueueAtom, { ...currenciesToDeleteFromQueue, [currencyAddress]: currency }) +export const removeCurrencyToUsdPriceFromQueue = atom(null, (get, set, token: Token) => { + const currencyAddress = token.address.toLowerCase() + const usdPriceQueueSubscribersCount = get(usdPriceQueueSubscribersCountAtom) + const currenciesToLoadUsdPrice = { ...get(currenciesUsdPriceQueueAtom) } - setTimeout(() => { - const currenciesToLoadUsdPrice = { ...get(currenciesUsdPriceQueueAtom) } + const subscribersCount = Math.max((usdPriceQueueSubscribersCount[currencyAddress] || 0) - 1, 0) - // Remove the currency from USD price queue only if it's not added again - if (get(currenciesToDeleteFromQueueAtom)[currencyAddress]) { - set(currenciesToDeleteFromQueueAtom, { ...get(currenciesToDeleteFromQueueAtom), [currencyAddress]: undefined }) + // Decrease the subscribers count + set(usdPriceQueueSubscribersCountAtom, { + ...usdPriceQueueSubscribersCount, + [currencyAddress]: subscribersCount, + }) - delete currenciesToLoadUsdPrice[currencyAddress] + // If there are no subscribers, then delete the token from queue + if (subscribersCount === 0) { + delete currenciesToLoadUsdPrice[currencyAddress] - set(currenciesUsdPriceQueueAtom, currenciesToLoadUsdPrice) - } - }, DELETION_FROM_QUEUE_DEBOUNCE) + set(currenciesUsdPriceQueueAtom, currenciesToLoadUsdPrice) } }) From a3993015692ba3c6d47e9a99c872de70322ab440 Mon Sep 17 00:00:00 2001 From: Alexandr Kazachenko Date: Thu, 7 Sep 2023 23:21:56 +0600 Subject: [PATCH 3/3] chore: debounce usd prices queue to avoid excessive requests --- .../modules/usdAmount/updaters/UsdPricesUpdater.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts b/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts index 4be20f6d85..b2dfe59839 100644 --- a/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts +++ b/apps/cowswap-frontend/src/modules/usdAmount/updaters/UsdPricesUpdater.ts @@ -8,6 +8,7 @@ import ms from 'ms.macro' import useSWR, { SWRConfiguration } from 'swr' import { USDC } from 'legacy/constants/tokens' +import useDebounce from 'legacy/hooks/useDebounce' import { useWalletInfo } from 'modules/wallet' @@ -31,6 +32,8 @@ const swrOptions: SWRConfiguration = { revalidateOnFocus: true, } +const USD_PRICES_QUEUE_DEBOUNCE_TIME = ms`0.5s` + export function UsdPricesUpdater() { const { chainId } = useWalletInfo() const setUsdPrices = useSetAtom(usdRawPricesAtom) @@ -40,15 +43,17 @@ export function UsdPricesUpdater() { const queue = useMemo(() => Object.values(currenciesUsdPriceQueue), [currenciesUsdPriceQueue]) + const debouncedQueue = useDebounce(queue, USD_PRICES_QUEUE_DEBOUNCE_TIME) + const swrResponse = useSWR( - ['UsdPricesUpdater', queue, chainId], + ['UsdPricesUpdater', debouncedQueue, chainId], () => { const getUsdcPrice = usdcPriceLoader(chainId) - setUsdPricesLoading(queue) + setUsdPricesLoading(debouncedQueue) - return processQueue(queue, getUsdcPrice).catch((error) => { - resetUsdPrices(queue) + return processQueue(debouncedQueue, getUsdcPrice).catch((error) => { + resetUsdPrices(debouncedQueue) return Promise.reject(error) })