diff --git a/packages/interface/package.json b/packages/interface/package.json index 54c568aa..a3c43158 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -20,6 +20,7 @@ "@hatsprotocol/sdk-v1-core": "^0.10.0", "@hookform/resolvers": "^3.3.4", "@nivo/line": "^0.84.0", + "@openzeppelin/merkle-tree": "^1.0.7", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@rainbow-me/rainbowkit": "^2.0.1", diff --git a/packages/interface/src/config.ts b/packages/interface/src/config.ts index 28fa3f6e..f6aa30b2 100644 --- a/packages/interface/src/config.ts +++ b/packages/interface/src/config.ts @@ -92,6 +92,7 @@ export const config = { roundOrganizer: process.env.NEXT_PUBLIC_ROUND_ORGANIZER ?? "PSE", roundLogo: process.env.NEXT_PUBLIC_ROUND_LOGO, semaphoreSubgraphUrl: process.env.NEXT_PUBLIC_SEMAPHORE_SUBGRAPH, + treeUrl: process.env.NEXT_PUBLIC_TREE_URL, }; export const theme = { diff --git a/packages/interface/src/contexts/Maci.tsx b/packages/interface/src/contexts/Maci.tsx index eee7116a..a827b491 100644 --- a/packages/interface/src/contexts/Maci.tsx +++ b/packages/interface/src/contexts/Maci.tsx @@ -1,7 +1,8 @@ /* eslint-disable no-console */ +import { StandardMerkleTree } from "@openzeppelin/merkle-tree"; import { Identity } from "@semaphore-protocol/core"; import { isAfter } from "date-fns"; -import { type Signer, BrowserProvider } from "ethers"; +import { type Signer, BrowserProvider, AbiCoder } from "ethers"; import { signup, isRegisteredUser, @@ -24,6 +25,7 @@ import { getHatsClient } from "~/utils/hatsProtocol"; import { getSemaphoreProof } from "~/utils/semaphore"; import type { IVoteArgs, MaciContextType, MaciProviderProps } from "./types"; +import type { StandardMerkleTreeData } from "@openzeppelin/merkle-tree/dist/standard"; import type { EIP1193Provider } from "viem"; import type { Attestation } from "~/utils/types"; @@ -45,6 +47,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv const [error, setError] = useState(); const [pollData, setPollData] = useState(); const [tallyData, setTallyData] = useState(); + const [treeData, setTreeData] = useState>(); const [semaphoreIdentity, setSemaphoreIdentity] = useState(); const [maciPrivKey, setMaciPrivKey] = useState(); @@ -150,6 +153,25 @@ export const MaciProvider: React.FC = ({ children }: MaciProv case GatekeeperTrait.FreeForAll: setIsLoading(false); break; + case GatekeeperTrait.MerkleProof: + if (!signer) { + setIsLoading(false); + return; + } + if (!treeData) { + setIsLoading(false); + return; + } + try { + const merkleTree = StandardMerkleTree.load(treeData); + const proof = merkleTree.getProof([signer.address]); + const encodedProof = AbiCoder.defaultAbiCoder().encode(["bytes32[]"], [proof]); + setSgData(encodedProof); + } catch (e) { + setSgData(undefined); + } + setIsLoading(false); + break; default: break; } @@ -421,6 +443,22 @@ export const MaciProvider: React.FC = ({ children }: MaciProv } }, [signer, votingEndsAt, setIsLoading, setTallyData, setPollData, poll.data]); + /// check the tree data + useEffect(() => { + // if we have the tree url then it means we can get the tree data through there + if (config.treeUrl) { + setIsLoading(true); + fetch(config.treeUrl) + .then((res) => res.json()) + .then((res: StandardMerkleTreeData) => { + setTreeData(res); + }) + .catch(() => undefined); + + setIsLoading(false); + } + }, [setIsLoading]); + const value = useMemo( () => ({ isLoading, @@ -437,6 +475,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv onSignup, onVote, gatekeeperTrait, + treeData, }), [ isLoading, @@ -452,6 +491,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv onSignup, onVote, gatekeeperTrait, + treeData, ], ); diff --git a/packages/interface/src/env.js b/packages/interface/src/env.js index 11cb918a..cf7d3a20 100644 --- a/packages/interface/src/env.js +++ b/packages/interface/src/env.js @@ -68,6 +68,7 @@ module.exports = createEnv({ NEXT_PUBLIC_ROUND_LOGO: z.string().optional(), NEXT_PUBLIC_SEMAPHORE_SUBGRAPH: z.string().url().optional(), + NEXT_PUBLIC_TREE_URL: z.string().url().optional(), }, /** @@ -107,6 +108,7 @@ module.exports = createEnv({ NEXT_PUBLIC_ROUND_LOGO: process.env.NEXT_PUBLIC_ROUND_LOGO, NEXT_PUBLIC_SEMAPHORE_SUBGRAPH: process.env.NEXT_PUBLIC_SEMAPHORE_SUBGRAPH, + NEXT_PUBLIC_TREE_URL: process.env.NEXT_PUBLIC_TREE_URL, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b1fac88..7134a3ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -322,6 +322,9 @@ importers: '@nivo/line': specifier: ^0.84.0 version: 0.84.0(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@openzeppelin/merkle-tree': + specifier: ^1.0.7 + version: 1.0.7 '@radix-ui/react-dialog': specifier: ^1.0.5 version: 1.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2808,6 +2811,9 @@ packages: '@openzeppelin/contracts@5.0.2': resolution: {integrity: sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==} + '@openzeppelin/merkle-tree@1.0.7': + resolution: {integrity: sha512-i93t0YYv6ZxTCYU3CdO5Q+DXK0JH10A4dCBOMlzYbX+ujTXm+k1lXiEyVqmf94t3sqmv8sm/XT5zTa0+efnPgQ==} + '@panva/hkdf@1.2.1': resolution: {integrity: sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==} @@ -17146,6 +17152,13 @@ snapshots: '@openzeppelin/contracts@5.0.2': {} + '@openzeppelin/merkle-tree@1.0.7': + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/bytes': 5.7.0 + '@ethersproject/constants': 5.7.0 + '@ethersproject/keccak256': 5.7.0 + '@panva/hkdf@1.2.1': {} '@parcel/watcher-android-arm64@2.4.1': @@ -20070,7 +20083,7 @@ snapshots: axe-core@4.10.0: {} - axios-debug-log@1.0.0(axios@1.7.3(debug@4.3.6)): + axios-debug-log@1.0.0(axios@1.7.3): dependencies: '@types/debug': 4.1.12 axios: 1.7.3(debug@4.3.6) @@ -29307,7 +29320,7 @@ snapshots: '@aduh95/viz.js': 3.7.0 '@solidity-parser/parser': 0.16.2 axios: 1.7.3(debug@4.3.6) - axios-debug-log: 1.0.0(axios@1.7.3(debug@4.3.6)) + axios-debug-log: 1.0.0(axios@1.7.3) cli-color: 2.0.4 commander: 11.1.0 convert-svg-to-png: 0.6.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)