From a6787b53f436c55995e26470f55a6e04300ee520 Mon Sep 17 00:00:00 2001 From: John Guilding <54913924+JohnGuilding@users.noreply.github.com> Date: Wed, 28 Aug 2024 22:32:38 +0100 Subject: [PATCH] submit application & use useSmartAccount hook (#17) * submit application & use useSmartAccount hook * Use Hex & Address types --- packages/interface/package.json | 1 + .../src/components/EligibilityDialog.tsx | 14 +--- .../interface/src/components/JoinButton.tsx | 4 +- packages/interface/src/config.ts | 3 +- packages/interface/src/contexts/Ballot.tsx | 9 --- packages/interface/src/contexts/Maci.tsx | 6 +- .../components/ApplicationButtons.tsx | 4 +- .../components/ApplicationForm.tsx | 14 ++-- .../hooks/useApproveApplication.ts | 8 ++- .../hooks/useCreateApplication.ts | 6 +- .../features/voters/hooks/useApproveVoters.ts | 8 ++- packages/interface/src/hooks/useEAS.ts | 60 +++++++++++++--- .../interface/src/hooks/useEthersSigner.ts | 2 +- packages/interface/src/hooks/useIsAdmin.ts | 6 +- .../src/hooks/useIsCorrectNetwork.ts | 5 +- .../interface/src/hooks/useSmartAccount.ts | 13 +++- packages/interface/src/layouts/BaseLayout.tsx | 8 +-- .../interface/src/layouts/DefaultLayout.tsx | 6 +- packages/interface/src/pages/ballot/index.tsx | 8 +-- packages/interface/src/pages/signup/index.tsx | 4 +- .../src/pages/signup/registerEmail.tsx | 5 +- packages/interface/src/pages/stats/index.tsx | 19 +---- .../interface/src/pages/voters/status.tsx | 5 +- packages/interface/src/utils/types.ts | 2 +- pnpm-lock.yaml | 70 ++++++++++++++++++- 25 files changed, 190 insertions(+), 100 deletions(-) diff --git a/packages/interface/package.json b/packages/interface/package.json index 8403456..5dd1bfa 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -16,6 +16,7 @@ "test:e2e": "playwright test --project=chromium" }, "dependencies": { + "@ethereum-attestation-service/eas-contracts": "^1.7.1", "@ethereum-attestation-service/eas-sdk": "^1.5.0", "@hatsprotocol/sdk-v1-core": "^0.10.0", "@hookform/resolvers": "^3.3.4", diff --git a/packages/interface/src/components/EligibilityDialog.tsx b/packages/interface/src/components/EligibilityDialog.tsx index 57f952e..2f566d7 100644 --- a/packages/interface/src/components/EligibilityDialog.tsx +++ b/packages/interface/src/components/EligibilityDialog.tsx @@ -1,17 +1,16 @@ import { useRouter } from "next/router"; import { useState, useCallback, useEffect } from "react"; import { toast } from "sonner"; -import { useAccount, useDisconnect } from "wagmi"; import { useMaci } from "~/contexts/Maci"; import { useAppState } from "~/utils/state"; import { EAppState } from "~/utils/types"; import { Dialog } from "./ui/Dialog"; +import useSmartAccount from "~/hooks/useSmartAccount"; export const EligibilityDialog = (): JSX.Element | null => { - const { address } = useAccount(); - const { disconnect } = useDisconnect(); + const { address } = useSmartAccount(); const [openDialog, setOpenDialog] = useState(!!address); const { onSignup, isEligibleToVote, isRegistered } = useMaci(); @@ -34,10 +33,6 @@ export const EligibilityDialog = (): JSX.Element | null => { setOpenDialog(false); }, [setOpenDialog]); - const handleDisconnect = useCallback(() => { - disconnect(); - }, [disconnect]); - const handleGoToProjects = useCallback(() => { router.push("/projects"); }, [router]); @@ -46,7 +41,7 @@ export const EligibilityDialog = (): JSX.Element | null => { router.push("/applications/new"); }, [router]); - if (appState === EAppState.APPLICATION) { + if (appState === EAppState.APPLICATION && isEligibleToVote) { return ( { if (appState === EAppState.VOTING && !isEligibleToVote) { return ( { )} - {appState !== EAppState.TALLYING && !isEligibleToVote && !isRegistered && ( - )} diff --git a/packages/interface/src/config.ts b/packages/interface/src/config.ts index 6c15f70..d9a0ecc 100644 --- a/packages/interface/src/config.ts +++ b/packages/interface/src/config.ts @@ -1,5 +1,6 @@ import * as wagmiChains from "wagmi/chains"; import { Config } from "./utils/types"; +import { Address } from "viem"; export const metadata = { title: "MACI PLATFORM", @@ -104,7 +105,7 @@ export const config: Config = { tokenName: process.env.NEXT_PUBLIC_TOKEN_NAME!, eventName: process.env.NEXT_PUBLIC_EVENT_NAME ?? "MACI-PLATFORM", roundId: process.env.NEXT_PUBLIC_ROUND_ID!, - admin: (process.env.NEXT_PUBLIC_ADMIN_ADDRESS ?? "") as `0x${string}`, + admin: (process.env.NEXT_PUBLIC_ADMIN_ADDRESS ?? "") as Address, network: wagmiChains[process.env.NEXT_PUBLIC_CHAIN_NAME as keyof typeof wagmiChains], maciAddress: process.env.NEXT_PUBLIC_MACI_ADDRESS, maciStartBlock: Number(process.env.NEXT_PUBLIC_MACI_START_BLOCK ?? 0), diff --git a/packages/interface/src/contexts/Ballot.tsx b/packages/interface/src/contexts/Ballot.tsx index 137b23c..fbb8702 100644 --- a/packages/interface/src/contexts/Ballot.tsx +++ b/packages/interface/src/contexts/Ballot.tsx @@ -1,5 +1,4 @@ import React, { createContext, useContext, useState, useEffect, useMemo, useCallback } from "react"; -import { useAccount } from "wagmi"; import { config } from "~/config"; @@ -14,8 +13,6 @@ export const BallotProvider: React.FC = ({ children }: Ball const [ballot, setBallot] = useState(defaultBallot); const [isLoading, setLoading] = useState(true); - const { isDisconnected } = useAccount(); - // when summing the ballot we take the individual vote and square it // if the mode is quadratic voting, otherwise we just add the amount const sumBallot = useCallback( @@ -102,12 +99,6 @@ export const BallotProvider: React.FC = ({ children }: Ball } }, [ballot, ballot.votes, ballot.published, saveBallot]); - useEffect(() => { - if (isDisconnected) { - deleteBallot(); - } - }, [isDisconnected, deleteBallot]); - const value = useMemo( () => ({ ballot, diff --git a/packages/interface/src/contexts/Maci.tsx b/packages/interface/src/contexts/Maci.tsx index 756eedf..d56582d 100644 --- a/packages/interface/src/contexts/Maci.tsx +++ b/packages/interface/src/contexts/Maci.tsx @@ -14,6 +14,7 @@ import { getHatsSingleGatekeeperData, } from "maci-cli/sdk"; import React, { createContext, useContext, useCallback, useEffect, useMemo, useState } from "react"; +import { Address, type EIP1193Provider } from "viem"; import { config } from "~/config"; import { useEthersSigner } from "~/hooks/useEthersSigner"; @@ -23,7 +24,6 @@ import { getHatsClient } from "~/utils/hatsProtocol"; import { getSemaphoreProof } from "~/utils/semaphore"; import type { IVoteArgs, MaciContextType, MaciProviderProps } from "./types"; -import { type EIP1193Provider } from "viem"; import type { Attestation } from "~/utils/types"; import signUp from "~/utils/signUp"; @@ -159,11 +159,11 @@ export const MaciProvider: React.FC = ({ children }: MaciProv updateEligibility(sgData, address); }, [sgData, address]) - const updateEligibility = (_sgData: string | undefined, _address: `0x${string}` | undefined) => { + const updateEligibility = (_sgData: string | undefined, _address: Address | undefined) => { setIsEligibleToVote(checkEligibility(_sgData, _address)); } - function checkEligibility(sgData: string | undefined, address: `0x${string}` | undefined): boolean { + function checkEligibility(sgData: string | undefined, address: Address | undefined): boolean { return (gatekeeperTrait && (gatekeeperTrait === GatekeeperTrait.FreeForAll || Boolean(sgData)) && Boolean(address)) ?? false; } diff --git a/packages/interface/src/features/applications/components/ApplicationButtons.tsx b/packages/interface/src/features/applications/components/ApplicationButtons.tsx index bfff33d..55db7b3 100644 --- a/packages/interface/src/features/applications/components/ApplicationButtons.tsx +++ b/packages/interface/src/features/applications/components/ApplicationButtons.tsx @@ -1,6 +1,5 @@ import { useMemo, useCallback, useState } from "react"; import { useFormContext } from "react-hook-form"; -import { useAccount } from "wagmi"; import { Button, IconButton } from "~/components/ui/Button"; import { Dialog } from "~/components/ui/Dialog"; @@ -9,6 +8,7 @@ import { useIsCorrectNetwork } from "~/hooks/useIsCorrectNetwork"; import type { Application } from "../types"; import type { ImpactMetrix, ContributionLink, FundingSource } from "~/features/projects/types"; +import useSmartAccount from "~/hooks/useSmartAccount"; export enum EApplicationStep { PROFILE, @@ -33,7 +33,7 @@ export const ApplicationButtons = ({ }: IApplicationButtonsProps): JSX.Element => { const { isCorrectNetwork } = useIsCorrectNetwork(); - const { address } = useAccount(); + const { address } = useSmartAccount(); const [showDialog, setShowDialog] = useState(false); diff --git a/packages/interface/src/features/applications/components/ApplicationForm.tsx b/packages/interface/src/features/applications/components/ApplicationForm.tsx index 70b6bcf..0fb873b 100644 --- a/packages/interface/src/features/applications/components/ApplicationForm.tsx +++ b/packages/interface/src/features/applications/components/ApplicationForm.tsx @@ -1,9 +1,8 @@ -import { Transaction } from "@ethereum-attestation-service/eas-sdk"; import { useRouter } from "next/router"; import { useState, useCallback } from "react"; import { useLocalStorage } from "react-use"; import { toast } from "sonner"; -import { useAccount } from "wagmi"; +import { Hex } from "viem"; import { ImageUpload } from "~/components/ImageUpload"; import { FieldArray, Form, FormControl, FormSection, Select, Textarea } from "~/components/ui/Form"; @@ -17,13 +16,14 @@ import { ApplicationButtons, EApplicationStep } from "./ApplicationButtons"; import { ApplicationSteps } from "./ApplicationSteps"; import { ImpactTags } from "./ImpactTags"; import { ReviewApplicationDetails } from "./ReviewApplicationDetails"; +import useSmartAccount from "~/hooks/useSmartAccount"; export const ApplicationForm = (): JSX.Element => { const clearDraft = useLocalStorage("application-draft")[2]; const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork(); - const { address } = useAccount(); + const { address } = useSmartAccount(); const router = useRouter(); @@ -52,14 +52,14 @@ export const ApplicationForm = (): JSX.Element => { }, [step, setStep]); const create = useCreateApplication({ - onSuccess: (data: Transaction) => { + onSuccess: (hash: Hex) => { clearDraft(); - router.push(`/applications/confirmation?txHash=${data.tx.hash}`); + router.push(`/applications/confirmation?txHash=${hash}`); }, - onError: (err: { reason?: string; data?: { message: string } }) => + onError: (err: { reason?: string; data?: { message: string } }) => { toast.error("Application create error", { description: err.reason ?? err.data?.message, - }), + })}, }); const { error: createError } = create; diff --git a/packages/interface/src/features/applications/hooks/useApproveApplication.ts b/packages/interface/src/features/applications/hooks/useApproveApplication.ts index e1d052f..b999ca5 100644 --- a/packages/interface/src/features/applications/hooks/useApproveApplication.ts +++ b/packages/interface/src/features/applications/hooks/useApproveApplication.ts @@ -1,18 +1,20 @@ -import { type Transaction } from "@ethereum-attestation-service/eas-sdk"; import { type UseMutationResult, useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; +import { Hex } from "viem"; import { config, eas } from "~/config"; import { type TransactionError } from "~/features/voters/hooks/useApproveVoters"; import { useAttest } from "~/hooks/useEAS"; import { useEthersSigner } from "~/hooks/useEthersSigner"; +import useSmartAccount from "~/hooks/useSmartAccount"; import { createAttestation } from "~/lib/eas/createAttestation"; export function useApproveApplication(opts?: { onSuccess?: () => void; -}): UseMutationResult, Error | TransactionError, string[]> { +}): UseMutationResult { const attest = useAttest(); - const signer = useEthersSigner(); + const { smartAccountClient } = useSmartAccount(); + const signer = useEthersSigner({ client: smartAccountClient }); return useMutation({ mutationFn: async (applicationIds: string[]) => { diff --git a/packages/interface/src/features/applications/hooks/useCreateApplication.ts b/packages/interface/src/features/applications/hooks/useCreateApplication.ts index b7cefa9..7b86cab 100644 --- a/packages/interface/src/features/applications/hooks/useCreateApplication.ts +++ b/packages/interface/src/features/applications/hooks/useCreateApplication.ts @@ -1,4 +1,5 @@ import { type UseMutationResult, useMutation } from "@tanstack/react-query"; +import { Hex } from "viem"; import { config, eas } from "~/config"; import { type TransactionError } from "~/features/voters/hooks/useApproveVoters"; @@ -6,10 +7,9 @@ import { useAttest, useCreateAttestation } from "~/hooks/useEAS"; import { useUploadMetadata } from "~/hooks/useMetadata"; import type { Application } from "../types"; -import type { Transaction } from "@ethereum-attestation-service/eas-sdk"; export type TUseCreateApplicationReturn = Omit< - UseMutationResult, Error | TransactionError, Application>, + UseMutationResult, "error" > & { error: Error | TransactionError | null; @@ -18,7 +18,7 @@ export type TUseCreateApplicationReturn = Omit< }; export function useCreateApplication(options: { - onSuccess: (data: Transaction) => void; + onSuccess: (hash: Hex) => void; onError: (err: TransactionError) => void; }): TUseCreateApplicationReturn { const attestation = useCreateAttestation(); diff --git a/packages/interface/src/features/voters/hooks/useApproveVoters.ts b/packages/interface/src/features/voters/hooks/useApproveVoters.ts index eaf7ff3..e563c65 100644 --- a/packages/interface/src/features/voters/hooks/useApproveVoters.ts +++ b/packages/interface/src/features/voters/hooks/useApproveVoters.ts @@ -1,9 +1,10 @@ -import { type Transaction } from "@ethereum-attestation-service/eas-sdk"; import { type UseMutationResult, useMutation } from "@tanstack/react-query"; +import { Hex } from "viem"; import { config, eas } from "~/config"; import { useAttest } from "~/hooks/useEAS"; import { useEthersSigner } from "~/hooks/useEthersSigner"; +import useSmartAccount from "~/hooks/useSmartAccount"; import { createAttestation } from "~/lib/eas/createAttestation"; // TODO: Move this to a shared folders @@ -15,9 +16,10 @@ export interface TransactionError { export function useApproveVoters(options: { onSuccess: () => void; onError: (err: TransactionError) => void; -}): UseMutationResult, unknown, string[]> { +}): UseMutationResult { const attest = useAttest(); - const signer = useEthersSigner(); + const { smartAccountClient } = useSmartAccount(); + const signer = useEthersSigner({ client: smartAccountClient }); return useMutation({ mutationFn: async (voters: string[]) => { diff --git a/packages/interface/src/hooks/useEAS.ts b/packages/interface/src/hooks/useEAS.ts index ff5867b..6ed9bee 100644 --- a/packages/interface/src/hooks/useEAS.ts +++ b/packages/interface/src/hooks/useEAS.ts @@ -1,16 +1,21 @@ -import { AttestationRequest, Transaction, type MultiAttestationRequest } from "@ethereum-attestation-service/eas-sdk"; +import { AttestationRequest, NO_EXPIRATION, ZERO_ADDRESS, ZERO_BYTES32, type MultiAttestationRequest } from "@ethereum-attestation-service/eas-sdk"; +import { EAS__factory as EASFactory } from '@ethereum-attestation-service/eas-contracts'; import { type DefaultError, type UseMutationResult, useMutation } from "@tanstack/react-query"; import { useEthersSigner } from "~/hooks/useEthersSigner"; import { createAttestation } from "~/lib/eas/createAttestation"; -import { createEAS } from "~/lib/eas/createEAS"; +import useSmartAccount from "./useSmartAccount"; +import { Address, Hex } from "viem"; +import { publicClient } from "~/utils/permissionless"; +import { eas } from "~/config"; export function useCreateAttestation(): UseMutationResult< AttestationRequest, DefaultError, { values: Record; schemaUID: string } > { - const signer = useEthersSigner(); + const { smartAccountClient } = useSmartAccount(); + const signer = useEthersSigner({ client: smartAccountClient }); return useMutation({ mutationFn: async (data: { values: Record; schemaUID: string }) => { @@ -23,17 +28,50 @@ export function useCreateAttestation(): UseMutationResult< }); } -export function useAttest(): UseMutationResult, DefaultError, MultiAttestationRequest[]> { - const signer = useEthersSigner(); +export function useAttest(): UseMutationResult { + const { smartAccount, smartAccountClient } = useSmartAccount(); + const signer = useEthersSigner({ client: smartAccountClient }); return useMutation({ - mutationFn: (attestations: MultiAttestationRequest[]) => { - if (!signer) { - throw new Error("Connect wallet first"); + mutationFn: async (attestations: MultiAttestationRequest[]) => { + if (!smartAccount || !smartAccountClient) { + throw new Error("Smart account not connected"); + } + + const multiAttestationRequests = attestations.map((r) => ({ + schema: r.schema as Hex, + data: r.data.map((d) => ({ + recipient: (d.recipient ?? ZERO_ADDRESS) as Address, + expirationTime: d.expirationTime ?? NO_EXPIRATION, + revocable: d.revocable ?? true, + refUID: (d.refUID ?? ZERO_BYTES32) as Hex, + data: (d.data ?? ZERO_BYTES32) as Hex, + value: d.value ?? 0n + })) + })); + + const requestedValue = multiAttestationRequests.reduce((res, { data }) => { + const total = data.reduce((res, r) => res + r.value, 0n); + return res + total; + }, 0n); + + if (requestedValue > 0n) { + throw new Error("Cannot sponsor a user operation that sends value") + } + + try { + const { request } = await publicClient.simulateContract({ + account: smartAccount, + address: eas.contracts.eas as Address, + abi: EASFactory.abi, + functionName: "multiAttest", + args: [multiAttestationRequests], + }); + return await smartAccountClient.writeContract(request); + } catch (error: unknown) { + console.error(error); + throw new Error("Error attesting"); } - const eas = createEAS(signer); - - return eas.multiAttest(attestations); }, }); } diff --git a/packages/interface/src/hooks/useEthersSigner.ts b/packages/interface/src/hooks/useEthersSigner.ts index f20b5e6..eb28056 100644 --- a/packages/interface/src/hooks/useEthersSigner.ts +++ b/packages/interface/src/hooks/useEthersSigner.ts @@ -7,7 +7,7 @@ import { useConnectorClient } from "wagmi"; import { getRPCURL } from "~/config"; function clientToSigner(client: SmartAccountClient): JsonRpcSigner | undefined { - const { account, chain, transport } = client; + const { account, chain } = client; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (!chain || !account) { diff --git a/packages/interface/src/hooks/useIsAdmin.ts b/packages/interface/src/hooks/useIsAdmin.ts index a8a6454..073d8c3 100644 --- a/packages/interface/src/hooks/useIsAdmin.ts +++ b/packages/interface/src/hooks/useIsAdmin.ts @@ -1,9 +1,9 @@ -import { useAccount } from "wagmi"; - import { config } from "~/config"; +import useSmartAccount from "./useSmartAccount"; export function useIsAdmin(): boolean { - const { address } = useAccount(); + // TODO: (merge-ok) figure out how we set embedded smart account to admin + const { address } = useSmartAccount(); return config.admin === address!; } diff --git a/packages/interface/src/hooks/useIsCorrectNetwork.ts b/packages/interface/src/hooks/useIsCorrectNetwork.ts index f179541..ebb7287 100644 --- a/packages/interface/src/hooks/useIsCorrectNetwork.ts +++ b/packages/interface/src/hooks/useIsCorrectNetwork.ts @@ -1,6 +1,7 @@ -import { useAccount, useChainId } from "wagmi"; +import { useChainId } from "wagmi"; import { config } from "~/config"; +import useSmartAccount from "./useSmartAccount"; export interface IUseIsCorrectNetworkReturn { isCorrectNetwork: boolean; @@ -8,7 +9,7 @@ export interface IUseIsCorrectNetworkReturn { } export function useIsCorrectNetwork(): IUseIsCorrectNetworkReturn { - const { isConnected } = useAccount(); + const { isConnected } = useSmartAccount(); const chainId = useChainId(); const isCorrectNetwork = isConnected && chainId === config.network.id; diff --git a/packages/interface/src/hooks/useSmartAccount.ts b/packages/interface/src/hooks/useSmartAccount.ts index f2eea0e..34593ca 100644 --- a/packages/interface/src/hooks/useSmartAccount.ts +++ b/packages/interface/src/hooks/useSmartAccount.ts @@ -1,5 +1,5 @@ import { useState, useEffect } from "react"; -import { HttpTransport, Chain, http, Address } from "viem"; +import { HttpTransport, Chain, http, Address, Hex } from "viem"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; import { createSmartAccountClient, @@ -22,7 +22,7 @@ import { config, getPimlicoRPCURL } from "~/config"; const createAccount = async ( privateKey: string ): Promise> => { - const signer = privateKeyToAccount(privateKey); + const signer = privateKeyToAccount(privateKey as Hex); return await signerToEcdsaKernelSmartAccount(publicClient, { entryPoint: ENTRYPOINT_ADDRESS_V07, signer, @@ -48,6 +48,7 @@ const createAccountClient = async ( const useSmartAccount = () => { const [address, setAddress] = useState
(); + const [isConnected, setIsConnected] = useState(false); const [smartAccount, setSmartAccount] = useState>(); const [smartAccountClient, setSmartAccountClient] = @@ -83,13 +84,19 @@ const useSmartAccount = () => { } setAddress(kernelAccount.address); + setIsConnected(true); setSmartAccount(kernelAccount); setSmartAccountClient(kernelAccountClient); }; getSmartAccount(); }, []); - return { address, smartAccount, smartAccountClient }; + return { + address, + isConnected, + smartAccount, + smartAccountClient, + }; }; export default useSmartAccount; diff --git a/packages/interface/src/layouts/BaseLayout.tsx b/packages/interface/src/layouts/BaseLayout.tsx index 21c41b0..00bb967 100644 --- a/packages/interface/src/layouts/BaseLayout.tsx +++ b/packages/interface/src/layouts/BaseLayout.tsx @@ -12,12 +12,12 @@ import { useMemo, } from "react"; import { tv } from "tailwind-variants"; -import { useAccount } from "wagmi"; import { Footer } from "~/components/Footer"; import { createComponent } from "~/components/ui"; import { metadata } from "~/config"; import { useMaci } from "~/contexts/Maci"; +import useSmartAccount from "~/hooks/useSmartAccount"; const Context = createContext({ eligibilityCheck: false, showBallot: false }); @@ -79,14 +79,14 @@ export const BaseLayout = ({ >): JSX.Element => { const { theme } = useTheme(); const router = useRouter(); - const { address, isConnecting } = useAccount(); + const { address } = useSmartAccount(); const { isRegistered } = useMaci(); const manageDisplay = useCallback(() => { - if ((requireAuth && !address && !isConnecting) || (requireRegistration && !isRegistered)) { + if ((requireAuth && !address) || (requireRegistration && !isRegistered)) { router.push("/"); } - }, [requireAuth, address, isConnecting, requireRegistration, isRegistered, router]); + }, [requireAuth, address, requireRegistration, isRegistered, router]); useEffect(() => { manageDisplay(); diff --git a/packages/interface/src/layouts/DefaultLayout.tsx b/packages/interface/src/layouts/DefaultLayout.tsx index 66503a4..8718a61 100644 --- a/packages/interface/src/layouts/DefaultLayout.tsx +++ b/packages/interface/src/layouts/DefaultLayout.tsx @@ -1,5 +1,4 @@ import { type ReactNode, type PropsWithChildren, useMemo } from "react"; -import { useAccount } from "wagmi"; import { BallotOverview } from "~/components/BallotOverview"; import Header from "~/components/Header"; @@ -13,6 +12,7 @@ import { useAppState } from "~/utils/state"; import { EAppState } from "~/utils/types"; import { BaseLayout, type LayoutProps } from "./BaseLayout"; +import useSmartAccount from "~/hooks/useSmartAccount"; interface ILayoutProps extends PropsWithChildren { sidebar?: "left" | "right"; @@ -22,7 +22,7 @@ interface ILayoutProps extends PropsWithChildren { } export const Layout = ({ children = null, ...props }: ILayoutProps): JSX.Element => { - const { address } = useAccount(); + const { address } = useSmartAccount(); const appState = useAppState(); const { ballot } = useBallot(); const { isRegistered } = useMaci(); @@ -83,7 +83,7 @@ export const Layout = ({ children = null, ...props }: ILayoutProps): JSX.Element export const LayoutWithSidebar = ({ ...props }: ILayoutProps): JSX.Element => { const { isRegistered } = useMaci(); - const { address } = useAccount(); + const { address } = useSmartAccount(); const { ballot } = useBallot(); const appState = useAppState(); diff --git a/packages/interface/src/pages/ballot/index.tsx b/packages/interface/src/pages/ballot/index.tsx index eabc494..66404ef 100644 --- a/packages/interface/src/pages/ballot/index.tsx +++ b/packages/interface/src/pages/ballot/index.tsx @@ -3,7 +3,6 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { useEffect, useState, useMemo, useCallback } from "react"; import { useFormContext } from "react-hook-form"; -import { useAccount } from "wagmi"; import { Button } from "~/components/ui/Button"; import { Dialog } from "~/components/ui/Dialog"; @@ -13,6 +12,7 @@ import { useBallot } from "~/contexts/Ballot"; import { useMaci } from "~/contexts/Maci"; import { AllocationFormWrapper } from "~/features/ballot/components/AllocationFormWrapper"; import { BallotSchema } from "~/features/ballot/types"; +import useSmartAccount from "~/hooks/useSmartAccount"; import { LayoutWithSidebar } from "~/layouts/DefaultLayout"; import { formatNumber } from "~/utils/formatNumber"; import { useAppState } from "~/utils/state"; @@ -124,16 +124,16 @@ const BallotAllocationForm = (): JSX.Element => { }; const BallotPage = (): JSX.Element => { - const { address, isConnecting } = useAccount(); + const { address } = useSmartAccount(); const { ballot, sumBallot } = useBallot(); const router = useRouter(); const appState = useAppState(); useEffect(() => { - if (!address && !isConnecting) { + if (!address) { router.push("/"); } - }, [address, isConnecting, router]); + }, [address, router]); const handleSubmit = useCallback(() => { sumBallot(); diff --git a/packages/interface/src/pages/signup/index.tsx b/packages/interface/src/pages/signup/index.tsx index 3df58a6..9563f66 100644 --- a/packages/interface/src/pages/signup/index.tsx +++ b/packages/interface/src/pages/signup/index.tsx @@ -15,7 +15,7 @@ import { useAppState } from "~/utils/state"; import { EAppState } from "~/utils/types"; const SignupPage = (): JSX.Element => { - const { isRegistered } = useMaci(); + const { isRegistered, isEligibleToVote } = useMaci(); const { address } = useSmartAccount(); const appState = useAppState(); @@ -44,7 +44,7 @@ const SignupPage = (): JSX.Element => {

- {address && isRegistered && appState === EAppState.APPLICATION && ( + {address && isEligibleToVote && appState === EAppState.APPLICATION && ( diff --git a/packages/interface/src/pages/signup/registerEmail.tsx b/packages/interface/src/pages/signup/registerEmail.tsx index 196c4de..25c9d81 100644 --- a/packages/interface/src/pages/signup/registerEmail.tsx +++ b/packages/interface/src/pages/signup/registerEmail.tsx @@ -27,9 +27,10 @@ import { useEthersSigner } from "~/hooks/useEthersSigner"; const RegisterEmail = (): JSX.Element => { const { address, smartAccount, smartAccountClient } = useSmartAccount(); - const router = useRouter(); - const { updateEligibility } = useMaci(); const signer = useEthersSigner({ client: smartAccountClient }); + const { updateEligibility } = useMaci(); + const router = useRouter(); + const [emailField, setEmail] = useState(); const registerEmail = async (emailField: EmailField) => { diff --git a/packages/interface/src/pages/stats/index.tsx b/packages/interface/src/pages/stats/index.tsx index f28e927..2bd7653 100644 --- a/packages/interface/src/pages/stats/index.tsx +++ b/packages/interface/src/pages/stats/index.tsx @@ -1,14 +1,13 @@ import { differenceInDays } from "date-fns"; import dynamic from "next/dynamic"; import { useMemo, type PropsWithChildren } from "react"; -import { useAccount } from "wagmi"; -import { ConnectButton } from "~/components/ConnectButton"; import { Alert } from "~/components/ui/Alert"; import { Heading } from "~/components/ui/Heading"; import { config } from "~/config"; import { useMaci } from "~/contexts/Maci"; import { useProjectCount, useProjectsResults, useResults } from "~/hooks/useResults"; +import useSmartAccount from "~/hooks/useSmartAccount"; import { Layout } from "~/layouts/DefaultLayout"; import { formatNumber } from "~/utils/formatNumber"; import { useAppState } from "~/utils/state"; @@ -31,7 +30,7 @@ const Stats = () => { const results = useResults(pollData); const count = useProjectCount(); const { data: projectsResults } = useProjectsResults(pollData); - const { isConnected } = useAccount(); + const { isConnected } = useSmartAccount(); const { averageVotes, projects = {} } = results.data ?? {}; @@ -46,22 +45,10 @@ const Stats = () => { return [{ id: "awarded", data }]; }, [projects, projectsResults]); - if (isLoading) { + if (isLoading || !isConnected) { return
Loading...
; } - if (!pollData && !isConnected) { - return ( - - Connect your wallet to see results - -
- -
-
- ); - } - if (!pollData) { return
Something went wrong. Try later.
; } diff --git a/packages/interface/src/pages/voters/status.tsx b/packages/interface/src/pages/voters/status.tsx index 0b24ebc..696f7bd 100644 --- a/packages/interface/src/pages/voters/status.tsx +++ b/packages/interface/src/pages/voters/status.tsx @@ -1,12 +1,11 @@ -import { useAccount } from "wagmi"; - import { Alert } from "~/components/ui/Alert"; import { Spinner } from "~/components/ui/Spinner"; import { useApprovedVoter } from "~/features/voters/hooks/useApprovedVoter"; +import useSmartAccount from "~/hooks/useSmartAccount"; import { Layout } from "~/layouts/DefaultLayout"; const VotersPage = (): JSX.Element => { - const { address } = useAccount(); + const { address } = useSmartAccount(); const approved = useApprovedVoter(address!); if (approved.isLoading) { diff --git a/packages/interface/src/utils/types.ts b/packages/interface/src/utils/types.ts index dea81c1..c4594ce 100644 --- a/packages/interface/src/utils/types.ts +++ b/packages/interface/src/utils/types.ts @@ -95,7 +95,7 @@ export type Config = { tokenName: string; eventName: string; roundId: string; - admin: `0x${string}`; + admin: Address; network: Chain; maciAddress: string | undefined; maciStartBlock: number; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05874a9..445e81e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,6 +106,9 @@ importers: packages/interface: dependencies: + '@ethereum-attestation-service/eas-contracts': + specifier: ^1.7.1 + version: 1.7.1(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) '@ethereum-attestation-service/eas-sdk': specifier: ^1.5.0 version: 1.6.1(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) @@ -11948,7 +11951,7 @@ snapshots: - typescript - utf-8-validate - '@ethereum-attestation-service/eas-contracts@1.7.1(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)': + '@ethereum-attestation-service/eas-contracts@1.7.1(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: hardhat: 2.22.4(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) transitivePeerDependencies: @@ -11959,6 +11962,17 @@ snapshots: - typescript - utf-8-validate + '@ethereum-attestation-service/eas-contracts@1.7.1(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4)': + dependencies: + hardhat: 2.22.4(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4) + transitivePeerDependencies: + - bufferutil + - c-kzg + - supports-color + - ts-node + - typescript + - utf-8-validate + '@ethereum-attestation-service/eas-sdk@1.6.1(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)': dependencies: '@ethereum-attestation-service/eas-contracts': 1.4.1(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10) @@ -18639,6 +18653,60 @@ snapshots: - supports-color - utf-8-validate + hardhat@2.22.4(ts-node@10.9.2(typescript@5.5.4))(typescript@5.5.4): + dependencies: + '@ethersproject/abi': 5.7.0 + '@metamask/eth-sig-util': 4.0.1 + '@nomicfoundation/edr': 0.3.8 + '@nomicfoundation/ethereumjs-common': 4.0.4 + '@nomicfoundation/ethereumjs-tx': 5.0.4 + '@nomicfoundation/ethereumjs-util': 9.0.4 + '@nomicfoundation/solidity-analyzer': 0.1.2 + '@sentry/node': 5.30.0 + '@types/bn.js': 5.1.5 + '@types/lru-cache': 5.1.1 + adm-zip: 0.4.16 + aggregate-error: 3.1.0 + ansi-escapes: 4.3.2 + boxen: 5.1.2 + chalk: 2.4.2 + chokidar: 3.6.0 + ci-info: 2.0.0 + debug: 4.3.6(supports-color@8.1.1) + enquirer: 2.4.1 + env-paths: 2.2.1 + ethereum-cryptography: 1.2.0 + ethereumjs-abi: 0.6.8 + find-up: 2.1.0 + fp-ts: 1.19.3 + fs-extra: 7.0.1 + glob: 7.2.0 + immutable: 4.3.7 + io-ts: 1.10.4 + keccak: 3.0.4 + lodash: 4.17.21 + mnemonist: 0.38.5 + mocha: 10.7.0 + p-map: 4.0.0 + raw-body: 2.5.2 + resolve: 1.17.0 + semver: 6.3.1 + solc: 0.7.3(debug@4.3.6) + source-map-support: 0.5.21 + stacktrace-parser: 0.1.10 + tsort: 0.0.1 + undici: 5.28.4 + uuid: 8.3.2 + ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + ts-node: 10.9.2(@types/node@22.1.0)(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - bufferutil + - c-kzg + - supports-color + - utf-8-validate + hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10): dependencies: '@ethersproject/abi': 5.7.0