From 2c8b44bdd9858d6135622f92f73629dbfcc92b97 Mon Sep 17 00:00:00 2001
From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com>
Date: Tue, 1 Oct 2024 13:28:17 +0200
Subject: [PATCH] feat: implement contract registries
---
.../contracts/interfaces/IRegistryManager.sol | 3 +
.../registryManager/RegistryManager.sol | 7 +-
packages/contracts/hardhat.config.ts | 2 +
packages/contracts/package.json | 8 +-
.../tests/EASRegistryManager.test.ts | 10 +-
.../contracts/tests/RegistryManager.test.ts | 10 +
packages/contracts/ts/index.ts | 2 +
packages/interface/package.json | 1 +
.../src/components/AddedProjects.tsx | 11 +-
.../interface/src/components/EmptyState.tsx | 2 +-
packages/interface/src/contexts/Ballot.tsx | 10 +-
packages/interface/src/contexts/Maci.tsx | 52 +---
packages/interface/src/contexts/types.ts | 9 +-
.../components/ApplicationForm.tsx | 9 +-
.../components/ApplicationHeader.tsx | 4 +-
.../components/ApplicationItem.tsx | 23 +-
.../components/ApplicationsToApprove.tsx | 73 ++---
.../applications/components/ReviewBar.tsx | 63 ++--
.../components/SelectAllButton.tsx | 6 +-
.../hooks/useApplicationByTxHash.ts | 8 -
.../applications/hooks/useApplicationId.ts | 8 +
.../applications/hooks/useApplications.ts | 17 +-
.../hooks/useApproveApplication.ts | 41 ++-
.../hooks/useApprovedApplications.ts | 8 -
.../hooks/useCreateApplication.ts | 89 ++++--
.../src/features/applications/types/index.ts | 1 +
.../components/AllocationFormWrapper.tsx | 2 +-
.../ballot/components/BallotConfirmation.tsx | 11 +-
.../components/ProjectAvatarWithName.tsx | 10 +-
.../ballot/components/SubmitBallotButton.tsx | 24 +-
.../src/features/ballot/types/index.ts | 1 +
.../projects/components/ProjectAvatar.tsx | 16 +-
.../projects/components/ProjectBanner.tsx | 22 +-
.../components/ProjectContributions.tsx | 8 +-
.../projects/components/ProjectDetails.tsx | 21 +-
.../projects/components/ProjectItem.tsx | 12 +-
.../features/projects/components/Projects.tsx | 25 +-
.../projects/components/ProjectsResults.tsx | 2 +-
.../projects/components/VotingWidget.tsx | 11 +-
.../features/projects/hooks/useProjects.ts | 61 ++--
.../projects/hooks/useSelectProjects.ts | 52 ----
packages/interface/src/hooks/useMetadata.ts | 5 +
packages/interface/src/hooks/useProfile.ts | 20 --
packages/interface/src/hooks/useRegistry.ts | 50 ++++
packages/interface/src/hooks/useResults.ts | 32 +--
.../src/pages/applications/confirmation.tsx | 20 +-
packages/interface/src/pages/ballot/index.tsx | 12 +-
.../pages/projects/[projectId]/Project.tsx | 14 +-
packages/interface/src/pages/stats/index.tsx | 15 +-
packages/interface/src/server/api/root.ts | 2 -
.../src/server/api/routers/applications.ts | 43 ++-
.../src/server/api/routers/profile.ts | 23 --
.../src/server/api/routers/projects.ts | 178 ++----------
.../src/server/api/routers/results.ts | 50 ++--
.../interface/src/utils/fetchApplications.ts | 271 ++++++++++++++++++
packages/interface/src/utils/fetchMetadata.ts | 6 +
packages/interface/src/utils/fetchPoll.ts | 33 ++-
packages/interface/src/utils/fetchProjects.ts | 103 +++++++
packages/interface/src/utils/registry.ts | 231 +++++++++++++++
packages/interface/src/utils/types.ts | 160 ++++++++++-
packages/subgraph/schemas/schema.v1.graphql | 3 +-
packages/subgraph/src/poll.ts | 3 +
packages/subgraph/src/registry.ts | 3 +
.../subgraph/templates/subgraph.template.yaml | 8 +-
pnpm-lock.yaml | 119 ++++----
65 files changed, 1387 insertions(+), 772 deletions(-)
delete mode 100644 packages/interface/src/features/applications/hooks/useApplicationByTxHash.ts
create mode 100644 packages/interface/src/features/applications/hooks/useApplicationId.ts
delete mode 100644 packages/interface/src/features/applications/hooks/useApprovedApplications.ts
delete mode 100644 packages/interface/src/features/projects/hooks/useSelectProjects.ts
delete mode 100644 packages/interface/src/hooks/useProfile.ts
create mode 100644 packages/interface/src/hooks/useRegistry.ts
delete mode 100644 packages/interface/src/server/api/routers/profile.ts
create mode 100644 packages/interface/src/utils/fetchApplications.ts
create mode 100644 packages/interface/src/utils/fetchProjects.ts
create mode 100644 packages/interface/src/utils/registry.ts
diff --git a/packages/contracts/contracts/interfaces/IRegistryManager.sol b/packages/contracts/contracts/interfaces/IRegistryManager.sol
index 339eb107..24754f33 100644
--- a/packages/contracts/contracts/interfaces/IRegistryManager.sol
+++ b/packages/contracts/contracts/interfaces/IRegistryManager.sol
@@ -39,6 +39,7 @@ interface IRegistryManager {
address indexed registry,
RequestType indexed requestType,
bytes32 indexed recipient,
+ uint256 recipientIndex,
uint256 index,
address payout,
string metadataUrl
@@ -47,6 +48,7 @@ interface IRegistryManager {
address indexed registry,
RequestType indexed requestType,
bytes32 indexed recipient,
+ uint256 recipientIndex,
uint256 index,
address payout,
string metadataUrl
@@ -55,6 +57,7 @@ interface IRegistryManager {
address indexed registry,
RequestType indexed requestType,
bytes32 indexed recipient,
+ uint256 recipientIndex,
uint256 index,
address payout,
string metadataUrl
diff --git a/packages/contracts/contracts/registryManager/RegistryManager.sol b/packages/contracts/contracts/registryManager/RegistryManager.sol
index 184c1328..6d7b5780 100644
--- a/packages/contracts/contracts/registryManager/RegistryManager.sol
+++ b/packages/contracts/contracts/registryManager/RegistryManager.sol
@@ -57,17 +57,20 @@ contract RegistryManager is Ownable, IRegistryManager, ICommon {
/// @inheritdoc IRegistryManager
function process(Request memory request) public virtual override isValidRequest(request) {
request.status = Status.Pending;
- requests[requestCount] = request;
+ uint256 index = requestCount;
unchecked {
requestCount++;
}
+ requests[index] = request;
+
emit RequestSent(
request.registry,
request.requestType,
request.recipient.id,
request.index,
+ index,
request.recipient.recipient,
request.recipient.metadataUrl
);
@@ -85,6 +88,7 @@ contract RegistryManager is Ownable, IRegistryManager, ICommon {
request.requestType,
request.recipient.id,
request.index,
+ index,
request.recipient.recipient,
request.recipient.metadataUrl
);
@@ -108,6 +112,7 @@ contract RegistryManager is Ownable, IRegistryManager, ICommon {
request.requestType,
request.recipient.id,
request.index,
+ index,
request.recipient.recipient,
request.recipient.metadataUrl
);
diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts
index 26bc11c6..abe7c514 100644
--- a/packages/contracts/hardhat.config.ts
+++ b/packages/contracts/hardhat.config.ts
@@ -6,6 +6,8 @@ import "hardhat-contract-sizer";
import "maci-contracts/tasks/deploy";
import "maci-contracts/tasks/runner/deployFull";
import "maci-contracts/tasks/runner/deployPoll";
+import "maci-contracts/tasks/runner/merge";
+import "maci-contracts/tasks/runner/prove";
import "maci-contracts/tasks/runner/verifyFull";
import "solidity-docgen";
diff --git a/packages/contracts/package.json b/packages/contracts/package.json
index d2594c5c..46843b40 100644
--- a/packages/contracts/package.json
+++ b/packages/contracts/package.json
@@ -44,7 +44,13 @@
"deploy:optimism-sepolia": "pnpm run deploy --network optimism_sepolia",
"deploy-poll:optimism-sepolia": "pnpm run deploy-poll --network optimism_sepolia",
"initPoll:optimism-sepolia": "pnpm run initPoll --network optimism_sepolia",
- "verify:optimism-sepolia": "pnpm run verify --network optimism_sepolia"
+ "verify:optimism-sepolia": "pnpm run verify --network optimism_sepolia",
+ "merge": "hardhat merge",
+ "merge:localhost": "pnpm run merge",
+ "merge:optimism-sepolia": "pnpm run merge --network optimism_sepolia",
+ "prove": "hardhat prove",
+ "prove:localhost": "pnpm run prove",
+ "prove:optimism-sepolia": "pnpm run prove --network optimism_sepolia"
},
"dependencies": {
"@nomicfoundation/hardhat-ethers": "^3.0.8",
diff --git a/packages/contracts/tests/EASRegistryManager.test.ts b/packages/contracts/tests/EASRegistryManager.test.ts
index 87ebedc9..234bfa03 100644
--- a/packages/contracts/tests/EASRegistryManager.test.ts
+++ b/packages/contracts/tests/EASRegistryManager.test.ts
@@ -64,23 +64,25 @@ describe("EASRegistryManager", () => {
});
it("should allow owner to approve requests to the registry", async () => {
- const addRequest = await registryManager.getRequest(0);
+ const requestIndex = 0;
+ const addRequest = await registryManager.getRequest(requestIndex);
expect(addRequest.status).to.equal(ERegistryManagerRequestStatus.Pending);
- await expect(registryManager.connect(owner).approve(0))
+ await expect(registryManager.connect(owner).approve(requestIndex))
.to.emit(registryManager, "RequestApproved")
.withArgs(
addRequest.registry,
addRequest.requestType,
addRequest.recipient.id,
addRequest.index,
+ requestIndex,
addRequest.recipient.recipient,
addRequest.recipient.metadataUrl,
);
const changeRequest = {
- index: 0,
+ index: requestIndex,
registry: await mockRegistry.getAddress(),
requestType: ERegistryManagerRequestType.Change,
status: ERegistryManagerRequestStatus.Pending,
@@ -98,6 +100,7 @@ describe("EASRegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 1,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
@@ -109,6 +112,7 @@ describe("EASRegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 1,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
diff --git a/packages/contracts/tests/RegistryManager.test.ts b/packages/contracts/tests/RegistryManager.test.ts
index 86ff4a48..5ca49dc7 100644
--- a/packages/contracts/tests/RegistryManager.test.ts
+++ b/packages/contracts/tests/RegistryManager.test.ts
@@ -130,6 +130,7 @@ describe("RegistryManager", () => {
addRequest.requestType,
addRequest.recipient.id,
addRequest.index,
+ addRequest.index,
addRequest.recipient.recipient,
addRequest.recipient.metadataUrl,
);
@@ -177,6 +178,7 @@ describe("RegistryManager", () => {
addRequest.requestType,
addRequest.recipient.id,
addRequest.index,
+ addRequest.index,
addRequest.recipient.recipient,
addRequest.recipient.metadataUrl,
);
@@ -200,6 +202,7 @@ describe("RegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 1,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
@@ -211,6 +214,7 @@ describe("RegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 1,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
@@ -274,6 +278,7 @@ describe("RegistryManager", () => {
addRequest.requestType,
addRequest.recipient.id,
addRequest.index,
+ 2,
addRequest.recipient.recipient,
addRequest.recipient.metadataUrl,
);
@@ -285,6 +290,7 @@ describe("RegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 3,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
@@ -296,6 +302,7 @@ describe("RegistryManager", () => {
addRequest.requestType,
addRequest.recipient.id,
addRequest.index,
+ 2,
addRequest.recipient.recipient,
addRequest.recipient.metadataUrl,
);
@@ -307,6 +314,7 @@ describe("RegistryManager", () => {
changeRequest.requestType,
changeRequest.recipient.id,
changeRequest.index,
+ 3,
changeRequest.recipient.recipient,
changeRequest.recipient.metadataUrl,
);
@@ -352,6 +360,7 @@ describe("RegistryManager", () => {
removeRequest.requestType,
removeRequest.recipient.id,
removeRequest.index,
+ 4,
removeRequest.recipient.recipient,
removeRequest.recipient.metadataUrl,
);
@@ -365,6 +374,7 @@ describe("RegistryManager", () => {
removeRequest.requestType,
removeRequest.recipient.id,
removeRequest.index,
+ count - 1n,
removeRequest.recipient.recipient,
removeRequest.recipient.metadataUrl,
);
diff --git a/packages/contracts/ts/index.ts b/packages/contracts/ts/index.ts
index 98027a6c..5c74968e 100644
--- a/packages/contracts/ts/index.ts
+++ b/packages/contracts/ts/index.ts
@@ -1 +1,3 @@
export { ERegistryManagerRequestType, ERegistryManagerRequestStatus } from "./constants";
+
+export * from "../typechain-types";
diff --git a/packages/interface/package.json b/packages/interface/package.json
index 8b81a2f9..617fe4ae 100644
--- a/packages/interface/package.json
+++ b/packages/interface/package.json
@@ -48,6 +48,7 @@
"lucide-react": "^0.316.0",
"maci-cli": "^2.4.0",
"maci-domainobjs": "^2.4.0",
+ "maci-platform-contracts": "workspace:*",
"next": "^14.1.0",
"next-auth": "^4.24.5",
"next-themes": "^0.2.1",
diff --git a/packages/interface/src/components/AddedProjects.tsx b/packages/interface/src/components/AddedProjects.tsx
index bb1c2ac8..79f538db 100644
--- a/packages/interface/src/components/AddedProjects.tsx
+++ b/packages/interface/src/components/AddedProjects.tsx
@@ -1,10 +1,17 @@
+import { zeroAddress } from "viem";
+import { useAccount } from "wagmi";
+
import { useBallot } from "~/contexts/Ballot";
+import { useMaci } from "~/contexts/Maci";
import { useProjectCount } from "~/features/projects/hooks/useProjects";
export const AddedProjects = (): JSX.Element => {
const { ballot } = useBallot();
+ const { pollData } = useMaci();
+ const { chain } = useAccount();
+ const { data: projectCount } = useProjectCount({ registryAddress: pollData?.registry ?? zeroAddress, chain: chain! });
+
const allocations = ballot.votes;
- const { data: projectCount } = useProjectCount();
return (
@@ -20,7 +27,7 @@ export const AddedProjects = (): JSX.Element => {
- {projectCount?.count}
+ {projectCount?.count.toString()}
diff --git a/packages/interface/src/components/EmptyState.tsx b/packages/interface/src/components/EmptyState.tsx
index 1d1d242b..c14cdc3e 100644
--- a/packages/interface/src/components/EmptyState.tsx
+++ b/packages/interface/src/components/EmptyState.tsx
@@ -3,7 +3,7 @@ import { type PropsWithChildren } from "react";
import { Heading } from "./ui/Heading";
export const EmptyState = ({ title, children = null }: PropsWithChildren<{ title: string }>): JSX.Element => (
-
+
{title}
diff --git a/packages/interface/src/contexts/Ballot.tsx b/packages/interface/src/contexts/Ballot.tsx
index 5a53aec3..f7bc04c9 100644
--- a/packages/interface/src/contexts/Ballot.tsx
+++ b/packages/interface/src/contexts/Ballot.tsx
@@ -8,6 +8,7 @@ import { useMaci } from "./Maci";
export const BallotContext = createContext
(undefined);
+// the default ballot is an empty ballot
const defaultBallot = { votes: [], published: false, edited: false };
export const BallotProvider: React.FC = ({ children }: BallotProviderProps) => {
@@ -28,7 +29,8 @@ export const BallotProvider: React.FC = ({ children }: Ball
[pollData],
);
- const ballotContains = useCallback((id: string) => ballot.votes.find((v) => v.projectId === id), [ballot]);
+ // check if the ballot contains a specific project based on its index
+ const ballotContains = useCallback((index: number) => ballot.votes.find((v) => v.projectIndex === index), [ballot]);
const toObject = useCallback(
(key: string, arr: object[] = []) => arr.reduce((acc, x) => ({ ...acc, [x[key as keyof typeof acc]]: x }), {}),
@@ -55,8 +57,8 @@ export const BallotProvider: React.FC = ({ children }: Ball
// remove certain project from the ballot
const removeFromBallot = useCallback(
- (projectId: string) => {
- const votes = ballot.votes.filter((v) => v.projectId !== projectId);
+ (projectIndex: number) => {
+ const votes = ballot.votes.filter((v) => v.projectIndex !== projectIndex);
setBallot({ ...ballot, votes });
},
@@ -86,7 +88,7 @@ export const BallotProvider: React.FC = ({ children }: Ball
setBallot({ ...ballot, published: true, edited: false });
}, [ballot, setBallot]);
- /// Read existing ballot in localStorage
+ // Read existing ballot in localStorage
useEffect(() => {
const savedBallot = JSON.parse(
localStorage.getItem("ballot") ?? JSON.stringify(defaultBallot),
diff --git a/packages/interface/src/contexts/Maci.tsx b/packages/interface/src/contexts/Maci.tsx
index 89c7ba9d..9221f262 100644
--- a/packages/interface/src/contexts/Maci.tsx
+++ b/packages/interface/src/contexts/Maci.tsx
@@ -3,15 +3,12 @@ import { StandardMerkleTree } from "@openzeppelin/merkle-tree";
import { type StandardMerkleTreeData } from "@openzeppelin/merkle-tree/dist/standard";
import { type ZKEdDSAEventTicketPCD } from "@pcd/zk-eddsa-event-ticket-pcd/ZKEdDSAEventTicketPCD";
import { Identity } from "@semaphore-protocol/core";
-import { isAfter } from "date-fns";
-import { type Signer, BrowserProvider, AbiCoder } from "ethers";
+import { type Signer, AbiCoder } from "ethers";
import {
signup,
isRegisteredUser,
publishBatch,
type TallyData,
- type IGetPollData,
- getPoll,
genKeyPair,
GatekeeperTrait,
getGatekeeperTrait,
@@ -29,7 +26,7 @@ import { getSemaphoreProof } from "~/utils/semaphore";
import type { IVoteArgs, MaciContextType, MaciProviderProps } from "./types";
import type { PCD } from "@pcd/pcd-types";
-import type { EIP1193Provider } from "viem";
+import type { IPollData } from "~/utils/fetchPoll";
import type { Attestation } from "~/utils/types";
export const MaciContext = createContext(undefined);
@@ -48,7 +45,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv
const [initialVoiceCredits, setInitialVoiceCredits] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState();
- const [pollData, setPollData] = useState();
+ const [pollData, setPollData] = useState();
const [tallyData, setTallyData] = useState();
const [treeData, setTreeData] = useState>();
@@ -251,6 +248,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv
setSemaphoreIdentity(newSemaphoreIdentity);
}, [address, signatureMessage, signMessageAsync, setMaciPrivKey, setMaciPubKey, setSemaphoreIdentity]);
+ // callback to be called from external component to store the zupass proof
const storeZupassProof = useCallback(
(proof: PCD) => {
setZupassProof(proof);
@@ -262,7 +260,7 @@ export const MaciProvider: React.FC = ({ children }: MaciProv
const votingEndsAt = useMemo(
() =>
pollData && pollData.duration !== 0
- ? new Date(Number(pollData.deployTime) * 1000 + Number(pollData.duration) * 1000)
+ ? new Date(Number(pollData.initTime) * 1000 + Number(pollData.duration) * 1000)
: config.resultsAt,
[pollData?.deployTime, pollData?.duration],
);
@@ -410,15 +408,14 @@ export const MaciProvider: React.FC = ({ children }: MaciProv
/// check the poll data and tally data
useEffect(() => {
- setIsLoading(true);
-
// 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;
}
+ setIsLoading(true);
+
const { isMerged, id } = poll.data;
setPollData(poll.data);
@@ -433,41 +430,6 @@ export const MaciProvider: React.FC = ({ children }: MaciProv
}
setIsLoading(false);
- } else {
- if (!window.ethereum) {
- setIsLoading(false);
- return;
- }
-
- const provider = new BrowserProvider(window.ethereum as unknown as EIP1193Provider, {
- chainId: config.network.id,
- name: config.network.name,
- });
-
- getPoll({
- maciAddress: config.maciAddress!,
- provider,
- })
- .then((data) => {
- setPollData(data);
- return data;
- })
- .then(async (data) => {
- if (!data.isMerged || (votingEndsAt && isAfter(votingEndsAt, new Date()))) {
- return undefined;
- }
-
- return fetch(`${config.tallyUrl}/tally-${data.id}.json`)
- .then((res) => res.json() as Promise)
- .then((res) => {
- setTallyData(res);
- })
- .catch(() => undefined);
- })
- .catch(console.error)
- .finally(() => {
- setIsLoading(false);
- });
}
}, [signer, votingEndsAt, setIsLoading, setTallyData, setPollData, poll.data]);
diff --git a/packages/interface/src/contexts/types.ts b/packages/interface/src/contexts/types.ts
index 8ceb3ad8..2c9470f6 100644
--- a/packages/interface/src/contexts/types.ts
+++ b/packages/interface/src/contexts/types.ts
@@ -1,9 +1,10 @@
import { type StandardMerkleTreeData } from "@openzeppelin/merkle-tree/dist/standard";
-import { type TallyData, type IGetPollData, type GatekeeperTrait } from "maci-cli/sdk";
+import { type TallyData, type GatekeeperTrait } from "maci-cli/sdk";
import { type ReactNode } from "react";
import type { PCD } from "@pcd/pcd-types";
import type { Ballot, Vote } from "~/features/ballot/types";
+import type { IPollData } from "~/utils/fetchPoll";
export interface IVoteArgs {
voteOptionIndex: bigint;
@@ -19,7 +20,7 @@ export interface MaciContextType {
isRegistered?: boolean;
pollId?: string;
error?: string;
- pollData?: IGetPollData;
+ pollData?: IPollData;
tallyData?: TallyData;
maciPubKey?: string;
gatekeeperTrait?: GatekeeperTrait;
@@ -41,9 +42,9 @@ export interface BallotContextType {
ballot: Ballot;
isLoading: boolean;
addToBallot: (votes: Vote[], pollId?: string) => void;
- removeFromBallot: (projectId: string) => void;
+ removeFromBallot: (projectIndex: number) => void;
deleteBallot: () => void;
- ballotContains: (id: string) => Vote | undefined;
+ ballotContains: (index: number) => Vote | undefined;
sumBallot: (votes?: Vote[]) => number;
publishBallot: () => void;
}
diff --git a/packages/interface/src/features/applications/components/ApplicationForm.tsx b/packages/interface/src/features/applications/components/ApplicationForm.tsx
index 70b6bcff..49ef343a 100644
--- a/packages/interface/src/features/applications/components/ApplicationForm.tsx
+++ b/packages/interface/src/features/applications/components/ApplicationForm.tsx
@@ -1,4 +1,3 @@
-import { Transaction } from "@ethereum-attestation-service/eas-sdk";
import { useRouter } from "next/router";
import { useState, useCallback } from "react";
import { useLocalStorage } from "react-use";
@@ -52,13 +51,13 @@ export const ApplicationForm = (): JSX.Element => {
}, [step, setStep]);
const create = useCreateApplication({
- onSuccess: (data: Transaction) => {
+ onSuccess: (id: bigint) => {
clearDraft();
- router.push(`/applications/confirmation?txHash=${data.tx.hash}`);
+ router.push(`/applications/confirmation?id=${id.toString()}`);
},
- onError: (err: { reason?: string; data?: { message: string } }) =>
+ onError: (err: { message: string }) =>
toast.error("Application create error", {
- description: err.reason ?? err.data?.message,
+ description: err.message,
}),
});
diff --git a/packages/interface/src/features/applications/components/ApplicationHeader.tsx b/packages/interface/src/features/applications/components/ApplicationHeader.tsx
index 2d341e95..e74f8003 100644
--- a/packages/interface/src/features/applications/components/ApplicationHeader.tsx
+++ b/packages/interface/src/features/applications/components/ApplicationHeader.tsx
@@ -1,9 +1,9 @@
-import type { Attestation } from "~/utils/types";
+import type { IRequest } from "~/utils/types";
import { SelectAllButton } from "./SelectAllButton";
interface IApplicationHeaderProps {
- applications?: Attestation[];
+ applications?: IRequest[];
}
export const ApplicationHeader = ({ applications = [] }: IApplicationHeaderProps): JSX.Element => (
diff --git a/packages/interface/src/features/applications/components/ApplicationItem.tsx b/packages/interface/src/features/applications/components/ApplicationItem.tsx
index d5f7dc8e..a320e926 100644
--- a/packages/interface/src/features/applications/components/ApplicationItem.tsx
+++ b/packages/interface/src/features/applications/components/ApplicationItem.tsx
@@ -10,41 +10,40 @@ import { useMetadata } from "~/hooks/useMetadata";
import { formatDate } from "~/utils/time";
import type { Application } from "~/features/applications/types";
-import type { Attestation } from "~/utils/types";
+import type { IRecipient, IRecipientContract } from "~/utils/types";
-export interface IApplicationItemProps extends Attestation {
+export interface IApplicationItemProps {
+ index: string;
+ recipient: IRecipient | IRecipientContract;
isApproved?: boolean;
isLoading?: boolean;
}
export const ApplicationItem = ({
- id,
+ index,
recipient,
- name,
- metadataPtr,
- time,
isApproved = false,
isLoading = false,
}: IApplicationItemProps): JSX.Element => {
- const metadata = useMetadata(metadataPtr);
+ const metadata = useMetadata(recipient.metadataUrl);
const form = useFormContext();
const { fundingSources = [], profileImageUrl } = metadata.data ?? {};
return (
-
+
-
+
-
+
- {name}
+ {metadata.data?.name}
@@ -57,7 +56,7 @@ export const ApplicationItem = ({
- {formatDate(time * 1000)}
+ {metadata.data?.submittedAt ? formatDate(metadata.data.submittedAt) : "N/A"}
diff --git a/packages/interface/src/features/applications/components/ApplicationsToApprove.tsx b/packages/interface/src/features/applications/components/ApplicationsToApprove.tsx
index 17d8bfab..29a2df99 100644
--- a/packages/interface/src/features/applications/components/ApplicationsToApprove.tsx
+++ b/packages/interface/src/features/applications/components/ApplicationsToApprove.tsx
@@ -1,57 +1,37 @@
import Link from "next/link";
-import { useMemo, useEffect, useState } from "react";
+import { useEffect } from "react";
import { FiAlertCircle } from "react-icons/fi";
+import { zeroAddress } from "viem";
import { EmptyState } from "~/components/EmptyState";
import { Button } from "~/components/ui/Button";
import { Form } from "~/components/ui/Form";
import { Heading } from "~/components/ui/Heading";
import { Spinner } from "~/components/ui/Spinner";
-import { useApplications } from "~/features/applications/hooks/useApplications";
-import { fetchApprovedApplications } from "~/utils/fetchAttestationsWithoutCache";
-
-import type { Attestation } from "~/utils/types";
+import { useMaci } from "~/contexts/Maci";
+import { useApprovedApplications, usePendingApplications } from "~/features/applications/hooks/useApplications";
import { useApproveApplication } from "../hooks/useApproveApplication";
-import { useApprovedApplications } from "../hooks/useApprovedApplications";
import { ApplicationsToApproveSchema } from "../types";
import { ApplicationHeader } from "./ApplicationHeader";
import { ApplicationItem } from "./ApplicationItem";
import { ApproveButton } from "./ApproveButton";
+/**
+ * Displays the applications that are pending approval.
+ */
export const ApplicationsToApprove = (): JSX.Element => {
- const applications = useApplications();
- const approved = useApprovedApplications();
- const approve = useApproveApplication();
- const [refetchedData, setRefetchedData] = useState
();
-
- const approvedById = useMemo(
- () =>
- [...(approved.data ?? []), ...(refetchedData ?? [])].reduce((map, x) => {
- map.set(x.refUID, true);
- return map;
- }, new Map()),
- [approved.data, refetchedData],
- );
+ const { pollData } = useMaci();
- const applicationsToApprove = applications.data?.filter((application) => !approvedById.get(application.id));
+ const approved = useApprovedApplications(pollData?.registry ?? zeroAddress);
+ const pending = usePendingApplications(pollData?.registry ?? zeroAddress);
+ const approve = useApproveApplication();
useEffect(() => {
- const fetchData = async () => {
- const ret = await fetchApprovedApplications();
- setRefetchedData(ret);
- };
-
- /// delay refetch data for 5 seconds
- const timeout = setTimeout(() => {
- fetchData();
- }, 5000);
-
- return () => {
- clearTimeout(timeout);
- };
- }, [approve.isPending, approve.isSuccess]);
+ approved.refetch().catch();
+ pending.refetch().catch();
+ }, [approve.isSuccess, approve.isPending, approve.isError]);
return (
@@ -61,7 +41,7 @@ export const ApplicationsToApprove = (): JSX.Element => {
- Select the applications you want to approve. You must be a configured admin to approve applications.
+ Select the applications you want to approve. You must be an admin to be able to approve applications.
@@ -70,7 +50,7 @@ export const ApplicationsToApprove = (): JSX.Element => {
Newly submitted applications can take 10 minutes to show up.
-
{`${applications.data?.length} applications found`}
+
{`${(pending.data?.length ?? 0) + (approved.data?.length ?? 0)} applications found`}
-
+
+
+ {pending.data?.map((item) => (
+
+ ))}
- {applications.data?.map((item) => (
-
+ {approved.data?.map((item) => (
+
))}
diff --git a/packages/interface/src/features/applications/components/ReviewBar.tsx b/packages/interface/src/features/applications/components/ReviewBar.tsx
index 9f3b1e6a..a09c9651 100644
--- a/packages/interface/src/features/applications/components/ReviewBar.tsx
+++ b/packages/interface/src/features/applications/components/ReviewBar.tsx
@@ -1,17 +1,18 @@
-import { useMemo, useCallback, useState, useEffect } from "react";
+import { useMemo, useCallback, useEffect } from "react";
import { FiAlertCircle } from "react-icons/fi";
+import { zeroAddress } from "viem";
import { StatusBar } from "~/components/StatusBar";
import { Button } from "~/components/ui/Button";
import { Spinner } from "~/components/ui/Spinner";
+import { useMaci } from "~/contexts/Maci";
+import { useProjectById } from "~/features/projects/hooks/useProjects";
import { useIsAdmin } from "~/hooks/useIsAdmin";
import { useIsCorrectNetwork } from "~/hooks/useIsCorrectNetwork";
-import { fetchApprovedApplications } from "~/utils/fetchAttestationsWithoutCache";
-
-import type { Attestation } from "~/utils/types";
+import { IRecipient } from "~/utils/types";
+import { useApplicationByProjectId } from "../hooks/useApplications";
import { useApproveApplication } from "../hooks/useApproveApplication";
-import { useApprovedApplications } from "../hooks/useApprovedApplications";
interface IReviewBarProps {
projectId: string;
@@ -20,44 +21,42 @@ interface IReviewBarProps {
export const ReviewBar = ({ projectId }: IReviewBarProps): JSX.Element => {
const isAdmin = useIsAdmin();
const { isCorrectNetwork, correctNetwork } = useIsCorrectNetwork();
+ const { pollData } = useMaci();
- const rawReturn = useApprovedApplications([projectId]);
- const [refetchedData, setRefetchedData] = useState
();
+ const project = useProjectById(projectId, pollData?.registry ?? zeroAddress);
+ const application = useApplicationByProjectId(projectId, pollData?.registry ?? zeroAddress);
+ const approve = useApproveApplication();
- const approved = useMemo(
- () => (rawReturn.data && rawReturn.data.length > 0) || (refetchedData && refetchedData.length > 0),
- [rawReturn.data, refetchedData],
- );
+ // determine whether the project is approved ornot
+ const isApproved = useMemo(() => {
+ if (project.data && (project.data as unknown as IRecipient).initialized) {
+ return true;
+ }
- const approve = useApproveApplication();
+ return false;
+ }, [project.data, approve.isSuccess, approve.isError]);
+ // approve the application
const onClick = useCallback(() => {
- approve.mutate([projectId]);
- }, [approve, projectId]);
+ if (!application.data) {
+ return;
+ }
+ approve.mutate([application.data.index]);
+ }, [approve, application.data]);
+ // refetch the application and project data when the approve mutation is successful or pending
useEffect(() => {
- const fetchData = async () => {
- const ret = await fetchApprovedApplications([projectId]);
- setRefetchedData(ret);
- };
-
- /// delay refetch data for 5 seconds
- const timeout = setTimeout(() => {
- fetchData();
- }, 5000);
-
- return () => {
- clearTimeout(timeout);
- };
- }, [approve.isPending, approve.isSuccess, projectId]);
+ application.refetch().catch();
+ project.refetch().catch();
+ }, [approve.isSuccess, approve.isPending]);
return (
- {approved &&
}
+ {isApproved &&
}
- {!approved && isAdmin &&
}
+ {!isApproved && isAdmin &&
}
- {!approved && !isAdmin && (
+ {!isApproved && !isAdmin && (
@@ -72,7 +71,7 @@ export const ReviewBar = ({ projectId }: IReviewBarProps): JSX.Element => {
/>
)}
- {isAdmin && !approved && (
+ {isAdmin && !isApproved && (
{approve.isPending && }
diff --git a/packages/interface/src/features/applications/components/SelectAllButton.tsx b/packages/interface/src/features/applications/components/SelectAllButton.tsx
index c7f6ebfb..789b3f6f 100644
--- a/packages/interface/src/features/applications/components/SelectAllButton.tsx
+++ b/packages/interface/src/features/applications/components/SelectAllButton.tsx
@@ -3,10 +3,10 @@ import { useFormContext } from "react-hook-form";
import { Button } from "~/components/ui/Button";
import type { TApplicationsToApprove } from "../types";
-import type { Attestation } from "~/utils/types";
+import type { IRequest } from "~/utils/types";
interface ISelectAllButtonProps {
- applications?: Attestation[];
+ applications?: IRequest[];
}
export const SelectAllButton = ({ applications = [] }: ISelectAllButtonProps): JSX.Element => {
@@ -18,7 +18,7 @@ export const SelectAllButton = ({ applications = [] }: ISelectAllButtonProps): J
disabled={!applications.length}
type="button"
onClick={() => {
- const selectAll = isAllSelected ? [] : applications.map(({ id }) => id);
+ const selectAll = isAllSelected ? [] : applications.map(({ index }) => index);
form.setValue("selected", selectAll);
}}
>
diff --git a/packages/interface/src/features/applications/hooks/useApplicationByTxHash.ts b/packages/interface/src/features/applications/hooks/useApplicationByTxHash.ts
deleted file mode 100644
index c70ae65a..00000000
--- a/packages/interface/src/features/applications/hooks/useApplicationByTxHash.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { api } from "~/utils/api";
-
-import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { Attestation } from "~/utils/types";
-
-export function useApplicationByTxHash(transactionId: string): UseTRPCQueryResult {
- return api.projects.getByTransactionId.useQuery({ transactionId });
-}
diff --git a/packages/interface/src/features/applications/hooks/useApplicationId.ts b/packages/interface/src/features/applications/hooks/useApplicationId.ts
new file mode 100644
index 00000000..2cf017c6
--- /dev/null
+++ b/packages/interface/src/features/applications/hooks/useApplicationId.ts
@@ -0,0 +1,8 @@
+import { api } from "~/utils/api";
+
+import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
+import type { IRequest } from "~/utils/types";
+
+export function useApplicationById(registryAddress: string, id: string): UseTRPCQueryResult {
+ return api.applications.getById.useQuery({ registryAddress, id });
+}
diff --git a/packages/interface/src/features/applications/hooks/useApplications.ts b/packages/interface/src/features/applications/hooks/useApplications.ts
index bd92b59d..0e118bdd 100644
--- a/packages/interface/src/features/applications/hooks/useApplications.ts
+++ b/packages/interface/src/features/applications/hooks/useApplications.ts
@@ -1,8 +1,19 @@
import { api } from "~/utils/api";
import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { Attestation } from "~/utils/types";
+import type { IRequest } from "~/utils/types";
-export function useApplications(): UseTRPCQueryResult {
- return api.applications.list.useQuery({});
+export function useApprovedApplications(registryAddress: string): UseTRPCQueryResult {
+ return api.applications.approvals.useQuery({ registryAddress });
+}
+
+export function usePendingApplications(registryAddress: string): UseTRPCQueryResult {
+ return api.applications.pending.useQuery({ registryAddress });
+}
+
+export function useApplicationByProjectId(
+ projectId: string,
+ registryAddress: string,
+): UseTRPCQueryResult {
+ return api.applications.getByProjectId.useQuery({ projectId, registryAddress });
}
diff --git a/packages/interface/src/features/applications/hooks/useApproveApplication.ts b/packages/interface/src/features/applications/hooks/useApproveApplication.ts
index e1d052f7..1cce3f96 100644
--- a/packages/interface/src/features/applications/hooks/useApproveApplication.ts
+++ b/packages/interface/src/features/applications/hooks/useApproveApplication.ts
@@ -1,38 +1,33 @@
-import { type Transaction } from "@ethereum-attestation-service/eas-sdk";
import { type UseMutationResult, useMutation } from "@tanstack/react-query";
import { toast } from "sonner";
-import { config, eas } from "~/config";
import { type TransactionError } from "~/features/voters/hooks/useApproveVoters";
-import { useAttest } from "~/hooks/useEAS";
-import { useEthersSigner } from "~/hooks/useEthersSigner";
-import { createAttestation } from "~/lib/eas/createAttestation";
+import { useSubmitApproval } from "~/hooks/useRegistry";
+/**
+ * Approve applications
+ *
+ * @param opts - Options for the mutation
+ * @returns the result of the mutation
+ */
export function useApproveApplication(opts?: {
onSuccess?: () => void;
-}): UseMutationResult, Error | TransactionError, string[]> {
- const attest = useAttest();
- const signer = useEthersSigner();
+}): UseMutationResult {
+ const approve = useSubmitApproval();
return useMutation({
mutationFn: async (applicationIds: string[]) => {
- if (!signer) {
- throw new Error("Connect wallet first");
+ const successes: boolean[] = [];
+
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
+ for (let i = 0; i < applicationIds.length; i += 1) {
+ const applicationId = applicationIds[i];
+ // eslint-disable-next-line no-await-in-loop
+ const success = await approve.mutateAsync({ refUID: applicationId! });
+ successes.push(success);
}
- const attestations = await Promise.all(
- applicationIds.map((refUID) =>
- createAttestation(
- {
- values: { type: "application", round: config.roundId },
- schemaUID: eas.schemas.approval,
- refUID,
- },
- signer,
- ),
- ),
- );
- return attest.mutateAsync(attestations.map((att) => ({ ...att, data: [att.data] })));
+ return successes;
},
onSuccess: () => {
toast.success("Application approved successfully!");
diff --git a/packages/interface/src/features/applications/hooks/useApprovedApplications.ts b/packages/interface/src/features/applications/hooks/useApprovedApplications.ts
deleted file mode 100644
index 6c3ed93b..00000000
--- a/packages/interface/src/features/applications/hooks/useApprovedApplications.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { api } from "~/utils/api";
-
-import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { Attestation } from "~/utils/types";
-
-export function useApprovedApplications(ids?: string[]): UseTRPCQueryResult {
- return api.applications.approvals.useQuery({ ids });
-}
diff --git a/packages/interface/src/features/applications/hooks/useCreateApplication.ts b/packages/interface/src/features/applications/hooks/useCreateApplication.ts
index b7cefa9c..4d223b2c 100644
--- a/packages/interface/src/features/applications/hooks/useCreateApplication.ts
+++ b/packages/interface/src/features/applications/hooks/useCreateApplication.ts
@@ -1,36 +1,49 @@
import { type UseMutationResult, useMutation } from "@tanstack/react-query";
+import { RegistryManager__factory as RegistryManagerFactory } from "maci-platform-contracts/typechain-types";
+import { createPublicClient, custom, Hex } from "viem";
+import { useAccount } from "wagmi";
-import { config, eas } from "~/config";
+import { useMaci } from "~/contexts/Maci";
import { type TransactionError } from "~/features/voters/hooks/useApproveVoters";
-import { useAttest, useCreateAttestation } from "~/hooks/useEAS";
+import { useEthersSigner } from "~/hooks/useEthersSigner";
import { useUploadMetadata } from "~/hooks/useMetadata";
+import { useSubmitApplication } from "~/hooks/useRegistry";
+import { getRegistryManagerContract } from "~/utils/registry";
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;
- isAttesting: boolean;
isUploading: boolean;
};
export function useCreateApplication(options: {
- onSuccess: (data: Transaction) => void;
- onError: (err: TransactionError) => void;
+ onSuccess: (id: bigint) => void;
+ onError: (err: Error) => void;
}): TUseCreateApplicationReturn {
- const attestation = useCreateAttestation();
- const attest = useAttest();
const upload = useUploadMetadata();
+ const { chain } = useAccount();
+ const { pollData } = useMaci();
+ const submitApplication = useSubmitApplication();
+ const signer = useEthersSigner();
const mutation = useMutation({
mutationFn: async (values: Application) => {
+ if (!signer || !chain) {
+ throw new Error("Please connect your wallet first");
+ }
+
if (!values.bannerImageUrl || !values.profileImageUrl) {
throw new Error("No images included.");
}
+ if (!pollData?.registry) {
+ throw new Error("No registry found");
+ }
+
const [profileImageFile, bannerImageFile] = await Promise.all([
fetch(values.profileImageUrl),
fetch(values.bannerImageUrl),
@@ -41,22 +54,43 @@ export function useCreateApplication(options: {
upload.mutateAsync(new File([bannerImageFile], "bannerImage")),
]);
- const metadataValues = { ...values, profileImageUrl: profileImageUrl.url, bannerImageUrl: bannerImageUrl.url };
-
- return Promise.all([
- upload.mutateAsync(metadataValues).then(({ url: metadataPtr }) =>
- attestation.mutateAsync({
- schemaUID: eas.schemas.metadata,
- values: {
- name: values.name,
- metadataType: 0, // "http"
- metadataPtr,
- type: "application",
- round: config.roundId,
- },
- }),
- ),
- ]).then((attestations) => attest.mutateAsync(attestations.map((att) => ({ ...att, data: [att.data] }))));
+ const metadataValues = {
+ ...values,
+ profileImageUrl: profileImageUrl.url,
+ bannerImageUrl: bannerImageUrl.url,
+ submittedAt: Date.now().valueOf(),
+ };
+
+ const uploadRes = await upload.mutateAsync(metadataValues);
+
+ const recipient = values.payoutAddress as Hex;
+
+ // now we submit the approval request
+ const res = await submitApplication.mutateAsync({
+ metadataUrl: uploadRes.url,
+ recipient,
+ registryAddress: pollData.registry,
+ });
+
+ if (res.status !== "success") {
+ throw new Error("Failed to submit application");
+ }
+
+ // get the last application id
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+
+ const registryManagerAddress = await getRegistryManagerContract(chain);
+
+ const requestCount = (await publicClient.readContract({
+ address: registryManagerAddress,
+ abi: RegistryManagerFactory.abi,
+ functionName: "requestCount",
+ })) as bigint;
+
+ return requestCount - 1n;
},
...options,
@@ -64,8 +98,9 @@ export function useCreateApplication(options: {
return {
...mutation,
- error: attest.error ?? upload.error ?? mutation.error,
- isAttesting: attest.isPending,
+ error: submitApplication.error ?? upload.error ?? mutation.error,
isUploading: upload.isPending,
+ isPending: submitApplication.isPending || upload.isPending,
+ isSuccess: submitApplication.isSuccess && upload.isSuccess,
};
}
diff --git a/packages/interface/src/features/applications/types/index.ts b/packages/interface/src/features/applications/types/index.ts
index a348e6d8..83c10c14 100644
--- a/packages/interface/src/features/applications/types/index.ts
+++ b/packages/interface/src/features/applications/types/index.ts
@@ -35,6 +35,7 @@ export const ApplicationSchema = z.object({
/^(http:\/\/|https:\/\/)/i.test(url) ? url : `https://${url}`,
),
payoutAddress: EthAddressSchema,
+ submittedAt: z.number().optional(),
github: z.string().optional(),
twitter: z.string().optional(),
contributionDescription: z.string().min(3),
diff --git a/packages/interface/src/features/ballot/components/AllocationFormWrapper.tsx b/packages/interface/src/features/ballot/components/AllocationFormWrapper.tsx
index cdac7d42..03498db0 100644
--- a/packages/interface/src/features/ballot/components/AllocationFormWrapper.tsx
+++ b/packages/interface/src/features/ballot/components/AllocationFormWrapper.tsx
@@ -65,7 +65,7 @@ export const AllocationFormWrapper = ({
variant="none"
onClick={() => {
remove(i);
- onRemove(project.projectId);
+ onRemove(project.projectIndex);
}}
/>
diff --git a/packages/interface/src/features/ballot/components/BallotConfirmation.tsx b/packages/interface/src/features/ballot/components/BallotConfirmation.tsx
index 6620d8e6..17f06fe4 100644
--- a/packages/interface/src/features/ballot/components/BallotConfirmation.tsx
+++ b/packages/interface/src/features/ballot/components/BallotConfirmation.tsx
@@ -2,6 +2,8 @@ import { format } from "date-fns";
import Link from "next/link";
import { useMemo } from "react";
import { tv } from "tailwind-variants";
+import { zeroAddress } from "viem";
+import { useAccount } from "wagmi";
import { createComponent } from "~/components/ui";
import { Button } from "~/components/ui/Button";
@@ -9,6 +11,7 @@ import { Heading } from "~/components/ui/Heading";
import { Notice } from "~/components/ui/Notice";
import { config } from "~/config";
import { useBallot } from "~/contexts/Ballot";
+import { useMaci } from "~/contexts/Maci";
import { useProjectCount } from "~/features/projects/hooks/useProjects";
import { formatNumber } from "~/utils/formatNumber";
import { useAppState } from "~/utils/state";
@@ -16,7 +19,7 @@ import { EAppState } from "~/utils/types";
import { ProjectAvatarWithName } from "./ProjectAvatarWithName";
-const feedbackUrl = process.env.NEXT_PUBLIC_FEEDBACK_URL;
+const feedbackUrl = process.env.NEXT_PUBLIC_FEEDBACK_URL!;
const Card = createComponent(
"div",
@@ -28,7 +31,11 @@ const Card = createComponent(
export const BallotConfirmation = (): JSX.Element => {
const { ballot, sumBallot } = useBallot();
const allocations = ballot.votes;
- const { data: projectCount } = useProjectCount();
+
+ const { pollData } = useMaci();
+ const { chain } = useAccount();
+ const { data: projectCount } = useProjectCount({ registryAddress: pollData?.registry ?? zeroAddress, chain: chain! });
+
const appState = useAppState();
const sum = useMemo(() => formatNumber(sumBallot(ballot.votes)), [ballot, sumBallot]);
diff --git a/packages/interface/src/features/ballot/components/ProjectAvatarWithName.tsx b/packages/interface/src/features/ballot/components/ProjectAvatarWithName.tsx
index cd41c4fd..6a848c78 100644
--- a/packages/interface/src/features/ballot/components/ProjectAvatarWithName.tsx
+++ b/packages/interface/src/features/ballot/components/ProjectAvatarWithName.tsx
@@ -1,5 +1,7 @@
import Link from "next/link";
+import { zeroAddress } from "viem";
+import { useMaci } from "~/contexts/Maci";
import { ProjectAvatar } from "~/features/projects/components/ProjectAvatar";
import { useProjectById, useProjectMetadata } from "~/features/projects/hooks/useProjects";
@@ -16,8 +18,10 @@ export const ProjectAvatarWithName = ({
showDescription = false,
allocation = 0,
}: ProjectAvatarWithNameProps): JSX.Element => {
- const { data: projects } = useProjectById(id);
- const metadata = useProjectMetadata(projects?.[0]?.metadataPtr);
+ const { pollData } = useMaci();
+
+ const { data: projects } = useProjectById(id, pollData?.registry ?? zeroAddress);
+ const metadata = useProjectMetadata(projects?.metadataUrl);
const Component = isLink ? Link : "div";
@@ -26,7 +30,7 @@ export const ProjectAvatarWithName = ({
-
{projects?.[0]?.name}
+
{metadata.data?.name}
{showDescription && (metadata.data?.bio ?? null)}
diff --git a/packages/interface/src/features/ballot/components/SubmitBallotButton.tsx b/packages/interface/src/features/ballot/components/SubmitBallotButton.tsx
index 3961bc2d..66d393d8 100644
--- a/packages/interface/src/features/ballot/components/SubmitBallotButton.tsx
+++ b/packages/interface/src/features/ballot/components/SubmitBallotButton.tsx
@@ -6,14 +6,12 @@ import { Button } from "~/components/ui/Button";
import { Dialog } from "~/components/ui/Dialog";
import { useBallot } from "~/contexts/Ballot";
import { useMaci } from "~/contexts/Maci";
-import { useProjectIdMapping } from "~/features/projects/hooks/useProjects";
export const SubmitBallotButton = (): JSX.Element => {
const router = useRouter();
const [isOpen, setOpen] = useState(false);
const { onVote, isLoading, initialVoiceCredits } = useMaci();
const { ballot, publishBallot, sumBallot } = useBallot();
- const projectIndices = useProjectIdMapping(ballot);
const ableToSubmit = useMemo(
() => sumBallot(ballot.votes) <= initialVoiceCredits,
@@ -25,30 +23,24 @@ export const SubmitBallotButton = (): JSX.Element => {
}, []);
const handleSubmitBallot = useCallback(async () => {
- const votes = ballot.votes.map(({ amount, projectId }) => {
- const index = projectIndices[projectId];
- if (index === undefined || index === -1) {
- throw new Error("There are some problems due to project index mapping.");
- }
-
- return {
- voteOptionIndex: BigInt(index),
- newVoteWeight: BigInt(amount),
- };
- });
+ const votes = ballot.votes.map(({ amount, projectId, projectIndex }) => ({
+ projectId,
+ voteOptionIndex: BigInt(projectIndex),
+ newVoteWeight: BigInt(amount),
+ }));
await onVote(votes, onVotingError, () => {
publishBallot();
router.push("/ballot/confirmation");
});
- }, [ballot, router, onVote, publishBallot, onVotingError, projectIndices]);
+ }, [ballot, router, onVote, publishBallot, onVotingError]);
const handleOpenDialog = useCallback(() => {
setOpen(true);
}, [setOpen]);
return (
- <>
+
{ableToSubmit ? "submit your ballot" : "Exceed initial voice credits"}
@@ -73,6 +65,6 @@ export const SubmitBallotButton = (): JSX.Element => {
title="exceed initial voice credits"
onOpenChange={setOpen}
/>
- >
+
);
};
diff --git a/packages/interface/src/features/ballot/types/index.ts b/packages/interface/src/features/ballot/types/index.ts
index 0d664c91..1c91bc2f 100644
--- a/packages/interface/src/features/ballot/types/index.ts
+++ b/packages/interface/src/features/ballot/types/index.ts
@@ -2,6 +2,7 @@ import { z } from "zod";
export const VoteSchema = z.object({
projectId: z.string(),
+ projectIndex: z.number().min(0),
amount: z.number().min(0),
});
diff --git a/packages/interface/src/features/projects/components/ProjectAvatar.tsx b/packages/interface/src/features/projects/components/ProjectAvatar.tsx
index 22bba644..bbcb595d 100644
--- a/packages/interface/src/features/projects/components/ProjectAvatar.tsx
+++ b/packages/interface/src/features/projects/components/ProjectAvatar.tsx
@@ -1,21 +1,11 @@
import { type ComponentProps } from "react";
-import { type Address } from "viem";
import { Avatar } from "~/components/ui/Avatar";
-import { useProfileWithMetadata } from "~/hooks/useProfile";
export interface IProjectAvatarProps extends ComponentProps
{
- profileId?: Address;
url?: string;
}
-export const ProjectAvatar = ({
- profileId = undefined,
- url = undefined,
- ...rest
-}: IProjectAvatarProps): JSX.Element => {
- const profile = useProfileWithMetadata(profileId);
- const { profileImageUrl } = profile.data ?? {};
-
- return ;
-};
+export const ProjectAvatar = ({ url = undefined, ...rest }: IProjectAvatarProps): JSX.Element => (
+
+);
diff --git a/packages/interface/src/features/projects/components/ProjectBanner.tsx b/packages/interface/src/features/projects/components/ProjectBanner.tsx
index c70292cf..caa6d3a5 100644
--- a/packages/interface/src/features/projects/components/ProjectBanner.tsx
+++ b/packages/interface/src/features/projects/components/ProjectBanner.tsx
@@ -1,25 +1,13 @@
import { type ComponentProps } from "react";
-import { type Address } from "viem";
import { Banner } from "~/components/ui/Banner";
-import { useProfileWithMetadata } from "~/hooks/useProfile";
export interface IProjectBannerProps extends ComponentProps {
- profileId?: Address;
url?: string;
}
-export const ProjectBanner = ({
- profileId = undefined,
- url = undefined,
- ...rest
-}: IProjectBannerProps): JSX.Element => {
- const profile = useProfileWithMetadata(profileId);
- const { profileImageUrl, bannerImageUrl } = profile.data ?? {};
-
- return (
-
-
-
- );
-};
+export const ProjectBanner = ({ url = undefined, ...rest }: IProjectBannerProps): JSX.Element => (
+
+
+
+);
diff --git a/packages/interface/src/features/projects/components/ProjectContributions.tsx b/packages/interface/src/features/projects/components/ProjectContributions.tsx
index 7e8b1a62..70dab902 100644
--- a/packages/interface/src/features/projects/components/ProjectContributions.tsx
+++ b/packages/interface/src/features/projects/components/ProjectContributions.tsx
@@ -13,7 +13,7 @@ interface IProjectContributionsProps {
}
const ProjectContributions = ({ isLoading, project = undefined }: IProjectContributionsProps): JSX.Element => (
- <>
+
Contributions
@@ -34,7 +34,7 @@ const ProjectContributions = ({ isLoading, project = undefined }: IProjectContri
OTHER: Globe,
}[link.type];
return (
- <>
+
{createElement(icon ?? "div", {
className: "w-4 h-4 mt-1",
})}
@@ -42,13 +42,13 @@ const ProjectContributions = ({ isLoading, project = undefined }: IProjectContri
{link.description}
- >
+
);
}}
/>
- >
+
);
export default ProjectContributions;
diff --git a/packages/interface/src/features/projects/components/ProjectDetails.tsx b/packages/interface/src/features/projects/components/ProjectDetails.tsx
index f640620e..ad69fba9 100644
--- a/packages/interface/src/features/projects/components/ProjectDetails.tsx
+++ b/packages/interface/src/features/projects/components/ProjectDetails.tsx
@@ -8,7 +8,7 @@ import { VotingWidget } from "~/features/projects/components/VotingWidget";
import { useAppState } from "~/utils/state";
import { EAppState } from "~/utils/types";
-import type { Attestation } from "~/utils/types";
+import type { IRecipient } from "~/utils/types";
import { useProjectMetadata } from "../hooks/useProjects";
@@ -17,16 +17,11 @@ import { ProjectDescriptionSection } from "./ProjectDescriptionSection";
export interface IProjectDetailsProps {
action?: ReactNode;
- projectId?: string;
- attestation?: Attestation;
+ project: IRecipient;
}
-const ProjectDetails = ({
- projectId = "",
- attestation = undefined,
- action = undefined,
-}: IProjectDetailsProps): JSX.Element => {
- const metadata = useProjectMetadata(attestation?.metadataPtr);
+const ProjectDetails = ({ project, action = undefined }: IProjectDetailsProps): JSX.Element => {
+ const metadata = useProjectMetadata(project.metadataUrl);
const { bio, websiteUrl, payoutAddress, github, twitter, fundingSources, profileImageUrl, bannerImageUrl } =
metadata.data ?? {};
@@ -36,7 +31,7 @@ const ProjectDetails = ({
return (
-
+
@@ -49,10 +44,12 @@ const ProjectDetails = ({
- {attestation?.name}
+ {metadata.data?.name}
- {appState === EAppState.VOTING && }
+ {appState === EAppState.VOTING && (
+
+ )}
diff --git a/packages/interface/src/features/projects/components/ProjectItem.tsx b/packages/interface/src/features/projects/components/ProjectItem.tsx
index 9af1dd0f..521e8d3c 100644
--- a/packages/interface/src/features/projects/components/ProjectItem.tsx
+++ b/packages/interface/src/features/projects/components/ProjectItem.tsx
@@ -8,7 +8,7 @@ import { formatNumber } from "~/utils/formatNumber";
import { useAppState } from "~/utils/state";
import { EAppState } from "~/utils/types";
-import type { Attestation } from "~/utils/types";
+import type { IRecipient } from "~/utils/types";
import { useProjectMetadata } from "../hooks/useProjects";
import { EProjectState } from "../types";
@@ -18,25 +18,25 @@ import { ProjectAvatar } from "./ProjectAvatar";
import { ProjectBanner } from "./ProjectBanner";
export interface IProjectItemProps {
- attestation: Attestation;
+ recipient: IRecipient;
isLoading: boolean;
state?: EProjectState;
action?: (e: Event) => void;
}
export const ProjectItem = ({
- attestation,
+ recipient,
isLoading,
state = undefined,
action = undefined,
}: IProjectItemProps): JSX.Element => {
- const metadata = useProjectMetadata(attestation.metadataPtr);
+ const metadata = useProjectMetadata(recipient.metadataUrl);
const appState = useAppState();
return (
@@ -46,7 +46,7 @@ export const ProjectItem = ({
- {attestation.name}
+ {metadata.data?.name}
diff --git a/packages/interface/src/features/projects/components/Projects.tsx b/packages/interface/src/features/projects/components/Projects.tsx
index d8af65d8..6a26b618 100644
--- a/packages/interface/src/features/projects/components/Projects.tsx
+++ b/packages/interface/src/features/projects/components/Projects.tsx
@@ -2,6 +2,7 @@ import clsx from "clsx";
import Link from "next/link";
import { useCallback } from "react";
import { FiAlertCircle } from "react-icons/fi";
+import { zeroAddress } from "viem";
import { InfiniteLoading } from "~/components/InfiniteLoading";
import { SortFilter } from "~/components/SortFilter";
@@ -20,24 +21,26 @@ import { ProjectItem, ProjectItemAwarded } from "./ProjectItem";
export const Projects = (): JSX.Element => {
const appState = useAppState();
- const projects = useSearchProjects({ needApproval: appState !== EAppState.APPLICATION });
const { pollData, pollId, isRegistered } = useMaci();
+ const projects = useSearchProjects({ search: "", registryAddress: pollData?.registry ?? zeroAddress });
+
const { addToBallot, removeFromBallot, ballotContains, ballot } = useBallot();
const results = useResults(pollData);
const handleAction = useCallback(
- (projectId: string) => (e: Event) => {
+ (projectIndex: number, projectId: string) => (e: Event) => {
e.preventDefault();
if (!pollId) {
return;
}
- if (!ballotContains(projectId)) {
+ if (!ballotContains(projectIndex)) {
addToBallot(
[
{
+ projectIndex,
projectId,
amount: 0,
},
@@ -45,20 +48,20 @@ export const Projects = (): JSX.Element => {
pollId,
);
} else {
- removeFromBallot(projectId);
+ removeFromBallot(projectIndex);
}
},
[ballotContains, addToBallot, removeFromBallot, pollId],
);
- const defineState = (projectId: string): EProjectState => {
+ const defineState = (projectIndex: number): EProjectState => {
if (!isRegistered) {
return EProjectState.UNREGISTERED;
}
- if (ballotContains(projectId) && ballot.published && !ballot.edited) {
+ if (ballotContains(projectIndex) && ballot.published && !ballot.edited) {
return EProjectState.SUBMITTED;
}
- if (ballotContains(projectId)) {
+ if (ballotContains(projectIndex)) {
return EProjectState.ADDED;
}
return EProjectState.DEFAULT;
@@ -113,10 +116,10 @@ export const Projects = (): JSX.Element => {
) : null}
)}
diff --git a/packages/interface/src/features/projects/components/ProjectsResults.tsx b/packages/interface/src/features/projects/components/ProjectsResults.tsx
index 6e6ba5bc..6f8a1322 100644
--- a/packages/interface/src/features/projects/components/ProjectsResults.tsx
+++ b/packages/interface/src/features/projects/components/ProjectsResults.tsx
@@ -39,8 +39,8 @@ export const ProjectsResults = (): JSX.Element => {
diff --git a/packages/interface/src/features/projects/components/VotingWidget.tsx b/packages/interface/src/features/projects/components/VotingWidget.tsx
index fab111d4..84ff5687 100644
--- a/packages/interface/src/features/projects/components/VotingWidget.tsx
+++ b/packages/interface/src/features/projects/components/VotingWidget.tsx
@@ -11,12 +11,13 @@ import { EButtonState } from "../types";
interface IVotingWidgetProps {
projectId: string;
+ projectIndex: number;
}
-export const VotingWidget = ({ projectId }: IVotingWidgetProps): JSX.Element => {
+export const VotingWidget = ({ projectIndex, projectId }: IVotingWidgetProps): JSX.Element => {
const { pollId, initialVoiceCredits } = useMaci();
const { ballotContains, removeFromBallot, addToBallot } = useBallot();
- const projectBallot = useMemo(() => ballotContains(projectId), [ballotContains, projectId]);
+ const projectBallot = useMemo(() => ballotContains(projectIndex), [ballotContains, projectId]);
const projectIncluded = useMemo(() => !!projectBallot, [projectBallot]);
const [amount, setAmount] = useState
(projectBallot?.amount);
@@ -32,10 +33,10 @@ export const VotingWidget = ({ projectId }: IVotingWidgetProps): JSX.Element =>
);
const handleRemove = useCallback(() => {
- removeFromBallot(projectId);
+ removeFromBallot(projectIndex);
setAmount(undefined);
setButtonState(0);
- }, [projectId, removeFromBallot]);
+ }, [projectIndex, removeFromBallot]);
const handleInput = (e: ChangeEvent) => {
setAmount(e.target.value as unknown as number);
@@ -50,7 +51,7 @@ export const VotingWidget = ({ projectId }: IVotingWidgetProps): JSX.Element =>
return;
}
- addToBallot([{ projectId, amount }], pollId);
+ addToBallot([{ projectId, projectIndex, amount }], pollId);
if (buttonState === EButtonState.DEFAULT) {
setButtonState(EButtonState.ADDED);
} else {
diff --git a/packages/interface/src/features/projects/hooks/useProjects.ts b/packages/interface/src/features/projects/hooks/useProjects.ts
index 8dc1fdee..112823c6 100644
--- a/packages/interface/src/features/projects/hooks/useProjects.ts
+++ b/packages/interface/src/features/projects/hooks/useProjects.ts
@@ -1,64 +1,45 @@
-import { useMemo } from "react";
+import { Chain, Hex } from "viem";
import { type Application } from "~/features/applications/types";
-import { useFilter } from "~/features/filter/hooks/useFilter";
-import { type Filter } from "~/features/filter/types";
import { useMetadata } from "~/hooks/useMetadata";
import { api } from "~/utils/api";
import type { UseTRPCInfiniteQueryResult, UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { Ballot } from "~/features/ballot/types";
-import type { Attestation } from "~/utils/types";
+import type { IRecipient } from "~/utils/types";
interface IUseSearchProjectsProps {
- filterOverride?: Partial;
- needApproval?: boolean;
+ search: string;
+ registryAddress: string;
}
-export function useProjectById(id: string): UseTRPCQueryResult {
- return api.projects.get.useQuery({ ids: [id] }, { enabled: Boolean(id) });
+export function useProjectById(id: string, registryAddress: string): UseTRPCQueryResult {
+ return api.projects.get.useQuery({ ids: [id], registryAddress }, { enabled: Boolean(id) });
}
-export function useProjectsById(ids: string[]): UseTRPCQueryResult {
- return api.projects.get.useQuery({ ids }, { enabled: Boolean(ids.length) });
+export function useProjectsById(ids: string[], registryAddress: string): UseTRPCQueryResult {
+ return api.projects.get.useQuery({ ids, registryAddress }, { enabled: Boolean(ids.length) });
}
-const seed = 0;
export function useSearchProjects({
- filterOverride = {},
- needApproval = true,
-}: IUseSearchProjectsProps): UseTRPCInfiniteQueryResult {
- const { ...filter } = useFilter();
-
+ search,
+ registryAddress,
+}: IUseSearchProjectsProps): UseTRPCInfiniteQueryResult {
return api.projects.search.useInfiniteQuery(
- { seed, ...filter, ...filterOverride, needApproval },
- {
- getNextPageParam: (_, pages) => pages.length,
- },
+ { search, registryAddress },
+ { getNextPageParam: (_, pages) => pages.length },
);
}
-export function useProjectIdMapping(ballot: Ballot): Record {
- const { data } = api.projects.allApproved.useQuery();
-
- const projectIndices = useMemo(
- () =>
- ballot.votes.reduce>((acc, { projectId }) => {
- const index = data?.findIndex((attestation) => attestation.id.toLowerCase() === projectId.toLowerCase());
- acc[projectId] = index ?? -1;
-
- return acc;
- }, {}),
- [data, ballot],
- );
-
- return projectIndices;
-}
-
export function useProjectMetadata(metadataPtr?: string): UseTRPCQueryResult {
return useMetadata(metadataPtr);
}
-export function useProjectCount(): UseTRPCQueryResult<{ count: number }, unknown> {
- return api.projects.count.useQuery();
+export function useProjectCount({
+ chain,
+ registryAddress,
+}: {
+ chain: Chain;
+ registryAddress: Hex;
+}): UseTRPCQueryResult<{ count: number }, unknown> {
+ return api.projects.count.useQuery({ chain, registryAddress });
}
diff --git a/packages/interface/src/features/projects/hooks/useSelectProjects.ts b/packages/interface/src/features/projects/hooks/useSelectProjects.ts
deleted file mode 100644
index b20d3f8e..00000000
--- a/packages/interface/src/features/projects/hooks/useSelectProjects.ts
+++ /dev/null
@@ -1,52 +0,0 @@
-import { useMemo, useState } from "react";
-
-import { useBallot } from "~/contexts/Ballot";
-import { useMaci } from "~/contexts/Maci";
-
-export interface IUseSelectProjectsReturn {
- count: number;
- add: () => void;
- reset: () => void;
- toggle: (id: string) => void;
- getState: (id: string) => 0 | 1 | 2;
-}
-
-export function useSelectProjects(): IUseSelectProjectsReturn {
- const { addToBallot, ballotContains } = useBallot();
- const { pollId } = useMaci();
-
- const [selected, setSelected] = useState>({});
-
- const toAdd = useMemo(
- () =>
- Object.keys(selected)
- .filter((id) => selected[id])
- .map((projectId) => ({ projectId, amount: 0 })),
- [selected],
- );
-
- return {
- count: toAdd.length,
- add: () => {
- addToBallot(toAdd, pollId);
- setSelected({});
- },
- reset: () => {
- setSelected({});
- },
- toggle: (id: string) => {
- if (!id) {
- return;
- }
-
- setSelected((s) => ({ ...s, [id]: !selected[id] }));
- },
- getState: (id: string) => {
- if (ballotContains(id)) {
- return 2;
- }
-
- return selected[id] ? 1 : 0;
- },
- };
-}
diff --git a/packages/interface/src/hooks/useMetadata.ts b/packages/interface/src/hooks/useMetadata.ts
index 0c122010..ff23c001 100644
--- a/packages/interface/src/hooks/useMetadata.ts
+++ b/packages/interface/src/hooks/useMetadata.ts
@@ -8,6 +8,11 @@ export function useMetadata(metadataPtr?: string): UseTRPCQueryResult | File> {
return useMutation({
mutationFn: async (data: Record | File) => {
diff --git a/packages/interface/src/hooks/useProfile.ts b/packages/interface/src/hooks/useProfile.ts
deleted file mode 100644
index a5072186..00000000
--- a/packages/interface/src/hooks/useProfile.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { type Address } from "viem";
-
-import { api } from "~/utils/api";
-
-import type { UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { Attestation } from "~/utils/types";
-
-import { useMetadata } from "./useMetadata";
-
-export function useProfile(id?: Address): UseTRPCQueryResult {
- return api.profile.get.useQuery({ id: String(id) }, { enabled: Boolean(id) });
-}
-
-export function useProfileWithMetadata(
- id?: Address,
-): UseTRPCQueryResult<{ profileImageUrl: string; bannerImageUrl: string }, unknown> {
- const profile = useProfile(id);
-
- return useMetadata<{ profileImageUrl: string; bannerImageUrl: string }>(profile.data?.metadataPtr);
-}
diff --git a/packages/interface/src/hooks/useRegistry.ts b/packages/interface/src/hooks/useRegistry.ts
new file mode 100644
index 00000000..45e72f56
--- /dev/null
+++ b/packages/interface/src/hooks/useRegistry.ts
@@ -0,0 +1,50 @@
+import { DefaultError, useMutation, UseMutationResult } from "@tanstack/react-query";
+import { useAccount } from "wagmi";
+
+import { approveApplication, submitApplication } from "~/utils/registry";
+
+import type { Hex, TransactionReceipt } from "viem";
+
+/**
+ * Submit an application for approval
+ *
+ * @returns whether the submission was successful
+ */
+export function useSubmitApplication(): UseMutationResult<
+ TransactionReceipt,
+ DefaultError,
+ { refUID?: string | undefined; metadataUrl: string; registryAddress: Hex; recipient: `0x${string}` }
+> {
+ const { chain } = useAccount();
+
+ return useMutation({
+ mutationFn: async ({
+ refUID,
+ metadataUrl,
+ registryAddress,
+ recipient,
+ }: {
+ refUID?: string;
+ metadataUrl: string;
+ registryAddress: Hex;
+ recipient: Hex;
+ }) => submitApplication(chain!, metadataUrl, registryAddress, recipient, refUID),
+ });
+}
+
+/**
+ * Approve an application for a given refUID
+ *
+ * @returns whether the approval was successful
+ */
+export function useSubmitApproval(): UseMutationResult {
+ const { chain } = useAccount();
+
+ if (!chain) {
+ throw new Error("Connect wallet first");
+ }
+
+ return useMutation({
+ mutationFn: async ({ refUID }: { refUID: string }) => approveApplication(chain, refUID),
+ });
+}
diff --git a/packages/interface/src/hooks/useResults.ts b/packages/interface/src/hooks/useResults.ts
index a807cd25..fbfbd963 100644
--- a/packages/interface/src/hooks/useResults.ts
+++ b/packages/interface/src/hooks/useResults.ts
@@ -1,44 +1,38 @@
-import { config } from "~/config";
+import { zeroAddress } from "viem";
+
import { api } from "~/utils/api";
import { useAppState } from "~/utils/state";
import { EAppState } from "~/utils/types";
import type { UseTRPCInfiniteQueryResult, UseTRPCQueryResult } from "@trpc/react-query/shared";
-import type { IGetPollData } from "maci-cli/sdk";
-import type { Attestation } from "~/utils/types";
+import type { IPollData } from "~/utils/fetchPoll";
+import type { IRecipient } from "~/utils/types";
export function useResults(
- pollData?: IGetPollData,
+ pollData?: IPollData,
): UseTRPCQueryResult<{ averageVotes: number; projects: Record }, unknown> {
const appState = useAppState();
- return api.results.votes.useQuery({ pollId: pollData?.id.toString() }, { enabled: appState === EAppState.RESULTS });
+ return api.results.votes.useQuery(
+ { pollId: pollData?.id.toString(), registryAddress: pollData?.registry ?? zeroAddress },
+ { enabled: appState === EAppState.RESULTS },
+ );
}
-const seed = 0;
-export function useProjectsResults(
- pollData?: IGetPollData,
-): UseTRPCInfiniteQueryResult {
+export function useProjectsResults(pollData?: IPollData): UseTRPCInfiniteQueryResult {
return api.results.projects.useInfiniteQuery(
- { limit: config.pageSize, seed, pollId: pollData?.id.toString() },
+ { registryAddress: pollData?.registry ?? zeroAddress },
{
getNextPageParam: (_, pages) => pages.length,
},
);
}
-export function useProjectCount(): UseTRPCQueryResult<{ count: number }, unknown> {
- return api.projects.count.useQuery();
-}
-
-export function useProjectResults(
- id: string,
- pollData?: IGetPollData,
-): UseTRPCQueryResult<{ amount: number }, unknown> {
+export function useProjectResults(id: string, pollData?: IPollData): UseTRPCQueryResult<{ amount: number }, unknown> {
const appState = useAppState();
return api.results.project.useQuery(
- { id, pollId: pollData?.id.toString() },
+ { id, pollId: pollData?.id.toString(), registryAddress: pollData?.registry ?? zeroAddress },
{ enabled: appState === EAppState.RESULTS },
);
}
diff --git a/packages/interface/src/pages/applications/confirmation.tsx b/packages/interface/src/pages/applications/confirmation.tsx
index 7c9817fc..a04259b4 100644
--- a/packages/interface/src/pages/applications/confirmation.tsx
+++ b/packages/interface/src/pages/applications/confirmation.tsx
@@ -2,23 +2,27 @@ import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { useMemo } from "react";
import { FiAlertCircle } from "react-icons/fi";
+import { zeroAddress } from "viem";
import { Alert } from "~/components/ui/Alert";
import { Heading } from "~/components/ui/Heading";
-import { useApplicationByTxHash } from "~/features/applications/hooks/useApplicationByTxHash";
+import { useMaci } from "~/contexts/Maci";
+import { useApplicationById } from "~/features/applications/hooks/useApplicationId";
import { ProjectItem } from "~/features/projects/components/ProjectItem";
import { Layout } from "~/layouts/DefaultLayout";
import { useAppState } from "~/utils/state";
-import { EAppState } from "~/utils/types";
+import { EAppState, IRecipient } from "~/utils/types";
const ConfirmProjectPage = (): JSX.Element => {
const state = useAppState();
+ const { pollData } = useMaci();
+
const searchParams = useSearchParams();
- const txHash = useMemo(() => searchParams.get("txHash"), [searchParams]);
- const project = useApplicationByTxHash(txHash ?? "");
+ const applicationId = useMemo(() => searchParams.get("id"), [searchParams]);
+ const application = useApplicationById(pollData?.registry ?? zeroAddress, applicationId ?? "");
- const attestation = useMemo(() => project.data, [project]);
+ const project = useMemo(() => application.data, [application]);
return (
@@ -40,9 +44,9 @@ const ConfirmProjectPage = (): JSX.Element => {
{state !== EAppState.APPLICATION && }
- {attestation && (
-
-
+ {project && (
+
+
)}
diff --git a/packages/interface/src/pages/ballot/index.tsx b/packages/interface/src/pages/ballot/index.tsx
index 9957677c..524d8209 100644
--- a/packages/interface/src/pages/ballot/index.tsx
+++ b/packages/interface/src/pages/ballot/index.tsx
@@ -38,7 +38,7 @@ const ClearBallot = (): JSX.Element | null => {
};
return (
- <>
+
{
title="Are you sure?"
onOpenChange={setOpen}
/>
- >
+
);
};
+/**
+ * Empty ballot component
+ * @returns a component that displays a message and a button to view projects
+ */
const EmptyBallot = (): JSX.Element => (
@@ -81,6 +85,10 @@ const EmptyBallot = (): JSX.Element => (
);
+/**
+ * Ballot allocation form component
+ * @returns a component that displays the ballot allocation form
+ */
const BallotAllocationForm = (): JSX.Element => {
const appState = useAppState();
const { ballot, sumBallot } = useBallot();
diff --git a/packages/interface/src/pages/projects/[projectId]/Project.tsx b/packages/interface/src/pages/projects/[projectId]/Project.tsx
index f05cd1c7..e073a945 100644
--- a/packages/interface/src/pages/projects/[projectId]/Project.tsx
+++ b/packages/interface/src/pages/projects/[projectId]/Project.tsx
@@ -1,26 +1,30 @@
import { type GetServerSideProps } from "next";
+import { zeroAddress } from "viem";
+import { useMaci } from "~/contexts/Maci";
import { ReviewBar } from "~/features/applications/components/ReviewBar";
import ProjectDetails from "~/features/projects/components/ProjectDetails";
import { useProjectById } from "~/features/projects/hooks/useProjects";
import { LayoutWithSidebar } from "~/layouts/DefaultLayout";
import { useAppState } from "~/utils/state";
-import { EAppState } from "~/utils/types";
+import { EAppState, IRecipient } from "~/utils/types";
export interface IProjectDetailsProps {
projectId?: string;
}
const ProjectDetailsPage = ({ projectId = "" }: IProjectDetailsProps): JSX.Element => {
- const projects = useProjectById(projectId);
- const { name } = projects.data?.[0] ?? {};
+ const { pollData } = useMaci();
+
+ const projects = useProjectById(projectId, pollData?.registry ?? zeroAddress);
+
const appState = useAppState();
return (
-
+
{appState === EAppState.APPLICATION && }
-
+ {projects.data && }
);
};
diff --git a/packages/interface/src/pages/stats/index.tsx b/packages/interface/src/pages/stats/index.tsx
index f28e9274..c444506d 100644
--- a/packages/interface/src/pages/stats/index.tsx
+++ b/packages/interface/src/pages/stats/index.tsx
@@ -1,6 +1,7 @@
import { differenceInDays } from "date-fns";
import dynamic from "next/dynamic";
-import { useMemo, type PropsWithChildren } from "react";
+import { useEffect, useMemo, type PropsWithChildren } from "react";
+import { zeroAddress } from "viem";
import { useAccount } from "wagmi";
import { ConnectButton } from "~/components/ConnectButton";
@@ -8,7 +9,8 @@ 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 { useProjectCount } from "~/features/projects/hooks/useProjects";
+import { useProjectsResults, useResults } from "~/hooks/useResults";
import { Layout } from "~/layouts/DefaultLayout";
import { formatNumber } from "~/utils/formatNumber";
import { useAppState } from "~/utils/state";
@@ -28,17 +30,18 @@ const Stat = ({ title, children = null }: PropsWithChildren<{ title: string }>)
const Stats = () => {
const { isLoading, pollData } = useMaci();
+ const { chain, isConnected } = useAccount();
const results = useResults(pollData);
- const count = useProjectCount();
+
+ const count = useProjectCount({ chain: chain!, registryAddress: pollData?.registry ?? zeroAddress });
const { data: projectsResults } = useProjectsResults(pollData);
- const { isConnected } = useAccount();
const { averageVotes, projects = {} } = results.data ?? {};
const chartData = useMemo(() => {
const data = (projectsResults?.pages[0] ?? [])
.map((project) => ({
- x: project.name,
+ x: project.index,
y: projects[project.id]?.votes,
}))
.slice(0, 15);
@@ -75,7 +78,7 @@ const Stats = () => {
-
{count.data?.count}
+
{count.data?.count.toString()}
{Object.keys(projects).length}
diff --git a/packages/interface/src/server/api/root.ts b/packages/interface/src/server/api/root.ts
index 2078b349..077a3b94 100644
--- a/packages/interface/src/server/api/root.ts
+++ b/packages/interface/src/server/api/root.ts
@@ -1,7 +1,6 @@
import { applicationsRouter } from "~/server/api/routers/applications";
import { maciRouter } from "~/server/api/routers/maci";
import { metadataRouter } from "~/server/api/routers/metadata";
-import { profileRouter } from "~/server/api/routers/profile";
import { projectsRouter } from "~/server/api/routers/projects";
import { resultsRouter } from "~/server/api/routers/results";
import { votersRouter } from "~/server/api/routers/voters";
@@ -16,7 +15,6 @@ export const appRouter = createTRPCRouter({
results: resultsRouter,
voters: votersRouter,
applications: applicationsRouter,
- profile: profileRouter,
metadata: metadataRouter,
projects: projectsRouter,
maci: maciRouter,
diff --git a/packages/interface/src/server/api/routers/applications.ts b/packages/interface/src/server/api/routers/applications.ts
index c1c90d76..dfe3d57c 100644
--- a/packages/interface/src/server/api/routers/applications.ts
+++ b/packages/interface/src/server/api/routers/applications.ts
@@ -1,31 +1,24 @@
import { z } from "zod";
-import { config, eas } from "~/config";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { fetchAttestations } from "~/utils/fetchAttestations";
-import { createDataFilter } from "~/utils/fetchAttestationsUtils";
-
-export const FilterSchema = z.object({
- limit: z.number().default(3 * 8),
- cursor: z.number().default(0),
-});
+import {
+ fetchApplicationById,
+ fetchApplicationByProjectId,
+ fetchApprovedApplications,
+ fetchPendingApplications,
+} from "~/utils/fetchApplications";
export const applicationsRouter = createTRPCRouter({
- approvals: publicProcedure.input(z.object({ ids: z.array(z.string()).optional() })).query(async ({ input }) =>
- fetchAttestations([eas.schemas.approval], {
- where: {
- attester: { equals: config.admin },
- refUID: input.ids ? { in: input.ids } : undefined,
- AND: [createDataFilter("type", "bytes32", "application"), createDataFilter("round", "bytes32", config.roundId)],
- },
- }),
- ),
- list: publicProcedure.input(FilterSchema).query(async () =>
- fetchAttestations([eas.schemas.metadata], {
- orderBy: [{ time: "desc" }],
- where: {
- AND: [createDataFilter("type", "bytes32", "application"), createDataFilter("round", "bytes32", config.roundId)],
- },
- }),
- ),
+ approvals: publicProcedure
+ .input(z.object({ registryAddress: z.string() }))
+ .query(async ({ input }) => fetchApprovedApplications(input.registryAddress)),
+ pending: publicProcedure
+ .input(z.object({ registryAddress: z.string() }))
+ .query(async ({ input }) => fetchPendingApplications(input.registryAddress)),
+ getById: publicProcedure
+ .input(z.object({ registryAddress: z.string(), id: z.string() }))
+ .query(async ({ input }) => fetchApplicationById(input.registryAddress, input.id)),
+ getByProjectId: publicProcedure
+ .input(z.object({ registryAddress: z.string(), projectId: z.string() }))
+ .query(async ({ input }) => fetchApplicationByProjectId(input.projectId, input.registryAddress)),
});
diff --git a/packages/interface/src/server/api/routers/profile.ts b/packages/interface/src/server/api/routers/profile.ts
deleted file mode 100644
index bf36f11e..00000000
--- a/packages/interface/src/server/api/routers/profile.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { TRPCError } from "@trpc/server";
-import { z } from "zod";
-
-import { eas } from "~/config";
-import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { fetchAttestations } from "~/utils/fetchAttestations";
-import { createDataFilter } from "~/utils/fetchAttestationsUtils";
-
-export const profileRouter = createTRPCRouter({
- get: publicProcedure.input(z.object({ id: z.string() })).query(async ({ input }) =>
- fetchAttestations([eas.schemas.metadata], {
- where: {
- recipient: { in: [input.id] },
- ...createDataFilter("type", "bytes32", "profile"),
- },
- }).then(([attestation]) => {
- if (!attestation) {
- throw new TRPCError({ code: "NOT_FOUND" });
- }
- return attestation;
- }),
- ),
-});
diff --git a/packages/interface/src/server/api/routers/projects.ts b/packages/interface/src/server/api/routers/projects.ts
index fbb5d2e0..bb4dfb2e 100644
--- a/packages/interface/src/server/api/routers/projects.ts
+++ b/packages/interface/src/server/api/routers/projects.ts
@@ -1,166 +1,38 @@
import { TRPCError } from "@trpc/server";
import { z } from "zod";
-import { config, eas } from "~/config";
-import { type Filter, FilterSchema, OrderBy, SortOrder } from "~/features/filter/types";
+import { FilterSchema } from "~/features/filter/types";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { fetchAttestations } from "~/utils/fetchAttestations";
-import { createDataFilter, createSearchFilter } from "~/utils/fetchAttestationsUtils";
-import { fetchMetadata } from "~/utils/fetchMetadata";
+import { fetchApprovedProjects, fetchProjects } from "~/utils/fetchProjects";
+import { getProjectCount } from "~/utils/registry";
-import type { Attestation } from "~/utils/types";
+import type { Chain, Hex } from "viem";
export const projectsRouter = createTRPCRouter({
- count: publicProcedure.query(async () =>
- fetchAttestations([eas.schemas.approval], {
- where: {
- attester: { equals: config.admin },
- AND: [createDataFilter("type", "bytes32", "application"), createDataFilter("round", "bytes32", config.roundId)],
- },
- }).then((attestations = []) =>
- // Handle multiple approvals of an application - group by refUID
- ({
- count: Object.keys(attestations.reduce((acc, x) => ({ ...acc, [x.refUID]: true }), {})).length,
- }),
+ count: publicProcedure
+ .input(z.object({ chain: z.custom
(), registryAddress: z.string() }))
+ .query(async ({ input }) =>
+ getProjectCount(input.chain, input.registryAddress as Hex).then((count) => ({
+ count,
+ })),
),
- ),
- get: publicProcedure.input(z.object({ ids: z.array(z.string()) })).query(async ({ input: { ids } }) => {
- if (!ids.length) {
- throw new TRPCError({ code: "NOT_FOUND" });
- }
+ get: publicProcedure
+ .input(z.object({ ids: z.array(z.string()), registryAddress: z.string() }))
+ .query(async ({ input: { ids, registryAddress } }) => {
+ if (!ids.length) {
+ throw new TRPCError({ code: "NOT_FOUND" });
+ }
- return fetchAttestations([eas.schemas.metadata], {
- where: { id: { in: ids } },
- });
- }),
+ const projects = await fetchProjects(registryAddress);
- getByTransactionId: publicProcedure
- .input(z.object({ transactionId: z.string() }))
- .query(async ({ input: { transactionId } }) =>
- fetchAttestations([eas.schemas.metadata], {
- where: { txid: { equals: transactionId } },
- }).then((applications) => applications[0]),
- ),
-
- search: publicProcedure.input(FilterSchema).query(async ({ input }) => {
- const filters = [
- createDataFilter("type", "bytes32", "application"),
- createDataFilter("round", "bytes32", config.roundId),
- ];
-
- if (input.search) {
- filters.push(createSearchFilter(input.search));
- }
-
- if (input.needApproval) {
- return fetchAttestations([eas.schemas.approval], {
- where: {
- attester: { equals: config.admin },
- ...createDataFilter("type", "bytes32", "application"),
- },
- }).then((attestations = []) => {
- const approvedIds = attestations.map(({ refUID }) => refUID).filter(Boolean);
-
- return fetchAttestations([eas.schemas.metadata], {
- take: input.limit,
- skip: input.cursor * input.limit,
- orderBy: [createOrderBy(input.orderBy, input.sortOrder)],
- where: {
- id: { in: approvedIds },
- AND: filters,
- },
- });
- });
- }
-
- return fetchAttestations([eas.schemas.metadata], {
- take: input.limit,
- skip: input.cursor * input.limit,
- orderBy: [createOrderBy(input.orderBy, input.sortOrder)],
- where: {
- attester: { equals: config.admin },
- AND: filters,
- },
- });
- }),
-
- // Used for distribution to get the projects' payoutAddress
- // To get this data we need to fetch all projects and their metadata
- payoutAddresses: publicProcedure.input(z.object({ ids: z.array(z.string()) })).query(async ({ input }) =>
- fetchAttestations([eas.schemas.metadata], {
- where: { id: { in: input.ids } },
- })
- .then((attestations) =>
- Promise.all(
- attestations.map((attestation) =>
- fetchMetadata(attestation.metadataPtr).then((data) => {
- const { payoutAddress } = data as unknown as {
- payoutAddress: string;
- };
-
- return { projectId: attestation.id, payoutAddress };
- }),
- ),
- ),
- )
- .then((projects) => projects.reduce((acc, x) => ({ ...acc, [x.projectId]: x.payoutAddress }), {})),
- ),
+ return projects.find((project) => ids.includes(project.id));
+ }),
- allApproved: publicProcedure.query(async () => {
- const filters = [
- createDataFilter("type", "bytes32", "application"),
- createDataFilter("round", "bytes32", config.roundId),
- ];
-
- return fetchAttestations([eas.schemas.approval], {
- where: {
- attester: { equals: config.admin },
- ...createDataFilter("type", "bytes32", "application"),
- },
- }).then((attestations = []) => {
- const approvedIds = attestations.map(({ refUID }) => refUID).filter(Boolean);
-
- return fetchAttestations([eas.schemas.metadata], {
- orderBy: [createOrderBy(OrderBy.time, SortOrder.asc)],
- where: {
- id: { in: approvedIds },
- AND: filters,
- },
- });
- });
- }),
+ search: publicProcedure
+ .input(FilterSchema.extend({ search: z.string(), registryAddress: z.string() }))
+ .query(async ({ input }) =>
+ // get the projects that are approved
+ fetchApprovedProjects(input.registryAddress),
+ ),
});
-
-export async function getAllApprovedProjects(): Promise {
- const filters = [
- createDataFilter("type", "bytes32", "application"),
- createDataFilter("round", "bytes32", config.roundId),
- ];
-
- return fetchAttestations([eas.schemas.approval], {
- where: {
- attester: { equals: config.admin },
- ...createDataFilter("type", "bytes32", "application"),
- },
- }).then((attestations = []) => {
- const approvedIds = attestations.map(({ refUID }) => refUID).filter(Boolean);
-
- return fetchAttestations([eas.schemas.metadata], {
- orderBy: [createOrderBy(OrderBy.time, SortOrder.asc)],
- where: {
- id: { in: approvedIds },
- AND: filters,
- },
- });
- });
-}
-
-function createOrderBy(orderBy: Filter["orderBy"], sortOrder: Filter["sortOrder"]) {
- const key = {
- time: "time",
- name: "decodedDataJson",
- }[orderBy];
-
- return { [key]: sortOrder };
-}
diff --git a/packages/interface/src/server/api/routers/results.ts b/packages/interface/src/server/api/routers/results.ts
index cdb876ac..bb88bd9e 100644
--- a/packages/interface/src/server/api/routers/results.ts
+++ b/packages/interface/src/server/api/routers/results.ts
@@ -2,49 +2,35 @@ import { TRPCError } from "@trpc/server";
import { type TallyData } from "maci-cli/sdk";
import { z } from "zod";
-import { config, eas } from "~/config";
+import { config } from "~/config";
import { FilterSchema } from "~/features/filter/types";
import { createTRPCRouter, publicProcedure } from "~/server/api/trpc";
-import { fetchAttestations } from "~/utils/fetchAttestations";
-
-import { getAllApprovedProjects } from "./projects";
+import { fetchApprovedProjects, fetchProjects } from "~/utils/fetchProjects";
export const resultsRouter = createTRPCRouter({
votes: publicProcedure
- .input(z.object({ pollId: z.string().nullish() }))
- .query(async ({ input }) => calculateMaciResults(input.pollId)),
+ .input(z.object({ pollId: z.string().nullish(), registryAddress: z.string() }))
+ .query(async ({ input }) => calculateMaciResults(input.registryAddress, input.pollId)),
project: publicProcedure
- .input(z.object({ id: z.string(), pollId: z.string().nullish() }))
+ .input(z.object({ id: z.string(), pollId: z.string().nullish(), registryAddress: z.string() }))
.query(async ({ input }) => {
- const { projects } = await calculateMaciResults(input.pollId);
+ const { projects } = await calculateMaciResults(input.registryAddress, input.pollId);
return {
amount: projects[input.id]?.votes ?? 0,
};
}),
- projects: publicProcedure.input(FilterSchema.extend({ pollId: z.string().nullish() })).query(async ({ input }) => {
- const { projects } = await calculateMaciResults(input.pollId);
-
- const sortedIDs = Object.entries(projects)
- .sort((a, b) => b[1].votes - a[1].votes)
- .map(([id]) => id)
- .slice(input.cursor * input.limit, input.cursor * input.limit + input.limit);
-
- return fetchAttestations([eas.schemas.metadata], {
- where: {
- id: { in: sortedIDs },
- },
- }).then((attestations) =>
- // Results aren't returned from EAS in the same order as the `where: { in: sortedIDs }`
- // Sort the attestations based on the sorted array
- attestations.sort((a, b) => sortedIDs.indexOf(a.id) - sortedIDs.indexOf(b.id)),
- );
- }),
+ projects: publicProcedure
+ .input(FilterSchema.extend({ pollId: z.string().nullish(), registryAddress: z.string() }))
+ .query(async ({ input }) => fetchProjects(input.registryAddress)),
});
-export async function calculateMaciResults(pollId?: string | null): Promise<{
+export async function calculateMaciResults(
+ registryAddress: string,
+ pollId?: string | null,
+): Promise<{
averageVotes: number;
projects: Record;
}> {
@@ -56,7 +42,7 @@ export async function calculateMaciResults(pollId?: string | null): Promise<{
fetch(`${config.tallyUrl}/tally-${pollId}.json`)
.then((res) => res.json() as Promise)
.catch(() => undefined),
- getAllApprovedProjects(),
+ fetchApprovedProjects(registryAddress),
]);
if (!tallyData) {
@@ -83,7 +69,13 @@ export async function calculateMaciResults(pollId?: string | null): Promise<{
};
}
-function calculateAverage(votes: number[]) {
+/**
+ * Calculate the average of an array of numbers
+ *
+ * @param votes - An array of numbers
+ * @returns The average of the array
+ */
+function calculateAverage(votes: number[]): number {
if (votes.length === 0) {
return 0;
}
diff --git a/packages/interface/src/utils/fetchApplications.ts b/packages/interface/src/utils/fetchApplications.ts
new file mode 100644
index 00000000..465b2e24
--- /dev/null
+++ b/packages/interface/src/utils/fetchApplications.ts
@@ -0,0 +1,271 @@
+import { config } from "~/config";
+
+import type { IRequest } from "./types";
+
+export interface GraphQLResponse {
+ data?: {
+ requests: IRequest[];
+ };
+}
+
+// Fetch only pending add requests
+const PendingRequests = `
+ query PendingAddRequests($registryAddress: String!) {
+ requests(where: { requestType: "Add", status: "Pending", recipient_: { registry: $registryAddress } }) {
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
+// Fetch only rejected add requests
+const RejectedRequests = `
+ query RejectedRequests($registryAddress: String!) {
+ requests(where: { status: "Rejected", requestType: "Add", recipient_: { registry: $registryAddress } }) {
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
+// Fetch only approved add requests
+const ApprovedRequests = `
+ query ApprovedRequests($registryAddress: String!) {
+ requests(where: { status: "Approved", requestType: "Add", recipient_: { registry: $registryAddress } }) {
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
+const IndividualRequest = `
+ query PendingAddRequests($registryAddress: String!, $recipientId: String!) {
+ requests(where: { requestType: "Add", status: "Pending", recipient_: {
+ registry: $registryAddress,
+ id: $recipientId
+ }
+ }) {
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
+const ApplicationById = `
+ query ApplicationById($registryAddress: String!, $id: String!) {
+ requests(where: { recipient_: { registry: $registryAddress, initialized: false }, id: $id, requestType: "Add", status: "Pending" }) {
+ id
+ index
+ recipient {
+ id
+ metadataUrl
+ index
+ initialized
+ payout
+ registry {
+ id
+ }
+ }
+ }
+ }
+`;
+
+/**
+ * Fetch application by project ID
+ *
+ * @param projectId - the project ID
+ * @param registryAddress - the registry ID
+ */
+export async function fetchApplicationByProjectId(projectId: string, registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: IndividualRequest,
+ variables: { recipientId: projectId, registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.requests[0]!;
+}
+
+/**
+ * Fetch all pending applications
+ *
+ * @returns the pending add requests
+ */
+export async function fetchPendingApplications(registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: PendingRequests,
+ variables: { registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.requests;
+}
+
+/**
+ * Fetch applications that have been rejected
+ *
+ * @param registryAddress - the registry ID to filter by
+ * @returns the rejected add requests
+ */
+export async function fetchRejectedApplications(registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: RejectedRequests,
+ variables: { registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.requests;
+}
+
+/**
+ * Fetch applications that have been approved
+ *
+ * @param registryAddress - the registry ID to filter by
+ * @returns the approved add requests
+ */
+export async function fetchApprovedApplications(registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: ApprovedRequests,
+ variables: { registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.requests;
+}
+
+/**
+ * Fetch all applications
+ *
+ * @param registryAddress - the registry ID to filter by
+ * @returns the add requests
+ */
+export async function fetchApplications(registryAddress: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: ApprovedRequests,
+ variables: { registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data) {
+ throw new Error("No data returned from GraphQL query");
+ }
+
+ return result.data.requests;
+}
+
+/**
+ * Fetch application by ID
+ *
+ * @param registryAddress - the registry ID
+ * @param id - the application ID
+ * @returns the application
+ */
+export async function fetchApplicationById(registryAddress: string, id: string): Promise {
+ const response = await fetch(config.maciSubgraphUrl, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ query: ApplicationById,
+ variables: { id, registryAddress },
+ }),
+ });
+
+ const result = (await response.json()) as GraphQLResponse;
+
+ if (!result.data?.requests) {
+ throw new Error("No application found with this ID");
+ }
+
+ const request = result.data.requests[0]!;
+
+ return request;
+}
diff --git a/packages/interface/src/utils/fetchMetadata.ts b/packages/interface/src/utils/fetchMetadata.ts
index 7954f929..d4b72e8a 100644
--- a/packages/interface/src/utils/fetchMetadata.ts
+++ b/packages/interface/src/utils/fetchMetadata.ts
@@ -4,6 +4,12 @@ import { createCachedFetch } from "./fetch";
const ttl = 2147483647;
const cachedFetch = createCachedFetch({ ttl });
+/**
+ * Fetches project metadata from a given URL.
+ *
+ * @param url - The URL to fetch the metadata from.
+ * @returns The metadata.
+ */
export async function fetchMetadata(url: string): Promise<{ data: T; error: Error }> {
const ipfsGateway = process.env.NEXT_PUBLIC_IPFS_GATEWAY ?? "https://dweb.link/ipfs/";
diff --git a/packages/interface/src/utils/fetchPoll.ts b/packages/interface/src/utils/fetchPoll.ts
index 9294d6c1..5c3d60a9 100644
--- a/packages/interface/src/utils/fetchPoll.ts
+++ b/packages/interface/src/utils/fetchPoll.ts
@@ -1,22 +1,15 @@
+import { BigNumberish } from "ethers";
import { IGetPollData } from "maci-cli/sdk";
+import { type Hex, zeroAddress } from "viem";
import { config } from "~/config";
+import type { Poll } from "./types";
+
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;
- mode: string;
-}
-
export interface GraphQLResponse {
data?: {
polls: Poll[];
@@ -34,11 +27,25 @@ const PollQuery = `
numSignups
id
mode
+ initTime
+ registry {
+ id
+ }
}
}
`;
-export async function fetchPoll(): Promise {
+export interface IPollData extends IGetPollData {
+ registry: Hex;
+ initTime: BigNumberish;
+}
+
+/**
+ * Fetches the poll data from the subgraph
+ *
+ * @returns The poll data
+ */
+export async function fetchPoll(): Promise {
const poll = (
await cachedFetch<{ polls: Poll[] }>(config.maciSubgraphUrl, {
method: "POST",
@@ -57,5 +64,7 @@ export async function fetchPoll(): Promise {
numSignups: poll?.numSignups ?? 0,
address: poll?.id ?? "",
mode: poll?.mode ?? "",
+ initTime: poll?.initTime ?? 0,
+ registry: poll?.registry ? (poll.registry.id as Hex) : zeroAddress,
};
}
diff --git a/packages/interface/src/utils/fetchProjects.ts b/packages/interface/src/utils/fetchProjects.ts
new file mode 100644
index 00000000..ce62ac0b
--- /dev/null
+++ b/packages/interface/src/utils/fetchProjects.ts
@@ -0,0 +1,103 @@
+import { config } from "~/config";
+
+import { createCachedFetch } from "./fetch";
+import { IRecipient } from "./types";
+
+const cachedFetch = createCachedFetch({ ttl: 1000 });
+
+export interface GraphQLResponse {
+ data?: {
+ recipients: IRecipient[];
+ };
+}
+
+// query to fetch all approved projects
+const ApprovedProjects = `
+ query Recipients($registryAddress: String) {
+ recipients (where:{ deleted:false, initialized: true, registry: $registryAddress }) {
+ id
+ payout
+ metadataUrl
+ index
+ initialized
+ registry {
+ id
+ }
+ }
+ }`;
+
+const Projects = `
+ query Recipients($registryAddress: String) {
+ recipients (where:{ deleted:false, registry: $registryAddress }) {
+ id
+ payout
+ metadataUrl
+ index
+ initialized
+ registry {
+ id
+ }
+ }
+ }`;
+
+/**
+ * Fetch all projects
+ *
+ * @returns the projects
+ */
+export async function fetchProjects(registryAddress: string): Promise {
+ const response = await cachedFetch<{ recipients: IRecipient[] }>(config.maciSubgraphUrl, {
+ method: "POST",
+ body: JSON.stringify({
+ query: Projects,
+ variables: { registryAddress },
+ }),
+ }).then((resp: GraphQLResponse) => resp.data?.recipients);
+
+ const recipients = response?.map((request) => ({
+ id: request.id,
+ metadataUrl: request.metadataUrl,
+ payout: request.payout,
+ initialized: request.initialized,
+ index: request.index,
+ }));
+
+ return recipients ?? [];
+}
+
+/**
+ * Fetch all approved projects
+ *
+ * @returns the projects
+ */
+export async function fetchApprovedProjects(registryAddress: string): Promise {
+ const response = await cachedFetch<{ recipients: IRecipient[] }>(config.maciSubgraphUrl, {
+ method: "POST",
+ body: JSON.stringify({
+ query: ApprovedProjects,
+ variables: { registryAddress },
+ }),
+ }).then((resp: GraphQLResponse) => resp.data?.recipients);
+
+ const recipients = response?.map((request) => ({
+ id: request.id,
+ metadataUrl: request.metadataUrl,
+ payout: request.payout,
+ initialized: request.initialized,
+ index: request.index,
+ }));
+
+ return recipients ?? [];
+}
+
+/**
+ * Fetch a project by ID
+ *
+ * @param registryAddress - the registry ID
+ * @param id - the project ID
+ * @returns the project
+ */
+export async function fetchProjectById(registryAddress: string, id: string): Promise {
+ const projects = await fetchProjects(registryAddress);
+ return projects.find((project) => project.id === id);
+}
diff --git a/packages/interface/src/utils/registry.ts b/packages/interface/src/utils/registry.ts
new file mode 100644
index 00000000..bdbb4887
--- /dev/null
+++ b/packages/interface/src/utils/registry.ts
@@ -0,0 +1,231 @@
+import { hexlify, randomBytes } from "ethers";
+import {
+ MACI__factory as MACIFactory,
+ RegistryManager__factory as RegistryManagerFactory,
+ BaseRegistry__factory as BaseRegistryFactory,
+ Poll__factory as PollFactory,
+} from "maci-platform-contracts/typechain-types";
+import { Chain, createPublicClient, createWalletClient, custom, Hex, http, TransactionReceipt } from "viem";
+
+import { config } from "~/config";
+
+import { ERequestStatus, ERequestType, IRequest } from "./types";
+
+/**
+ * Get the registry address
+ *
+ * @param chain - The chain to use
+ * @param pollId - The poll id
+ * @returns The registry address
+ */
+export const getRegistryAddress = async (chain: Chain, pollId: bigint): Promise => {
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+
+ const pollAddress = (
+ (await publicClient.readContract({
+ abi: MACIFactory.abi,
+ address: config.maciAddress as Hex,
+ functionName: "getPoll",
+ args: [pollId],
+ })) as { poll: Hex; messageProcessor: Hex; tally: Hex }
+ ).poll;
+
+ return (await publicClient.readContract({
+ abi: PollFactory.abi,
+ address: pollAddress,
+ functionName: "registry",
+ })) as Hex;
+};
+
+/**
+ * Get the registry manager contract address
+ */
+export const getRegistryManagerContract = async (chain: Chain): Promise => {
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+
+ const registryManagerAddress = (await publicClient.readContract({
+ abi: MACIFactory.abi,
+ address: config.maciAddress as Hex,
+ functionName: "registryManager",
+ })) as Hex;
+
+ return registryManagerAddress;
+};
+
+/**
+ * Approve an application to join the registry
+ * @param chain - The chain to use
+ * @param index - The index of the application to approve
+ * @returns True if the application was approved, false otherwise
+ */
+export const approveApplication = async (chain: Chain, index: string): Promise => {
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+
+ // eslint-disable-next-line
+ const [account] = await window.ethereum!.request({ method: "eth_requestAccounts" });
+
+ const client = createWalletClient({
+ // eslint-disable-next-line
+ account,
+ chain,
+ transport: custom(window.ethereum),
+ });
+
+ const registryManagerAddress = await getRegistryManagerContract(chain);
+
+ try {
+ const tx = await client.writeContract({
+ abi: RegistryManagerFactory.abi,
+ address: registryManagerAddress,
+ functionName: "approve",
+ args: [index],
+ chain,
+ // eslint-disable-next-line
+ account,
+ });
+
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
+ return receipt.status === "success";
+ } catch (error) {
+ return false;
+ }
+};
+
+/**
+ * Reject an application to join the registry
+ *
+ * @param chain - The chain to use
+ * @param index - The index of the application to reject
+ * @returns True if the application was rejected, false otherwise
+ */
+export const rejectApplication = async (chain: Chain, index: bigint): Promise => {
+ // eslint-disable-next-line
+ const [account] = await window.ethereum!.request({ method: "eth_requestAccounts" });
+
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+ const client = createWalletClient({
+ // eslint-disable-next-line
+ account,
+ chain,
+ transport: custom(window.ethereum),
+ });
+
+ const registryManagerAddress = await getRegistryManagerContract(chain);
+
+ try {
+ const tx = await client.writeContract({
+ abi: RegistryManagerFactory.abi,
+ address: registryManagerAddress,
+ functionName: "rejectApplication",
+ args: [index],
+ chain,
+ // eslint-disable-next-line
+ account,
+ });
+
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
+ return receipt.status === "success";
+ } catch (error) {
+ return false;
+ }
+};
+
+/**
+ * Submit an application to join the registry
+ *
+ * @param chain - The chain to use
+ * @param index - The index of the application to submit
+ * @param metadataPtr - The metadata url
+ * @param registryAddress - The registry address
+ * @param recipientAddress - The recipient address
+ * @param attestationId - The attestation id
+ * @returns The transaction receipt
+ */
+export const submitApplication = async (
+ chain: Chain,
+ metadataUrl: string,
+ registryAddress: Hex,
+ recipientAddress: Hex,
+ attestationId?: string,
+): Promise => {
+ // eslint-disable-next-line
+ const [account] = await window.ethereum!.request({ method: "eth_requestAccounts" });
+
+ const publicClient = createPublicClient({
+ transport: custom(window.ethereum),
+ chain,
+ });
+
+ const walletClient = createWalletClient({
+ // eslint-disable-next-line
+ account,
+ chain,
+ transport: custom(window.ethereum),
+ });
+
+ const registryManagerAddress = await getRegistryManagerContract(chain);
+
+ const request: IRequest = {
+ registry: registryAddress,
+ requestType: ERequestType.Add,
+ recipient: {
+ recipient: recipientAddress,
+ metadataUrl,
+ id: attestationId ?? (hexlify(randomBytes(32)) as Hex),
+ },
+ index: "0",
+ status: ERequestStatus.Pending,
+ };
+
+ try {
+ const tx = await walletClient.writeContract({
+ abi: RegistryManagerFactory.abi,
+ address: registryManagerAddress,
+ functionName: "process",
+ args: [request],
+ chain,
+ // eslint-disable-next-line
+ account,
+ });
+
+ const receipt = await publicClient.waitForTransactionReceipt({ hash: tx });
+
+ return receipt;
+ } catch (error) {
+ throw new Error("Failed to submit application");
+ }
+};
+
+/**
+ * Get the number of projects in the registry
+ *
+ * @param chain - The chain to use
+ * @param registryAddress - The registry address
+ * @returns The number of projects in the registry
+ */
+export const getProjectCount = async (chain: Chain, registryAddress: Hex): Promise => {
+ const publicClient = createPublicClient({
+ transport: http(),
+ chain,
+ });
+
+ const count = (await publicClient.readContract({
+ abi: BaseRegistryFactory.abi,
+ address: registryAddress,
+ functionName: "recipientCount",
+ })) as bigint;
+
+ return count;
+};
diff --git a/packages/interface/src/utils/types.ts b/packages/interface/src/utils/types.ts
index 036951d1..63ddccf0 100644
--- a/packages/interface/src/utils/types.ts
+++ b/packages/interface/src/utils/types.ts
@@ -1,4 +1,4 @@
-import { type Address } from "viem";
+import type { Hex, Address } from "viem";
export enum EAppState {
LOADING = "LOADING",
@@ -88,3 +88,161 @@ export const AttestationsQuery = `
}
}
`;
+
+/**
+ * The request type
+ */
+export enum ERequestType {
+ /**
+ * Add a new recipient
+ */
+ Add = 0,
+ /**
+ * Change a recipient
+ */
+ Change = 1,
+ /**
+ * Remove a recipient
+ */
+ Remove = 2,
+}
+
+/**
+ * The request status
+ */
+export enum ERequestStatus {
+ /**
+ * The request is pending
+ */
+ Pending = 0,
+ /**
+ * The request is approved
+ */
+ Approved = 1,
+ /**
+ * The request is rejected
+ */
+ Rejected = 2,
+}
+
+/**
+ * The recipient data
+ */
+export interface IRecipient {
+ /**
+ * The recipient id (optional on chain so can use 0)
+ */
+ id: string;
+ /**
+ * The recipient metadata url
+ */
+ metadataUrl: string;
+ /**
+ * The recipient address
+ */
+ payout: Hex;
+ /**
+ * The recipient index
+ */
+ index: string;
+ /**
+ * Whether it was approved or not
+ */
+ initialized?: boolean;
+}
+
+/**
+ * The recipient data for the contract
+ */
+export interface IRecipientContract {
+ /**
+ * The recipient id (optional on chain so can use 0)
+ */
+ id: string;
+ /**
+ * The recipient metadata url
+ */
+ metadataUrl: string;
+ /**
+ * The recipient address
+ */
+ recipient: Hex;
+ /**
+ * Whether it was approved or not
+ */
+ initialized?: boolean;
+}
+
+/**
+ * The request data
+ */
+export interface IRequest {
+ /**
+ * The index of the application (optional onchain for Add so can use 0)
+ */
+ index: string;
+ /**
+ * The registry address
+ */
+ registry: Hex;
+ /**
+ * The request type
+ */
+ requestType: ERequestType;
+ /**
+ * The request status
+ */
+ status: ERequestStatus;
+ /**
+ * The recipient data
+ */
+ recipient: IRecipient | IRecipientContract;
+}
+
+/**
+ * The poll data
+ */
+export interface Poll {
+ /**
+ * The poll id
+ */
+ pollId: string;
+ /**
+ * The poll creation date
+ */
+ createdAt: string;
+ /**
+ * The poll duration
+ */
+ duration: string;
+ /**
+ * MACI's state root
+ */
+ stateRoot: string;
+ /**
+ * The poll message root
+ */
+ messageRoot: string;
+ /**
+ * The poll number of signups
+ */
+ numSignups: string;
+ /**
+ * The poll id
+ */
+ id: string;
+ /**
+ * The poll mode
+ */
+ mode: string;
+ /**
+ * The poll init time
+ */
+ initTime: string;
+ /**
+ * The poll registry address
+ */
+ registry: {
+ id: string;
+ };
+}
diff --git a/packages/subgraph/schemas/schema.v1.graphql b/packages/subgraph/schemas/schema.v1.graphql
index f474836e..bff1400c 100644
--- a/packages/subgraph/schemas/schema.v1.graphql
+++ b/packages/subgraph/schemas/schema.v1.graphql
@@ -40,7 +40,7 @@ enum Status {
}
type Request @entity {
- id: ID!
+ id: String!
requestType: RequestType!
index: BigInt
status: Status!
@@ -68,6 +68,7 @@ type Recipient @entity {
type Registry @entity {
id: Bytes! # address
+ metadataUrl: String
"relations"
poll: Poll
recipients: [Recipient!]! @derivedFrom(field: "registry")
diff --git a/packages/subgraph/src/poll.ts b/packages/subgraph/src/poll.ts
index cd6be511..46603b5e 100644
--- a/packages/subgraph/src/poll.ts
+++ b/packages/subgraph/src/poll.ts
@@ -9,6 +9,7 @@ import {
PublishMessage as PublishMessageEvent,
SetRegistry as SetRegistryEvent,
} from "../generated/templates/Poll/Poll";
+import { Registry as RegistryContract } from "../generated/templates/Registry/Registry";
import { ONE_BIG_INT } from "./utils/constants";
import { createOrLoadRegistry } from "./utils/entity";
@@ -75,8 +76,10 @@ export function handleSetRegistry(event: SetRegistryEvent): void {
return;
}
+ const contract = RegistryContract.bind(event.params.registry);
const registry = createOrLoadRegistry(event.params.registry);
registry.poll = poll.id;
+ registry.metadataUrl = contract.getRegistryMetadataUrl();
registry.save();
poll.registry = event.params.registry;
diff --git a/packages/subgraph/src/registry.ts b/packages/subgraph/src/registry.ts
index 0e89bd34..0ba2a48f 100644
--- a/packages/subgraph/src/registry.ts
+++ b/packages/subgraph/src/registry.ts
@@ -13,6 +13,8 @@ export function handleAddRecipient(event: RecipientAdded): void {
event.address,
);
+ // we want to ensure that the index is the recipient index in the registry
+ recipient.index = event.params.index;
recipient.initialized = true;
recipient.save();
}
@@ -25,6 +27,7 @@ export function handleChangeRecipient(event: RecipientChanged): void {
}
recipient.metadataUrl = event.params.metadataUrl.toString();
+ recipient.id = event.params.id;
recipient.index = event.params.index;
recipient.initialized = true;
recipient.deleted = false;
diff --git a/packages/subgraph/templates/subgraph.template.yaml b/packages/subgraph/templates/subgraph.template.yaml
index d533a3e4..a20113fa 100644
--- a/packages/subgraph/templates/subgraph.template.yaml
+++ b/packages/subgraph/templates/subgraph.template.yaml
@@ -51,11 +51,11 @@ dataSources:
- name: BaseRegistry
file: ./node_modules/maci-platform-contracts/build/artifacts/contracts/registry/BaseRegistry.sol/BaseRegistry.json
eventHandlers:
- - event: RequestSent(indexed address,indexed uint8,indexed bytes32,uint256,address,string)
+ - event: RequestSent(indexed address,indexed uint8,indexed bytes32,uint256,uint256,address,string)
handler: handleRequestSent
- - event: RequestApproved(indexed address,indexed uint8,indexed bytes32,uint256,address,string)
+ - event: RequestApproved(indexed address,indexed uint8,indexed bytes32,uint256,uint256,address,string)
handler: handleRequestApproved
- - event: RequestRejected(indexed address,indexed uint8,indexed bytes32,uint256,address,string)
+ - event: RequestRejected(indexed address,indexed uint8,indexed bytes32,uint256,uint256,address,string)
handler: handleRequestRejected
file: ./src/registryManager.ts
templates:
@@ -73,6 +73,8 @@ templates:
abis:
- name: Poll
file: ./node_modules/maci-platform-contracts/build/artifacts/contracts/maci/Poll.sol/Poll.json
+ - name: Registry
+ file: ./node_modules/maci-platform-contracts/build/artifacts/contracts/registry/BaseRegistry.sol/BaseRegistry.json
eventHandlers:
- event: MergeMaciState(indexed uint256,indexed uint256)
handler: handleMergeMaciState
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 57f5f457..1a239221 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -402,10 +402,13 @@ importers:
version: 0.316.0(react@18.2.0)
maci-cli:
specifier: ^2.4.0
- version: 2.4.0(3cqgokiuvxx4ajhx37zjyqxqrq)
+ version: 2.4.0(ajppvhbj75go4cdbr4h4vhruha)
maci-domainobjs:
specifier: ^2.4.0
version: 2.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ maci-platform-contracts:
+ specifier: workspace:*
+ version: link:../contracts
next:
specifier: ^14.1.0
version: 14.2.5(@babel/core@7.25.2)(@playwright/test@1.46.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
@@ -538,7 +541,7 @@ importers:
version: 9.1.0(eslint@8.57.0)
eslint-import-resolver-typescript:
specifier: ^3.6.1
- version: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ version: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0)
eslint-plugin-import:
specifier: ^2.30.0
version: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
@@ -16558,9 +16561,9 @@ snapshots:
'@nomicfoundation/ethereumjs-rlp': 5.0.4
ethereum-cryptography: 0.1.3
- '@nomicfoundation/hardhat-chai-matchers@2.0.7(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
+ '@nomicfoundation/hardhat-chai-matchers@2.0.7(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
dependencies:
- '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
'@types/chai-as-promised': 7.1.8
chai: 4.5.0
chai-as-promised: 7.1.2(chai@4.5.0)
@@ -16591,15 +16594,6 @@ snapshots:
hardhat: 2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
ordinal: 1.0.3
- '@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
- dependencies:
- debug: 4.3.6(supports-color@8.1.1)
- ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
- hardhat: 2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
- lodash.isequal: 4.5.0
- transitivePeerDependencies:
- - supports-color
-
'@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
dependencies:
debug: 4.3.6(supports-color@8.1.1)
@@ -16618,9 +16612,9 @@ snapshots:
transitivePeerDependencies:
- supports-color
- '@nomicfoundation/hardhat-ignition-ethers@0.15.5(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
+ '@nomicfoundation/hardhat-ignition-ethers@0.15.5(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
dependencies:
- '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
'@nomicfoundation/hardhat-ignition': 0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
'@nomicfoundation/ignition-core': 0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)
ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -16702,6 +16696,27 @@ snapshots:
ethereumjs-util: 7.1.5
hardhat: 2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
+ '@nomicfoundation/hardhat-toolbox@5.0.0(4lzaewf5vhmhgkr3zdkxldxwse)':
+ dependencies:
+ '@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-ignition-ethers': 0.15.5(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-network-helpers': 1.0.11(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-verify': 2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@typechain/ethers-v6': 0.5.1(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))(typescript@5.5.4)
+ '@typechain/hardhat': 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))(typescript@5.5.4))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))
+ '@types/chai': 4.3.17
+ '@types/mocha': 10.0.7
+ '@types/node': 20.14.14
+ chai: 4.5.0
+ ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
+ hardhat: 2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
+ hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
+ solidity-coverage: 0.8.12(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
+ typechain: 8.3.2(typescript@5.5.4)
+ typescript: 5.5.4
+
'@nomicfoundation/hardhat-toolbox@5.0.0(6zbd34p7xszhw7fs5zk33ckexe)':
dependencies:
'@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@22.2.0)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
@@ -16744,27 +16759,6 @@ snapshots:
typechain: 8.3.2(typescript@5.5.4)
typescript: 5.5.4
- '@nomicfoundation/hardhat-toolbox@5.0.0(wq4v7k5ocgbchgotiijfxhcogu)':
- dependencies:
- '@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(chai@4.5.0)(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@nomicfoundation/hardhat-ignition-ethers': 0.15.5(@nomicfoundation/hardhat-ethers@3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)))(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10))(@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@nomicfoundation/hardhat-network-helpers': 1.0.11(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@nomicfoundation/hardhat-verify': 2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@typechain/ethers-v6': 0.5.1(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))(typescript@5.5.4)
- '@typechain/hardhat': 9.1.0(@typechain/ethers-v6@0.5.1(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))(typescript@5.5.4))(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(typechain@8.3.2(typescript@5.5.4))
- '@types/chai': 4.3.17
- '@types/mocha': 10.0.7
- '@types/node': 20.14.14
- chai: 4.5.0
- ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
- hardhat: 2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
- hardhat-gas-reporter: 1.0.10(bufferutil@4.0.8)(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)
- solidity-coverage: 0.8.12(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4)
- typechain: 8.3.2(typescript@5.5.4)
- typescript: 5.5.4
-
'@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))':
dependencies:
'@ethersproject/abi': 5.7.0
@@ -17554,7 +17548,7 @@ snapshots:
'@semaphore-protocol/identity': 3.15.2
'@types/json-bigint': 1.0.4
'@zk-kit/eddsa-poseidon': 1.0.3
- '@zk-kit/utils': 1.2.1
+ '@zk-kit/utils': 1.2.0
js-sha256: 0.11.0
json-bigint: 1.0.0
poseidon-lite: 0.3.0
@@ -22771,7 +22765,7 @@ snapshots:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.4)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0)
eslint-plugin-react: 7.35.0(eslint@8.57.0)
@@ -22794,12 +22788,29 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0):
+ dependencies:
+ debug: 4.3.6(supports-color@8.1.1)
+ enhanced-resolve: 5.17.1
+ eslint: 8.57.0
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
+ fast-glob: 3.3.2
+ get-tsconfig: 4.7.6
+ is-core-module: 2.15.0
+ is-glob: 4.0.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-node
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0):
dependencies:
debug: 4.3.6(supports-color@8.1.1)
enhanced-resolve: 5.17.1
eslint: 8.57.0
- eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0)
fast-glob: 3.3.2
get-tsconfig: 4.7.6
@@ -22835,7 +22846,7 @@ snapshots:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.4)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
@@ -22850,14 +22861,24 @@ snapshots:
transitivePeerDependencies:
- supports-color
- eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0):
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7(supports-color@8.1.1)
optionalDependencies:
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.4)
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
- eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint@8.57.0)
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.30.0)(eslint@8.57.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0):
+ dependencies:
+ debug: 3.2.7(supports-color@8.1.1)
+ optionalDependencies:
+ '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.4)
+ eslint: 8.57.0
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.30.0)(eslint@8.57.0)
transitivePeerDependencies:
- supports-color
@@ -26678,16 +26699,16 @@ snapshots:
- typescript
- utf-8-validate
- maci-cli@2.4.0(3cqgokiuvxx4ajhx37zjyqxqrq):
+ maci-cli@2.4.0(ajppvhbj75go4cdbr4h4vhruha):
dependencies:
'@commander-js/extra-typings': 12.1.0(commander@12.1.0)
- '@nomicfoundation/hardhat-toolbox': 5.0.0(wq4v7k5ocgbchgotiijfxhcogu)
+ '@nomicfoundation/hardhat-toolbox': 5.0.0(4lzaewf5vhmhgkr3zdkxldxwse)
commander: 12.1.0
dotenv: 16.4.5
ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)
hardhat: 2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10)
maci-circuits: 2.4.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10)
- maci-contracts: 2.4.0(fiyhxdrnyqwclxh26nwcfabgem)
+ maci-contracts: 2.4.0(tnqgnvgpfdbm2nfzhwjuiounlm)
maci-core: 2.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
maci-crypto: 2.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
maci-domainobjs: 2.4.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)
@@ -26789,10 +26810,10 @@ snapshots:
- typescript
- utf-8-validate
- maci-contracts@2.4.0(fiyhxdrnyqwclxh26nwcfabgem):
+ maci-contracts@2.4.0(tnqgnvgpfdbm2nfzhwjuiounlm):
dependencies:
- '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.7(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
- '@nomicfoundation/hardhat-toolbox': 5.0.0(wq4v7k5ocgbchgotiijfxhcogu)
+ '@nomicfoundation/hardhat-ethers': 3.0.8(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.8(bufferutil@4.0.8)(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4))(typescript@5.5.4)(utf-8-validate@5.0.10))
+ '@nomicfoundation/hardhat-toolbox': 5.0.0(4lzaewf5vhmhgkr3zdkxldxwse)
'@openzeppelin/contracts': 5.0.2
'@openzeppelin/merkle-tree': 1.0.7
circomlibjs: 0.1.7(bufferutil@4.0.8)(utf-8-validate@5.0.10)