diff --git a/earn/src/components/advanced/SmartWalletButton.tsx b/earn/src/components/advanced/SmartWalletButton.tsx index ec0cc524..2ee0bf96 100644 --- a/earn/src/components/advanced/SmartWalletButton.tsx +++ b/earn/src/components/advanced/SmartWalletButton.tsx @@ -1,11 +1,11 @@ import TokenIcons from 'shared/lib/components/common/TokenIcons'; import { Display } from 'shared/lib/components/common/Typography'; import { Token } from 'shared/lib/data/Token'; +import { rgba } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import { ReactComponent as PlusIcon } from '../../assets/svg/plus.svg'; import useProminentColor from '../../data/hooks/UseProminentColor'; -import { rgba } from '../../util/Colors'; const Container = styled.button.attrs( (props: { backgroundGradient: string; active: boolean; $animate: boolean }) => props diff --git a/earn/src/components/advanced/TokenAllocationPieChartWidget.tsx b/earn/src/components/advanced/TokenAllocationPieChartWidget.tsx index e489aa1c..4f117828 100644 --- a/earn/src/components/advanced/TokenAllocationPieChartWidget.tsx +++ b/earn/src/components/advanced/TokenAllocationPieChartWidget.tsx @@ -5,11 +5,11 @@ import { sqrtRatioToPrice, sqrtRatioToTick } from 'shared/lib/data/BalanceSheet' import { RESPONSIVE_BREAKPOINT_MD } from 'shared/lib/data/constants/Breakpoints'; import { GREY_800 } from 'shared/lib/data/constants/Colors'; import { GN } from 'shared/lib/data/GoodNumber'; +import { rgb, rgba } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import tw from 'twin.macro'; import { BorrowerNftBorrower } from '../../data/BorrowerNft'; -import { rgb, rgba } from '../../util/Colors'; // MARK: Capturing Mouse Data on container div --------------------------------------- diff --git a/earn/src/components/markets/borrow/BorrowingWidget.tsx b/earn/src/components/markets/borrow/BorrowingWidget.tsx index bf8a3fe1..cdbdef1c 100644 --- a/earn/src/components/markets/borrow/BorrowingWidget.tsx +++ b/earn/src/components/markets/borrow/BorrowingWidget.tsx @@ -14,13 +14,13 @@ import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentS import { LendingPair, LendingPairBalancesMap } from 'shared/lib/data/LendingPair'; import { Token } from 'shared/lib/data/Token'; import { fetchUniswapNFTPositions, UniswapNFTPosition } from 'shared/lib/data/Uniswap'; +import { rgba } from 'shared/lib/util/Colors'; import { formatTokenAmount, roundPercentage } from 'shared/lib/util/Numbers'; import styled from 'styled-components'; import { Address, Chain } from 'viem'; import { Config, useClient } from 'wagmi'; import { BorrowerNftBorrower } from '../../../data/BorrowerNft'; -import { rgba } from '../../../util/Colors'; import { useEthersProvider } from '../../../util/Provider'; import HealthGauge from '../../common/HealthGauge'; import BorrowModal from '../modal/BorrowModal'; diff --git a/earn/src/components/portfolio/AssetBar.tsx b/earn/src/components/portfolio/AssetBar.tsx index 0f6f6c31..c175c175 100644 --- a/earn/src/components/portfolio/AssetBar.tsx +++ b/earn/src/components/portfolio/AssetBar.tsx @@ -3,12 +3,12 @@ import React, { useEffect, useMemo, useState } from 'react'; import { RESPONSIVE_BREAKPOINT_SM } from 'shared/lib/data/constants/Breakpoints'; import useHover from 'shared/lib/data/hooks/UseHover'; import { Token } from 'shared/lib/data/Token'; +import { rgb } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import { SearchBar } from './SearchBar'; import { ReactComponent as MoreIcon } from '../../assets/svg/more_ellipsis.svg'; import { TokenBalance } from '../../pages/PortfolioPage'; -import { rgb } from '../../util/Colors'; export type AssetBarItem = { token: Token; diff --git a/earn/src/components/portfolio/PortfolioGrid.tsx b/earn/src/components/portfolio/PortfolioGrid.tsx index 7fa6711b..fa0c0723 100644 --- a/earn/src/components/portfolio/PortfolioGrid.tsx +++ b/earn/src/components/portfolio/PortfolioGrid.tsx @@ -1,13 +1,13 @@ import { Display, Text } from 'shared/lib/components/common/Typography'; import { GREY_800 } from 'shared/lib/data/constants/Colors'; import { Token } from 'shared/lib/data/Token'; +import { rgb } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import AssetPriceChartWidget from './AssetPriceChartWidget'; import PortfolioMetrics from './PortfolioMetrics'; import { RESPONSIVE_BREAKPOINT_SM } from '../../data/constants/Breakpoints'; import { TokenBalance, TokenPriceData, TokenQuote } from '../../pages/PortfolioPage'; -import { rgb } from '../../util/Colors'; const STATUS_GREEN = 'rgba(0, 196, 140, 1)'; const STATUS_GREEN_LIGHT = 'rgba(0, 196, 140, 0.75)'; diff --git a/earn/src/components/portfolio/PortfolioMetrics.tsx b/earn/src/components/portfolio/PortfolioMetrics.tsx index e245dcb3..f27d0883 100644 --- a/earn/src/components/portfolio/PortfolioMetrics.tsx +++ b/earn/src/components/portfolio/PortfolioMetrics.tsx @@ -2,12 +2,12 @@ import { useState } from 'react'; import { Display, Text } from 'shared/lib/components/common/Typography'; import { Token } from 'shared/lib/data/Token'; +import { rgba } from 'shared/lib/util/Colors'; import { formatTokenAmount, roundPercentage } from 'shared/lib/util/Numbers'; import { APYContainer, BalanceContainer, PieChartContainer } from './PortfolioGrid'; import PortfolioPieChartWidget, { PortfolioPieChartSlice } from './PortfolioPieChartWidget'; import { TokenBalance } from '../../pages/PortfolioPage'; -import { rgba } from '../../util/Colors'; export type PortfolioMetricsProps = { balances: TokenBalance[]; diff --git a/earn/src/components/portfolio/SearchBar.tsx b/earn/src/components/portfolio/SearchBar.tsx index 326d72a9..3435bfac 100644 --- a/earn/src/components/portfolio/SearchBar.tsx +++ b/earn/src/components/portfolio/SearchBar.tsx @@ -3,13 +3,13 @@ import { useMemo, useState } from 'react'; import { DropdownOption } from 'shared/lib/components/common/Dropdown'; import { RESPONSIVE_BREAKPOINT_TABLET } from 'shared/lib/data/constants/Breakpoints'; import { Token } from 'shared/lib/data/Token'; +import { rgb } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import { AssetBarItem, AssetChunk } from './AssetBar'; import SearchInput from './TokenSearchInput'; import { ReactComponent as BackArrowIcon } from '../../assets/svg/back_arrow.svg'; import { TokenBalance } from '../../pages/PortfolioPage'; -import { rgb } from '../../util/Colors'; const Container = styled.div` position: relative; diff --git a/earn/src/data/MarginAccount.ts b/earn/src/data/MarginAccount.ts index e50aab49..5961754d 100644 --- a/earn/src/data/MarginAccount.ts +++ b/earn/src/data/MarginAccount.ts @@ -127,7 +127,7 @@ export async function fetchBorrowerDatas( // Fetch all the data for the margin accounts addresses.forEach((accountAddress) => { const uniswapPool = borrowerUniswapPools[accountAddress].callsReturnContext[0].returnValues[0]; - const uniswapPoolInfo = uniswapPoolDataMap.get(uniswapPool.toLowerCase()) ?? null; + const uniswapPoolInfo = uniswapPoolDataMap.get(uniswapPool) ?? null; if (uniswapPoolInfo === null) return; diff --git a/earn/src/data/hooks/UseAvailablePools.ts b/earn/src/data/hooks/UseAvailablePools.ts deleted file mode 100644 index 2c781e3e..00000000 --- a/earn/src/data/hooks/UseAvailablePools.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { useEffect } from 'react'; - -import { ethers } from 'ethers'; -import { uniswapV3PoolAbi } from 'shared/lib/abis/UniswapV3Pool'; -import { ALOE_II_FACTORY_ADDRESS } from 'shared/lib/data/constants/ChainSpecific'; -import useChain from 'shared/lib/data/hooks/UseChain'; -import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentState'; -import { getToken } from 'shared/lib/data/TokenData'; -import { Address } from 'viem'; -import { useClient, Config } from 'wagmi'; - -import { useEthersProvider } from '../../util/Provider'; -import { UNISWAP_POOL_DENYLIST } from '../constants/Addresses'; -import { TOPIC0_CREATE_MARKET_EVENT } from '../constants/Signatures'; -import { UniswapPoolInfo } from '../MarginAccount'; - -// TODO: Deprecate this, it sucks -export default function useAvailablePools() { - const activeChain = useChain(); - const client = useClient({ chainId: activeChain.id }); - const provider = useEthersProvider(client); - const [availablePools, setAvailablePools] = useChainDependentState( - new Map(), - activeChain.id - ); - useEffect(() => { - (async () => { - if (!provider) return; - // NOTE: Use chainId from provider instead of `activeChain.id` since one may update before the other - // when rendering. We want to stay consistent to avoid fetching things from the wrong address. - const chainId = (await provider.getNetwork()).chainId; - let logs: ethers.providers.Log[] = []; - try { - logs = await provider.getLogs({ - fromBlock: 0, - toBlock: 'latest', - address: ALOE_II_FACTORY_ADDRESS[chainId], - topics: [TOPIC0_CREATE_MARKET_EVENT], - }); - } catch (e) { - console.error(e); - } - - const poolAddresses = logs - .map((e) => `0x${e.topics[1].slice(-40)}`) - .filter((addr) => { - return !UNISWAP_POOL_DENYLIST.includes(addr.toLowerCase()); - }); - const poolInfoTuples = await Promise.all( - poolAddresses.map((addr) => { - const poolContract = new ethers.Contract(addr, uniswapV3PoolAbi, provider); - return Promise.all([poolContract.token0(), poolContract.token1(), poolContract.fee()]); - }) - ); // TODO: multicall - - const poolInfoMap = new Map(); - poolAddresses.forEach((addr, i) => { - const token0 = getToken(chainId, poolInfoTuples[i][0] as Address); - const token1 = getToken(chainId, poolInfoTuples[i][1] as Address); - const fee = poolInfoTuples[i][2] as number; - if (token0 && token1) poolInfoMap.set(addr.toLowerCase(), { token0, token1, fee }); - }); - - setAvailablePools(poolInfoMap); - })(); - }, [provider, setAvailablePools]); - - return availablePools; -} diff --git a/earn/src/data/hooks/UseProminentColor.ts b/earn/src/data/hooks/UseProminentColor.ts index c7bb9c83..b1f61826 100644 --- a/earn/src/data/hooks/UseProminentColor.ts +++ b/earn/src/data/hooks/UseProminentColor.ts @@ -1,7 +1,8 @@ import { useEffect, useState } from 'react'; -import { getProminentColor } from '../../util/Colors'; +import { getProminentColor } from 'shared/lib/util/Colors'; +// TODO: deprecate this in favor of useTokenColors export default function useProminentColor(iconPath: string) { const [prominentColor, setProminentColor] = useState('0, 0, 0'); useEffect(() => { diff --git a/earn/src/pages/AdvancedPage.tsx b/earn/src/pages/AdvancedPage.tsx index b822880d..7c606bba 100644 --- a/earn/src/pages/AdvancedPage.tsx +++ b/earn/src/pages/AdvancedPage.tsx @@ -13,6 +13,8 @@ import { GN } from 'shared/lib/data/GoodNumber'; import useChain from 'shared/lib/data/hooks/UseChain'; import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentState'; import { useLendingPair, useLendingPairs } from 'shared/lib/data/hooks/UseLendingPairs'; +import { useTokenColors } from 'shared/lib/data/hooks/UseTokenColors'; +import { useUniswapPools } from 'shared/lib/data/hooks/UseUniswapPools'; import { Token } from 'shared/lib/data/Token'; import { fetchUniswapNFTPositions, UniswapNFTPosition } from 'shared/lib/data/Uniswap'; import { getEtherscanUrlForChain } from 'shared/lib/util/Chains'; @@ -37,9 +39,7 @@ import { UniswapPositionList } from '../components/advanced/UniswapPositionList' import PendingTxnModal, { PendingTxnModalStatus } from '../components/common/PendingTxnModal'; import { BorrowerNftBorrower, fetchListOfBorrowerNfts } from '../data/BorrowerNft'; import { RESPONSIVE_BREAKPOINT_SM } from '../data/constants/Breakpoints'; -import useAvailablePools from '../data/hooks/UseAvailablePools'; import { fetchBorrowerDatas } from '../data/MarginAccount'; -import { getProminentColor } from '../util/Colors'; import { useEthersProvider } from '../util/Provider'; const BORROW_TITLE_TEXT_COLOR = 'rgba(130, 160, 182, 1)'; @@ -157,7 +157,6 @@ export default function AdvancedPage() { const [openedModal, setOpenedModal] = useState(OpenedModal.NONE); const [pendingTxn, setPendingTxn] = useState(null); const [pendingTxnModalStatus, setPendingTxnModalStatus] = useState(null); - const [tokenColors, setTokenColors] = useChainDependentState>(new Map(), activeChain.id); const [searchParams, setSearchParams] = useSearchParams(); @@ -172,13 +171,14 @@ export default function AdvancedPage() { }, [borrowerNftBorrowers, searchParams]); const { lendingPairs } = useLendingPairs(activeChain.id); + const { data: tokenColors } = useTokenColors(lendingPairs); const market = useLendingPair( lendingPairs, selectedMarginAccount?.token0.address, selectedMarginAccount?.token1.address ); - const availablePools = useAvailablePools(); + const availablePools = useUniswapPools(lendingPairs); // MARK: Fetch margin accounts useEffect(() => { @@ -204,29 +204,6 @@ export default function AdvancedPage() { })(); }, [userAddress, provider, availablePools, setBorrowerNftBorrowers]); - const uniqueTokens = useMemo(() => { - const tokenSet = new Set(); - borrowerNftBorrowers?.forEach((borrower) => { - tokenSet.add(borrower.token0); - tokenSet.add(borrower.token1); - }); - return Array.from(tokenSet.values()); - }, [borrowerNftBorrowers]); - - // MARK: Computing token colors - useEffect(() => { - (async () => { - // Compute colors for each token logo (local, but still async) - const colorPromises = uniqueTokens.map((token) => getProminentColor(token.logoURI || '')); - const colors = await Promise.all(colorPromises); - - // Convert response to the desired Map format - const addressToColorMap: Map = new Map(); - uniqueTokens.forEach((token, index) => addressToColorMap.set(token.address, colors[index])); - setTokenColors(addressToColorMap); - })(); - }, [uniqueTokens, setTokenColors]); - // MARK: Reset search param if margin account doesn't exist useEffect(() => { if ( @@ -387,7 +364,7 @@ export default function AdvancedPage() { withdrawableUniswapNFTs={withdrawableUniswapNFTPositions} setPendingTxn={setPendingTxn} /> - + {selectedMarginAccount && (
diff --git a/earn/src/pages/BoostPage.tsx b/earn/src/pages/BoostPage.tsx index f85a4cec..15d0a787 100644 --- a/earn/src/pages/BoostPage.tsx +++ b/earn/src/pages/BoostPage.tsx @@ -12,6 +12,7 @@ import { GN } from 'shared/lib/data/GoodNumber'; import useChain from 'shared/lib/data/hooks/UseChain'; import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentState'; import { UniswapNFTPosition, computePoolAddress, fetchUniswapNFTPositions } from 'shared/lib/data/Uniswap'; +import { getProminentColor, rgb } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import { Address } from 'viem'; import { Config, useAccount, useClient, useReadContracts } from 'wagmi'; @@ -20,7 +21,6 @@ import BoostCard from '../components/boost/BoostCard'; import { BoostCardPlaceholder } from '../components/boost/BoostCardPlaceholder'; import NoPositions from '../components/boost/NoPositions'; import { BoostCardInfo, BoostCardType, fetchBoostBorrower, fetchBoostBorrowersList } from '../data/Uniboost'; -import { getProminentColor, rgb } from '../util/Colors'; import { useEthersProvider } from '../util/Provider'; const DEFAULT_COLOR0 = 'white'; @@ -197,6 +197,7 @@ export default function BoostPage() { /*////////////////////////////////////////////////////////////// COMPUTE COLORS //////////////////////////////////////////////////////////////*/ + // TODO: useTokenColors useEffect(() => { (async () => { const tokenLogos = new Set(); diff --git a/earn/src/pages/MarketsPage.tsx b/earn/src/pages/MarketsPage.tsx index 431d4631..bd70657b 100644 --- a/earn/src/pages/MarketsPage.tsx +++ b/earn/src/pages/MarketsPage.tsx @@ -6,16 +6,16 @@ import AppPage from 'shared/lib/components/common/AppPage'; import { Display, Text } from 'shared/lib/components/common/Typography'; import { getChainLogo } from 'shared/lib/data/constants/ChainSpecific'; import { GREY_400, GREY_600 } from 'shared/lib/data/constants/Colors'; -import { GetNumericFeeTier } from 'shared/lib/data/FeeTier'; import useChain from 'shared/lib/data/hooks/UseChain'; import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentState'; import { useLendingPairsBalances } from 'shared/lib/data/hooks/UseLendingPairBalances'; import { useLendingPairs } from 'shared/lib/data/hooks/UseLendingPairs'; import { useLatestPriceRelay } from 'shared/lib/data/hooks/UsePriceRelay'; +import { useTokenColors } from 'shared/lib/data/hooks/UseTokenColors'; +import { useUniswapPools } from 'shared/lib/data/hooks/UseUniswapPools'; import { Token } from 'shared/lib/data/Token'; import { formatUSDAuto } from 'shared/lib/util/Numbers'; import styled from 'styled-components'; -import { Address } from 'viem'; import { linea } from 'viem/chains'; import { Config, useAccount, useBlockNumber, useClient, usePublicClient, useWatchBlockNumber } from 'wagmi'; @@ -26,8 +26,7 @@ import InfoTab from '../components/markets/monitor/InfoTab'; import SupplyTable, { SupplyTableRow } from '../components/markets/supply/SupplyTable'; import { BorrowerNftBorrower, fetchListOfFuse2BorrowNfts } from '../data/BorrowerNft'; import { ZERO_ADDRESS } from '../data/constants/Addresses'; -import { fetchBorrowerDatas, UniswapPoolInfo } from '../data/MarginAccount'; -import { getProminentColor } from '../util/Colors'; +import { fetchBorrowerDatas } from '../data/MarginAccount'; import { useEthersProvider } from '../util/Provider'; const SECONDARY_COLOR = 'rgba(130, 160, 182, 1)'; @@ -80,7 +79,6 @@ export default function MarketsPage() { const activeChain = useChain(); // MARK: component state const [borrowers, setBorrowers] = useChainDependentState(null, activeChain.id); - const [tokenColors, setTokenColors] = useChainDependentState>(new Map(), activeChain.id); const [pendingTxn, setPendingTxn] = useState(null); const [isPendingTxnModalOpen, setIsPendingTxnModalOpen] = useState(false); const [pendingTxnModalStatus, setPendingTxnModalStatus] = useState(null); @@ -100,6 +98,7 @@ export default function MarketsPage() { // MARK: custom hooks const { lendingPairs, refetchOracleData, refetchLenderData } = useLendingPairs(activeChain.id); + const { data: tokenColors } = useTokenColors(lendingPairs); const { data: tokenQuotes } = useLatestPriceRelay(lendingPairs); const { balances: balancesMap, refetch: refetchBalances } = useLendingPairsBalances(lendingPairs, activeChain.id); @@ -111,19 +110,7 @@ export default function MarketsPage() { }, }); - // NOTE: Instead of `useAvailablePools()`, we're able to compute `availablePools` from `lendingPairs`. - // This saves a lot of data. - const availablePools = useMemo(() => { - const poolInfoMap = new Map(); - lendingPairs.forEach((pair) => - poolInfoMap.set(pair.uniswapPool.toLowerCase(), { - token0: pair.token0, - token1: pair.token1, - fee: GetNumericFeeTier(pair.uniswapFeeTier), - }) - ); - return poolInfoMap; - }, [lendingPairs]); + const availablePools = useUniswapPools(lendingPairs); const doesGuardianSenseManipulation = useMemo(() => { return lendingPairs.some((pair) => pair.oracleData.manipulationMetric > pair.manipulationThreshold); @@ -163,20 +150,6 @@ export default function MarketsPage() { })(); }, [publicClient, pendingTxn, setIsPendingTxnModalOpen, setPendingTxnModalStatus]); - // MARK: Computing token colors - useEffect(() => { - (async () => { - // Compute colors for each token logo (local, but still async) - const colorPromises = uniqueTokens.map((token) => getProminentColor(token.logoURI || '')); - const colors = await Promise.all(colorPromises); - - // Convert response to the desired Map format - const addressToColorMap: Map = new Map(); - uniqueTokens.forEach((token, index) => addressToColorMap.set(token.address, colors[index])); - setTokenColors(addressToColorMap); - })(); - }, [uniqueTokens, setTokenColors]); - // MARK: Fetch margin accounts useEffect(() => { (async () => { @@ -282,7 +255,7 @@ export default function MarketsPage() { uniqueTokens={uniqueTokens} tokenBalances={balancesMap} tokenQuotes={tokenQuotes!} - tokenColors={tokenColors} + tokenColors={tokenColors!} setPendingTxn={setPendingTxn} /> ); @@ -294,7 +267,7 @@ export default function MarketsPage() { provider={provider} blockNumber={blockNumber} lendingPairs={lendingPairs} - tokenColors={tokenColors} + tokenColors={tokenColors!} setPendingTxn={setPendingTxn} /> ); diff --git a/earn/src/pages/PortfolioPage.tsx b/earn/src/pages/PortfolioPage.tsx index 6d2b2406..eb85a37f 100644 --- a/earn/src/pages/PortfolioPage.tsx +++ b/earn/src/pages/PortfolioPage.tsx @@ -9,6 +9,7 @@ import useChain from 'shared/lib/data/hooks/UseChain'; import { useLendingPairsBalances } from 'shared/lib/data/hooks/UseLendingPairBalances'; import { useLendingPairs } from 'shared/lib/data/hooks/UseLendingPairs'; import { useConsolidatedPriceRelay } from 'shared/lib/data/hooks/UsePriceRelay'; +import { useTokenColors } from 'shared/lib/data/hooks/UseTokenColors'; import { Token } from 'shared/lib/data/Token'; import { getTokenBySymbol } from 'shared/lib/data/TokenData'; import styled from 'styled-components'; @@ -32,7 +33,6 @@ import PortfolioBalance from '../components/portfolio/PortfolioBalance'; import PortfolioGrid from '../components/portfolio/PortfolioGrid'; import PortfolioPageWidgetWrapper from '../components/portfolio/PortfolioPageWidgetWrapper'; import { RESPONSIVE_BREAKPOINT_SM, RESPONSIVE_BREAKPOINT_XS } from '../data/constants/Breakpoints'; -import { getProminentColor } from '../util/Colors'; const ASSET_BAR_TOOLTIP_TEXT = `This bar shows the assets in your portfolio. Hover/click on a segment to see more details.`; @@ -102,7 +102,6 @@ export default function PortfolioPage() { const activeChain = useChain(); const [pendingTxn, setPendingTxn] = useState(null); - const [tokenColors, setTokenColors] = useState>(new Map()); const [activeAsset, setActiveAsset] = useState(null); const [isSendCryptoModalOpen, setIsSendCryptoModalOpen] = useState(false); const [isEarnInterestModalOpen, setIsEarnInterestModalOpen] = useState(false); @@ -113,6 +112,7 @@ export default function PortfolioPage() { const { lendingPairs } = useLendingPairs(activeChain.id); const { balances: balancesMap, refetch: refetchBalances } = useLendingPairsBalances(lendingPairs, activeChain.id); + const { data: tokenColors } = useTokenColors(lendingPairs); const { data: consolidatedPriceData, isPending: isPendingPrices, @@ -152,18 +152,6 @@ export default function PortfolioPage() { }; }, [activeChain.id, consolidatedPriceData]); - useEffect(() => { - (async () => { - const tokenColorMap: Map = new Map(); - const colorPromises = uniqueTokens.map((token) => getProminentColor(token.logoURI || '')); - const colors = await Promise.all(colorPromises); - uniqueTokens.forEach((token: Token, index: number) => { - tokenColorMap.set(token.address, colors[index]); - }); - setTokenColors(tokenColorMap); - })(); - }, [lendingPairs, setTokenColors, uniqueTokens]); - const publicClient = usePublicClient({ chainId: activeChain.id }); useEffect(() => { (async () => { @@ -292,7 +280,7 @@ export default function PortfolioPage() { return ( { setActiveAsset(updatedAsset); @@ -346,7 +334,7 @@ export default function PortfolioPage() { { (async () => { if (!uniswapNftPosition) return; diff --git a/earn/src/pages/boost/ManageBoostPage.tsx b/earn/src/pages/boost/ManageBoostPage.tsx index 98356748..b3118a36 100644 --- a/earn/src/pages/boost/ManageBoostPage.tsx +++ b/earn/src/pages/boost/ManageBoostPage.tsx @@ -14,6 +14,7 @@ import { ALOE_II_BORROWER_NFT_ADDRESS } from 'shared/lib/data/constants/ChainSpe import useChain from 'shared/lib/data/hooks/UseChain'; import { useChainDependentState } from 'shared/lib/data/hooks/UseChainDependentState'; import { PriceRelayLatestResponse } from 'shared/lib/data/hooks/UsePriceRelay'; +import { getProminentColor, rgb } from 'shared/lib/util/Colors'; import styled from 'styled-components'; import { Address } from 'viem'; import { Config, useAccount, useClient, usePublicClient, useReadContract } from 'wagmi'; @@ -24,7 +25,6 @@ import CollectFeesWidget from '../../components/boost/CollectFeesWidget'; import PendingTxnModal, { PendingTxnModalStatus } from '../../components/common/PendingTxnModal'; import { API_PRICE_RELAY_LATEST_URL } from '../../data/constants/Values'; import { BoostCardInfo, BoostCardType, fetchBoostBorrower } from '../../data/Uniboost'; -import { getProminentColor, rgb } from '../../util/Colors'; import { useEthersProvider } from '../../util/Provider'; import { BackButtonWrapper } from '../BoostPage'; @@ -94,6 +94,7 @@ export default function ManageBoostPage() { })(); }, [publicClient, pendingTxn, setIsPendingTxnModalOpen, setPendingTxnModalStatus]); + // TODO: useTokenColors useEffect(() => { (async () => { if (!cardInfo) return; diff --git a/shared/src/data/hooks/UseTokenColors.ts b/shared/src/data/hooks/UseTokenColors.ts new file mode 100644 index 00000000..5f2e7641 --- /dev/null +++ b/shared/src/data/hooks/UseTokenColors.ts @@ -0,0 +1,48 @@ +import { useEffect, useState } from 'react'; +import { LendingPair } from '../LendingPair'; +import { Address } from 'viem'; +import { getProminentColor } from '../../util/Colors'; +import { useQuery } from '@tanstack/react-query'; + +export function useTokenColors(lendingPairs: LendingPair[]) { + const [tokens, setTokens] = useState(new Map()); + + useEffect(() => { + const newTokens = new Map(); + let shouldUpdate = false; + + lendingPairs.forEach((pair) => { + const { token0, token1 } = pair; + + newTokens.set(token0.address, token0.logoURI); + newTokens.set(token1.address, token1.logoURI); + + // If the old map (a) doesn't include the tokens OR (b) includes them, but with the wrong URI, do an update + if (token0.logoURI !== tokens.get(token0.address) || token1.logoURI !== tokens.get(token1.address)) { + shouldUpdate = true; + } + }); + + if (shouldUpdate) setTokens(newTokens); + }, [lendingPairs, tokens]); + + const queryFn = async () => { + const colors = await Promise.all( + Array.from(tokens.entries()).map(async ([k, v]) => [k, await getProminentColor(v || '')] as [Address, string]) + ); + const addressToColorMap = new Map(colors); + return addressToColorMap; + }; + + const queryKey = ['useTokenColors', tokens]; + + return useQuery({ + queryKey, + queryFn, + staleTime: Infinity, + refetchOnMount: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + placeholderData: new Map(), + }); +} diff --git a/shared/src/data/hooks/UseUniswapPools.ts b/shared/src/data/hooks/UseUniswapPools.ts new file mode 100644 index 00000000..ff80899f --- /dev/null +++ b/shared/src/data/hooks/UseUniswapPools.ts @@ -0,0 +1,36 @@ +import { useEffect, useState } from 'react'; +import { LendingPair } from '../LendingPair'; +import { Token } from '../Token'; +import { GetNumericFeeTier } from '../FeeTier'; +import { Address } from 'viem'; + +type UniswapPoolsMap = Map; + +export function useUniswapPools(lendingPairs: LendingPair[]) { + const [uniswapPools, setUniswapPools] = useState(new Map()); + + useEffect(() => { + const newUniswapPools: UniswapPoolsMap = new Map(); + let shouldUpdate = lendingPairs.length < uniswapPools.size; + + lendingPairs.forEach((pair) => { + const { token0, token1 } = pair; + if (token0.chainId !== token1.chainId) { + throw new Error(`Token chainIds mismatched in a LendingPair (Uniswap: ${pair.uniswapPool})`); + } + + newUniswapPools.set(pair.uniswapPool, { token0, token1, fee: GetNumericFeeTier(pair.uniswapFeeTier) }); + // If the old map (a) doesn't include the pool OR (b) it includes it, but on the wrong chain, do an update + if ( + !uniswapPools.has(pair.uniswapPool) || + token0.chainId !== uniswapPools.get(pair.uniswapPool)!.token0.chainId + ) { + shouldUpdate = true; + } + }); + + if (shouldUpdate) setUniswapPools(newUniswapPools); + }, [lendingPairs, uniswapPools]); + + return uniswapPools; +} diff --git a/earn/src/util/Colors.ts b/shared/src/util/Colors.ts similarity index 100% rename from earn/src/util/Colors.ts rename to shared/src/util/Colors.ts