From ce6c9dce440b229adbe3501937be50d2c86c59a4 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 26 Jun 2024 21:05:27 +0100 Subject: [PATCH] feat: fetch poll data from subgraph --- src/contexts/Maci.tsx | 52 +++++++++++++++++++++++------- src/server/api/routers/maci.ts | 2 ++ src/utils/fetchPoll.ts | 58 ++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 11 deletions(-) create mode 100644 src/utils/fetchPoll.ts diff --git a/src/contexts/Maci.tsx b/src/contexts/Maci.tsx index 6bc26e28..27157cbe 100644 --- a/src/contexts/Maci.tsx +++ b/src/contexts/Maci.tsx @@ -44,6 +44,8 @@ export const MaciProvider: React.FC = ({ children }: MaciProv { enabled: Boolean(maciPubKey && config.maciSubgraphUrl) }, ); + const poll = api.maci.poll.useQuery(undefined, { enabled: Boolean(config.maciSubgraphUrl) }); + const attestations = api.voters.approvedAttestations.useQuery({ address, }); @@ -70,6 +72,15 @@ export const MaciProvider: React.FC = ({ children }: MaciProv } }, [setMaciPrivKey, setMaciPubKey]); + // on load we fetch the data from the poll + useEffect(() => { + if (poll.data) { + return; + } + + poll.refetch().catch(console.error); + }, [poll]); + const generateKeypair = useCallback(async () => { // if we are not connected then do not generate the key pair if (!address) { @@ -98,15 +109,13 @@ export const MaciProvider: React.FC = ({ children }: MaciProv setIsLoading(true); try { - const { stateIndex: index, hash } = await signup({ + const { stateIndex: index } = await signup({ maciPubKey, maciAddress: config.maciAddress!, sgDataArg: attestationId, signer, }); - console.log(`Signup transaction hash: ${hash}`); - if (index) { setIsRegistered(true); setStateIndex(index); @@ -234,7 +243,28 @@ export const MaciProvider: React.FC = ({ children }: MaciProv setIsLoading(true); - Promise.all([ + // if we have the subgraph url then it means we can get the poll data through there + if (config.maciSubgraphUrl) { + if (!poll.data) { + setIsLoading(false); + return; + } + + const { isStateAqMerged, id } = poll.data; + + setPollData(poll.data); + + if (isStateAqMerged) { + fetch(`${config.tallyUrl}/tally-${id}.json`) + .then((res) => res.json() as Promise) + .then((res) => { + setTallyData(res); + }) + .catch(() => undefined); + } + + setIsLoading(false); + } else { getPoll({ maciAddress: config.maciAddress!, signer, @@ -255,13 +285,13 @@ export const MaciProvider: React.FC = ({ children }: MaciProv setTallyData(res); }) .catch(() => undefined); - }), - ]) - .catch(console.error) - .finally(() => { - setIsLoading(false); - }); - }, [signer, votingEndsAt, setIsLoading, setTallyData, setPollData]); + }) + .catch(console.error) + .finally(() => { + setIsLoading(false); + }); + } + }, [signer, votingEndsAt, setIsLoading, setTallyData, setPollData, poll.data]); const value = useMemo( () => ({ diff --git a/src/server/api/routers/maci.ts b/src/server/api/routers/maci.ts index dcba5abb..2e278fc6 100644 --- a/src/server/api/routers/maci.ts +++ b/src/server/api/routers/maci.ts @@ -2,10 +2,12 @@ import { PubKey } from "maci-domainobjs"; import { z } from "zod"; import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; +import { fetchPoll } from "~/utils/fetchPoll"; import { fetchUser } from "~/utils/fetchUser"; export const maciRouter = createTRPCRouter({ user: publicProcedure .input(z.object({ publicKey: z.string() })) .query(async ({ input }) => fetchUser(PubKey.deserialize(input.publicKey).rawPubKey)), + poll: publicProcedure.query(async () => fetchPoll()), }); diff --git a/src/utils/fetchPoll.ts b/src/utils/fetchPoll.ts new file mode 100644 index 00000000..ad0880ea --- /dev/null +++ b/src/utils/fetchPoll.ts @@ -0,0 +1,58 @@ +import { IGetPollData } from "maci-cli/sdk"; + +import { config } from "~/config"; + +import { createCachedFetch } from "./fetch"; + +const cachedFetch = createCachedFetch({ ttl: 1000 * 60 * 10 }); + +interface Poll { + pollId: string; + createdAt: string; + duration: string; + stateRoot: string; + messageRoot: string; + numSignups: string; + id: string; +} + +export interface GraphQLResponse { + data?: { + polls: Poll[]; + }; +} + +const PollQuery = ` + query Poll { + polls(orderBy: createdAt, orderDirection: desc, first: 1) { + pollId + duration + createdAt + stateRoot + messageRoot + numSignups + id + } + } +`; + +export async function fetchPoll(): Promise { + const poll = ( + await cachedFetch<{ polls: Poll[] }>(config.maciSubgraphUrl, { + method: "POST", + body: JSON.stringify({ + query: PollQuery, + }), + }).then((response: GraphQLResponse) => response.data?.polls) + )?.at(0); + + // cast this to a IGetPollData object so that we can deal with one object only in MACIContext + return { + isStateAqMerged: !!poll?.messageRoot, + id: poll?.pollId ?? 0, + duration: poll?.duration ?? 0, + deployTime: poll?.createdAt ?? 0, + numSignups: poll?.numSignups ?? 0, + address: poll?.id ?? "", + }; +}