From 6e234b1ecd494895693d9a7763eb7fe2678a3c2a Mon Sep 17 00:00:00 2001 From: aleots Date: Sun, 24 Mar 2024 11:34:38 +0200 Subject: [PATCH 01/19] Sorting strategies without wallet connected --- src/components/_pages/PageHome.tsx | 91 +++++++++++++++++------------- 1 file changed, 52 insertions(+), 39 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index f75817fd2..6b3fe4f0d 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -23,8 +23,7 @@ import { useDepositModalStore, } from "data/hooks/useDepositModalStore" import useBetterMediaQuery from "hooks/utils/useBetterMediaQuery" -import { useMemo, useState, useEffect } from "react" -import { InfoBanner } from "components/_banners/InfoBanner" +import { useMemo, useState } from "react" import { ChainFilter } from "components/_filters/ChainFilter" import { chainConfig } from "src/data/chainConfig" import { @@ -39,6 +38,7 @@ import { } from "components/_filters/MiscFilter" import { isEqual } from "lodash" import { DeleteCircleIcon } from "components/_icons" +import { add, isBefore } from "date-fns" export const PageHome = () => { const { @@ -237,51 +237,64 @@ export const PageHome = () => { } const strategyData = useMemo(() => { - return ( - data?.filter((item) => { - // Chain filter - const isChainSelected = selectedChainIds.includes( - item?.config.chain.id! - ) + const filteredData = data?.filter((item) => { + // Chain filter + const isChainSelected = selectedChainIds.includes( + item?.config.chain.id! + ) - // Deposit asset filter - const hasSelectedDepositAsset = cellarDataMap[ - item!.slug + // Deposit asset filter + const hasSelectedDepositAsset = cellarDataMap[ + item!.slug ].depositTokens.list.some((tokenSymbol) => - selectedDepositAssets.hasOwnProperty(tokenSymbol) - ) + selectedDepositAssets.hasOwnProperty(tokenSymbol) + ) - // Deprecated filter - const isDeprecated = cellarDataMap[item!.slug].deprecated - const deprecatedCondition = showDeprecated - ? isDeprecated - : !isDeprecated + // Deprecated filter + const isDeprecated = cellarDataMap[item!.slug].deprecated + const deprecatedCondition = showDeprecated + ? isDeprecated + : !isDeprecated - // Incentivised filter - // Badge check for custom rewards - const hasGreenBadge = cellarDataMap[ - item!.slug + // Incentivised filter + // Badge check for custom rewards + const hasGreenBadge = cellarDataMap[ + item!.slug ].config.badges?.some( - (badge) => badge.customStrategyHighlightColor === "#00C04B" - ) + (badge) => badge.customStrategyHighlightColor === "#00C04B" + ) - // Staking period check for somm/vesting rewards - const hasLiveStakingPeriod = - item?.rewardsApy?.value !== undefined && - item?.rewardsApy?.value > 0 + // Staking period check for somm/vesting rewards + const hasLiveStakingPeriod = + item?.rewardsApy?.value !== undefined && + item?.rewardsApy?.value > 0 - const incentivisedCondition = showIncentivised - ? hasGreenBadge || hasLiveStakingPeriod - : true + const incentivisedCondition = showIncentivised + ? hasGreenBadge || hasLiveStakingPeriod + : true - return ( - isChainSelected && - hasSelectedDepositAsset && - deprecatedCondition && - incentivisedCondition - ) - }) || [] - ) + return ( + isChainSelected && + hasSelectedDepositAsset && + deprecatedCondition && + incentivisedCondition + ) + }) || [] + + return filteredData.sort((a, b) => { + const isANew = isBefore(new Date(), add(new Date(a?.launchDate ?? ''), { weeks: 4 })); + const isBNew = isBefore(new Date(), add(new Date(b?.launchDate ?? ''), { weeks: 4 })); + if (isANew && isBNew) { + return new Date(b?.launchDate ?? '').getTime() - new Date(a?.launchDate ?? '').getTime(); + } else if (isANew || isBNew) { + return isANew ? -1 : 1; + } + + if ((a?.rewardsApy || b?.rewardsApy) && !(a?.rewardsApy && b?.rewardsApy)) { + return a?.rewardsApy ? -1 : 1; + } + return parseFloat(b?.tvm?.value ?? '') - parseFloat(a?.tvm?.value ?? ''); + }) }, [ data, selectedChainIds, From 9d73610ba9886533f92dab7833016c2a80f9d46e Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 19:23:50 +0200 Subject: [PATCH 02/19] Added sorting priority when wallet is connected --- src/components/_pages/PageHome.tsx | 35 +++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index 6b3fe4f0d..9be85d7f9 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -39,6 +39,9 @@ import { import { isEqual } from "lodash" import { DeleteCircleIcon } from "components/_icons" import { add, isBefore } from "date-fns" +import { useUserDataAllStrategies } from "data/hooks/useUserDataAllStrategies" +import { useAccount } from "wagmi" +import { StrategyData } from "data/actions/types" export const PageHome = () => { const { @@ -62,6 +65,9 @@ export const PageHome = () => { } = useDepositModalStore() const { timeline } = useHome() + let { data: userData } = useUserDataAllStrategies(); + const { isConnected } = useAccount(); + const columns = isDesktop ? StrategyDesktopColumn({ timeline, @@ -282,25 +288,48 @@ export const PageHome = () => { }) || [] return filteredData.sort((a, b) => { - const isANew = isBefore(new Date(), add(new Date(a?.launchDate ?? ''), { weeks: 4 })); - const isBNew = isBefore(new Date(), add(new Date(b?.launchDate ?? ''), { weeks: 4 })); + + // 1. Priority - strategies that user holds + if (isConnected) { + const doesUserHoldStrategy = (strategy: StrategyData) => userData?.strategies + .some(s => s?.userStrategyData?.strategyData?.slug === strategy?.slug); + + const userHoldsA = doesUserHoldStrategy(a); + const userHoldsB = doesUserHoldStrategy(b); + + if ((userHoldsA || userHoldsB) && !(userHoldsA && userHoldsB)) { + return userHoldsA ? -1 : 1; + } + if (userHoldsA && userHoldsB){ + return 0; + } + } + // 2. Priority - new strategies + const isNewStrategy = (strategy: StrategyData) => isBefore(new Date(), add(new Date(strategy?.launchDate ?? ''), { weeks: 4 })); + const isANew = isNewStrategy(a); + const isBNew = isNewStrategy(b); if (isANew && isBNew) { return new Date(b?.launchDate ?? '').getTime() - new Date(a?.launchDate ?? '').getTime(); } else if (isANew || isBNew) { return isANew ? -1 : 1; } + // 3. Priority - Somm rewards if ((a?.rewardsApy || b?.rewardsApy) && !(a?.rewardsApy && b?.rewardsApy)) { return a?.rewardsApy ? -1 : 1; } + + // 4. Priority - TVL return parseFloat(b?.tvm?.value ?? '') - parseFloat(a?.tvm?.value ?? ''); - }) + }); }, [ data, selectedChainIds, selectedDepositAssets, showDeprecated, showIncentivised, + userData, + isConnected ]) const loading = isFetching || isRefetching || isLoading From 23be09944907c4b3c1468ecbd7b277f4a183f4f4 Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 20:08:02 +0200 Subject: [PATCH 03/19] Solved some Next.js errors from console --- src/components/_buttons/WithdrawButton.tsx | 6 +++--- src/components/_cards/ApyPerfomanceCard.tsx | 2 -- src/components/_cards/PortfolioCard/index.tsx | 9 +++++++-- src/components/_layout/LayoutWithSidebar.tsx | 14 +++++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/components/_buttons/WithdrawButton.tsx b/src/components/_buttons/WithdrawButton.tsx index f2f87bcbb..37809df25 100644 --- a/src/components/_buttons/WithdrawButton.tsx +++ b/src/components/_buttons/WithdrawButton.tsx @@ -20,7 +20,7 @@ export const WithdrawButton: VFC< ButtonProps & { isDeprecated?: boolean } -> = (props) => { +> = ({ isDeprecated, ...buttonProps }) => { const { isOpen, onOpen, onClose } = useDisclosure() function closeModal() { @@ -59,9 +59,9 @@ export const WithdrawButton: VFC< onOpen() }} - {...props} + {...buttonProps} > - {props.isDeprecated + {isDeprecated ? "Withdraw Only" : "Withdraw"} diff --git a/src/components/_cards/ApyPerfomanceCard.tsx b/src/components/_cards/ApyPerfomanceCard.tsx index 477eb3afb..1e2ee63e1 100644 --- a/src/components/_cards/ApyPerfomanceCard.tsx +++ b/src/components/_cards/ApyPerfomanceCard.tsx @@ -115,9 +115,7 @@ export const ApyPerfomanceCard: VFC = (props) => { - {timeline} {apyChartLabel(cellarConfig)} - diff --git a/src/components/_cards/PortfolioCard/index.tsx b/src/components/_cards/PortfolioCard/index.tsx index d35a50517..dc8f01bd3 100644 --- a/src/components/_cards/PortfolioCard/index.tsx +++ b/src/components/_cards/PortfolioCard/index.tsx @@ -54,14 +54,13 @@ import { Rewards } from "./Rewards" import { useNetwork } from "wagmi" import WithdrawQueueCard from "../WithdrawQueueCard" import withdrawQueueV0821 from "src/abi/withdraw-queue-v0.8.21.json" -import { add } from "lodash" import { CellarNameKey } from "data/types" import { PointsDisplay } from "./PointsDisplay" export const PortfolioCard: VFC = (props) => { const theme = useTheme() const isMounted = useIsMounted() - const { address, isConnected } = useAccount() + const { address, isConnected: connected } = useAccount() const id = useRouter().query.id as string const cellarConfig = cellarDataMap[id].config const slug = cellarDataMap[id].slug @@ -73,6 +72,12 @@ export const PortfolioCard: VFC = (props) => { cellarConfig.chain.id ) as Token[] + // using local state to avoid Next.js errors + const [isConnected, setConnected] = useState(false); + useEffect(() => { + setConnected(connected) + }, [connected]) + const { lpToken } = useUserBalances(cellarConfig) let { data: lpTokenData } = lpToken const lpTokenDisabled = diff --git a/src/components/_layout/LayoutWithSidebar.tsx b/src/components/_layout/LayoutWithSidebar.tsx index 886442344..83b15d783 100644 --- a/src/components/_layout/LayoutWithSidebar.tsx +++ b/src/components/_layout/LayoutWithSidebar.tsx @@ -2,20 +2,24 @@ import { Box, Container, Flex } from "@chakra-ui/react" import Footer from "components/Footer" import { Nav } from "components/Nav" import { Sidebar } from "components/_sidebar" -import { useAllStrategiesData } from "data/hooks/useAllStrategiesData" -import { FC, useRef } from "react" +import { FC, useEffect, useRef, useState } from "react" import { useAccount } from "wagmi" import { useInView } from "react-intersection-observer" export const LayoutWithSidebar: FC = ({ children }) => { - const { isConnected } = useAccount() - - const { isLoading } = useAllStrategiesData() + const { isConnected: connected } = useAccount() const containerRef = useRef(null) const { ref, inView } = useInView({ threshold: 0, }) + + // using local state to avoid Next.js errors + const [isConnected, setConnected] = useState(false); + useEffect(() => { + setConnected(connected) + }, [connected]) + return ( From 9c5576faf66f9c82ebc8a18a1dfd774cdaf018f0 Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 20:20:59 +0200 Subject: [PATCH 04/19] Solved errors about memory leak from console --- src/data/hooks/useAllStrategiesData.ts | 3 +++ src/data/hooks/useUserDataAllStrategies.ts | 4 +--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/hooks/useAllStrategiesData.ts b/src/data/hooks/useAllStrategiesData.ts index e4cb90f51..d6185aef7 100644 --- a/src/data/hooks/useAllStrategiesData.ts +++ b/src/data/hooks/useAllStrategiesData.ts @@ -40,6 +40,9 @@ export const useAllStrategiesData = () => { } }) .catch((error) => setError(error)) + return () => { + setError(null); + }; }, []) const query = useQuery( diff --git a/src/data/hooks/useUserDataAllStrategies.ts b/src/data/hooks/useUserDataAllStrategies.ts index 9aff7a6d7..320fa7049 100644 --- a/src/data/hooks/useUserDataAllStrategies.ts +++ b/src/data/hooks/useUserDataAllStrategies.ts @@ -4,7 +4,6 @@ import { useAccount, useSigner } from "wagmi" import { useAllContracts } from "./useAllContracts" import { useAllStrategiesData } from "./useAllStrategiesData" import { useCoinGeckoPrice } from "./useCoinGeckoPrice" -import { useState, useEffect } from "react" import { useNetwork } from "wagmi" import { chainConfig } from "data/chainConfig" import { tokenConfig } from "data/tokenConfig" @@ -14,7 +13,6 @@ export const useUserDataAllStrategies = () => { const { address } = useAccount() const { data: allContracts } = useAllContracts() const strategies = useAllStrategiesData() - const [error, setError] = useState(null) const { chain } = useNetwork() @@ -62,6 +60,6 @@ export const useUserDataAllStrategies = () => { return { ...query, - isError: Boolean(error) || query.isError, + isError: query.isError, } } From 279bfd4de79cab801437978aa6ef7dedc1f6ff49 Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 20:51:47 +0200 Subject: [PATCH 05/19] Add prioritizing users strategies by net value --- src/components/_pages/PageHome.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index 9be85d7f9..c9bc6f4ba 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -42,6 +42,7 @@ import { add, isBefore } from "date-fns" import { useUserDataAllStrategies } from "data/hooks/useUserDataAllStrategies" import { useAccount } from "wagmi" import { StrategyData } from "data/actions/types" +import BigNumber from "bignumber.js" export const PageHome = () => { const { @@ -300,8 +301,15 @@ export const PageHome = () => { if ((userHoldsA || userHoldsB) && !(userHoldsA && userHoldsB)) { return userHoldsA ? -1 : 1; } + + // if user holds both, prioritize by net value on asset if (userHoldsA && userHoldsB){ - return 0; + const getUserValueOnStrategy = (strategy: StrategyData) => userData?.strategies + .find(s => s?.userStrategyData?.strategyData?.slug === strategy?.slug)?.netValue + ?? new BigNumber(0); + const aValue = getUserValueOnStrategy(a); + const bValue = getUserValueOnStrategy(b); + return bValue.minus(aValue).toNumber(); } } // 2. Priority - new strategies From 908ebadca46e7301658f800227c2adec783dbdc2 Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 21:00:47 +0200 Subject: [PATCH 06/19] Fix memory leak error in Nav.tsx and import error --- src/components/Nav.tsx | 5 ++--- src/components/_pages/PageHome.tsx | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Nav.tsx b/src/components/Nav.tsx index 05505982e..c411a6b8e 100644 --- a/src/components/Nav.tsx +++ b/src/components/Nav.tsx @@ -18,14 +18,12 @@ import ConnectButton from "components/_buttons/ConnectButton" import { Link } from "components/Link" import { useRouter } from "next/router" import { NAV_LINKS } from "utils/navLinks" -import { useIsMounted } from "hooks/utils/useIsMounted" import { LogoTextIcon } from "./_icons" import useBetterMediaQuery from "hooks/utils/useBetterMediaQuery" import { useScrollDirection } from "hooks/utils/useScrollDirection" import { HamburgerIcon } from "./_icons/HamburgerIcon" export const Nav: VFC = (props) => { - const isMounted = useIsMounted() const [scrolled, setScrolled] = useState(false) const scrollDirection = useScrollDirection() const { isOpen, onOpen, onClose } = useDisclosure() @@ -43,7 +41,8 @@ export const Nav: VFC = (props) => { } }) return () => { - window.removeEventListener("scroll", () => {}) + window.removeEventListener("scroll", () => {}); + setScrolled(false); } }, []) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index c9bc6f4ba..b3f254f3d 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -24,6 +24,7 @@ import { } from "data/hooks/useDepositModalStore" import useBetterMediaQuery from "hooks/utils/useBetterMediaQuery" import { useMemo, useState } from "react" +import { InfoBanner } from "components/_banners/InfoBanner" import { ChainFilter } from "components/_filters/ChainFilter" import { chainConfig } from "src/data/chainConfig" import { From 420918bfc061f43c4c98c45bce7842b6cebf508c Mon Sep 17 00:00:00 2001 From: aleots Date: Mon, 25 Mar 2024 21:03:26 +0200 Subject: [PATCH 07/19] Another memory leak fix --- src/data/hooks/useAllStrategiesData.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/hooks/useAllStrategiesData.ts b/src/data/hooks/useAllStrategiesData.ts index d6185aef7..690c1e8a8 100644 --- a/src/data/hooks/useAllStrategiesData.ts +++ b/src/data/hooks/useAllStrategiesData.ts @@ -42,6 +42,7 @@ export const useAllStrategiesData = () => { .catch((error) => setError(error)) return () => { setError(null); + setcellarData(undefined); }; }, []) From 27ca7a5377fd8739203c4aa05b3305f6f3349e34 Mon Sep 17 00:00:00 2001 From: aleots Date: Fri, 29 Mar 2024 12:35:09 +0200 Subject: [PATCH 08/19] Fetch assets balance from wallet on current chain --- .../common/getUserDataAllStrategies.ts | 22 +++++++++++++++++++ src/data/tokenConfig.ts | 18 ++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/data/actions/common/getUserDataAllStrategies.ts b/src/data/actions/common/getUserDataAllStrategies.ts index aee16fdaa..3f4f5dda7 100644 --- a/src/data/actions/common/getUserDataAllStrategies.ts +++ b/src/data/actions/common/getUserDataAllStrategies.ts @@ -5,6 +5,10 @@ import { getUserData } from "./getUserData" import { fetchCoingeckoPrice } from "queries/get-coingecko-price" import { cellarDataMap } from "data/cellarDataMap" import { ConfigProps } from "data/types" +import { fetchBalance } from "@wagmi/core" +import { getAddress } from "ethers/lib/utils" +import { getAcceptedDepositAssetsByChain } from "data/tokenConfig" +import { ResolvedConfig } from "abitype" export const getUserDataAllStrategies = async ({ allContracts, @@ -74,6 +78,23 @@ export const getUserDataAllStrategies = async ({ ) ) + const tokenList = getAcceptedDepositAssetsByChain(chain); + const depositAssetBalances : { + decimals: ResolvedConfig['IntType']; + formatted: string; + symbol: string; + value: ResolvedConfig['BigIntType']; + }[] = []; + for (const token of tokenList) { + const balance = await fetchBalance({ + token: getAddress(token!.address), + address: getAddress(userAddress) + }); + if (!balance.value.isZero()) { + depositAssetBalances.push(balance); + } + } + const userData = userDataRes.filter((item) => !!item) const totalNetValue = (() => { @@ -116,6 +137,7 @@ export const getUserDataAllStrategies = async ({ value: totalNetValue, formatted: formatUSD(String(totalNetValue)), }, + depositAssetBalances, totalSommRewards: { value: totalSommRewards, formatted: Number( diff --git a/src/data/tokenConfig.ts b/src/data/tokenConfig.ts index df72271b4..9a69f3cf2 100644 --- a/src/data/tokenConfig.ts +++ b/src/data/tokenConfig.ts @@ -562,7 +562,7 @@ export const acceptedETHDepositTokenMap = Object.keys( }, {} as { [symbol: string]: Token }) // --- ARB ACCEPTED TOKENS --- -let acceptedARBDepositTokens = ["USDC", "USDT", "DAI", "USDC.e"] +let acceptedARBDepositTokens = ["USDC", "USDT", "DAI", "USDC.e", 'WETH', 'wstETH', 'rETH']; let depositTokenMapARB = tokenConfig.reduce((map, token) => { if (acceptedARBDepositTokens.includes(token.symbol)) { @@ -581,6 +581,16 @@ export const acceptedARBDepositTokenMap = Object.keys( return obj }, {} as { [symbol: string]: Token }) +// --- OPTIMISM ACCEPTED TOKENS --- +let acceptedOPTDepositTokens = ['WETH', 'wstETH', 'rETH']; + + +const acceptedDepositTokensByChain: { [key: string]: any } = { + ethereum: acceptedETHDepositTokens, + arbitrum: acceptedARBDepositTokens, + optimism: acceptedOPTDepositTokens, +}; + // Creatae a map from each token symbol to its config export const tokenConfigMap = tokenConfig.reduce((map, token) => { map[`${token.symbol}_${token.chain.toUpperCase()}`] = token @@ -607,6 +617,12 @@ export const depositAssetTokenConfig: Token[] = tokenConfig.filter( depositAssetTokenList.includes(token.chain) ) +export const getAcceptedDepositAssetsByChain = (chainId: string) => { + return acceptedDepositTokensByChain[chainId] + ? getTokenConfig(acceptedDepositTokensByChain[chainId], chainId) + : [] +} + export function getTokenConfig(tokenList: string[], chain: string) { return tokenList.map((list) => tokenConfig.find( From 1cc610564cb509ef5d5a3cec313b801163477813 Mon Sep 17 00:00:00 2001 From: aleots Date: Fri, 29 Mar 2024 12:36:40 +0200 Subject: [PATCH 09/19] Sort strategies based on users wallet contents --- src/components/_pages/PageHome.tsx | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index b3f254f3d..e70084e08 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -24,7 +24,6 @@ import { } from "data/hooks/useDepositModalStore" import useBetterMediaQuery from "hooks/utils/useBetterMediaQuery" import { useMemo, useState } from "react" -import { InfoBanner } from "components/_banners/InfoBanner" import { ChainFilter } from "components/_filters/ChainFilter" import { chainConfig } from "src/data/chainConfig" import { @@ -43,7 +42,6 @@ import { add, isBefore } from "date-fns" import { useUserDataAllStrategies } from "data/hooks/useUserDataAllStrategies" import { useAccount } from "wagmi" import { StrategyData } from "data/actions/types" -import BigNumber from "bignumber.js" export const PageHome = () => { const { @@ -291,26 +289,18 @@ export const PageHome = () => { return filteredData.sort((a, b) => { - // 1. Priority - strategies that user holds + // 1. Priority - does user own same assets as in strategy if (isConnected) { - const doesUserHoldStrategy = (strategy: StrategyData) => userData?.strategies - .some(s => s?.userStrategyData?.strategyData?.slug === strategy?.slug); + const doesUserHaveStrategyAssets = (strategy: StrategyData) => strategy?.tradedAssets?.some( + asset => userData?.depositAssetBalances.some( + balance => asset.symbol === balance.symbol) + ) - const userHoldsA = doesUserHoldStrategy(a); - const userHoldsB = doesUserHoldStrategy(b); + const userHasAAssets = doesUserHaveStrategyAssets(a); + const userHasBAssets = doesUserHaveStrategyAssets(b); - if ((userHoldsA || userHoldsB) && !(userHoldsA && userHoldsB)) { - return userHoldsA ? -1 : 1; - } - - // if user holds both, prioritize by net value on asset - if (userHoldsA && userHoldsB){ - const getUserValueOnStrategy = (strategy: StrategyData) => userData?.strategies - .find(s => s?.userStrategyData?.strategyData?.slug === strategy?.slug)?.netValue - ?? new BigNumber(0); - const aValue = getUserValueOnStrategy(a); - const bValue = getUserValueOnStrategy(b); - return bValue.minus(aValue).toNumber(); + if ((userHasAAssets || userHasBAssets) && !(userHasAAssets && userHasBAssets)) { + return userHasAAssets ? -1 : 1; } } // 2. Priority - new strategies From 530a9f355e3251f0ef49f8b39d147f8e0e27add6 Mon Sep 17 00:00:00 2001 From: aleots Date: Fri, 29 Mar 2024 13:35:14 +0200 Subject: [PATCH 10/19] Catch error from fetching balance --- src/data/actions/common/getUserDataAllStrategies.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/data/actions/common/getUserDataAllStrategies.ts b/src/data/actions/common/getUserDataAllStrategies.ts index 3f4f5dda7..e0fd8755b 100644 --- a/src/data/actions/common/getUserDataAllStrategies.ts +++ b/src/data/actions/common/getUserDataAllStrategies.ts @@ -86,13 +86,15 @@ export const getUserDataAllStrategies = async ({ value: ResolvedConfig['BigIntType']; }[] = []; for (const token of tokenList) { - const balance = await fetchBalance({ + await fetchBalance({ token: getAddress(token!.address), address: getAddress(userAddress) - }); - if (!balance.value.isZero()) { - depositAssetBalances.push(balance); - } + }).then((balance) => { + if (!balance.value.isZero()) { + depositAssetBalances.push(balance); + } + }) + .catch((error) => console.log("error", error)) } const userData = userDataRes.filter((item) => !!item) From e0710bc43226bd8965e47f39a4e3d8bb76a2bfab Mon Sep 17 00:00:00 2001 From: aleots Date: Fri, 29 Mar 2024 19:39:40 +0200 Subject: [PATCH 11/19] Sort assets also by their value in users wallet --- src/components/_pages/PageHome.tsx | 25 +++++++++++-------- .../common/getUserDataAllStrategies.ts | 6 +++++ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index e70084e08..23d23e20f 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -290,17 +290,20 @@ export const PageHome = () => { return filteredData.sort((a, b) => { // 1. Priority - does user own same assets as in strategy - if (isConnected) { - const doesUserHaveStrategyAssets = (strategy: StrategyData) => strategy?.tradedAssets?.some( - asset => userData?.depositAssetBalances.some( - balance => asset.symbol === balance.symbol) - ) - - const userHasAAssets = doesUserHaveStrategyAssets(a); - const userHasBAssets = doesUserHaveStrategyAssets(b); - - if ((userHasAAssets || userHasBAssets) && !(userHasAAssets && userHasBAssets)) { - return userHasAAssets ? -1 : 1; + if (isConnected && userData?.depositAssetBalances) { + for (const balance of userData?.depositAssetBalances) { + const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.tradedAssets?.some( + asset => asset.symbol === balance.symbol + ) + const strategyAHasAsset = doesStrategyHaveAsset(a); + const strategyBHasAsset = doesStrategyHaveAsset(b); + + if (strategyAHasAsset && strategyBHasAsset) { + break; + } + if ((strategyAHasAsset || strategyBHasAsset) && !(strategyAHasAsset && strategyBHasAsset)) { + return strategyAHasAsset ? -1 : 1; + } } } // 2. Priority - new strategies diff --git a/src/data/actions/common/getUserDataAllStrategies.ts b/src/data/actions/common/getUserDataAllStrategies.ts index e0fd8755b..bb33f2b04 100644 --- a/src/data/actions/common/getUserDataAllStrategies.ts +++ b/src/data/actions/common/getUserDataAllStrategies.ts @@ -9,6 +9,7 @@ import { fetchBalance } from "@wagmi/core" import { getAddress } from "ethers/lib/utils" import { getAcceptedDepositAssetsByChain } from "data/tokenConfig" import { ResolvedConfig } from "abitype" +import BigNumber from "bignumber.js" export const getUserDataAllStrategies = async ({ allContracts, @@ -85,6 +86,7 @@ export const getUserDataAllStrategies = async ({ symbol: string; value: ResolvedConfig['BigIntType']; }[] = []; + for (const token of tokenList) { await fetchBalance({ token: getAddress(token!.address), @@ -97,6 +99,10 @@ export const getUserDataAllStrategies = async ({ .catch((error) => console.log("error", error)) } + depositAssetBalances.sort( + (x, y) => new BigNumber(y.value._hex).minus(new BigNumber(x.value._hex)).toNumber() + ) + const userData = userDataRes.filter((item) => !!item) const totalNetValue = (() => { From fd3aab5ef7bbc36db21407ae7e8479b96dd30891 Mon Sep 17 00:00:00 2001 From: aleots Date: Tue, 2 Apr 2024 17:00:02 +0300 Subject: [PATCH 12/19] Separate fetching user balances to different hook --- .../_buttons/DepositAndWithdrawButton.tsx | 5 +- src/components/_cards/PortfolioCard/index.tsx | 4 +- src/components/_forms/BondForm/index.tsx | 4 +- src/components/_forms/UnstakeForm.tsx | 4 +- src/components/_forms/WithdrawForm.tsx | 4 +- src/components/_forms/WithdrawQueueForm.tsx | 4 +- src/components/_modals/UnstakeModal.tsx | 4 +- src/components/_pages/PageHome.tsx | 20 +++--- src/components/_sidebar/PortofolioItem.tsx | 4 +- .../common/getUserDataAllStrategies.ts | 24 ------- ...useUserBalances.tsx => useUserBalance.tsx} | 2 +- src/data/hooks/useUserBalances.ts | 68 +++++++++++++++++++ src/data/hooks/useUserStrategyData.ts | 5 +- 13 files changed, 96 insertions(+), 56 deletions(-) rename src/data/hooks/{useUserBalances.tsx => useUserBalance.tsx} (90%) create mode 100644 src/data/hooks/useUserBalances.ts diff --git a/src/components/_buttons/DepositAndWithdrawButton.tsx b/src/components/_buttons/DepositAndWithdrawButton.tsx index 3641b18be..bdccdf84e 100644 --- a/src/components/_buttons/DepositAndWithdrawButton.tsx +++ b/src/components/_buttons/DepositAndWithdrawButton.tsx @@ -10,9 +10,8 @@ import { } from "@chakra-ui/react" import { cellarDataMap } from "data/cellarDataMap" import { DepositModalType } from "data/hooks/useDepositModalStore" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { isBefore } from "date-fns" -import { analytics } from "utils/analytics" import { toEther } from "utils/formatCurrency" import { useAccount, useNetwork } from "wagmi" import { BaseButton } from "./BaseButton" @@ -86,7 +85,7 @@ export function DepositAndWithdrawButton({ }: DepositAndWithdrawButtonProps) { const id = row.original.slug const cellarConfig = cellarDataMap[id].config - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) const { data: lpTokenData } = lpToken const lpTokenDisabled = checkLPtokenDisabled(lpTokenData) diff --git a/src/components/_cards/PortfolioCard/index.tsx b/src/components/_cards/PortfolioCard/index.tsx index dc8f01bd3..48774fdb0 100644 --- a/src/components/_cards/PortfolioCard/index.tsx +++ b/src/components/_cards/PortfolioCard/index.tsx @@ -26,7 +26,7 @@ import { LighterSkeleton } from "components/_skeleton" import { cellarDataMap } from "data/cellarDataMap" import { useGetPreviewRedeem } from "data/hooks/useGetPreviewRedeem" import { useStrategyData } from "data/hooks/useStrategyData" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { useUserStrategyData } from "data/hooks/useUserStrategyData" import { getTokenConfig, Token } from "data/tokenConfig" import { @@ -78,7 +78,7 @@ export const PortfolioCard: VFC = (props) => { setConnected(connected) }, [connected]) - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) let { data: lpTokenData } = lpToken const lpTokenDisabled = !lpTokenData || Number(lpTokenData?.value ?? "0") <= 0 diff --git a/src/components/_forms/BondForm/index.tsx b/src/components/_forms/BondForm/index.tsx index 757131618..e8d45bf5c 100644 --- a/src/components/_forms/BondForm/index.tsx +++ b/src/components/_forms/BondForm/index.tsx @@ -29,7 +29,7 @@ import { analytics } from "utils/analytics" import { cellarDataMap } from "data/cellarDataMap" import { useRouter } from "next/router" import { useCreateContracts } from "data/hooks/useCreateContracts" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { bondingPeriodOptions } from "data/uiConfig" import { estimateGasLimitWithRetry } from "utils/estimateGasLimit" import { useGeo } from "context/geoContext" @@ -55,7 +55,7 @@ export const BondForm: VFC = ({ onClose }) => { ) const { stakerSigner } = useCreateContracts(cellarConfig) - const { lpToken, lpTokenInfo } = useUserBalances(cellarConfig) + const { lpToken, lpTokenInfo } = useUserBalance(cellarConfig) const { data: lpTokenData } = lpToken const methods = useForm({ diff --git a/src/components/_forms/UnstakeForm.tsx b/src/components/_forms/UnstakeForm.tsx index de6854aef..7918bd682 100644 --- a/src/components/_forms/UnstakeForm.tsx +++ b/src/components/_forms/UnstakeForm.tsx @@ -22,7 +22,7 @@ import { analytics } from "utils/analytics" import { useRouter } from "next/router" import { cellarDataMap } from "data/cellarDataMap" import { useCreateContracts } from "data/hooks/useCreateContracts" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { useGeo } from "context/geoContext" import { useUserStrategyData } from "data/hooks/useUserStrategyData" interface FormValues { @@ -51,7 +51,7 @@ export const UnstakeForm: VFC = ({ onClose }) => { const { cellarSigner } = useCreateContracts(cellarConfig) const { refetch } = useUserStrategyData(cellarConfig.cellar.address, cellarConfig.chain.id) - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) const { data: lpTokenData } = lpToken const { doHandleTransaction } = useHandleTransaction() diff --git a/src/components/_forms/WithdrawForm.tsx b/src/components/_forms/WithdrawForm.tsx index a32c19e15..30ac86d5e 100644 --- a/src/components/_forms/WithdrawForm.tsx +++ b/src/components/_forms/WithdrawForm.tsx @@ -32,7 +32,7 @@ import { analytics } from "utils/analytics" import { useRouter } from "next/router" import { cellarDataMap } from "data/cellarDataMap" import { useCreateContracts } from "data/hooks/useCreateContracts" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { estimateGasLimitWithRetry } from "utils/estimateGasLimit" import { useGeo } from "context/geoContext" import { waitTime } from "data/uiConfig" @@ -87,7 +87,7 @@ export const WithdrawForm: VFC = ({ onClose }) => { const { cellarSigner } = useCreateContracts(cellarConfig) - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) const { data: lpTokenData, isLoading: isBalanceLoading } = lpToken const { doHandleTransaction } = useHandleTransaction() diff --git a/src/components/_forms/WithdrawQueueForm.tsx b/src/components/_forms/WithdrawQueueForm.tsx index aa2e17366..6990a2536 100644 --- a/src/components/_forms/WithdrawQueueForm.tsx +++ b/src/components/_forms/WithdrawQueueForm.tsx @@ -28,7 +28,7 @@ import { ethers } from "ethers" import { useHandleTransaction } from "hooks/web3" import { useRouter } from "next/router" import { cellarDataMap } from "data/cellarDataMap" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { estimateGasLimitWithRetry } from "utils/estimateGasLimit" import { useGeo } from "context/geoContext" import { useUserStrategyData } from "data/hooks/useUserStrategyData" @@ -140,7 +140,7 @@ export const WithdrawQueueForm: VFC = ({ skip: true, }) - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) const { data: lpTokenData, isLoading: isBalanceLoading } = lpToken let strategyBaseAsset: Token = cellarConfig.baseAsset diff --git a/src/components/_modals/UnstakeModal.tsx b/src/components/_modals/UnstakeModal.tsx index a5dedd209..607de09c7 100644 --- a/src/components/_modals/UnstakeModal.tsx +++ b/src/components/_modals/UnstakeModal.tsx @@ -6,7 +6,7 @@ import { toEther } from "utils/formatCurrency" import { UnstakeForm } from "components/_forms/UnstakeForm" import { useRouter } from "next/router" import { cellarDataMap } from "data/cellarDataMap" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" type UnstakeModalProps = Pick & { onCloseProp: () => void @@ -19,7 +19,7 @@ export const UnstakeModal: VFC = ({ }) => { const id = useRouter().query.id as string const cellarConfig = cellarDataMap[id].config - const { lpToken } = useUserBalances(cellarConfig) + const { lpToken } = useUserBalance(cellarConfig) const { data: lpTokenData } = lpToken return ( diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index 23d23e20f..c3dc7b632 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -39,9 +39,9 @@ import { import { isEqual } from "lodash" import { DeleteCircleIcon } from "components/_icons" import { add, isBefore } from "date-fns" -import { useUserDataAllStrategies } from "data/hooks/useUserDataAllStrategies" -import { useAccount } from "wagmi" +import { useAccount, useNetwork } from "wagmi" import { StrategyData } from "data/actions/types" +import { useUserBalances } from "data/hooks/useUserBalances" export const PageHome = () => { const { @@ -64,9 +64,10 @@ export const PageHome = () => { id, } = useDepositModalStore() - const { timeline } = useHome() - let { data: userData } = useUserDataAllStrategies(); + const { timeline } = useHome(); const { isConnected } = useAccount(); + const { chain: currentChain } = useNetwork(); + const { userBalances } = useUserBalances(); const columns = isDesktop ? StrategyDesktopColumn({ @@ -290,17 +291,14 @@ export const PageHome = () => { return filteredData.sort((a, b) => { // 1. Priority - does user own same assets as in strategy - if (isConnected && userData?.depositAssetBalances) { - for (const balance of userData?.depositAssetBalances) { + if (isConnected && userBalances) { + for (const balance of userBalances) { const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.tradedAssets?.some( - asset => asset.symbol === balance.symbol + asset => (strategy.config.chain.wagmiId === currentChain?.id) && (asset.symbol?.toUpperCase() === balance.symbol.toUpperCase()) ) const strategyAHasAsset = doesStrategyHaveAsset(a); const strategyBHasAsset = doesStrategyHaveAsset(b); - if (strategyAHasAsset && strategyBHasAsset) { - break; - } if ((strategyAHasAsset || strategyBHasAsset) && !(strategyAHasAsset && strategyBHasAsset)) { return strategyAHasAsset ? -1 : 1; } @@ -330,7 +328,7 @@ export const PageHome = () => { selectedDepositAssets, showDeprecated, showIncentivised, - userData, + userBalances, isConnected ]) diff --git a/src/components/_sidebar/PortofolioItem.tsx b/src/components/_sidebar/PortofolioItem.tsx index 0a59f180b..460a80105 100644 --- a/src/components/_sidebar/PortofolioItem.tsx +++ b/src/components/_sidebar/PortofolioItem.tsx @@ -9,7 +9,7 @@ import { Img, } from "@chakra-ui/react" import { cellarDataMap } from "data/cellarDataMap" -import { useUserBalances } from "data/hooks/useUserBalances" +import { useUserBalance } from "data/hooks/useUserBalance" import { useRouter } from "next/router" import { FC } from "react" import { formatUSD, toEther } from "utils/formatCurrency" @@ -48,7 +48,7 @@ export const PortofolioItem: FC = ({ }) => { const cellarData = cellarDataMap[slug] - const { lpToken } = useUserBalances(cellarData.config) + const { lpToken } = useUserBalance(cellarData.config) const { data: lpTokenData } = lpToken const baseAsset = tokenConfig.find( diff --git a/src/data/actions/common/getUserDataAllStrategies.ts b/src/data/actions/common/getUserDataAllStrategies.ts index bb33f2b04..bd17748fa 100644 --- a/src/data/actions/common/getUserDataAllStrategies.ts +++ b/src/data/actions/common/getUserDataAllStrategies.ts @@ -79,29 +79,6 @@ export const getUserDataAllStrategies = async ({ ) ) - const tokenList = getAcceptedDepositAssetsByChain(chain); - const depositAssetBalances : { - decimals: ResolvedConfig['IntType']; - formatted: string; - symbol: string; - value: ResolvedConfig['BigIntType']; - }[] = []; - - for (const token of tokenList) { - await fetchBalance({ - token: getAddress(token!.address), - address: getAddress(userAddress) - }).then((balance) => { - if (!balance.value.isZero()) { - depositAssetBalances.push(balance); - } - }) - .catch((error) => console.log("error", error)) - } - - depositAssetBalances.sort( - (x, y) => new BigNumber(y.value._hex).minus(new BigNumber(x.value._hex)).toNumber() - ) const userData = userDataRes.filter((item) => !!item) @@ -145,7 +122,6 @@ export const getUserDataAllStrategies = async ({ value: totalNetValue, formatted: formatUSD(String(totalNetValue)), }, - depositAssetBalances, totalSommRewards: { value: totalSommRewards, formatted: Number( diff --git a/src/data/hooks/useUserBalances.tsx b/src/data/hooks/useUserBalance.tsx similarity index 90% rename from src/data/hooks/useUserBalances.tsx rename to src/data/hooks/useUserBalance.tsx index ac7d54a6c..d15728ebc 100644 --- a/src/data/hooks/useUserBalances.tsx +++ b/src/data/hooks/useUserBalance.tsx @@ -2,7 +2,7 @@ import { ConfigProps } from "data/types" import { useAccount, useBalance, useToken } from "wagmi" import { getAddress } from "ethers/lib/utils.js" -export const useUserBalances = (config: ConfigProps) => { +export const useUserBalance = (config: ConfigProps) => { const { address } = useAccount() const lpToken = useBalance({ diff --git a/src/data/hooks/useUserBalances.ts b/src/data/hooks/useUserBalances.ts new file mode 100644 index 000000000..04ebd6049 --- /dev/null +++ b/src/data/hooks/useUserBalances.ts @@ -0,0 +1,68 @@ +import { getAcceptedDepositAssetsByChain } from "data/tokenConfig" +import { ResolvedConfig } from "abitype" +import { fetchBalance } from "@wagmi/core" +import { getAddress } from "ethers/lib/utils" +import { useAccount, useNetwork } from "wagmi" +import { chainConfig } from "data/chainConfig" +import { useEffect, useState } from "react" +import { fetchCoingeckoPrice } from "queries/get-coingecko-price" + +type Balance = { + decimals: ResolvedConfig['IntType']; + formatted: string; + symbol: string; + value: ResolvedConfig['BigIntType']; + valueInUSD: number; +} + +export const useUserBalances = () => { + + const [userBalances, setUserBalances] = useState(); + const { address, isConnected } = useAccount(); + const { chain } = useNetwork(); + + useEffect(() => { + const fetchBalances = async () => { + + const depositAssetBalances : Balance[] = []; + const chainObj = chainConfig.find( + (item) => item.wagmiId === chain?.id + )! + + const tokenList = getAcceptedDepositAssetsByChain(chainObj.id); + + await Promise.all(tokenList.map(async (token) => { + try { + const balance = await fetchBalance({ + token: getAddress(token!.address), + address: getAddress(address!) + }); + + if (!balance.value.isZero()) { + const price = await fetchCoingeckoPrice( + token!, + "usd" + ); + const valueInUSD = Number(balance.formatted) * Number(price || 0); + depositAssetBalances.push({...balance, valueInUSD}); + } + } catch (error) { + console.error("error", error); + } + })); + depositAssetBalances.sort( + (x, y) => y.valueInUSD - x.valueInUSD + ); + + setUserBalances(depositAssetBalances); + } + if (isConnected) { + fetchBalances(); + } + + }, [address, isConnected]) + + return { + userBalances + } +} diff --git a/src/data/hooks/useUserStrategyData.ts b/src/data/hooks/useUserStrategyData.ts index fb05075ae..401d24143 100644 --- a/src/data/hooks/useUserStrategyData.ts +++ b/src/data/hooks/useUserStrategyData.ts @@ -5,8 +5,7 @@ import { useAccount, useSigner } from "wagmi" import { useAllContracts } from "./useAllContracts" import { useCoinGeckoPrice } from "./useCoinGeckoPrice" import { useStrategyData } from "./useStrategyData" -import { useUserBalances } from "./useUserBalances" -import { useNetwork } from "wagmi" +import { useUserBalance } from "./useUserBalance" import { tokenConfig } from "data/tokenConfig" export const useUserStrategyData = (strategyAddress: string, chain: string) => { @@ -32,7 +31,7 @@ export const useUserStrategyData = (strategyAddress: string, chain: string) => { (item) => item.config.cellar.address === strategyAddress && item.config.chain.id === chain )?.config.isNoDataSource ) - const { lpToken } = useUserBalances(config) + const { lpToken } = useUserBalance(config) const baseAsset = config.baseAsset const { data: baseAssetPrice } = useCoinGeckoPrice( baseAsset From 6e7e3479ec5582591707e919b288159eb9e072bc Mon Sep 17 00:00:00 2001 From: aleots Date: Tue, 2 Apr 2024 20:11:30 +0300 Subject: [PATCH 13/19] Exception for token that has different symbol names --- src/data/hooks/useUserBalances.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/data/hooks/useUserBalances.ts b/src/data/hooks/useUserBalances.ts index 04ebd6049..37e9c9f7e 100644 --- a/src/data/hooks/useUserBalances.ts +++ b/src/data/hooks/useUserBalances.ts @@ -39,6 +39,10 @@ export const useUserBalances = () => { }); if (!balance.value.isZero()) { + // fix because token comes with different naming + if (balance.symbol === 'B-rETH-STABLE'){ + balance.symbol = 'rETH BPT' + } const price = await fetchCoingeckoPrice( token!, "usd" From 2b397793669e19b11c681777fa5113a6633a28ff Mon Sep 17 00:00:00 2001 From: aleots Date: Wed, 3 Apr 2024 17:47:55 +0300 Subject: [PATCH 14/19] Add depositTokens to returned strategy data --- src/data/actions/common/getStrategyData.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/data/actions/common/getStrategyData.ts b/src/data/actions/common/getStrategyData.ts index 449660fe7..f4a723125 100644 --- a/src/data/actions/common/getStrategyData.ts +++ b/src/data/actions/common/getStrategyData.ts @@ -93,6 +93,7 @@ export const getStrategyData = async ({ return tokens })() + const depositTokens = strategy.depositTokens.list; const stakingEnd = await getStakingEnd( stakerContract as CellarStakingV0815 ) @@ -106,7 +107,7 @@ export const getStrategyData = async ({ // Custom reward APY overrides // TODO: Eventually we just need to make this a type of list with the specific token reward and the APY - /** + /** if (strategy.slug === utilConfig.CONTRACT.TURBO_STETH.SLUG) { // Get wstETH price const wstethPrice = Number( @@ -145,7 +146,7 @@ export const getStrategyData = async ({ let extraRewardsApy = undefined // TODO: This is part of the tech debt above, this is extra rewards APYs if they should be in addition to SOMM rewards - /** + /** if (strategy.slug === utilConfig.CONTRACT.TURBO_GHO.SLUG) { // Get GHO price const ghoPrice = Number( @@ -235,7 +236,7 @@ export const getStrategyData = async ({ } } - /** + /** if (strategy.slug === utilConfig.CONTRACT.TURBO_STETH.SLUG) { const launchDay = launchDate ?? subDays(new Date(), 8) const launchEpoch = Math.floor(launchDay.getTime() / 1000) @@ -326,6 +327,7 @@ export const getStrategyData = async ({ baseApy: baseApyValue, baseApySumRewards, changes, + depositTokens, description, isNew, isStakingOngoing, From 0b99b7cb884f7e7e2bd56d24e5606cb53b5c0c57 Mon Sep 17 00:00:00 2001 From: aleots Date: Wed, 3 Apr 2024 17:49:03 +0300 Subject: [PATCH 15/19] Use useQuery hook to fetch user balances --- src/components/_pages/PageHome.tsx | 10 ++-- src/data/hooks/useUserBalances.ts | 81 +++++++++++++++--------------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index c3dc7b632..f6b5ea6d2 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -291,10 +291,10 @@ export const PageHome = () => { return filteredData.sort((a, b) => { // 1. Priority - does user own same assets as in strategy - if (isConnected && userBalances) { - for (const balance of userBalances) { - const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.tradedAssets?.some( - asset => (strategy.config.chain.wagmiId === currentChain?.id) && (asset.symbol?.toUpperCase() === balance.symbol.toUpperCase()) + if (isConnected && userBalances.data) { + for (const balance of userBalances.data) { + const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.depositTokens.some( + asset => (strategy.config.chain.wagmiId === currentChain?.id) && (asset.toUpperCase() === balance.symbol.toUpperCase()) ) const strategyAHasAsset = doesStrategyHaveAsset(a); const strategyBHasAsset = doesStrategyHaveAsset(b); @@ -328,7 +328,7 @@ export const PageHome = () => { selectedDepositAssets, showDeprecated, showIncentivised, - userBalances, + userBalances.data, isConnected ]) diff --git a/src/data/hooks/useUserBalances.ts b/src/data/hooks/useUserBalances.ts index 37e9c9f7e..e2e3c2f4c 100644 --- a/src/data/hooks/useUserBalances.ts +++ b/src/data/hooks/useUserBalances.ts @@ -4,8 +4,8 @@ import { fetchBalance } from "@wagmi/core" import { getAddress } from "ethers/lib/utils" import { useAccount, useNetwork } from "wagmi" import { chainConfig } from "data/chainConfig" -import { useEffect, useState } from "react" import { fetchCoingeckoPrice } from "queries/get-coingecko-price" +import { useQuery } from "@tanstack/react-query" type Balance = { decimals: ResolvedConfig['IntType']; @@ -17,56 +17,55 @@ type Balance = { export const useUserBalances = () => { - const [userBalances, setUserBalances] = useState(); - const { address, isConnected } = useAccount(); + const { address } = useAccount(); const { chain } = useNetwork(); - useEffect(() => { - const fetchBalances = async () => { + const fetchBalances = async () => { + const depositAssetBalances : Balance[] = []; + const chainObj = chainConfig.find( + (item) => item.wagmiId === chain?.id + )! - const depositAssetBalances : Balance[] = []; - const chainObj = chainConfig.find( - (item) => item.wagmiId === chain?.id - )! + const tokenList = getAcceptedDepositAssetsByChain(chainObj.id); - const tokenList = getAcceptedDepositAssetsByChain(chainObj.id); + await Promise.all(tokenList.map(async (token) => { + try { + const balance = await fetchBalance({ + token: getAddress(token!.address), + address: getAddress(address!) + }); - await Promise.all(tokenList.map(async (token) => { - try { - const balance = await fetchBalance({ - token: getAddress(token!.address), - address: getAddress(address!) - }); - - if (!balance.value.isZero()) { - // fix because token comes with different naming - if (balance.symbol === 'B-rETH-STABLE'){ - balance.symbol = 'rETH BPT' - } - const price = await fetchCoingeckoPrice( - token!, - "usd" - ); - const valueInUSD = Number(balance.formatted) * Number(price || 0); - depositAssetBalances.push({...balance, valueInUSD}); + if (!balance.value.isZero()) { + // fix because token comes with different naming + if (balance.symbol === 'B-rETH-STABLE'){ + balance.symbol = 'rETH BPT' } - } catch (error) { - console.error("error", error); + const price = await fetchCoingeckoPrice( + token!, + "usd" + ); + const valueInUSD = Number(balance.formatted) * Number(price || 0); + depositAssetBalances.push({...balance, valueInUSD}); } - })); - depositAssetBalances.sort( - (x, y) => y.valueInUSD - x.valueInUSD - ); + } catch (error) { + console.error("error", error); + } + })); + depositAssetBalances.sort( + (x, y) => y.valueInUSD - x.valueInUSD + ); - setUserBalances(depositAssetBalances); - } - if (isConnected) { - fetchBalances(); - } + return depositAssetBalances; + } - }, [address, isConnected]) + const query = useQuery( + ["USE_USER_BALANCES"], + async () => { + return await fetchBalances() + } + ) return { - userBalances + userBalances: query } } From 1eb446ad69e2be80e6f1a8d505bce2b84ff1a63a Mon Sep 17 00:00:00 2001 From: aleots Date: Tue, 9 Apr 2024 22:03:36 +0300 Subject: [PATCH 16/19] Fetch users ETH balance and consider it as WETH. --- src/components/_pages/PageHome.tsx | 10 +++++++++- src/data/hooks/useUserBalances.ts | 2 +- src/data/tokenConfig.ts | 23 +++++++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index f6b5ea6d2..31d1ac0f5 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -290,8 +290,16 @@ export const PageHome = () => { return filteredData.sort((a, b) => { - // 1. Priority - does user own same assets as in strategy + // 1. Priority - strategies deposit assets that user holds if (isConnected && userBalances.data) { + // if user has ETH consider it as they had WETH + const considerETHasWETH = (strategy: StrategyData) => { + if (strategy?.depositTokens.some(asset => asset === "WETH")) { + strategy?.depositTokens.push("ETH"); + } + }; + considerETHasWETH(a); + considerETHasWETH(b); for (const balance of userBalances.data) { const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.depositTokens.some( asset => (strategy.config.chain.wagmiId === currentChain?.id) && (asset.toUpperCase() === balance.symbol.toUpperCase()) diff --git a/src/data/hooks/useUserBalances.ts b/src/data/hooks/useUserBalances.ts index e2e3c2f4c..d25fd0843 100644 --- a/src/data/hooks/useUserBalances.ts +++ b/src/data/hooks/useUserBalances.ts @@ -31,7 +31,7 @@ export const useUserBalances = () => { await Promise.all(tokenList.map(async (token) => { try { const balance = await fetchBalance({ - token: getAddress(token!.address), + token: token?.symbol !== 'ETH' ? getAddress(token!.address) : undefined, address: getAddress(address!) }); diff --git a/src/data/tokenConfig.ts b/src/data/tokenConfig.ts index 9a69f3cf2..895b87cb4 100644 --- a/src/data/tokenConfig.ts +++ b/src/data/tokenConfig.ts @@ -510,6 +510,24 @@ export const tokenConfig: Token[] = [ decimals: 18, chain: chainSlugMap.ETHEREUM.id, }, + { + src: "", + alt: "", + symbol: "ETH", + address: "", + coinGeckoId: "ethereum", + decimals: 18, + chain: chainSlugMap.ETHEREUM.id, + }, + { + src: "", + alt: "", + symbol: "ETH", + address: "", + coinGeckoId: "ethereum", + decimals: 18, + chain: chainSlugMap.ARBITRUM.id, + }, ] // --- ETH ACCEPTED TOKENS --- let acceptedETHDepositTokens = [ @@ -542,6 +560,7 @@ let acceptedETHDepositTokens = [ "swETH", "BUSD", "rETH BPT", + "ETH" ] let depositTokenMapETH = tokenConfig.reduce((map, token) => { @@ -562,7 +581,7 @@ export const acceptedETHDepositTokenMap = Object.keys( }, {} as { [symbol: string]: Token }) // --- ARB ACCEPTED TOKENS --- -let acceptedARBDepositTokens = ["USDC", "USDT", "DAI", "USDC.e", 'WETH', 'wstETH', 'rETH']; +let acceptedARBDepositTokens = ["USDC", "USDT", "DAI", "USDC.e", 'WETH', 'wstETH', 'rETH', 'ETH']; let depositTokenMapARB = tokenConfig.reduce((map, token) => { if (acceptedARBDepositTokens.includes(token.symbol)) { @@ -582,7 +601,7 @@ export const acceptedARBDepositTokenMap = Object.keys( }, {} as { [symbol: string]: Token }) // --- OPTIMISM ACCEPTED TOKENS --- -let acceptedOPTDepositTokens = ['WETH', 'wstETH', 'rETH']; +let acceptedOPTDepositTokens = ['WETH', 'wstETH', 'rETH', 'ETH']; const acceptedDepositTokensByChain: { [key: string]: any } = { From 9c4a887bb9100bea2e7ee8db082977a11204cb67 Mon Sep 17 00:00:00 2001 From: aleots Date: Tue, 9 Apr 2024 22:06:33 +0300 Subject: [PATCH 17/19] Update sorting to not check if current chain and strategy chain matches --- src/components/_pages/PageHome.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index 31d1ac0f5..28c107bd8 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -39,7 +39,7 @@ import { import { isEqual } from "lodash" import { DeleteCircleIcon } from "components/_icons" import { add, isBefore } from "date-fns" -import { useAccount, useNetwork } from "wagmi" +import { useAccount } from "wagmi" import { StrategyData } from "data/actions/types" import { useUserBalances } from "data/hooks/useUserBalances" @@ -66,7 +66,6 @@ export const PageHome = () => { const { timeline } = useHome(); const { isConnected } = useAccount(); - const { chain: currentChain } = useNetwork(); const { userBalances } = useUserBalances(); const columns = isDesktop @@ -302,7 +301,7 @@ export const PageHome = () => { considerETHasWETH(b); for (const balance of userBalances.data) { const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.depositTokens.some( - asset => (strategy.config.chain.wagmiId === currentChain?.id) && (asset.toUpperCase() === balance.symbol.toUpperCase()) + asset => (asset.toUpperCase() === balance.symbol.toUpperCase()) ) const strategyAHasAsset = doesStrategyHaveAsset(a); const strategyBHasAsset = doesStrategyHaveAsset(b); From b414a0f95d3e7e22e2624fb2d56562e7c846bd08 Mon Sep 17 00:00:00 2001 From: aleots Date: Wed, 10 Apr 2024 11:16:33 +0300 Subject: [PATCH 18/19] simplify ETH-WETH exception --- src/components/_pages/PageHome.tsx | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/components/_pages/PageHome.tsx b/src/components/_pages/PageHome.tsx index 28c107bd8..4ceaf895c 100644 --- a/src/components/_pages/PageHome.tsx +++ b/src/components/_pages/PageHome.tsx @@ -291,17 +291,15 @@ export const PageHome = () => { // 1. Priority - strategies deposit assets that user holds if (isConnected && userBalances.data) { - // if user has ETH consider it as they had WETH - const considerETHasWETH = (strategy: StrategyData) => { - if (strategy?.depositTokens.some(asset => asset === "WETH")) { - strategy?.depositTokens.push("ETH"); - } - }; - considerETHasWETH(a); - considerETHasWETH(b); for (const balance of userBalances.data) { - const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.depositTokens.some( - asset => (asset.toUpperCase() === balance.symbol.toUpperCase()) + const doesStrategyHaveAsset = (strategy: StrategyData) => strategy?.depositTokens?.some( + asset => { + // if user has ETH consider it as they had WETH + if (balance.symbol.toUpperCase() === "ETH" && asset.toUpperCase() === "WETH") { + return true; + } + return asset.toUpperCase() === balance.symbol.toUpperCase() + } ) const strategyAHasAsset = doesStrategyHaveAsset(a); const strategyBHasAsset = doesStrategyHaveAsset(b); From 879a123da94bd7cbe13acbe029e0a6579a4a9246 Mon Sep 17 00:00:00 2001 From: aleots Date: Wed, 10 Apr 2024 11:25:27 +0300 Subject: [PATCH 19/19] add ETH token for Optimism --- src/data/tokenConfig.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/data/tokenConfig.ts b/src/data/tokenConfig.ts index 895b87cb4..8ea9183f3 100644 --- a/src/data/tokenConfig.ts +++ b/src/data/tokenConfig.ts @@ -528,6 +528,15 @@ export const tokenConfig: Token[] = [ decimals: 18, chain: chainSlugMap.ARBITRUM.id, }, + { + src: "", + alt: "", + symbol: "ETH", + address: "", + coinGeckoId: "ethereum", + decimals: 18, + chain: chainSlugMap.OPTIMISM.id, + }, ] // --- ETH ACCEPTED TOKENS --- let acceptedETHDepositTokens = [