Skip to content

Commit 3ad13c2

Browse files
committed
fix(coordinator): address PR comments
1 parent 1959c0b commit 3ad13c2

File tree

9 files changed

+104
-128
lines changed

9 files changed

+104
-128
lines changed

constants.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export const NEXT_PUBLIC_SECONDS_PER_BLOCK = Number(process.env.NEXT_PUBLIC_SECO
1313

1414
// MACI Coordinator service
1515
export const PUBLIC_COORDINATOR_SERVICE_URL = process.env.NEXT_PUBLIC_COORDINATOR_SERVICE_URL ?? "";
16-
export const NAVIGATION_AFTER_SCHEDULE_DELAY_SECONDS = 0.3;
16+
export const NAVIGATION_AFTER_SCHEDULE_DELAY_SECONDS = 0.2;
1717

1818
// UX improvements
1919
export const NEXT_MINIMUM_START_DELAY_IN_SECONDS = Number(process.env.NEXT_MINIMUM_START_DELAY_IN_SECONDS ?? 30);

plugins/maciVoting/components/PollCard.tsx

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const PollCard = ({ pollId }: { pollId: bigint }) => {
1717
const [error, setError] = useState<string | undefined>(undefined);
1818
const [voteOption, setVoteOption] = useState<VoteOption | undefined>(undefined);
1919

20-
const { data: { voteStartDate, tallied, voteEnded, disabled, results } = {} } = useGetPollData(pollId);
20+
const { data: { voteStartDate, voteEndDate, tallied, voteEnded, disabled, results } = {} } = useGetPollData(pollId);
2121
const { joinPollFunction, hasJoinedPoll, joinedPollData, isLoading: isLoadingJoinedPoll } = useJoinPoll(pollId);
2222
const { voteFunction } = useVote();
2323

@@ -58,14 +58,23 @@ const PollCard = ({ pollId }: { pollId: bigint }) => {
5858
setVoteOption(option);
5959
await voteFunction(pollId, pollStateIndex, voiceCredits, option)
6060
.catch((error) => {
61-
setError(error.message);
61+
let message: string | undefined;
62+
if (error.message.includes("0xa47dcd48")) {
63+
const endDate = voteEndDate;
64+
message = `The voting period finished at ${unixTimestampToDate(endDate!)}. You can no longer submit a vote.`;
65+
}
66+
if (error.message.includes("0x256eadc8")) {
67+
const startDate = voteStartDate;
68+
message = `The voting period has not begun. It will start at ${unixTimestampToDate(startDate!)}`;
69+
}
70+
setError(message);
6271
})
6372
.finally(() => {
6473
setVoteOption(undefined);
6574
setIsLoading(false);
6675
});
6776
},
68-
[joinedPollData, pollId, voteFunction]
77+
[joinedPollData, pollId, voteEndDate, voteFunction, voteStartDate]
6978
);
7079

7180
const buttonMessage = useMemo(() => {
@@ -159,8 +168,8 @@ const PollCard = ({ pollId }: { pollId: bigint }) => {
159168
</div>
160169
<div className="flex flex-col justify-between gap-y-2">
161170
<p>
162-
Submit your vote anonymously to the poll using any wallet. Results will be tallied after the voting period
163-
ends.
171+
Submit your vote anonymously to the poll <b>using any wallet</b>. Results will be tallied after the voting
172+
period ends.
164173
</p>
165174
{voteStartDate &&
166175
voteStartDate > Math.round(Date.now() / 1000) &&

plugins/maciVoting/contexts/MaciContext.tsx

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { PUBLIC_MACI_ADDRESS } from "@/constants";
1+
import { PUBLIC_CHAIN, PUBLIC_MACI_ADDRESS } from "@/constants";
22
import { useAlerts } from "@/context/Alerts";
33
import { Keypair, PrivateKey } from "@maci-protocol/domainobjs";
44
import {
@@ -9,9 +9,10 @@ import {
99
} from "@maci-protocol/sdk/browser";
1010
import { createContext, useCallback, useEffect, useMemo, useState, type ReactNode } from "react";
1111
import { keccak256, stringToHex } from "viem";
12-
import { useAccount, useSignMessage } from "wagmi";
13-
import { useEthersSigner } from "../hooks/useEthersSigner";
12+
import { useAccount, usePublicClient, useSignMessage } from "wagmi";
13+
import { clientToSigner, useEthersSigner } from "../hooks/useEthersSigner";
1414
import { type IMaciContextType } from "./types";
15+
import { useQuery, useQueryClient } from "@tanstack/react-query";
1516

1617
export const DEFAULT_SG_DATA = "0x0000000000000000000000000000000000000000000000000000000000000000";
1718
export const DEFAULT_IVCP_DATA = "0x0000000000000000000000000000000000000000000000000000000000000000";
@@ -29,28 +30,38 @@ export const MaciProvider = ({ children }: { children: ReactNode }) => {
2930
const [maciKeypair, setMaciKeypair] = useState<Keypair | undefined>();
3031
const [stateIndex, setStateIndex] = useState<string | undefined>(undefined);
3132

32-
// Artifacts
33-
const [artifacts, setArtifacts] = useState<
34-
Awaited<ReturnType<typeof downloadPollJoiningArtifactsBrowser>> | undefined
35-
>();
36-
3733
// Wallet variables
3834
const { isConnected } = useAccount();
35+
const publicClient = usePublicClient({ chainId: PUBLIC_CHAIN.id });
36+
const queryClient = useQueryClient();
3937
const { signMessageAsync } = useSignMessage();
4038
const signer = useEthersSigner();
4139

40+
const { data: artifacts } = useQuery({
41+
queryKey: ["artifacts"],
42+
queryFn: async () => {
43+
return await downloadPollJoiningArtifactsBrowser({
44+
testing: true,
45+
stateTreeDepth: 10,
46+
});
47+
},
48+
enabled: isConnected && !!signer && !!publicClient,
49+
refetchOnWindowFocus: false,
50+
staleTime: Infinity,
51+
gcTime: Infinity,
52+
});
53+
4254
// Functions
43-
const deleteKeypair = useCallback(() => {
55+
const deleteKeypair = useCallback(async () => {
4456
localStorage.removeItem("maciPrivateKey");
4557

4658
setIsRegistered(false);
4759
setMaciKeypair(undefined);
4860
setStateIndex(undefined);
49-
50-
setArtifacts(undefined);
51-
5261
setError(undefined);
53-
}, []);
62+
63+
await queryClient.invalidateQueries({ queryKey: ["artifacts"] });
64+
}, [queryClient]);
5465

5566
const onSignup = useCallback(async () => {
5667
setError(undefined);
@@ -154,17 +165,20 @@ export const MaciProvider = ({ children }: { children: ReactNode }) => {
154165
// check if user is registered
155166
useEffect(() => {
156167
(async () => {
157-
if (!isConnected || !signer || !maciKeypair) {
168+
if (!isConnected || !publicClient || !maciKeypair) {
158169
setIsRegistered(false);
159170
setStateIndex(undefined);
160171
return;
161172
}
162173

163174
try {
175+
// this is a read-only operation so we read using public client to avoid signer's cache
176+
const publicSigner = clientToSigner(publicClient);
177+
164178
const { isRegistered: _isRegistered, stateIndex: _stateIndex } = await getSignedupUserData({
165179
maciAddress: PUBLIC_MACI_ADDRESS,
166180
maciPublicKey: maciKeypair.publicKey.serialize(),
167-
signer,
181+
signer: publicSigner,
168182
});
169183

170184
setIsRegistered(_isRegistered);
@@ -175,18 +189,7 @@ export const MaciProvider = ({ children }: { children: ReactNode }) => {
175189
setError("Error checking if user is registered and generating state tree");
176190
}
177191
})();
178-
}, [isConnected, maciKeypair, signer, stateIndex]);
179-
180-
// download poll joining artifacts and store them in state
181-
useEffect(() => {
182-
(async () => {
183-
const downloadedArtifacts = await downloadPollJoiningArtifactsBrowser({
184-
testing: true,
185-
stateTreeDepth: 10,
186-
});
187-
setArtifacts(downloadedArtifacts);
188-
})();
189-
}, []);
192+
}, [isConnected, maciKeypair, publicClient, stateIndex]);
190193

191194
const value = useMemo<IMaciContextType>(
192195
() => ({

plugins/maciVoting/hooks/poll/useJoinPoll.tsx

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,20 @@
1-
import { PUBLIC_MACI_ADDRESS, PUBLIC_MACI_DEPLOYMENT_BLOCK } from "@/constants";
1+
import { PUBLIC_CHAIN, PUBLIC_MACI_ADDRESS, PUBLIC_MACI_DEPLOYMENT_BLOCK } from "@/constants";
22
import { generateMaciStateTreeWithEndKey, getJoinedUserData, joinPoll } from "@maci-protocol/sdk/browser";
33
import { useCallback, useMemo, useState } from "react";
44
import { usePublicClient } from "wagmi";
55
import { DEFAULT_IVCP_DATA, DEFAULT_SG_DATA } from "../../contexts/MaciContext";
66
import { clientToSigner, useEthersSigner } from "../useEthersSigner";
77
import { useMaci } from "../useMaci";
8-
import { type IJoinPollData } from "../../contexts/types";
98
import { useQuery, useQueryClient } from "@tanstack/react-query";
109

1110
export const useJoinPoll = (pollId?: bigint) => {
1211
const signer = useEthersSigner();
13-
const publicClient = usePublicClient();
12+
const publicClient = usePublicClient({ chainId: PUBLIC_CHAIN.id });
1413
const queryClient = useQueryClient();
1514
const { maciKeypair, isRegistered, stateIndex, artifacts } = useMaci();
1615

1716
const [isLoading, setIsLoading] = useState(false);
1817
const [error, setError] = useState<string | undefined>();
19-
// Keep track of newly joined poll data that will be returned when joining a poll
20-
const [newlyJoinedPollData, setNewlyJoinedPollData] = useState<IJoinPollData | undefined>();
2118

2219
// Query key for consistent cache access
2320
const joinedUserQueryKey = useMemo(
@@ -35,6 +32,7 @@ export const useJoinPoll = (pollId?: bigint) => {
3532

3633
setIsLoading(true);
3734
try {
35+
// this is a read-only operation so we read using public client to avoid signer's cache
3836
const publicSigner = clientToSigner(publicClient);
3937

4038
const joinedUser = await getJoinedUserData({
@@ -72,10 +70,6 @@ export const useJoinPoll = (pollId?: bigint) => {
7270
return;
7371
}
7472

75-
if (joinedPollData) {
76-
setNewlyJoinedPollData(joinedPollData);
77-
}
78-
7973
setIsLoading(true);
8074
setError(undefined);
8175

@@ -103,9 +97,8 @@ export const useJoinPoll = (pollId?: bigint) => {
10397
});
10498

10599
if (joinedData) {
106-
setNewlyJoinedPollData(joinedData);
107100
// After successfully joining, manually invalidate the query to trigger a refetch
108-
queryClient.invalidateQueries({ queryKey: joinedUserQueryKey });
101+
await queryClient.invalidateQueries({ queryKey: joinedUserQueryKey });
109102
}
110103
} catch (error: any) {
111104
if (error.message?.includes("0xa3281672")) {
@@ -119,27 +112,13 @@ export const useJoinPoll = (pollId?: bigint) => {
119112
} finally {
120113
setIsLoading(false);
121114
}
122-
}, [
123-
pollId,
124-
signer,
125-
maciKeypair,
126-
isRegistered,
127-
artifacts,
128-
joinedPollData,
129-
stateIndex,
130-
queryClient,
131-
joinedUserQueryKey,
132-
]);
133-
134-
// Use the query data or the newly joined data if available
135-
const effectiveJoinedPollData = newlyJoinedPollData ?? joinedPollData;
136-
const effectiveHasJoinedPoll = Boolean(effectiveJoinedPollData);
115+
}, [pollId, signer, maciKeypair, isRegistered, artifacts, stateIndex, queryClient, joinedUserQueryKey]);
137116

138117
return {
139118
isLoading: isLoading || isLoadingQuery,
140119
error,
141-
hasJoinedPoll: effectiveHasJoinedPoll,
142-
joinedPollData: effectiveJoinedPollData,
120+
hasJoinedPoll: Boolean(joinedPollData),
121+
joinedPollData: joinedPollData,
143122
joinPollFunction,
144123
};
145124
};

plugins/maciVoting/hooks/useEthersSigner.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ export function clientToSigner(client: Client<Transport, Chain, Account> | Publi
1313
};
1414

1515
const provider = new BrowserProvider(transport, network);
16+
17+
// in some cases we need a signer from a public client (which could not necessarily have an account.address linked). That is why we use zeroAddress as a fallback
1618
const signer = new JsonRpcSigner(provider, account ? account.address : zeroAddress);
1719
return signer;
1820
}

plugins/maciVoting/hooks/useGetPollData.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export const useGetPollData = (pollId?: string | bigint) => {
1919
queryFn: async () => {
2020
if (!publicClient) return;
2121

22+
// this is a read-only operation so we read using public client to avoid signer's cache
2223
const publicSigner = clientToSigner(publicClient);
2324

2425
const { startDate, endDate, isMerged } = await getPoll({

plugins/maciVoting/hooks/useProposal.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function useProposal(proposalId: string, autoRefresh = false) {
5353
// Creation event
5454
useEffect(() => {
5555
(async () => {
56-
if (!proposalData || !publicClient) return;
56+
if (!proposalData || !publicClient || proposalData.pollId === 0n) return;
5757

5858
const snapshotBlock = BigInt(proposalData.parameters.snapshotBlock);
5959

@@ -74,11 +74,8 @@ export function useProposal(proposalId: string, autoRefresh = false) {
7474
setProposalCreationEvent(log.args);
7575
setMetadata(fromHex(log.args.metadata as Hex, "string"));
7676
} catch (error) {
77-
// we use proposalId = 0 for starting proposal creation
78-
if (proposalId !== "0") {
79-
// eslint-disable-next-line no-console
80-
console.error("Could not fetch the proposal details", error);
81-
}
77+
// eslint-disable-next-line no-console
78+
console.error("Could not fetch the proposal details:", error);
8279
}
8380
})();
8481
}, [proposalData, proposalId, publicClient]);

0 commit comments

Comments
 (0)