diff --git a/packages/react-app-revamp/app/contest/[chain]/[address]/page.tsx b/packages/react-app-revamp/app/contest/[chain]/[address]/page.tsx index 1f2d5c57d..9092a0888 100644 --- a/packages/react-app-revamp/app/contest/[chain]/[address]/page.tsx +++ b/packages/react-app-revamp/app/contest/[chain]/[address]/page.tsx @@ -17,7 +17,7 @@ const isDev = process.env.NODE_ENV === "development"; async function getContestDetails(address: string, chainName: string) { const chainId = chains.filter( - (chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName, + (chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName.toLowerCase(), )?.[0]?.id; const { abi } = await getContestContractVersion(address, chainId); diff --git a/packages/react-app-revamp/components/Comments/index.tsx b/packages/react-app-revamp/components/Comments/index.tsx index 81bc0de5a..745f8b468 100644 --- a/packages/react-app-revamp/components/Comments/index.tsx +++ b/packages/react-app-revamp/components/Comments/index.tsx @@ -4,9 +4,6 @@ import Collapsible from "@components/UI/Collapsible"; import { ChevronUpIcon } from "@heroicons/react/24/outline"; import useComments from "@hooks/useComments"; import { useCommentsStore } from "@hooks/useComments/store"; -import useContractVersion from "@hooks/useContractVersion"; -import { compareVersions } from "compare-versions"; -import { COMMENTS_VERSION } from "lib/proposal"; import { useSearchParams } from "next/navigation"; import { FC, useEffect, useRef, useState } from "react"; import CommentsForm from "./components/Form"; diff --git a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/components/Panel/components/VotingOpenOrClosed/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/components/Panel/components/VotingOpenOrClosed/index.tsx index 4cfece477..4dfcbe61d 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/components/Panel/components/VotingOpenOrClosed/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/components/Panel/components/VotingOpenOrClosed/index.tsx @@ -1,6 +1,6 @@ import { chains } from "@config/wagmi"; import { extractPathSegments } from "@helpers/extractPath"; -import useContractVersion from "@hooks/useContractVersion"; +import { useContestStore } from "@hooks/useContest/store"; import { compareVersions } from "compare-versions"; import { usePathname } from "next/navigation"; import { FC } from "react"; @@ -15,12 +15,12 @@ const ProposalStatisticsPanelVotingOpenOrClosed: FC { const asPath = usePathname(); + const { version } = useContestStore(state => state); const { address, chainName } = extractPathSegments(asPath ?? ""); const chainId = chains.filter( (chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName, )?.[0]?.id; - const { version, isLoading, isError } = useContractVersion(address, chainId); - const isV3OrHigher = !isLoading && !isError && version && compareVersions(version, "3.0") >= 0; + const isV3OrHigher = compareVersions(version, "3.0") >= 0; return (
diff --git a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx index 75cc43a12..5771a6da9 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/ProposalStatistics/index.tsx @@ -5,6 +5,10 @@ import { FC, useMemo } from "react"; import ProposalStatisticsPanel from "./components/Panel"; import SortProposalsDropdown from "./components/SortDropdown"; import { ContestStateEnum, useContestStateStore } from "@hooks/useContestState/store"; +import { useContestStore } from "@hooks/useContest/store"; +import { usePathname } from "next/navigation"; +import { extractPathSegments } from "@helpers/extractPath"; +import { chains } from "@config/wagmi"; interface ProposalStatisticsProps { contestStatus: ContestStatus; @@ -12,15 +16,19 @@ interface ProposalStatisticsProps { } const ProposalStatistics: FC = ({ contestStatus, onMenuStateChange }) => { + const asPath = usePathname(); + const { chainName, address } = extractPathSegments(asPath ?? ""); + const chainId = chains.filter(chain => chain.name.toLowerCase() === chainName.toLowerCase())[0]?.id; const { sortBy, submissionsCount } = useProposalStore(state => state); const { sortProposalData } = useProposal(); + const { contestAbi: abi, version } = useContestStore(state => state); const isSubmissionOrVotingOpen = contestStatus === ContestStatus.SubmissionOpen || contestStatus === ContestStatus.VotingOpen; const { contestState } = useContestStateStore(state => state); const isContestCanceled = contestState === ContestStateEnum.Canceled; const handleSortTypeChange = (value: string) => { - sortProposalData(value as SortOptions); + sortProposalData({ address: address as `0x${string}`, abi, chainId }, version, value as SortOptions); }; const contestStatusTitle = useMemo(() => { diff --git a/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/components/VotingQualifier/index.tsx b/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/components/VotingQualifier/index.tsx index c533f249d..e27306e4e 100644 --- a/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/components/VotingQualifier/index.tsx +++ b/packages/react-app-revamp/components/_pages/Contest/components/StickyCards/components/VotingQualifier/index.tsx @@ -1,8 +1,12 @@ /* eslint-disable react/no-unescaped-entities */ +import ButtonV3, { ButtonSize } from "@components/UI/ButtonV3"; import { chains } from "@config/wagmi"; import { extractPathSegments } from "@helpers/extractPath"; +import { useAccountChange } from "@hooks/useAccountChange"; +import { ContractConfig } from "@hooks/useContest"; import { useContestStore } from "@hooks/useContest/store"; import { useContestStatusStore } from "@hooks/useContestStatus/store"; +import useUser from "@hooks/useUser"; import { useUserStore } from "@hooks/useUser/store"; import { useConnectModal } from "@rainbow-me/rainbowkit"; import Image from "next/image"; @@ -12,9 +16,10 @@ import { useMediaQuery } from "react-responsive"; import { formatEther } from "viem"; import { useAccount } from "wagmi"; import VotingQualifierMessage from "./components/VotingQualifierMessage"; +import { useEffect, useState } from "react"; const VotingContestQualifier = () => { - const { anyoneCanVote, charge } = useContestStore(state => state); + const { anyoneCanVote, charge, contestAbi, version } = useContestStore(state => state); const { isConnected, address } = useAccount(); const { openConnectModal } = useConnectModal(); const { @@ -28,7 +33,7 @@ const VotingContestQualifier = () => { const isMobile = useMediaQuery({ maxWidth: 768 }); const costToVoteFormatted = formatEther(BigInt(charge?.type.costToVote ?? 0)); const asPath = usePathname(); - const { chainName } = extractPathSegments(asPath ?? ""); + const { address: contestAddress, chainName } = extractPathSegments(asPath ?? ""); const nativeCurrencySymbol = chains.find(chain => chain.name.toLowerCase() === chainName.toLowerCase()) ?.nativeCurrency.symbol; const chainId = chains.find(chain => chain.name.toLowerCase() === chainName.toLowerCase())?.id; diff --git a/packages/react-app-revamp/components/_pages/DialogModalSendProposal/index.tsx b/packages/react-app-revamp/components/_pages/DialogModalSendProposal/index.tsx index 295f8c8a3..f60ccf06a 100644 --- a/packages/react-app-revamp/components/_pages/DialogModalSendProposal/index.tsx +++ b/packages/react-app-revamp/components/_pages/DialogModalSendProposal/index.tsx @@ -12,8 +12,6 @@ import { import { useContestStore } from "@hooks/useContest/store"; import { useEditorStore } from "@hooks/useEditor/store"; import useEmailSignup from "@hooks/useEmailSignup"; -import useMetadataFields from "@hooks/useMetadataFields"; -import { useMetadataStore } from "@hooks/useMetadataFields/store"; import useSubmitProposal from "@hooks/useSubmitProposal"; import { useSubmitProposalStore } from "@hooks/useSubmitProposal/store"; import { useUploadImageStore } from "@hooks/useUploadImage"; diff --git a/packages/react-app-revamp/components/_pages/ListProposals/index.tsx b/packages/react-app-revamp/components/_pages/ListProposals/index.tsx index 5e83cecad..8de9792d5 100644 --- a/packages/react-app-revamp/components/_pages/ListProposals/index.tsx +++ b/packages/react-app-revamp/components/_pages/ListProposals/index.tsx @@ -1,10 +1,13 @@ import ButtonV3, { ButtonSize } from "@components/UI/ButtonV3"; import ProposalContent from "@components/_pages/ProposalContent"; +import { chains } from "@config/wagmi"; +import { extractPathSegments } from "@helpers/extractPath"; import { useContestStore } from "@hooks/useContest/store"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; import useDeleteProposal from "@hooks/useDeleteProposal"; import useProposal, { PROPOSALS_PER_PAGE } from "@hooks/useProposal"; import { useProposalStore } from "@hooks/useProposal/store"; +import { usePathname } from "next/navigation"; import { useState } from "react"; import InfiniteScroll from "react-infinite-scroll-component"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; @@ -22,6 +25,12 @@ const ProposalSkeleton = ({ count, highlightColor }: { count?: number; highlight export const ListProposals = () => { const { address } = useAccount(); + const asPath = usePathname(); + const { address: contestAddress, chainName: contestChainName } = extractPathSegments(asPath); + const chainId = chains.filter( + (chain: { name: string }) => chain.name.toLowerCase() === contestChainName.toLowerCase(), + )?.[0]?.id; + const { fetchProposalsPage } = useProposal(); const { deleteProposal, isLoading: isDeleteInProcess, isSuccess: isDeleteSuccess } = useDeleteProposal(); const { @@ -34,7 +43,7 @@ export const ListProposals = () => { totalPagesPaginationProposals, listProposalsData, } = useProposalStore(state => state); - const { contestAuthorEthereumAddress } = useContestStore(state => state); + const { contestAuthorEthereumAddress, contestAbi: abi, version } = useContestStore(state => state); const contestStatus = useContestStatusStore(state => state.contestStatus); const allowDelete = (contestStatus === ContestStatus.SubmissionOpen || contestStatus === ContestStatus.VotingOpen) && @@ -83,6 +92,12 @@ export const ListProposals = () => { dataLength={listProposalsData.length} next={() => fetchProposalsPage( + { + chainId, + address: contestAddress as `0x${string}`, + abi, + }, + version, currentPagePaginationProposals + 1, indexPaginationProposals[currentPagePaginationProposals + 1], totalPagesPaginationProposals, diff --git a/packages/react-app-revamp/hooks/useCastVotes/index.ts b/packages/react-app-revamp/hooks/useCastVotes/index.ts index 77e50efd1..880e7ee64 100644 --- a/packages/react-app-revamp/hooks/useCastVotes/index.ts +++ b/packages/react-app-revamp/hooks/useCastVotes/index.ts @@ -50,6 +50,7 @@ export function useCastVotes() { canUpdateVotesInRealTime, charge, contestAbi: abi, + version, anyoneCanVote, rewardsModuleAddress, } = useContestStore(state => state); @@ -184,7 +185,7 @@ export function useCastVotes() { } } - await updateCurrentUserVotes(anyoneCanVote); + await updateCurrentUserVotes(abi, version, anyoneCanVote); refetchTotalVotesCastOnContest(); refetchCurrentUserVotesOnProposal(); setIsLoading(false); diff --git a/packages/react-app-revamp/hooks/useContest/index.ts b/packages/react-app-revamp/hooks/useContest/index.ts index ab1c14ea6..b311e328b 100644 --- a/packages/react-app-revamp/hooks/useContest/index.ts +++ b/packages/react-app-revamp/hooks/useContest/index.ts @@ -33,7 +33,7 @@ interface ContractConfigResult { version: string; } -interface ContractConfig { +export interface ContractConfig { address: `0x${string}`; abi: any; chainId: number; @@ -72,7 +72,6 @@ export function useContest() { setSortingEnabled, setVersion, setRewardsModuleAddress, - rewardsModuleAddress, setRewardsAbi, } = useContestStore(state => state); const { setIsListProposalsSuccess, setIsListProposalsLoading, setListProposalsIds } = useProposalStore( @@ -224,7 +223,7 @@ export function useContest() { setIsSuccess(true); setIsLoading(false); - await fetchProposalsIdsList(contractConfig.abi, { + await fetchProposalsIdsList(contractConfig, version, { submissionOpen: submissionsOpenDate, votesOpen: votesOpenDate, }); @@ -236,7 +235,7 @@ export function useContest() { await Promise.all([ fetchContestContractData(contractConfig, version, rewardsModuleAddress), - processUserQualifications(), + processUserQualifications(contractConfig, version), processRequirementsData(), ]); } catch (e) { @@ -284,7 +283,7 @@ export function useContest() { setIsLoading(false); // List of proposals for this contest - await fetchProposalsIdsList(contractConfig.abi, { + await fetchProposalsIdsList(contractConfig, version, { submissionOpen: submissionsOpenDate, votesOpen: votesOpenDate, }); @@ -370,10 +369,13 @@ export function useContest() { /** * Fetch merkle tree data from DB and re-create the tree */ - async function processUserQualifications() { + async function processUserQualifications(contractConfig: ContractConfig, version: string) { if (contestStatus === ContestStatus.VotingClosed) return; - await Promise.all([checkIfCurrentUserQualifyToSubmit(), checkIfCurrentUserQualifyToVote()]); + await Promise.all([ + checkIfCurrentUserQualifyToSubmit(contractConfig, version), + checkIfCurrentUserQualifyToVote(contractConfig, version), + ]); } async function processRequirementsData() { diff --git a/packages/react-app-revamp/hooks/useContractVersion/index.ts b/packages/react-app-revamp/hooks/useContractVersion/index.ts deleted file mode 100644 index 3cb664151..000000000 --- a/packages/react-app-revamp/hooks/useContractVersion/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import getContestContractVersion from "@helpers/getContestContractVersion"; -import { useQuery } from "@tanstack/react-query"; - -const useContractVersion = (address: string, chainId: number) => { - const { data, isLoading, isError } = useQuery({ - queryKey: ["contractVersion", address, chainId], - queryFn: () => getContestContractVersion(address as `0x${string}`, chainId), - enabled: !!address && !!chainId, - }); - - return { - version: data?.version ?? "", - isLoading, - isError, - }; -}; - -export default useContractVersion; diff --git a/packages/react-app-revamp/hooks/useMetadataFields/index.ts b/packages/react-app-revamp/hooks/useMetadataFields/index.ts index 6f44ec1e7..1b490a606 100644 --- a/packages/react-app-revamp/hooks/useMetadataFields/index.ts +++ b/packages/react-app-revamp/hooks/useMetadataFields/index.ts @@ -1,8 +1,7 @@ import { chains } from "@config/wagmi"; import { extractPathSegments } from "@helpers/extractPath"; -import getContestContractVersion from "@helpers/getContestContractVersion"; +import { useContestStore } from "@hooks/useContest/store"; import { MetadataField } from "@hooks/useDeployContest/store"; -import { useQuery } from "@tanstack/react-query"; import { compareVersions } from "compare-versions"; import { usePathname } from "next/navigation"; import { useEffect } from "react"; @@ -42,28 +41,19 @@ const useMetadataFields = () => { const pathname = usePathname(); const { address: contestAddress, chainName: contestChainName } = extractPathSegments(pathname ?? ""); const contestChainId = chains.find(chain => chain.name.toLowerCase().replace(" ", "") === contestChainName)?.id; - - const { - data: contractData, - isLoading: isContractDataLoading, - isError: isContractDataError, - } = useQuery({ - queryKey: ["contestContract", contestAddress, contestChainId], - queryFn: () => getContestContractVersion(contestAddress, contestChainId!), - enabled: Boolean(contestAddress && contestChainId), - }); + const { contestAbi, version } = useContestStore(state => state); const { data: metadataSchema, isError: isMetadataError, isLoading: isMetadataLoading, } = useReadContract({ - abi: contractData?.abi as Abi, + abi: contestAbi as Abi, address: contestAddress as `0x${string}`, chainId: contestChainId, functionName: "metadataFieldsSchema", query: { - enabled: Boolean(contractData && compareVersions(contractData.version, METADATA_FIELDS_VERSION) >= 0), + enabled: Boolean(compareVersions(version, METADATA_FIELDS_VERSION) >= 0), }, }); @@ -78,8 +68,8 @@ const useMetadataFields = () => { }, [metadataSchema, isMetadataError, setFields]); return { - isLoading: isContractDataLoading || isMetadataLoading, - isError: isContractDataError || isMetadataError, + isLoading: isMetadataLoading, + isError: isMetadataError, }; }; diff --git a/packages/react-app-revamp/hooks/useProposal/index.ts b/packages/react-app-revamp/hooks/useProposal/index.ts index 3fc894d7d..3627117f3 100644 --- a/packages/react-app-revamp/hooks/useProposal/index.ts +++ b/packages/react-app-revamp/hooks/useProposal/index.ts @@ -1,8 +1,7 @@ -import { toastError } from "@components/UI/Toast"; -import { chains, config } from "@config/wagmi"; +import { chains, serverConfig } from "@config/wagmi/server"; import arrayToChunks from "@helpers/arrayToChunks"; import { extractPathSegments } from "@helpers/extractPath"; -import getContestContractVersion from "@helpers/getContestContractVersion"; +import { ContractConfig } from "@hooks/useContest"; import { useError } from "@hooks/useError"; import { readContracts } from "@wagmi/core"; import { compareVersions } from "compare-versions"; @@ -11,7 +10,6 @@ import { COMMENTS_VERSION } from "lib/proposal"; import { shuffle, sortBy as sortUnique } from "lodash"; import { usePathname } from "next/navigation"; import { formatEther } from "viem"; -import { useAccount } from "wagmi"; import { MappedProposalIds, ProposalCore, SortOptions, useProposalStore } from "./store"; import { formatProposalData, @@ -23,12 +21,6 @@ import { export const PROPOSALS_PER_PAGE = 12; -interface ContractConfig { - address: `0x${string}`; - abi: any; - chainId: number; -} - export function useProposal() { const { setCurrentPagePaginationProposals, @@ -50,32 +42,11 @@ export function useProposal() { } = useProposalStore(state => state); const asPath = usePathname(); const { chainName, address } = extractPathSegments(asPath ?? ""); - const { chain } = useAccount(); const { error, handleError } = useError(); const chainId = chains.filter( - (chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName, + (chain: { name: string }) => chain.name.toLowerCase().replace(" ", "") === chainName.toLowerCase(), )?.[0]?.id; - async function getContractConfig(): Promise<{ contractConfig: ContractConfig; version: string } | null> { - const { abi, version } = await getContestContractVersion(address, chainId); - - if (abi === null) { - const errorMsg = `This contract doesn't exist on ${chain?.name ?? "this chain"}.`; - toastError(errorMsg); - setIsPageProposalsError(errorMsg); - return null; - } - - return { - contractConfig: { - address: address as `0x${string}`, - abi: abi, - chainId: chainId, - }, - version, - }; - } - /** * Fetch the data of each proposals in page X * @param pageIndex - index of the page of proposals to fetch @@ -84,6 +55,8 @@ export function useProposal() { * @param sorting - boolean to know if we need to sort the proposals */ async function fetchProposalsPage( + contractConfig: ContractConfig, + version: string, pageIndex: number, slice: Array, totalPagesPaginationProposals: number, @@ -95,12 +68,6 @@ export function useProposal() { setIsPageProposalsError(""); try { - const contractConfigResult = await getContractConfig(); - - if (!contractConfigResult) return; - - const { contractConfig, version } = contractConfigResult; - const commentsAllowed = compareVersions(version, COMMENTS_VERSION) == -1 ? false : true; const contracts: any[] = []; @@ -136,7 +103,7 @@ export function useProposal() { }); } - const results = await readContracts(config, { contracts }); + const results = await readContracts(serverConfig, { contracts }); structureAndRankProposals(results, slice, pageMappedProposals, sorting); @@ -153,18 +120,18 @@ export function useProposal() { * Fetch the list of proposals ids for this contest, order them by votes and set up pagination * @param abi - ABI to use */ - async function fetchProposalsIdsList(abi: any, contestDates: { submissionOpen: Date; votesOpen: Date }) { + async function fetchProposalsIdsList( + contractConfig: ContractConfig, + version: string, + contestDates: { submissionOpen: Date; votesOpen: Date }, + ) { setIsListProposalsLoading(true); try { const useLegacyGetAllProposalsIdFn = - abi?.filter((el: { name: string }) => el.name === "allProposalTotalVotes")?.length > 0 ? false : true; - - const contractConfig = { - address: address as `0x${string}`, - abi: abi, - chainId: chainId, - }; + contractConfig.abi?.filter((el: { name: string }) => el.name === "allProposalTotalVotes")?.length > 0 + ? false + : true; const proposalsIdsRawData = await getProposalIdsRaw(contractConfig, useLegacyGetAllProposalsIdFn); @@ -229,7 +196,8 @@ export function useProposal() { setTotalPagesPaginationProposals(paginationChunks.length); setIndexPaginationProposalPerId(paginationChunks); - if (paginationChunks.length) fetchProposalsPage(0, paginationChunks[0], paginationChunks.length, mappedProposals); + if (paginationChunks.length) + fetchProposalsPage(contractConfig, version, 0, paginationChunks[0], paginationChunks.length, mappedProposals); } catch (e) { handleError(e, "Something went wrong while getting proposal ids."); setIsListProposalsSuccess(false); @@ -241,14 +209,8 @@ export function useProposal() { * Fetch a single proposal based on its ID. * @param proposalId - the ID of the proposal to fetch */ - async function fetchSingleProposal(proposalId: any) { + async function fetchSingleProposal(contractConfig: ContractConfig, proposalId: any) { try { - const contractConfigResult = await getContractConfig(); - - if (!contractConfigResult) return; - - const { contractConfig } = contractConfigResult; - const contracts = [ { ...contractConfig, @@ -344,7 +306,7 @@ export function useProposal() { * Sort proposals by a given sorting option * @param sortBy - the sorting option to use */ - function sortProposalData(sortBy: SortOptions) { + function sortProposalData(contractConfig: ContractConfig, version: string, sortBy: SortOptions) { const sortedIds = sortProposals(sortBy, initialMappedProposalIds); if (listProposalsData.length === sortedIds.length) { @@ -358,7 +320,15 @@ export function useProposal() { setProposalData([]); setListProposalsIds(sortedIds); - fetchProposalsPage(0, paginationChunks[0], paginationChunks.length, initialMappedProposalIds, true); + fetchProposalsPage( + contractConfig, + version, + 0, + paginationChunks[0], + paginationChunks.length, + initialMappedProposalIds, + true, + ); } } diff --git a/packages/react-app-revamp/hooks/useProposal/utils.ts b/packages/react-app-revamp/hooks/useProposal/utils.ts index 552a0af3c..c2cede7f7 100644 --- a/packages/react-app-revamp/hooks/useProposal/utils.ts +++ b/packages/react-app-revamp/hooks/useProposal/utils.ts @@ -5,6 +5,7 @@ import { readContract, readContracts } from "@wagmi/core"; import { shuffle } from "lodash"; import { formatEther } from "viem"; import { MappedProposalIds, ProposalCore, SortOptions } from "./store"; +import { ContractConfig } from "@hooks/useContest"; interface RankDictionary { [key: string]: number; @@ -169,7 +170,7 @@ export function transformProposalData( * @param contractConfig * @param isLegacy */ -export async function getProposalIdsRaw(contractConfig: any, isLegacy: boolean) { +export async function getProposalIdsRaw(contractConfig: ContractConfig, isLegacy: boolean) { if (isLegacy) { return (await readContract(serverConfig, { ...contractConfig, diff --git a/packages/react-app-revamp/hooks/useRewards/index.ts b/packages/react-app-revamp/hooks/useRewards/index.ts index 3649da426..33a3f11fa 100644 --- a/packages/react-app-revamp/hooks/useRewards/index.ts +++ b/packages/react-app-revamp/hooks/useRewards/index.ts @@ -1,7 +1,6 @@ import { toastError } from "@components/UI/Toast"; import { chains, config } from "@config/wagmi"; import { extractPathSegments } from "@helpers/extractPath"; -import getContestContractVersion from "@helpers/getContestContractVersion"; import getRewardsModuleContractVersion from "@helpers/getRewardsModuleContractVersion"; import { useContestStore } from "@hooks/useContest/store"; import { useError } from "@hooks/useError"; @@ -14,7 +13,9 @@ import { useRewardsStore } from "./store"; export function useRewardsModule() { const asPath = usePathname(); - const { rewardsModuleAddress, rewardsAbi, setRewardsModuleAddress, setRewardsAbi } = useContestStore(state => state); + const { rewardsModuleAddress, rewardsAbi, setRewardsModuleAddress, setRewardsAbi, contestAbi } = useContestStore( + state => state, + ); const { chainName: contestChainName, address: contestAddress } = extractPathSegments(asPath ?? ""); const { rewards, setRewards, setIsLoading, setError, setIsSuccess } = useRewardsStore(state => state); const { error, handleError } = useError(); @@ -41,9 +42,7 @@ export function useRewardsModule() { const fetchRewardsModuleAddress = async (): Promise => { try { - const { abi: abiContest } = await getContestContractVersion(contestAddress, chainId); - - if (abiContest === null) { + if (!contestAbi) { setIsLoading(false); setIsSuccess(false); toastError(`This contract doesn't exist on ${contestChainName}.`); @@ -52,7 +51,7 @@ export function useRewardsModule() { const contestRewardModuleAddress = (await readContract(config, { address: contestAddress as `0x${string}`, - abi: abiContest as Abi, + abi: contestAbi, chainId, functionName: "officialRewardsModule", })) as string; diff --git a/packages/react-app-revamp/hooks/useSubmitProposal/index.ts b/packages/react-app-revamp/hooks/useSubmitProposal/index.ts index dc85822b2..c40c0cff4 100644 --- a/packages/react-app-revamp/hooks/useSubmitProposal/index.ts +++ b/packages/react-app-revamp/hooks/useSubmitProposal/index.ts @@ -1,10 +1,11 @@ import { toastLoading, toastSuccess } from "@components/UI/Toast"; -import { config } from "@config/wagmi"; +import { chains, config } from "@config/wagmi"; import { TransactionResponse } from "@ethersproject/abstract-provider"; import { extractPathSegments } from "@helpers/extractPath"; import { getProposalId } from "@helpers/getProposalId"; import { generateFieldInputsHTML, processFieldInputs } from "@helpers/metadata"; import { useContestStore } from "@hooks/useContest/store"; +import { Charge } from "@hooks/useDeployContest/types"; import { useError } from "@hooks/useError"; import { useGenerateProof } from "@hooks/useGenerateProof"; import { useMetadataStore } from "@hooks/useMetadataFields/store"; @@ -20,7 +21,6 @@ import { useMediaQuery } from "react-responsive"; import { formatEther } from "viem"; import { useAccount } from "wagmi"; import { useSubmitProposalStore } from "./store"; -import { Charge } from "@hooks/useDeployContest/types"; const targetMetadata = { targetAddress: "0x0000000000000000000000000000000000000000", @@ -56,6 +56,7 @@ export function useSubmitProposal() { const { address: userAddress, chain } = useAccount(); const asPath = usePathname(); const { chainName, address } = extractPathSegments(asPath ?? ""); + const chainId = chains.filter(chain => chain.name.toLowerCase() === chainName.toLowerCase())[0]?.id; const isMobile = useMediaQuery({ maxWidth: "768px" }); const showToast = !isMobile; const { charge, contestAbi: abi, rewardsModuleAddress } = useContestStore(state => state); @@ -76,6 +77,14 @@ export function useSubmitProposal() { return BigInt(charge.type.costToPropose); }; + const getContractConfig = () => { + return { + address: address as `0x${string}`, + abi: abi, + chainId: chainId, + }; + }; + async function sendProposal(proposalContent: string): Promise<{ tx: TransactionResponse; proposalId: string }> { if (showToast) toastLoading("proposal is deploying..."); setIsLoading(true); @@ -162,7 +171,7 @@ export function useSubmitProposal() { if (showToast) toastSuccess("proposal submitted successfully!"); increaseCurrentUserProposalCount(); setSubmissionsCount(submissionsCount + 1); - fetchSingleProposal(proposalId); + fetchSingleProposal(getContractConfig(), proposalId); if (metadataFields.length > 0) { const clearedFields = metadataFields.map(field => ({ diff --git a/packages/react-app-revamp/hooks/useTotalVotes/index.ts b/packages/react-app-revamp/hooks/useTotalVotes/index.ts index c90f754bf..654ca74f3 100644 --- a/packages/react-app-revamp/hooks/useTotalVotes/index.ts +++ b/packages/react-app-revamp/hooks/useTotalVotes/index.ts @@ -1,5 +1,5 @@ import { config } from "@config/wagmi"; -import getContestContractVersion from "@helpers/getContestContractVersion"; +import { useContestStore } from "@hooks/useContest/store"; import { useQuery } from "@tanstack/react-query"; import { readContract } from "@wagmi/core"; import { fetchDataFromBucket } from "lib/buckets"; @@ -8,32 +8,22 @@ import { Recipient } from "lib/merkletree/generateMerkleTree"; import { Abi } from "viem"; const useTotalVotesOnContest = (address: string, chainId: number) => { + const { contestAbi: abi } = useContestStore(state => state); const calculateTotalVotes = (data: Recipient[]) => { return data.reduce((sum, vote) => sum + Number(vote.numVotes), 0); }; - async function getContractConfig() { - try { - const { abi } = await getContestContractVersion(address, chainId); - if (!abi) { - return null; - } - - return { - address: address as `0x${string}`, - abi: abi as Abi, - chainId: chainId, - }; - } catch (e) { - return null; - } + function getContractConfig() { + return { + address: address as `0x${string}`, + abi: abi as Abi, + chainId: chainId, + }; } async function getVotingMerkleRoot() { try { - const contractConfig = await getContractConfig(); - - if (!contractConfig) return null; + const contractConfig = getContractConfig(); const votingMerkleRoot = (await readContract(config, { ...contractConfig, @@ -47,12 +37,6 @@ const useTotalVotesOnContest = (address: string, chainId: number) => { } const fetchTotalVotes = async () => { - const contractConfig = await getContractConfig(); - - if (!contractConfig) { - throw new Error("Contract configuration could not be fetched"); - } - const votingMerkleRoot = await getVotingMerkleRoot(); if (!votingMerkleRoot) { diff --git a/packages/react-app-revamp/hooks/useUser/index.ts b/packages/react-app-revamp/hooks/useUser/index.ts index 4c62c56aa..64cb8ad68 100644 --- a/packages/react-app-revamp/hooks/useUser/index.ts +++ b/packages/react-app-revamp/hooks/useUser/index.ts @@ -1,8 +1,7 @@ -/* eslint-disable react-hooks/exhaustive-deps */ import { supabase } from "@config/supabase"; import { chains, config } from "@config/wagmi"; import { extractPathSegments } from "@helpers/extractPath"; -import getContestContractVersion from "@helpers/getContestContractVersion"; +import { ContractConfig } from "@hooks/useContest"; import { useContestStore } from "@hooks/useContest/store"; import { getGasPrice, readContract, readContracts } from "@wagmi/core"; import { compareVersions } from "compare-versions"; @@ -41,37 +40,31 @@ export function useUser() { setIsCurrentUserVoteQualificationError, } = useUserStore(state => state); - const checkIfCurrentUserQualifyToSubmit = async () => { + const checkIfCurrentUserQualifyToSubmit = async (contractConfig: ContractConfig, version: string) => { setIsCurrentUserSubmitQualificationLoading(true); - const { abi, version } = await getContestContractVersion(address, chainId); - if (compareVersions(version, "3.0") == -1) { setIsCurrentUserSubmitQualificationLoading(false); return; } - if (!abi) { + if (!contractConfig.abi) { setIsCurrentUserSubmitQualificationError(true); setIsCurrentUserSubmitQualificationLoading(false); return; } - const contractConfig = { - address: address as `0x${string}`, - abi: abi as Abi, - chainId: chainId, - }; - const results = await readContracts(config, { contracts: [ { ...contractConfig, functionName: "submissionMerkleRoot", + args: [], }, { ...contractConfig, functionName: "numAllowedProposalSubmissions", + args: [], }, ], }); @@ -158,17 +151,14 @@ export function useUser() { /** * Check if the current user qualify to vote for this contest */ - async function checkIfCurrentUserQualifyToVote() { + async function checkIfCurrentUserQualifyToVote(contractConfig: ContractConfig, version: string) { setIsCurrentUserVoteQualificationLoading(true); - - const { abi, version } = await getContestContractVersion(address, chainId); - if (compareVersions(version, "3.0") == -1) { setIsCurrentUserVoteQualificationLoading(false); return; } - if (!abi) { + if (!contractConfig.abi) { setIsCurrentUserVoteQualificationError(true); setIsCurrentUserVoteQualificationSuccess(false); setIsCurrentUserVoteQualificationLoading(false); @@ -177,7 +167,7 @@ export function useUser() { const votingMerkleRoot = (await readContract(config, { address: address as `0x${string}`, - abi: abi as Abi, + abi: contractConfig.abi as Abi, chainId: chainId, functionName: "votingMerkleRoot", })) as string; @@ -190,7 +180,7 @@ export function useUser() { if (compareVersions(version, ANYONE_CAN_VOTE_VERSION) >= 0) { if (anyoneCanVote) { - await checkAnyoneCanVoteUserQualification(); + await checkAnyoneCanVoteUserQualification(contractConfig.abi, version); return; } } @@ -205,15 +195,6 @@ export function useUser() { .eq("network_name", lowerCaseChainName); if (data && data.length > 0 && data[0].num_votes > 0) { - const abi = await getContestContractVersion(address, chainId); - if (!abi) return; - - const contractConfig = { - address: address as `0x${string}`, - abi: abi.abi as Abi, - chainId: chainId, - }; - const currentUserTotalVotesCast = (await readContract(config, { ...contractConfig, functionName: "contestAddressTotalVotesCast", @@ -255,12 +236,10 @@ export function useUser() { } } - async function checkAnyoneCanVoteUserQualification() { + async function checkAnyoneCanVoteUserQualification(abi: any, version: string) { if (!userAddress) return; try { - const { abi, version } = await getContestContractVersion(address, chainId); - if (compareVersions(version, ANYONE_CAN_VOTE_VERSION) == -1) { setIsCurrentUserVoteQualificationSuccess(false); setIsCurrentUserVoteQualificationLoading(false); @@ -279,7 +258,7 @@ export function useUser() { getGasPrice(config, { chainId }), readContract(config, { address: address as `0x${string}`, - abi: abi, + abi: abi as Abi, chainId: chainId, functionName: "costToVote", }) as unknown as bigint, @@ -308,10 +287,9 @@ export function useUser() { /** * Update the amount of votes casted in this contest by the current user */ - async function updateCurrentUserVotes(anyoneCanVote?: boolean) { + async function updateCurrentUserVotes(abi: any, version: string, anyoneCanVote?: boolean) { setIsCurrentUserVoteQualificationLoading(true); - const { abi } = await getContestContractVersion(address, chainId); if (!abi) { setIsCurrentUserVoteQualificationError(true); setIsCurrentUserVoteQualificationSuccess(false); @@ -320,7 +298,7 @@ export function useUser() { } if (anyoneCanVote) { - await checkAnyoneCanVoteUserQualification(); + await checkAnyoneCanVoteUserQualification(abi, version); return; } diff --git a/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx b/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx index 30465dfeb..82ae3dd74 100644 --- a/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx +++ b/packages/react-app-revamp/layouts/LayoutViewContest/index.tsx @@ -20,7 +20,7 @@ import { generateUrlContest } from "@helpers/share"; import { MAX_MS_TIMEOUT } from "@helpers/timeout"; import { ArrowPathIcon } from "@heroicons/react/24/outline"; import { useAccountChange } from "@hooks/useAccountChange"; -import { useContest } from "@hooks/useContest"; +import { ContractConfig, useContest } from "@hooks/useContest"; import { useContestStore } from "@hooks/useContest/store"; import useContestEvents from "@hooks/useContestEvents"; import { ContestStatus, useContestStatusStore } from "@hooks/useContestStatus/store"; @@ -33,13 +33,14 @@ import { ReactNode, useEffect, useMemo, useState } from "react"; import { useMediaQuery } from "react-responsive"; import { useAccount, useAccountEffect } from "wagmi"; import LayoutViewContestError from "./components/Error"; +import { chains } from "@config/wagmi"; const LayoutViewContest = ({ children }: { children: React.ReactNode }) => { - const { refresh } = useRouter(); const pathname = usePathname(); const url = useUrl(); const { address: accountAddress } = useAccount(); const { chainName: chainNameFromUrl, address: addressFromUrl } = extractPathSegments(pathname ?? ""); + const chainId = chains.filter(chain => chain.name.toLowerCase() === chainNameFromUrl.toLowerCase())[0]?.id; const showRewards = useShowRewardsStore(state => state.showRewards); const { isLoading, address, fetchContestInfo, isSuccess, error, chainName } = useContest(); const { @@ -51,6 +52,8 @@ const LayoutViewContest = ({ children }: { children: React.ReactNode }) => { isReadOnly, rewardsModuleAddress, rewardsAbi, + contestAbi, + version, } = useContestStore(state => state); const accountChanged = useAccountChange(); const { checkIfCurrentUserQualifyToVote, checkIfCurrentUserQualifyToSubmit } = useUser(); @@ -108,14 +111,24 @@ const LayoutViewContest = ({ children }: { children: React.ReactNode }) => { }, [submissionsOpen, votesOpen, votesClose, setContestStatus, isLoading]); useEffect(() => { + if (isLoading || !isSuccess) return; + const fetchUserData = async () => { if (accountChanged) { - await Promise.all([checkIfCurrentUserQualifyToSubmit(), checkIfCurrentUserQualifyToVote()]); + const contractConfig: ContractConfig = { + address: address as `0x${string}`, + abi: contestAbi, + chainId: chainId, + }; + await Promise.all([ + checkIfCurrentUserQualifyToSubmit(contractConfig, version), + checkIfCurrentUserQualifyToVote(contractConfig, version), + ]); } }; fetchUserData(); - }, [accountChanged]); + }, [accountChanged, isLoading, isSuccess]); useEffect(() => { fetchContestInfo();