diff --git a/.eslintignore b/.eslintignore index ed13b0e4..1c8a578e 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1 @@ -app/graphql/**/generated \ No newline at end of file +lib/graphql/**/generated \ No newline at end of file diff --git a/.secretlintignore b/.secretlintignore index f09d69ff..afa78291 100644 --- a/.secretlintignore +++ b/.secretlintignore @@ -1,3 +1,4 @@ +.trunk .env*.local .next node_modules diff --git a/src/app/proposals/[id]/_components/execution-code.component.tsx b/src/app/proposals/[id]/_components/execution-code.component.tsx index 496b0db2..708f01e9 100644 --- a/src/app/proposals/[id]/_components/execution-code.component.tsx +++ b/src/app/proposals/[id]/_components/execution-code.component.tsx @@ -11,10 +11,13 @@ export default function ExecutionCode({ calls }: Props) { return (
-

+

Execution Code

- + +

+ Execution Code +

{formattedCalls.map((call, index) => (
{index > 0 &&
} diff --git a/src/app/proposals/[id]/_components/proposal-actions/disconnected-state.tsx b/src/app/proposals/[id]/_components/proposal-actions/disconnected-state.tsx index b49bfb62..08232b43 100644 --- a/src/app/proposals/[id]/_components/proposal-actions/disconnected-state.tsx +++ b/src/app/proposals/[id]/_components/proposal-actions/disconnected-state.tsx @@ -4,7 +4,7 @@ import { ProposalActionTitle } from "./proposal-action-title"; export const DisconnectedState = () => { return ( -
+
Please connect your wallet to participate in governance diff --git a/src/app/proposals/[id]/page.tsx b/src/app/proposals/[id]/page.tsx index 127e8cf8..af257944 100644 --- a/src/app/proposals/[id]/page.tsx +++ b/src/app/proposals/[id]/page.tsx @@ -9,6 +9,7 @@ import { stateToStatusColorMap } from "@/lib/interfaces/proposal.interface"; import { Avatar, BlockExplorerLink, + Card, Loader, MarkdownView, Status, @@ -19,7 +20,64 @@ import ExecutionCode from "./_components/execution-code.component"; import Participants from "./_components/participants.component"; import { Countdown, ProposalCurrentVotes } from "@/components/index"; import { ensureChainId } from "@/lib/helpers/ensureChainId"; -import { ProposalState } from "@/lib/graphql"; +import { Proposal, ProposalState } from "@/lib/graphql"; +import { CELO_BLOCK_TIME } from "@/config/config.constants"; + +const ProposalCountdown = ({ proposal }: { proposal: Proposal }) => { + const { data: currentBlock } = useBlockNumber({ + watch: true, + chainId: ensureChainId(useAccount().chainId), + }); + + const endBlock = useBlock({ + blockNumber: BigInt(proposal.endBlock), + query: { + enabled: true, + }, + }); + + const votingDeadline = useMemo(() => { + if (currentBlock) { + // If the end block is already mined, we can fetch the timestamp + if (Number(currentBlock) >= proposal.endBlock && endBlock.data) { + return new Date(Number(endBlock.data.timestamp) * 1000); + } else { + // If the end block is not mined yet, we estimate the time + return new Date( + Date.now() + + // Estimation of ~5 seconds per block + (proposal.endBlock - Number(currentBlock)) * CELO_BLOCK_TIME, + ); + } + } + }, [currentBlock, endBlock.data, proposal.endBlock]); + + const timeLockDeadLine = useMemo(() => { + if (proposal.state === ProposalState.Queued && proposal.proposalQueued[0]) { + return new Date(Number(proposal.proposalQueued[0].eta) * 1000); + } + }, [proposal]); + + if (timeLockDeadLine && timeLockDeadLine.getTime() >= new Date().getTime()) { + return ( + + ); + } + + if (proposal.state === ProposalState.Active && votingDeadline) { + return ( + + ); + } + + return null; +}; const Page = ({ params: { id } }: { params: { id: string } }) => { const { proposal } = useProposal(BigInt(id)); @@ -43,8 +101,6 @@ const Page = ({ params: { id } }: { params: { id: string } }) => { }, [proposal]); const votingDeadline = useMemo(() => { - const CELO_BLOCK_TIME = 5000; // 5 seconds - if (proposal && currentBlock) { // If the end block is already mined, we can fetch the timestamp if (Number(currentBlock) >= proposal.endBlock && endBlock.data) { @@ -75,115 +131,173 @@ const Page = ({ params: { id } }: { params: { id: string } }) => { } return ( -
-
- -
- {timeLockDeadLine && - timeLockDeadLine.getTime() >= new Date().getTime() && ( + <> +
+ +
+ +

+ {proposal.metadata?.title} +

+ +
+
+
+ + by{" "} + + + +
+
+ Proposed on: + + {proposedOn && ( + + {format(proposedOn, "MMMM do, yyyy")} + + )} + +
+
+ Voting deadline: + + {votingDeadline && ( + + {format(votingDeadline, "MMMM do, yyyy")}{" "} + + )} + +
+
+
+ {proposal.votes ? ( + + ) : ( + + )} + + }> + +

+ Proposal Description +

+ +
+
+ {proposal.calls && } + {proposal.votes && } +
+ +
+
+ +
+
+
+

+ }> + {proposal.metadata?.title} + +

+
+
+
+ {timeLockDeadLine && + timeLockDeadLine.getTime() >= new Date().getTime() && ( + + )} +
+ {proposal.state === ProposalState.Active && votingDeadline && ( )} +
-
-
-
-

+
+
}> - {proposal.metadata?.title} + + by{" "} + + + -

-
-
-
- {timeLockDeadLine && - timeLockDeadLine.getTime() >= new Date().getTime() && ( - - )}
- {proposal.state === ProposalState.Active && votingDeadline && ( - - )} -
-
-
-
- }> - - by{" "} - - +
+ Proposed on: + + }> + {proposedOn && ( + + {format(proposedOn, "MMMM do, yyyy 'at' hh:mm a")} + + )} + - -
-
- Proposed on: - - }> - {proposedOn && ( +
+
+ Voting deadline: + + {votingDeadline && ( - {format(proposedOn, "MMMM do, yyyy 'at' hh:mm a")} + {format(votingDeadline, "MMMM do, yyyy 'at' hh:mm a")}{" "} )} - - + +
-
- Voting deadline: - - {votingDeadline && ( - - {format(votingDeadline, "MMMM do, yyyy 'at' hh:mm a")}{" "} - +
+
+ {proposal.votes ? ( + + ) : ( + )} - -
-
-
-
- {proposal.votes ? ( - - ) : ( - - )} -
- +

+ Proposal Description +

+ }> + + + {proposal.calls && }
-

- Proposal Description -

- }> - - - {proposal.calls && } -
-
-
- +
+
+ +
+ {proposal.votes && }
- {proposal.votes && }
-
-
+ + ); }; diff --git a/src/components/_shared/connect-button/connect-button.component.tsx b/src/components/_shared/connect-button/connect-button.component.tsx index 1f4cb36b..91935e53 100644 --- a/src/components/_shared/connect-button/connect-button.component.tsx +++ b/src/components/_shared/connect-button/connect-button.component.tsx @@ -148,12 +148,16 @@ export const ConnectButton = ({ <>
{!connected ? ( -
); diff --git a/src/components/countdown/countdown.component.tsx b/src/components/countdown/countdown.component.tsx index db97320e..87bc0e6a 100644 --- a/src/components/countdown/countdown.component.tsx +++ b/src/components/countdown/countdown.component.tsx @@ -50,7 +50,7 @@ export const Countdown = ({ return (
{days} days
-
+
{ - return :; + return ( + + : + + ); }; const CountdownNumber = ({ children }: { children: React.ReactNode }) => { return ( -
+
{children}
); diff --git a/src/components/proposal-summary/proposal-summary.component.tsx b/src/components/proposal-summary/proposal-summary.component.tsx index a150d0be..f7d5b01b 100644 --- a/src/components/proposal-summary/proposal-summary.component.tsx +++ b/src/components/proposal-summary/proposal-summary.component.tsx @@ -1,13 +1,13 @@ import { useMemo } from "react"; -import { formatUnits } from "viem"; -import { useAccount, useBlockNumber } from "wagmi"; import { Card } from "@/components/_shared"; import useProposals from "@/lib/contracts/governor/useProposals"; -import useLockingWeek from "@/lib/contracts/locking/useLockingWeek"; import useAllLocks from "@/lib/contracts/locking/useAllLocks"; +import useLockingWeek from "@/lib/contracts/locking/useLockingWeek"; import useTokens from "@/lib/contracts/useTokens"; -import NumbersService from "@/lib/helpers/numbers.service"; import { ensureChainId } from "@/lib/helpers/ensureChainId"; +import NumbersService from "@/lib/helpers/numbers.service"; +import { formatUnits } from "viem"; +import { useAccount, useBlockNumber } from "wagmi"; export const ProposalSummaryComponent = () => { return ( @@ -73,7 +73,7 @@ const ContractDataGrid = () => { <> - + { return ( -
+
{value}
-
{label}
+
{label}
); }; + const ContractDataSkeleton = ({ label }: { label: string }) => { return ( -
+
000
@@ -113,7 +114,7 @@ const ContractDataGridSkeleton = () => { <> - + ); diff --git a/src/components/proposals-list/proposals-list.component.tsx b/src/components/proposals-list/proposals-list.component.tsx index b71db5de..8486c5e9 100644 --- a/src/components/proposals-list/proposals-list.component.tsx +++ b/src/components/proposals-list/proposals-list.component.tsx @@ -1,13 +1,13 @@ "use client"; +import { Card, ProgressBar, Status } from "@/components/_shared"; +import useProposals from "@/lib/contracts/governor/useProposals"; +import { Proposal } from "@/lib/graphql/subgraph/generated/subgraph"; import NumbersService from "@/lib/helpers/numbers.service"; import StringService from "@/lib/helpers/string.service"; import BaseComponentProps from "@/lib/interfaces/base-component-props.interface"; -import { Card, ProgressBar, Status } from "@/components/_shared"; -import Link from "next/link"; import { stateToStatusColorMap } from "@/lib/interfaces/proposal.interface"; +import Link from "next/link"; import { formatUnits } from "viem"; -import useProposals from "@/lib/contracts/governor/useProposals"; -import { Proposal } from "@/lib/graphql/subgraph/generated/subgraph"; import { EmptyProposals } from "../_icons"; import { MentoIcon } from "../_icons"; diff --git a/src/components/votes-list/votes-list.component.tsx b/src/components/votes-list/votes-list.component.tsx index 2b22dc91..8adce715 100644 --- a/src/components/votes-list/votes-list.component.tsx +++ b/src/components/votes-list/votes-list.component.tsx @@ -61,7 +61,6 @@ const getParticipantPercentage = ( if (totalVotes > 0n) { const decimals = 8; const accuracy = 10n ** BigInt(decimals); - const weightScaled = participant.weight * 100n; const valueInRawBN = (weightScaled * accuracy) / totalVotes; @@ -70,7 +69,7 @@ const getParticipantPercentage = ( if (formatted.includes(".")) { const [integers, remainingDecimals] = formatted.split("."); - return `${integers}.${remainingDecimals.substring(0, 2)}%`; + return `${integers}.${remainingDecimals.substring(0, 5)}%`; } else { return `${formatted}%`; } diff --git a/src/config/config.constants.ts b/src/config/config.constants.ts index b1718ac4..d88179d9 100644 --- a/src/config/config.constants.ts +++ b/src/config/config.constants.ts @@ -17,3 +17,5 @@ export const getSubgraphApiName = (chainId: number | undefined) => { if (!chainId || !isValidChainId(chainId)) return subgraphApiNames[0]; return subgraphApiNames[chainId]; }; + +export const CELO_BLOCK_TIME = 5000; // 5 seconds diff --git a/src/lib/contracts/governor/useProposal.ts b/src/lib/contracts/governor/useProposal.ts index 85768c77..93855ad0 100644 --- a/src/lib/contracts/governor/useProposal.ts +++ b/src/lib/contracts/governor/useProposal.ts @@ -13,7 +13,7 @@ import { useEnsureChainId } from "@/lib/hooks/useEnsureChainId"; import { NetworkStatus } from "@apollo/client"; import { useEffect, useMemo } from "react"; import { useBlockNumber, useReadContract } from "wagmi"; - +import { CELO_BLOCK_TIME } from "@/config/config.constants"; export const ProposalQueryKey = "proposal"; const useProposal = (proposalId: bigint) => { @@ -50,7 +50,7 @@ const useProposal = (proposalId: bigint) => { scopeKey: ProposalQueryKey, chainId: ensuredChainId, query: { - refetchInterval: 5000, + refetchInterval: CELO_BLOCK_TIME, enabled: graphNetworkStatus === NetworkStatus.ready && graphProposals.length > 0, }, diff --git a/src/lib/contracts/useTokens.ts b/src/lib/contracts/useTokens.ts index 63655b99..1e91633c 100644 --- a/src/lib/contracts/useTokens.ts +++ b/src/lib/contracts/useTokens.ts @@ -6,6 +6,7 @@ import { erc20Abi } from "viem"; import { useQueryClient } from "@tanstack/react-query"; import { formatUnitsWithRadix } from "@/lib/helpers/numbers.service"; import { useEnsureChainId } from "@/lib/hooks/useEnsureChainId"; +import { CELO_BLOCK_TIME } from "@/config/config.constants"; export type TokenBalance = { decimals: number; @@ -155,7 +156,7 @@ export const useTokens = () => { ], scopeKey: "token-hook", query: { - refetchInterval: 5000, + refetchInterval: CELO_BLOCK_TIME, enabled: isConnected && !!address, // initialData: [0n, 0n], }, diff --git a/src/lib/graphql/subgraph/fragments/proposalFields.graphql b/src/lib/graphql/subgraph/fragments/proposalFields.graphql index 540243c1..74886dd5 100644 --- a/src/lib/graphql/subgraph/fragments/proposalFields.graphql +++ b/src/lib/graphql/subgraph/fragments/proposalFields.graphql @@ -21,16 +21,22 @@ fragment ProposalFields on Proposal { # Votes votecast { - timestamp - voter { - id + id + support{ + weight } - support { - support + receipt { + id + voter { + id + } weight + support { + id + support + } } } - # Start & End Time startBlock endBlock diff --git a/src/lib/graphql/subgraph/generated/subgraph.tsx b/src/lib/graphql/subgraph/generated/subgraph.tsx index dec65ddc..88ca6125 100644 --- a/src/lib/graphql/subgraph/generated/subgraph.tsx +++ b/src/lib/graphql/subgraph/generated/subgraph.tsx @@ -7286,7 +7286,7 @@ export enum _SubgraphErrorPolicy_ { Deny = 'deny' } -export type ProposalFieldsFragment = { __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', timestamp: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', support: number, weight: any } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }; +export type ProposalFieldsFragment = { __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', id: string, support: { __typename?: 'ProposalSupport', weight: any }, receipt: { __typename?: 'VoteReceipt', id: string, weight: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', id: string, support: number } } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }; export type GetAllLocksQueryVariables = Exact<{ [key: string]: never; }>; @@ -7313,12 +7313,12 @@ export type GetProposalQueryVariables = Exact<{ }>; -export type GetProposalQuery = { __typename?: 'Query', proposals: Array<{ __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, calls: Array<{ __typename?: 'ProposalCall', index: number, value: any, signature: string, calldata: any, target: { __typename?: 'Account', id: any } }>, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', timestamp: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', support: number, weight: any } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }> }; +export type GetProposalQuery = { __typename?: 'Query', proposals: Array<{ __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, calls: Array<{ __typename?: 'ProposalCall', index: number, value: any, signature: string, calldata: any, target: { __typename?: 'Account', id: any } }>, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', id: string, support: { __typename?: 'ProposalSupport', weight: any }, receipt: { __typename?: 'VoteReceipt', id: string, weight: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', id: string, support: number } } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }> }; export type GetProposalsQueryVariables = Exact<{ [key: string]: never; }>; -export type GetProposalsQuery = { __typename?: 'Query', proposals: Array<{ __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', timestamp: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', support: number, weight: any } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }> }; +export type GetProposalsQuery = { __typename?: 'Query', proposals: Array<{ __typename?: 'Proposal', proposalId: any, description: string, startBlock: any, endBlock: any, queued: boolean, canceled: boolean, executed: boolean, state: ProposalState, proposer: { __typename?: 'Account', id: any }, proposalCreated: Array<{ __typename?: 'ProposalCreated', timestamp: any }>, proposalQueued: Array<{ __typename?: 'ProposalQueued', eta: any }>, proposalExecuted: Array<{ __typename?: 'ProposalExecuted', transaction: { __typename?: 'Transaction', id: string, timestamp: any } }>, votecast: Array<{ __typename?: 'VoteCast', id: string, support: { __typename?: 'ProposalSupport', weight: any }, receipt: { __typename?: 'VoteReceipt', id: string, weight: any, voter: { __typename?: 'Account', id: any }, support: { __typename?: 'ProposalSupport', id: string, support: number } } }>, metadata: { __typename?: 'ProposalMetadata', title: string, description: string }, votes: { __typename?: 'ProposalVotes', total: any, for: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, against: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> }, abstain: { __typename?: 'VoteType', total: any, participants: Array<{ __typename?: 'Participant', address: string, weight: any }> } } }> }; export const ProposalFieldsFragmentDoc = gql` fragment ProposalFields on Proposal { @@ -7340,14 +7340,21 @@ export const ProposalFieldsFragmentDoc = gql` } } votecast { - timestamp - voter { - id - } + id support { - support weight } + receipt { + id + voter { + id + } + weight + support { + id + support + } + } } startBlock endBlock diff --git a/src/lib/graphql/subgraph/policies/Proposal.ts b/src/lib/graphql/subgraph/policies/Proposal.ts index 6ecb8bd3..cbe1aff4 100644 --- a/src/lib/graphql/subgraph/policies/Proposal.ts +++ b/src/lib/graphql/subgraph/policies/Proposal.ts @@ -6,6 +6,7 @@ import { ProposalVotes, Scalars, VoteCast, + VoteReceipt, } from "@/lib/graphql/subgraph/generated/subgraph"; type ProposalID = Scalars["ID"]["output"]; @@ -46,46 +47,28 @@ export const ProposalPolicy: TypePolicy = { return votecastRefs.reduce( (acc: ProposalVotes, votecastRef) => { - const voterRef = readField("voter", votecastRef); - const supportRef = readField( - "support", - votecastRef, - ); - const rawWeight = readField("weight", supportRef) || ""; - const weight = BigInt(rawWeight); + const receipt = readField("receipt", votecastRef); + if (!receipt) return acc; + const voterRef = readField("voter", receipt); + const supportRef = readField("support", receipt); + const weight = BigInt(readField("weight", receipt) || "0"); const address = readField("id", voterRef) as Address; - const support = readField( - "support", - supportRef, - ); - + const support = readField("support", supportRef); switch (support) { - // AGAINST - case 0: + case 0: // AGAINST acc.against.total += weight; - acc.against.participants.push({ - address, - weight, - }); + acc.against.participants.push({ address, weight }); break; - // FOR - case 1: + case 1: // FOR acc.for.total += weight; - acc.for.participants.push({ - address, - weight, - }); + acc.for.participants.push({ address, weight }); break; - // ABSTAIN - case 2: + case 2: // ABSTAIN acc.abstain.total += weight; - acc.abstain.participants.push({ - address, - weight, - }); + acc.abstain.participants.push({ address, weight }); break; default: @@ -96,18 +79,9 @@ export const ProposalPolicy: TypePolicy = { return acc; }, { - for: { - participants: [], - total: 0n, - }, - against: { - participants: [], - total: 0n, - }, - abstain: { - participants: [], - total: 0n, - }, + for: { participants: [], total: 0n }, + against: { participants: [], total: 0n }, + abstain: { participants: [], total: 0n }, total: 0n, }, ); diff --git a/src/middleware.ts b/src/middleware.ts index 139c8356..a33d4698 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -9,10 +9,12 @@ export const config = { }; export const IS_PROD = process.env.NEXT_PUBLIC_VERCEL_ENV === "production"; +export const IS_DEV = process.env.NEXT_PUBLIC_VERCEL_ENV === "development"; +export const IS_PREVIEW = process.env.NEXT_PUBLIC_VERCEL_ENV === "preview"; export function middleware(request: NextRequest, event: NextFetchEvent) { const { pathname } = request.nextUrl; - if (!IS_PROD) return NextResponse.next(); + if (!IS_PROD && !IS_DEV && !IS_PREVIEW) return NextResponse.next(); if (pathname.startsWith("/proposals")) { const [, , id] = pathname.split("/"); @@ -23,33 +25,41 @@ export function middleware(request: NextRequest, event: NextFetchEvent) { transport: http(), }); - let valid = false; - const fetch = async () => { + return new Promise((resolve) => { try { - const proposal = await publicClient.readContract({ - address: Celo.contracts.MentoGovernor.address, - abi: GovernorABI, - functionName: "proposals", - args: [BigInt(id)], - }); + const parsedId = BigInt(id); - if (proposal) { - console.log("found"); - valid = true; - } + publicClient + .readContract({ + address: Celo.contracts.MentoGovernor.address, + abi: GovernorABI, + functionName: "proposals", + args: [BigInt(parsedId)], + }) + .then((proposal) => { + if (proposal) { + resolve(NextResponse.next()); + } else { + const url = new URL("/", request.url); + console.log("Proposal not found, redirecting"); + resolve(NextResponse.redirect(url.origin)); + } + }) + .catch((error) => { + console.log("Proposal not found on Celo chain, redirecting"); + const url = new URL("/", request.url); + resolve(NextResponse.redirect(url.origin)); + }); } catch (error) { - console.log("Proposal not found on Celo chain, redirecting"); + console.log("Proposal ID not found, redirecting"); + const url = new URL("/", request.url); + resolve(NextResponse.redirect(url.origin)); } - }; - event.waitUntil(fetch()); - - if (valid) { - return NextResponse.next(); - } else { - return NextResponse.redirect(new URL("/", request.url)); - } + }); } else { - return NextResponse.redirect(new URL("/", request.url)); + console.log("Proposal ID not found, redirecting"); + const url = new URL("/", request.url); + return NextResponse.redirect(url.origin); } } }