diff --git a/src/components/pages/Fuse/FusePoolsPage/FusePoolsPage.tsx b/src/components/pages/Fuse/FusePoolsPage/FusePoolsPage.tsx index 2e98c912..89039206 100644 --- a/src/components/pages/Fuse/FusePoolsPage/FusePoolsPage.tsx +++ b/src/components/pages/Fuse/FusePoolsPage/FusePoolsPage.tsx @@ -1,4 +1,4 @@ -import { memo } from "react"; +import { memo, useEffect, useState } from "react"; // Components import DashboardBox from "components/shared/DashboardBox"; @@ -6,18 +6,28 @@ import FuseStatsBar from "../FuseStatsBar"; import FuseTabBar from "../FuseTabBar"; // Hooks -import { useIsSmallScreen } from "hooks/useIsSmallScreen"; +import { useIsSmallScreen } from "hooks/useIsSmallScreen" +import { Fuse } from "../../../../esm/index"; // Utils -import { Column } from "lib/chakraUtils"; +import { Center, Column, Row } from "lib/chakraUtils"; import { PoolList } from "./PoolList"; import { useFilter } from "hooks/useFilter"; -import { useFusePools } from "hooks/fuse/useFusePools"; +import { createMergedPools, MergedPool, fetchPoolsManual, LensFusePool, LensFusePoolData, useFusePools, useFusePoolsList, useInifinitePools } from "hooks/fuse/useFusePools"; +import { useRari } from "context/RariContext"; +import { useInfiniteQuery } from "react-query"; +import { BigNumber } from "ethers"; +import { providers } from "@0xsequence/multicall"; +import { fetchCurrentETHPrice } from "utils/coingecko"; +import { ChainID } from "constants/networks"; +import { Spinner } from "@chakra-ui/react"; +import { ModalDivider } from "components/shared/Modal"; +import { Text } from "rari-components"; const FusePoolsPage = memo(() => { const isMobile = useIsSmallScreen(); const filter = useFilter(); -const filteredPools = useFusePools(filter); + return ( <> @@ -35,7 +45,7 @@ const filteredPools = useFusePools(filter); - + {filter === "unverified-pools" ? : } @@ -43,3 +53,135 @@ const filteredPools = useFusePools(filter); }); export default FusePoolsPage; + +const VerifiedPools = ({filter}: {filter: string | null}) => { + const filteredPools = useFusePools(filter); + return ( + + ) +} + +const UnverifiedPools = ({filter}: {filter: string}) => { + const [poolListLength, setPoolListLength] = useState(0) + const [poolBatch, setPoolBatch] = useState([0,20]) + const [poolSlice, setPoolSlice] = useState([[]]) + + const { fuse, address, chainId } = useRari() + const poolList: [][] = useFusePoolsList(fuse, address) + + useEffect(() => { + if(poolList) { + setPoolListLength(poolList[1].length) + setPoolSlice([poolList[0].slice(...poolBatch),poolList[1].slice(...poolBatch)]) + } + }, [poolList]) + + + const fetchPoolsManual = async ({pageParam = poolSlice}: any) => { + if (pageParam.length === 1) return + // Extract data from Directory call + let ids: string[] = (pageParam[0] ?? []).map((bn: BigNumber) => + bn.toString() + ); + let fusePools: LensFusePool[] = pageParam[1]; + let comptrollers = fusePools.map((pool: any) => pool[2]); + + // Query lens.getPoolSummary + let fusePoolsData: LensFusePoolData[] = await Promise.all( + comptrollers.map(async (comptroller) => { + const _data = await fuse.contracts.FusePoolLens.callStatic.getPoolSummary( + comptroller + ); + const data: LensFusePoolData = { + totalSupply: _data[0], + totalBorrow: _data[1], + underlyingTokens: _data[2], + underlyingSymbols: _data[3], + whitelistedAdmin: _data[4], + }; + return data; + }) + ).catch((err) => { + console.error("Error querying poolSummaries", err); + return []; + }); + + const multicallProvider = new providers.MulticallProvider(fuse.contracts.FusePoolLens.provider) + const multicallFuse = new Fuse(multicallProvider, chainId as any) + const poolRewardTokens = await Promise.all(fusePools.map((pool) => { + return multicallFuse.contracts.FusePoolLensSecondary.callStatic.getRewardSpeedsByPool( + pool.comptroller + ).then((rewards) => { + //reward token list + return rewards[2] + }) + })) + + const fetchETHPrice = fetchCurrentETHPrice(); + const merged: MergedPool[] = await createMergedPools(ids, fusePools, fusePoolsData, poolRewardTokens, fetchETHPrice); + return merged + }; + + const { + isLoading, + isError, + error, + data, + fetchNextPage, + isFetching, + isFetchingNextPage + //@ts-ignore + } = useInfiniteQuery(['colors'], fetchPoolsManual, { + enabled: poolList && poolSlice.length > 1, + refetchOnWindowFocus: false, + getNextPageParam: (lastPage, pages) => { + return poolSlice + } + }) + + + const handleInifiniteScrollClick = () => { + setPoolBatch([poolBatch[0] + 20, poolBatch[1] + 20]) + setPoolSlice([ + poolList[0].slice(poolBatch[0] + 20, poolBatch[1] + 20), + poolList[1].slice(poolBatch[0] + 20, poolBatch[1] + 20) + ]) + fetchNextPage({ + pageParam: [ + poolList[0].slice(poolBatch[0] + 20, poolBatch[1] + 20), + poolList[1].slice(poolBatch[0] + 20, poolBatch[1] + 20) + ] + }) + } + + return ( + <> + { data?.pages.map((page, index) => 0}/>)} + + { poolBatch[1] < poolListLength ? + <> + + + + Load more... + + + + + : null + } + + ) +} + diff --git a/src/components/pages/Fuse/FusePoolsPage/PoolList.tsx b/src/components/pages/Fuse/FusePoolsPage/PoolList.tsx index 3651abc0..abbb171d 100644 --- a/src/components/pages/Fuse/FusePoolsPage/PoolList.tsx +++ b/src/components/pages/Fuse/FusePoolsPage/PoolList.tsx @@ -12,7 +12,7 @@ import { Column, Row, useIsMobile } from "lib/chakraUtils"; import { filterPoolName } from "utils/fetchFusePoolData"; import { useState } from "react"; -export const PoolList = ({ pools }: { pools: MergedPool[] | null }) => { +export const PoolList = ({ pools, infiniteScroll }: { pools: MergedPool[] | null | undefined, infiniteScroll?: boolean }) => { const { t } = useTranslation(); const isMobile = useIsMobile(); @@ -23,6 +23,7 @@ export const PoolList = ({ pools }: { pools: MergedPool[] | null }) => { crossAxisAlignment="flex-start" expand > + {infiniteScroll ? null : { )} + } diff --git a/src/hooks/fuse/useFusePools.ts b/src/hooks/fuse/useFusePools.ts index 3daf424c..4c9415d0 100644 --- a/src/hooks/fuse/useFusePools.ts +++ b/src/hooks/fuse/useFusePools.ts @@ -29,7 +29,7 @@ export interface FusePool { isPrivate: boolean; } -interface LensFusePool { +export interface LensFusePool { blockPosted: string; name: string; creator: string; @@ -37,7 +37,7 @@ interface LensFusePool { timestampPosted: string; } -interface LensFusePoolData { +export interface LensFusePoolData { totalBorrow: string; totalSupply: string; underlyingSymbols: string[]; @@ -80,20 +80,15 @@ export const fetchPoolsManual = async ({ verification = false, chainId = 1, blockNum, + fusePoolsDirectoryResult }: { + fusePoolsDirectoryResult: any fuse: Fuse; address: string; verification?: boolean; chainId?: number; blockNum?: number; }) => { - // Query Directory - let fusePoolsDirectoryResult = - await fuse.contracts.FusePoolDirectory.callStatic.getPublicPoolsByVerification( - verification, - { from: address } - ); - // Extract data from Directory call let ids: string[] = (fusePoolsDirectoryResult[0] ?? []).map((bn: BigNumber) => bn.toString() @@ -136,6 +131,17 @@ export const fetchPoolsManual = async ({ return await createMergedPools(ids, fusePools, fusePoolsData, poolRewardTokens, fetchETHPrice); }; +export const fetchPoolsList = async ( + fuse: Fuse, + address: string, +) => { + const answer = await fuse.contracts.FusePoolDirectory.connect(fuse.contracts.FusePoolLens.provider).callStatic.getPublicPoolsByVerification( + false, + { from: address } + ) + return answer +} + export const fetchPools = async ({ fuse, address, @@ -209,7 +215,7 @@ export const fetchPools = async ({ return await createMergedPools(ids, fusePools, fusePoolsData, poolRewardTokens, fetchETHPrice); }; -const createMergedPools = async ( +export const createMergedPools = async ( ids: string[], fusePools: LensFusePool[], fusePoolsData: LensFusePoolData[], @@ -252,14 +258,6 @@ export const useFusePools = (filter: string | null): MergedPool[] | null => { const { data: pools } = useQuery( `${address} fusePoolList ${filter ?? ""} chainId: ${chainId}`, async () => { - if (chainId === ChainID.ARBITRUM && filter === "unverified-pools") { - return await fetchPoolsManual({ - fuse, - address, - verification: false, - chainId, - }); - } return await fetchPools({ fuse, address, filter, chainId }); } ); @@ -299,3 +297,77 @@ export const useFusePools = (filter: string | null): MergedPool[] | null => { return filteredPools; }; + +export const useFusePoolsList = ( + fuse: Fuse, + address: string, +) => { + const { data: pools } = useQuery('Fuse unverified pools list', async () => await fetchPoolsList(fuse, address)) + + return pools +} + +export const useInifinitePools = (fuse: Fuse, chainId: number, address: string) => { + + const fetchPoolsManual = async ({ + fusePoolsDirectoryResult + }: { + fusePoolsDirectoryResult: any + }) => { + // Extract data from Directory call + let ids: string[] = (fusePoolsDirectoryResult[0] ?? []).map((bn: BigNumber) => + bn.toString() + ); + let fusePools: LensFusePool[] = fusePoolsDirectoryResult[1]; + let comptrollers = fusePools.map(({ comptroller }) => comptroller); + + // Query lens.getPoolSummary + let fusePoolsData: LensFusePoolData[] = await Promise.all( + comptrollers.map(async (comptroller) => { + const _data = await fuse.contracts.FusePoolLens.callStatic.getPoolSummary( + comptroller + ); + const data: LensFusePoolData = { + totalSupply: _data[0], + totalBorrow: _data[1], + underlyingTokens: _data[2], + underlyingSymbols: _data[3], + whitelistedAdmin: _data[4], + }; + return data; + }) + ).catch((err) => { + console.error("Error querying poolSummaries", err); + return []; + }); + + const multicallProvider = new providers.MulticallProvider(fuse.provider) + const multicallFuse = new Fuse(multicallProvider, chainId) + const poolRewardTokens = await Promise.all(fusePools.map((pool) => { + return multicallFuse.contracts.FusePoolLensSecondary.callStatic.getRewardSpeedsByPool( + pool.comptroller + ).then((rewards) => { + //reward token list + return rewards[2] + }) + })) + + const fetchETHPrice = fetchCurrentETHPrice(); + return await createMergedPools(ids, fusePools, fusePoolsData, poolRewardTokens, fetchETHPrice); + }; + + const { + isLoading, + isError, + error, + data, + fetchNextPage, + isFetching, + isFetchingNextPage + //@ts-ignore + } = useInfiniteQuery(['colors'], fetchPoolsManual, { + getNextPageParam: (lastPage: any, pages: any) => { + return {} + } + }) +}