From df5c8394910aeb4f4cd8afb96956d8d72ce98c3a Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Sat, 24 Aug 2024 11:49:44 +0100 Subject: [PATCH] feat: add deployer service --- .github/scripts/deploy.sh | 0 packages/coordinator/package.json | 10 +- packages/coordinator/ts/app.module.ts | 2 + .../ts/common/accountAbstraction.ts | 90 ++ packages/coordinator/ts/common/errors.ts | 11 +- packages/coordinator/ts/common/index.ts | 1 + packages/coordinator/ts/common/networks.ts | 110 ++- .../__tests__/deployer.controller.test.ts | 97 +++ .../__tests__/deployer.service.test.ts | 543 ++++++++++++ .../ts/deployer/__tests__/utils.ts | 45 + .../ts/deployer/deployer.controller.ts | 62 ++ .../ts/deployer/deployer.module.ts | 15 + .../ts/deployer/deployer.service.ts | 785 ++++++++++++++++++ packages/coordinator/ts/deployer/dto.ts | 100 +++ packages/coordinator/ts/deployer/types.ts | 245 ++++++ packages/coordinator/ts/file/file.service.ts | 2 +- .../__tests__/sessionKeys.service.test.ts | 16 +- .../ts/sessionKeys/sessionKeys.module.ts | 1 + .../ts/sessionKeys/sessionKeys.service.ts | 24 +- pnpm-lock.yaml | 311 ++++--- 20 files changed, 2272 insertions(+), 198 deletions(-) mode change 100644 => 100755 .github/scripts/deploy.sh create mode 100644 packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts create mode 100644 packages/coordinator/ts/deployer/__tests__/deployer.service.test.ts create mode 100644 packages/coordinator/ts/deployer/__tests__/utils.ts create mode 100644 packages/coordinator/ts/deployer/deployer.controller.ts create mode 100644 packages/coordinator/ts/deployer/deployer.module.ts create mode 100644 packages/coordinator/ts/deployer/deployer.service.ts create mode 100644 packages/coordinator/ts/deployer/dto.ts create mode 100644 packages/coordinator/ts/deployer/types.ts diff --git a/.github/scripts/deploy.sh b/.github/scripts/deploy.sh old mode 100644 new mode 100755 diff --git a/packages/coordinator/package.json b/packages/coordinator/package.json index a4b4c6c8..5a0d785f 100644 --- a/packages/coordinator/package.json +++ b/packages/coordinator/package.json @@ -48,11 +48,11 @@ "hardhat": "^2.22.6", "helmet": "^7.1.0", "lowdb": "^1.0.0", - "maci-circuits": "^2.1.0", - "maci-cli": "^2.1.0", - "maci-contracts": "^2.1.0", - "maci-domainobjs": "^2.0.0", - "maci-subgraph": "^2.1.0", + "maci-circuits": "^2.2.0", + "maci-cli": "^2.2.0", + "maci-contracts": "^2.2.1", + "maci-domainobjs": "^2.2.0", + "maci-subgraph": "^2.2.0", "mustache": "^4.2.0", "permissionless": "^0.1.44", "reflect-metadata": "^0.2.0", diff --git a/packages/coordinator/ts/app.module.ts b/packages/coordinator/ts/app.module.ts index fdd623a5..5364d74c 100644 --- a/packages/coordinator/ts/app.module.ts +++ b/packages/coordinator/ts/app.module.ts @@ -2,6 +2,7 @@ import { Module } from "@nestjs/common"; import { ThrottlerModule } from "@nestjs/throttler"; import { CryptoModule } from "./crypto/crypto.module"; +import { DeployerModule } from "./deployer/deployer.module"; import { FileModule } from "./file/file.module"; import { ProofModule } from "./proof/proof.module"; import { SessionKeysModule } from "./sessionKeys/sessionKeys.module"; @@ -20,6 +21,7 @@ import { SubgraphModule } from "./subgraph/subgraph.module"; SubgraphModule, ProofModule, SessionKeysModule, + DeployerModule, ], }) export class AppModule {} diff --git a/packages/coordinator/ts/common/accountAbstraction.ts b/packages/coordinator/ts/common/accountAbstraction.ts index 07c24cec..dfb19333 100644 --- a/packages/coordinator/ts/common/accountAbstraction.ts +++ b/packages/coordinator/ts/common/accountAbstraction.ts @@ -1,6 +1,24 @@ +import { deserializePermissionAccount } from "@zerodev/permissions"; +import { toECDSASigner } from "@zerodev/permissions/signers"; +import { createKernelAccountClient, KernelAccountClient, KernelSmartAccount } from "@zerodev/sdk"; +import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; import dotenv from "dotenv"; +import { ENTRYPOINT_ADDRESS_V07 } from "permissionless"; +import { ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types"; +import { + createPublicClient, + http, + type HttpTransport, + type TransactionReceipt, + type Transport, + type Hex, + type PublicClient, + Chain, +} from "viem"; +import { privateKeyToAccount } from "viem/accounts"; import { ErrorCodes } from "./errors"; +import { ESupportedNetworks, viemChain } from "./networks"; dotenv.config(); @@ -18,3 +36,75 @@ export const genPimlicoRPCUrl = (network: string): string => { return `https://api.pimlico.io/v2/${network}/rpc?apikey=${pimlicoAPIKey}`; }; + +/** + * Get a public client + * @param rpcUrl - the RPC URL + * @returns the public client + */ +export const getPublicClient = (rpcUrl: string, chainName: ESupportedNetworks): PublicClient => + createPublicClient({ + transport: http(rpcUrl), + chain: viemChain(chainName), + }); + +/** + * Get a Kernel account handle given a session key + * @param sessionKey + * @param chainId + */ +export const getKernelClient = async ( + sessionKey: Hex, + approval: string, + chain: ESupportedNetworks, +): Promise< + KernelAccountClient< + ENTRYPOINT_ADDRESS_V07_TYPE, + Transport, + Chain, + KernelSmartAccount + > +> => { + const bundlerUrl = genPimlicoRPCUrl(chain); + const publicClient = getPublicClient(bundlerUrl, chain); + + // Using a stored private key + const sessionKeySigner = toECDSASigner({ + signer: privateKeyToAccount(sessionKey), + }); + + const sessionKeyAccount = await deserializePermissionAccount( + publicClient, + ENTRYPOINT_ADDRESS_V07, + KERNEL_V3_1, + approval, + sessionKeySigner, + ); + + const kernelClient = createKernelAccountClient({ + bundlerTransport: http(bundlerUrl), + entryPoint: ENTRYPOINT_ADDRESS_V07, + account: sessionKeyAccount, + chain: viemChain(chain), + }); + + return kernelClient; +}; + +/** + * The topic for the contract creation event + */ +export const contractCreationEventTopic = "0x4db17dd5e4732fb6da34a148104a592783ca119a1e7bb8829eba6cbadef0b511"; + +/** + * Get the address of the newly deployed contract from a transaction receipt + * @param receipt - The transaction receipt + * @returns The address of the newly deployed contract + */ +export const getDeployedContractAddress = (receipt: TransactionReceipt): string | undefined => { + const addr = receipt.logs.find((log) => log.topics[0] === contractCreationEventTopic); + + const deployedAddress = addr ? `0x${addr.topics[1]?.slice(26)}` : undefined; + + return deployedAddress; +}; diff --git a/packages/coordinator/ts/common/errors.ts b/packages/coordinator/ts/common/errors.ts index 293e4aa2..512ddf77 100644 --- a/packages/coordinator/ts/common/errors.ts +++ b/packages/coordinator/ts/common/errors.ts @@ -12,5 +12,14 @@ export enum ErrorCodes { SUBGRAPH_DEPLOY = "7", SESSION_KEY_NOT_FOUND = "8", PIMLICO_API_KEY_NOT_SET = "9", - INVALID_APPROVAL = "10", + UNSUPPORTED_VOICE_CREDIT_PROXY = "10", + UNSUPPORTED_GATEKEEPER = "11", + FAILED_TO_DEPLOY_CONTRACT = "12", + FAILED_TO_SET_MACI_INSTANCE_ON_GATEKEEPER = "13", + FAILED_TO_GET_NEXT_POLL_ID = "14", + INVALID_APPROVAL = "15", + MACI_NOT_DEPLOYED = "16", + VERIFIER_NOT_DEPLOYED = "17", + VK_REGISTRY_NOT_DEPLOYED = "18", + FAILED_TO_SET_VERIFYING_KEYS_ON_VK_REGISTRY = "19", } diff --git a/packages/coordinator/ts/common/index.ts b/packages/coordinator/ts/common/index.ts index 78bdf495..8f0cc710 100644 --- a/packages/coordinator/ts/common/index.ts +++ b/packages/coordinator/ts/common/index.ts @@ -1,2 +1,3 @@ export { ErrorCodes } from "./errors"; export { ESupportedNetworks } from "./networks"; +export const MESSAGE_TREE_ARITY = 5n; diff --git a/packages/coordinator/ts/common/networks.ts b/packages/coordinator/ts/common/networks.ts index a74e922e..65d77512 100644 --- a/packages/coordinator/ts/common/networks.ts +++ b/packages/coordinator/ts/common/networks.ts @@ -1,3 +1,23 @@ +import { + arbitrum, + arbitrumSepolia, + base, + baseSepolia, + bsc, + type Chain, + gnosis, + holesky, + linea, + lineaSepolia, + mainnet, + optimism, + optimismSepolia, + polygon, + scroll, + scrollSepolia, + sepolia, +} from "viem/chains"; + export enum ESupportedNetworks { ETHEREUM = "mainnet", OPTIMISM = "optimism", @@ -5,60 +25,60 @@ export enum ESupportedNetworks { BSC = "bsc", BSC_CHAPEL = "chapel", GNOSIS_CHAIN = "gnosis", - FUSE = "fuse", POLYGON = "matic", - FANTOM_OPERA = "fantom", - ZKSYNC_ERA_TESTNET = "zksync-era-testnet", - BOBA = "boba", - MOONBEAM = "moonbeam", - MOONRIVER = "moonriver", - MOONBASE_ALPHA = "mbase", - FANTOM_TESTNET = "fantom-testnet", ARBITRUM_ONE = "arbitrum-one", - CELO = "celo", - AVALANCHE_FUJI = "fuji", - AVALANCHE = "avalanche", - CELO_ALFAJORES = "celo-alfajores", HOLESKY = "holesky", - AURORA = "aurora", - AURORA_TESTNET = "aurora-testnet", - HARMONY = "harmony", LINEA_SEPOLIA = "linea-sepolia", - GNOSIS_CHIADO = "gnosis-chiado", - MODE_SEPOLIA = "mode-sepolia", - MODE = "mode-mainnet", BASE_SEPOLIA = "base-sepolia", - ZKSYNC_ERA_SEPOLIA = "zksync-era-sepolia", - POLYGON_ZKEVM = "polygon-zkevm", - ZKSYNC_ERA = "zksync-era", ETHEREUM_SEPOLIA = "sepolia", ARBITRUM_SEPOLIA = "arbitrum-sepolia", LINEA = "linea", BASE = "base", SCROLL_SEPOLIA = "scroll-sepolia", SCROLL = "scroll", - BLAST_MAINNET = "blast-mainnet", - ASTAR_ZKEVM_MAINNET = "astar-zkevm-mainnet", - SEI_TESTNET = "sei-testnet", - BLAST_TESTNET = "blast-testnet", - ETHERLINK_TESTNET = "etherlink-testnet", - XLAYER_SEPOLIA = "xlayer-sepolia", - XLAYER_MAINNET = "xlayer-mainnet", - POLYGON_AMOY = "polygon-amoy", - ZKYOTO_TESTNET = "zkyoto-testnet", - POLYGON_ZKEVM_CARDONA = "polygon-zkevm-cardona", - SEI_MAINNET = "sei-mainnet", - ROOTSTOCK_MAINNET = "rootstock", - IOTEX_MAINNET = "iotex", - NEAR_MAINNET = "near-mainnet", - NEAR_TESTNET = "near-testnet", - COSMOS = "cosmoshub-4", - COSMOS_HUB = "theta-testnet-001", - OSMOSIS = "osmosis-1", - OSMO_TESTNET = "osmo-test-4", - ARWEAVE = "arweave-mainnet", - BITCOIN = "btc", - SOLANA = "solana-mainnet-beta", - INJECTIVE_MAINNET = "injective-mainnet", - INJECTIVE_TESTNET = "injective-testnet", } + +/** + * Get the Viem chain for a given network + * + * @param network - the network to get the chain for + * @returns the Viem chain + */ +export const viemChain = (network: ESupportedNetworks): Chain => { + switch (network) { + case ESupportedNetworks.ETHEREUM: + return mainnet; + case ESupportedNetworks.ETHEREUM_SEPOLIA: + return sepolia; + case ESupportedNetworks.ARBITRUM_ONE: + return arbitrum; + case ESupportedNetworks.ARBITRUM_SEPOLIA: + return arbitrumSepolia; + case ESupportedNetworks.BASE_SEPOLIA: + return baseSepolia; + case ESupportedNetworks.LINEA_SEPOLIA: + return lineaSepolia; + case ESupportedNetworks.SCROLL_SEPOLIA: + return scrollSepolia; + case ESupportedNetworks.SCROLL: + return scroll; + case ESupportedNetworks.BASE: + return base; + case ESupportedNetworks.HOLESKY: + return holesky; + case ESupportedNetworks.LINEA: + return linea; + case ESupportedNetworks.BSC: + return bsc; + case ESupportedNetworks.GNOSIS_CHAIN: + return gnosis; + case ESupportedNetworks.POLYGON: + return polygon; + case ESupportedNetworks.OPTIMISM: + return optimism; + case ESupportedNetworks.OPTIMISM_SEPOLIA: + return optimismSepolia; + default: + throw new Error(`Unsupported network: ${network}`); + } +}; diff --git a/packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts b/packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts new file mode 100644 index 00000000..e3cd622a --- /dev/null +++ b/packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts @@ -0,0 +1,97 @@ +import { Test } from "@nestjs/testing"; +import { zeroAddress } from "viem"; + +import { ErrorCodes, ESupportedNetworks } from "../../common"; +import { CryptoService } from "../../crypto/crypto.service"; +import { FileService } from "../../file/file.service"; +import { SessionKeysService } from "../../sessionKeys/sessionKeys.service"; +import { DeployerController } from "../deployer.controller"; +import { DeployerService } from "../deployer.service"; + +import { testMaciDeploymentConfig, testPollDeploymentConfig } from "./utils"; + +describe("DeployerController", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + let deployerController: DeployerController; + + const mockDeployerService = { + deployMaci: jest.fn(), + deployPoll: jest.fn(), + }; + + const defaultDeployMaciReturn: string = zeroAddress; + const defaultDeployPollReturn = "0"; + + const deployerControllerFail = new DeployerController( + new DeployerService(new SessionKeysService(new CryptoService(), new FileService()), new FileService()), + ); + + beforeEach(async () => { + const app = await Test.createTestingModule({ + controllers: [DeployerController], + }) + .useMocker((token) => { + if (token === DeployerService) { + mockDeployerService.deployMaci.mockResolvedValue(defaultDeployMaciReturn); + mockDeployerService.deployPoll.mockResolvedValue(defaultDeployPollReturn); + return mockDeployerService; + } + + return jest.fn(); + }) + .compile(); + + deployerController = app.get(DeployerController); + }); + + describe("v1/deploy/maci", () => { + test("should deploy all contract", async () => { + const response = await deployerController.deployMACIContracts({ + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + approval: "", + sessionKeyAddress: "0x", + config: testMaciDeploymentConfig, + }); + + expect(response).toEqual(defaultDeployMaciReturn); + }); + + test("should return 400 bad request when the service throws", async () => { + await expect( + deployerControllerFail.deployMACIContracts({ + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + approval: "", + sessionKeyAddress: "0x", + config: testMaciDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.DECRYPTION); + }); + }); + + describe("v1/deploy/poll", () => { + test("should deploy a new poll", async () => { + const response = await deployerController.deployPoll({ + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + approval: "", + sessionKeyAddress: "0x", + config: testPollDeploymentConfig, + }); + + expect(response).toEqual(defaultDeployPollReturn); + }); + + test("should return 400 bad request when the service throws", async () => { + await expect( + deployerControllerFail.deployPoll({ + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + approval: "", + sessionKeyAddress: "0x", + config: testPollDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.MACI_NOT_DEPLOYED); + }); + }); +}); diff --git a/packages/coordinator/ts/deployer/__tests__/deployer.service.test.ts b/packages/coordinator/ts/deployer/__tests__/deployer.service.test.ts new file mode 100644 index 00000000..d1421a27 --- /dev/null +++ b/packages/coordinator/ts/deployer/__tests__/deployer.service.test.ts @@ -0,0 +1,543 @@ +import dotenv from "dotenv"; +import { BaseContract, zeroPadBytes } from "ethers"; +import { + InitialVoiceCreditProxy__factory as VoiceCreditProxyFactory, + ContractStorage, + EContracts, + EInitialVoiceCreditProxies, + EGatekeepers, + FreeForAllGatekeeper__factory as FreeForAllGatekeeperFactory, + EASGatekeeper__factory as EASGatekeeperFactory, + ZupassGatekeeper__factory as ZupassGatekeeperFactory, + SemaphoreGatekeeper__factory as SemaphoreGatekeeperFactory, + HatsGatekeeperSingle__factory as HatsGatekeeperSingleFactory, + GitcoinPassportGatekeeper__factory as GitcoinPassportGatekeeperFactory, + MACI__factory as MACIFactory, + Verifier__factory as VerifierFactory, +} from "maci-contracts"; +import { zeroAddress } from "viem"; + +import path from "path"; + +import type { KeyLike } from "crypto"; + +import { ErrorCodes, ESupportedNetworks } from "../../common"; +import { CryptoService } from "../../crypto/crypto.service"; +import { FileService } from "../../file/file.service"; +import { mockSessionKeyApproval } from "../../sessionKeys/__tests__/utils"; +import { SessionKeysService } from "../../sessionKeys/sessionKeys.service"; +import { DeployerService } from "../deployer.service"; + +import { testMaciDeploymentConfig, testPollDeploymentConfig } from "./utils"; + +dotenv.config(); + +describe("DeployerService", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + const chain = ESupportedNetworks.OPTIMISM_SEPOLIA; + + const fileService = new FileService(); + const cryptoService = new CryptoService(); + + const storageInstance = ContractStorage.getInstance(path.join(process.cwd(), "deployed-contracts.json")); + const sessionKeyService = new SessionKeysService(cryptoService, fileService); + const deployerService = new DeployerService(sessionKeyService, fileService); + const { sessionKeyAddress } = sessionKeyService.generateSessionKey(); + + let publicKey: KeyLike; + + beforeAll(async () => { + publicKey = (await fileService.getPublicKey()).publicKey; + }); + + describe("getVoiceCreditProxyData", () => { + test("should return the voice credit proxy data", () => { + const voiceCreditProxyData = deployerService.getVoiceCreditProxyData(EInitialVoiceCreditProxies.Constant, chain, { + amount: "50", + }); + + expect(voiceCreditProxyData).toBeDefined(); + expect(voiceCreditProxyData.alreadyDeployed).toBe(false); + expect(voiceCreditProxyData.abi).toBeDefined(); + expect(voiceCreditProxyData.bytecode).toBeDefined(); + }); + + test("should return the voice credit proxy data and that the voice credit proxy is already deployed", async () => { + await storageInstance.register({ + id: EInitialVoiceCreditProxies.Constant as unknown as EContracts, + contract: new BaseContract("0x", VoiceCreditProxyFactory.abi), + network: chain, + args: ["50"], + }); + const voiceCreditProxyData = deployerService.getVoiceCreditProxyData(EInitialVoiceCreditProxies.Constant, chain, { + amount: "50", + }); + + expect(voiceCreditProxyData).toBeDefined(); + expect(voiceCreditProxyData.alreadyDeployed).toBe(true); + expect(voiceCreditProxyData.abi).toBeDefined(); + expect(voiceCreditProxyData.bytecode).toBeDefined(); + + storageInstance.cleanup(chain); + }); + + it("should throw when the voice credits proxy is not existent", () => { + expect(() => + deployerService.getVoiceCreditProxyData("NotExistent" as unknown as EInitialVoiceCreditProxies, chain, { + amount: "50", + }), + ).toThrow(ErrorCodes.UNSUPPORTED_VOICE_CREDIT_PROXY); + }); + }); + + describe("getGatekeeperData", () => { + // we cleanup after each test so we don't have leftover saved contracts + afterEach(() => { + storageInstance.cleanup(chain); + }); + + it("should throw when the gatekeeper is not existent", () => { + expect(() => deployerService.getGatekeeperData("NotExistent" as unknown as EGatekeepers, chain)).toThrow( + ErrorCodes.UNSUPPORTED_GATEKEEPER, + ); + }); + + describe("FreeForAllGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.FreeForAll, chain); + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.FreeForAll as unknown as EContracts, + contract: new BaseContract("0x", FreeForAllGatekeeperFactory.abi), + network: chain, + args: [], + }); + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.FreeForAll, chain); + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + + describe("EASGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.EAS, chain); + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.EAS as unknown as EContracts, + contract: new BaseContract("0x", EASGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, zeroPadBytes("0x", 32), zeroAddress], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.EAS, chain, { + easAddress: zeroAddress, + schema: zeroPadBytes("0x", 32), + attester: zeroAddress, + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return that the gatekeeper is not deployed when the args are different", async () => { + await storageInstance.register({ + id: EGatekeepers.EAS as unknown as EContracts, + contract: new BaseContract("0x", EASGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, zeroPadBytes("0x", 32), zeroAddress.replace("0x0", "0x1")], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.EAS, chain, { + easAddress: zeroAddress, + schema: zeroPadBytes("0x", 32), + attester: zeroAddress, + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + + describe("ZupassGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Zupass, chain, { + signer1: zeroAddress, + signer2: zeroAddress, + eventId: "0x", + zupassVerifier: zeroAddress, + }); + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.Zupass as unknown as EContracts, + contract: new BaseContract("0x", ZupassGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, zeroAddress, "0x", zeroAddress], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Zupass, chain, { + signer1: zeroAddress, + signer2: zeroAddress, + eventId: "0x", + zupassVerifier: zeroAddress, + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return that the gatekeeper is not deployed when the args are different", async () => { + await storageInstance.register({ + id: EGatekeepers.Zupass as unknown as EContracts, + contract: new BaseContract("0x", ZupassGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, zeroAddress, "0x", zeroAddress.replace("0x0", "0x1")], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Zupass, chain, { + signer1: zeroAddress, + signer2: zeroAddress, + eventId: "0x", + zupassVerifier: zeroAddress, + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + + describe("SemaphoreGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Semaphore, chain, { + semaphoreContract: zeroAddress, + groupId: "0", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.Semaphore as unknown as EContracts, + contract: new BaseContract("0x", SemaphoreGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, "0"], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Semaphore, chain, { + semaphoreContract: zeroAddress, + groupId: "0", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return that the gatekeeper is not deployed when the args are different", async () => { + await storageInstance.register({ + id: EGatekeepers.Semaphore as unknown as EContracts, + contract: new BaseContract("0x", SemaphoreGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, "0"], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.Semaphore, chain, { + semaphoreContract: zeroAddress, + groupId: "1", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + + describe("HatsGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.HatsSingle, chain, { + hatsProtocolAddress: zeroAddress, + critrionHats: [zeroAddress], + }); + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.HatsSingle as unknown as EContracts, + contract: new BaseContract("0x", HatsGatekeeperSingleFactory.abi), + network: chain, + args: [zeroAddress, [zeroAddress]], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.HatsSingle, chain, { + hatsProtocolAddress: zeroAddress, + critrionHats: [zeroAddress], + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return that the gatekeeper is not deployed when the args are different", async () => { + await storageInstance.register({ + id: EGatekeepers.HatsSingle as unknown as EContracts, + contract: new BaseContract("0x", HatsGatekeeperSingleFactory.abi), + network: chain, + args: [zeroAddress, ["0x"]], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.HatsSingle, chain, { + hatsProtocolAddress: zeroAddress, + critrionHats: ["0x1"], + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + + describe("GitcoinPassportGatekeeper", () => { + it("should return the gatekeeper data and that the gatekeeper is not deployed", () => { + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.GitcoinPassport, chain, { + decoderAddress: zeroAddress, + passingScore: "0", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return the gatekeeper data and that the gatekeeper is already deployed", async () => { + await storageInstance.register({ + id: EGatekeepers.GitcoinPassport as unknown as EContracts, + contract: new BaseContract("0x", GitcoinPassportGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, "0"], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.GitcoinPassport, chain, { + decoderAddress: zeroAddress, + passingScore: "0", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(true); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + + it("should return that the gatekeeper is not deployed when the args are different", async () => { + await storageInstance.register({ + id: EGatekeepers.GitcoinPassport as unknown as EContracts, + contract: new BaseContract("0x", GitcoinPassportGatekeeperFactory.abi), + network: chain, + args: [zeroAddress, "0"], + }); + + const gatekeeperData = deployerService.getGatekeeperData(EGatekeepers.GitcoinPassport, chain, { + decoderAddress: zeroAddress, + passingScore: "1", + }); + + expect(gatekeeperData).toBeDefined(); + expect(gatekeeperData.alreadyDeployed).toBe(false); + expect(gatekeeperData.abi).toBeDefined(); + expect(gatekeeperData.bytecode).toBeDefined(); + }); + }); + }); + + describe("deployMaci", () => { + test("should throw when passing an invalid ciphertext", async () => { + await expect( + deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: "", + sessionKeyAddress: "0x", + }), + ).rejects.toThrow(ErrorCodes.DECRYPTION); + }); + + test("should throw when passing a non existent session key address", async () => { + const encryptedSessionKeyApproval = cryptoService.encrypt(publicKey, "test"); + await expect( + deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: encryptedSessionKeyApproval, + sessionKeyAddress: "0x5", + }), + ).rejects.toThrow(ErrorCodes.SESSION_KEY_NOT_FOUND); + }); + + test("should throw when the approval is not valid", async () => { + const encryptedSessionKeyApproval = cryptoService.encrypt(publicKey, "test"); + await expect( + deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: encryptedSessionKeyApproval, + sessionKeyAddress, + }), + ).rejects.toThrow(ErrorCodes.INVALID_APPROVAL); + }); + + test("should deploy all new contracts", async () => { + const approval = await mockSessionKeyApproval(sessionKeyAddress); + const encryptedApproval = cryptoService.encrypt(publicKey, approval); + + const mockDeployMaci = jest.fn().mockResolvedValue(zeroAddress); + jest.spyOn(DeployerService.prototype, "deployMaci").mockImplementation(mockDeployMaci); + + const maciAddress = await deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: encryptedApproval, + sessionKeyAddress, + }); + + expect(maciAddress).toBe(zeroAddress); + }); + }); + + describe("deployPoll", () => { + afterEach(() => { + storageInstance.cleanup(chain); + }); + + test("should throw when there is no maci contract deployed", async () => { + await expect( + deployerService.deployPoll({ + approval: "", + sessionKeyAddress: "0x", + chain, + config: testPollDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.MACI_NOT_DEPLOYED); + }); + + test("should throw when there is no maci contract deployed to this specific chain", async () => { + await storageInstance.register({ + id: EContracts.MACI, + contract: new BaseContract("0x", MACIFactory.abi), + network: ESupportedNetworks.ARBITRUM_ONE, + args: [], + }); + + await expect( + deployerService.deployPoll({ + approval: "", + sessionKeyAddress: "0x", + chain, + config: testPollDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.MACI_NOT_DEPLOYED); + }); + + it("should throw when there is no verifier deployed", async () => { + await storageInstance.register({ + id: EContracts.MACI, + contract: new BaseContract("0x", MACIFactory.abi), + network: chain, + args: [], + }); + + await expect( + deployerService.deployPoll({ + approval: "", + sessionKeyAddress: "0x", + chain, + config: testPollDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.VERIFIER_NOT_DEPLOYED); + }); + + it("should throw when there is no vk registry deployed", async () => { + await storageInstance.register({ + id: EContracts.MACI, + contract: new BaseContract("0x", MACIFactory.abi), + network: chain, + args: [], + }); + + await storageInstance.register({ + id: EContracts.Verifier, + contract: new BaseContract("0x", VerifierFactory.abi), + network: chain, + args: [], + }); + + await expect( + deployerService.deployPoll({ + approval: "", + sessionKeyAddress: "0x", + chain, + config: testPollDeploymentConfig, + }), + ).rejects.toThrow(ErrorCodes.VK_REGISTRY_NOT_DEPLOYED); + }); + + test("should deploy a poll", async () => { + const approval = await mockSessionKeyApproval(sessionKeyAddress); + const encryptedApproval = cryptoService.encrypt(publicKey, approval); + + const mockDeployPoll = jest.fn().mockResolvedValue("0"); + jest.spyOn(DeployerService.prototype, "deployPoll").mockImplementation(mockDeployPoll); + + const pollId = await deployerService.deployPoll({ + config: testPollDeploymentConfig, + chain, + approval: encryptedApproval, + sessionKeyAddress, + }); + + expect(pollId).toBe("0"); + }); + }); +}); diff --git a/packages/coordinator/ts/deployer/__tests__/utils.ts b/packages/coordinator/ts/deployer/__tests__/utils.ts new file mode 100644 index 00000000..d478257e --- /dev/null +++ b/packages/coordinator/ts/deployer/__tests__/utils.ts @@ -0,0 +1,45 @@ +import { EGatekeepers, EInitialVoiceCreditProxies } from "maci-contracts"; +import { Keypair } from "maci-domainobjs"; + +import { IDeployMaciConfig, IDeployPollConfig } from "../types"; + +/** + * MACI deployment configuration for testing + */ +export const testMaciDeploymentConfig: IDeployMaciConfig = { + gatekeeper: { + type: EGatekeepers.FreeForAll, + }, + initialVoiceCreditsProxy: { + type: EInitialVoiceCreditProxies.Constant, + args: { + amount: "100", + }, + }, + MACI: { + gatekeeper: EGatekeepers.FreeForAll, + stateTreeDepth: 10, + }, + VkRegistry: { + args: { + stateTreeDepth: 10n, + messageTreeDepth: 2n, + voteOptionTreeDepth: 2n, + messageBatchDepth: 1n, + intStateTreeDepth: 1n, + }, + }, +}; + +/** + * Poll deployment configuration for testing + */ +export const testPollDeploymentConfig: IDeployPollConfig = { + pollDuration: 100, + useQuadraticVoting: false, + coordinatorPubkey: new Keypair().pubKey.serialize(), + intStateTreeDepth: 1, + messageTreeDepth: 2, + voteOptionTreeDepth: 2, + messageTreeSubDepth: 1, +}; diff --git a/packages/coordinator/ts/deployer/deployer.controller.ts b/packages/coordinator/ts/deployer/deployer.controller.ts new file mode 100644 index 00000000..d364324c --- /dev/null +++ b/packages/coordinator/ts/deployer/deployer.controller.ts @@ -0,0 +1,62 @@ +/* eslint-disable @typescript-eslint/no-shadow */ +import { Body, Controller, HttpException, HttpStatus, Logger, Post, UseGuards } from "@nestjs/common"; +import { ApiBearerAuth, ApiBody, ApiResponse, ApiTags } from "@nestjs/swagger"; + +import { AccountSignatureGuard } from "../auth/AccountSignatureGuard.service"; + +import { DeployerService } from "./deployer.service"; +import { DeployerServiceDeployMaciDto, DeployerServiceDeployPollDto } from "./dto"; + +@ApiTags("v1/deploy") +@ApiBearerAuth() +@Controller("v1/deploy") +@UseGuards(AccountSignatureGuard) +export class DeployerController { + /** + * Logger + */ + private readonly logger = new Logger(DeployerController.name); + + /** + * Initialize DeployerController + * + * @param deployerService - deployer service + */ + constructor(private readonly deployerService: DeployerService) {} + + /** + * Deploy MACI contracts api method + * + * @param args - deploy maci dto + * @returns maci contract address + */ + @ApiBody({ type: DeployerServiceDeployMaciDto }) + @ApiResponse({ status: HttpStatus.CREATED, description: "The MACI contracts were successfully deployed" }) + @ApiResponse({ status: HttpStatus.FORBIDDEN, description: "Forbidden" }) + @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: "BadRequest" }) + @Post("maci") + async deployMACIContracts(@Body() args: DeployerServiceDeployMaciDto): Promise { + return this.deployerService.deployMaci(args).catch((error: Error) => { + this.logger.error(`Error:`, error); + throw new HttpException(error.message, HttpStatus.BAD_REQUEST); + }); + } + + /** + * Delete a session key api method + * + * @param args - delete session key dto + * @returns deleted session key address + */ + @ApiBody({ type: DeployerServiceDeployPollDto }) + @ApiResponse({ status: HttpStatus.CREATED, description: "The Poll was successfully deployed" }) + @ApiResponse({ status: HttpStatus.FORBIDDEN, description: "Forbidden" }) + @ApiResponse({ status: HttpStatus.BAD_REQUEST, description: "BadRequest" }) + @Post("poll") + async deployPoll(@Body() args: DeployerServiceDeployPollDto): Promise { + return this.deployerService.deployPoll(args).catch((error: Error) => { + this.logger.error(`Error:`, error); + throw new HttpException(error.message, HttpStatus.BAD_REQUEST); + }); + } +} diff --git a/packages/coordinator/ts/deployer/deployer.module.ts b/packages/coordinator/ts/deployer/deployer.module.ts new file mode 100644 index 00000000..95f95ce8 --- /dev/null +++ b/packages/coordinator/ts/deployer/deployer.module.ts @@ -0,0 +1,15 @@ +import { Module } from "@nestjs/common"; + +import { CryptoModule } from "../crypto/crypto.module"; +import { FileModule } from "../file/file.module"; +import { SessionKeysModule } from "../sessionKeys/sessionKeys.module"; + +import { DeployerController } from "./deployer.controller"; +import { DeployerService } from "./deployer.service"; + +@Module({ + imports: [FileModule, CryptoModule, SessionKeysModule], + controllers: [DeployerController], + providers: [DeployerService], +}) +export class DeployerModule {} diff --git a/packages/coordinator/ts/deployer/deployer.service.ts b/packages/coordinator/ts/deployer/deployer.service.ts new file mode 100644 index 00000000..c1f9ce76 --- /dev/null +++ b/packages/coordinator/ts/deployer/deployer.service.ts @@ -0,0 +1,785 @@ +import { Injectable, Logger } from "@nestjs/common"; +import { KernelAccountClient, KernelSmartAccount } from "@zerodev/sdk"; +import { BaseContract, InterfaceAbi } from "ethers"; +import { extractVk } from "maci-circuits"; +import { + AccQueueQuinaryMaci__factory as AccQueueQuinaryMaciFactory, + ConstantInitialVoiceCreditProxy__factory as ConstantInitialVoiceCreditProxyFactory, + ContractStorage, + EGatekeepers, + FreeForAllGatekeeper__factory as FreeForAllGatekeeperFactory, + EASGatekeeper__factory as EASGatekeeperFactory, + ZupassGatekeeper__factory as ZupassGatekeeperFactory, + HatsGatekeeperSingle__factory as HatsGatekeeperSingleFactory, + SemaphoreGatekeeper__factory as SemaphoreGatekeeperFactory, + GitcoinPassportGatekeeper__factory as GitcoinPassportGatekeeperFactory, + Verifier__factory as VerifierFactory, + PoseidonT3__factory as PoseidonT3Factory, + PoseidonT4__factory as PoseidonT4Factory, + PoseidonT5__factory as PoseidonT5Factory, + PoseidonT6__factory as PoseidonT6Factory, + VkRegistry__factory as VkRegistryFactory, + TallyFactory__factory as TallyFactoryFactory, + PollFactory__factory as PollFactoryFactory, + MessageProcessorFactory__factory as MessageProcessorFactoryFactory, + MessageProcessor__factory as MessageProcessorFactory, + Tally__factory as TallyFactory, + Poll__factory as PollFactory, + MACI__factory as MACIFactory, + EContracts, + EInitialVoiceCreditProxies, + genEmptyBallotRoots, + EMode, + IVerifyingKeyStruct, +} from "maci-contracts"; +import { IVkObjectParams, PubKey, VerifyingKey } from "maci-domainobjs"; +import { ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types"; +import { + Abi, + Chain, + encodeFunctionData, + HttpTransport, + PublicClient, + Transport, + type Hex, + keccak256, + toBytes, +} from "viem"; + +import path from "path"; + +import { ErrorCodes, ESupportedNetworks, MESSAGE_TREE_ARITY } from "../common"; +import { getDeployedContractAddress, getPublicClient } from "../common/accountAbstraction"; +import { FileService } from "../file/file.service"; +import { SessionKeysService } from "../sessionKeys/sessionKeys.service"; + +import { + IDeployMaciArgs, + IDeployPollArgs, + IEASGatekeeperArgs, + IGatekeeperArgs, + IGitcoinPassportGatekeeperArgs, + IHatsGatekeeperArgs, + IInitialVoiceCreditProxyArgs, + ISemaphoreGatekeeperArgs, + IZupassGatekeeperArgs, +} from "./types"; + +/** + * DeployerService is responsible for deploying contracts. + */ +@Injectable() +export class DeployerService { + /** + * Logger + */ + private readonly logger = new Logger(DeployerService.name); + + /** + * Contract storage instance + */ + private readonly storage: ContractStorage; + + /** + * Create a new instance of DeployerService + * + * @param fileService - file service + */ + constructor( + private readonly sessionKeysService: SessionKeysService, + private readonly fileService: FileService, + ) { + this.logger = new Logger(DeployerService.name); + this.storage = ContractStorage.getInstance(path.join(process.cwd(), "deployed-contracts.json")); + } + + /** + * Get the gatekeeper abi and bytecode based on the gatekeeper type + * and also check if there is already an instance deployed + * + * @param gatekeeperType - the gatekeeper type + * @param network - the network + * @param args - the gatekeeper args + * @returns - the gatekeeper abi and bytecode + */ + getGatekeeperData( + gatekeeperType: EGatekeepers, + network: ESupportedNetworks, + args?: IGatekeeperArgs, + ): { abi: Abi; bytecode: Hex; alreadyDeployed: boolean } { + let address: string | undefined; + let storedArgs: string[] | undefined; + let isAlreadyDeployed: boolean; + + // based on the gatekeeper type, we need to deploy the correct gatekeeper + switch (gatekeeperType) { + case EGatekeepers.FreeForAll: + address = this.storage.getAddress(gatekeeperType as unknown as EContracts, network); + return { + abi: FreeForAllGatekeeperFactory.abi, + bytecode: FreeForAllGatekeeperFactory.bytecode, + alreadyDeployed: !!address, + }; + case EGatekeepers.EAS: + storedArgs = this.storage.getContractArgs(gatekeeperType as unknown as EContracts, network); + isAlreadyDeployed = + !!storedArgs && + storedArgs.length === 3 && + storedArgs[0] === (args as IEASGatekeeperArgs).easAddress && + storedArgs[1] === (args as IEASGatekeeperArgs).schema && + storedArgs[2] === (args as IEASGatekeeperArgs).attester; + return { + abi: EASGatekeeperFactory.abi, + bytecode: EASGatekeeperFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + case EGatekeepers.Zupass: + storedArgs = this.storage.getContractArgs(gatekeeperType as unknown as EContracts, network); + isAlreadyDeployed = + !!storedArgs && + storedArgs.length === 4 && + storedArgs[0] === (args as IZupassGatekeeperArgs).signer1 && + storedArgs[1] === (args as IZupassGatekeeperArgs).signer2 && + storedArgs[2] === (args as IZupassGatekeeperArgs).eventId && + storedArgs[3] === (args as IZupassGatekeeperArgs).zupassVerifier; + return { + abi: ZupassGatekeeperFactory.abi, + bytecode: ZupassGatekeeperFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + case EGatekeepers.HatsSingle: + storedArgs = this.storage.getContractArgs(gatekeeperType as unknown as EContracts, network); + isAlreadyDeployed = + !!storedArgs && + storedArgs.length === 2 && + storedArgs[0] === (args as IHatsGatekeeperArgs).hatsProtocolAddress && + JSON.stringify(storedArgs[1]) === JSON.stringify((args as IHatsGatekeeperArgs).critrionHats); + return { + abi: HatsGatekeeperSingleFactory.abi, + bytecode: HatsGatekeeperSingleFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + case EGatekeepers.Semaphore: + storedArgs = this.storage.getContractArgs(gatekeeperType as unknown as EContracts, network); + isAlreadyDeployed = + !!storedArgs && + storedArgs.length === 2 && + storedArgs[0] === (args as ISemaphoreGatekeeperArgs).semaphoreContract && + storedArgs[1] === (args as ISemaphoreGatekeeperArgs).groupId; + return { + abi: SemaphoreGatekeeperFactory.abi, + bytecode: SemaphoreGatekeeperFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + case EGatekeepers.GitcoinPassport: + storedArgs = this.storage.getContractArgs(gatekeeperType as unknown as EContracts, network); + isAlreadyDeployed = + !!storedArgs && + storedArgs.length === 2 && + storedArgs[0] === (args as IGitcoinPassportGatekeeperArgs).decoderAddress && + storedArgs[1] === (args as IGitcoinPassportGatekeeperArgs).passingScore; + return { + abi: GitcoinPassportGatekeeperFactory.abi, + bytecode: GitcoinPassportGatekeeperFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + default: + throw new Error(ErrorCodes.UNSUPPORTED_GATEKEEPER); + } + } + + /** + * Get the voice credit proxy abi and bytecode based on the voice credit proxy type + * and also check if there is already an instance deployed + * + * @param voiceCreditProxyType - the voice credit proxy type + * @param network - the network + * @param args - the voice credit proxy args + * @returns - the voice credit proxy abi and bytecode + */ + getVoiceCreditProxyData( + voiceCreditProxyType: EInitialVoiceCreditProxies, + network: ESupportedNetworks, + args: IInitialVoiceCreditProxyArgs, + ): { + abi: Abi; + bytecode: Hex; + alreadyDeployed: boolean; + } { + let storedArgs: string[] | undefined; + let isAlreadyDeployed: boolean; + + switch (voiceCreditProxyType) { + case EInitialVoiceCreditProxies.Constant: + storedArgs = this.storage.getContractArgs(voiceCreditProxyType as unknown as EContracts, network); + isAlreadyDeployed = !!storedArgs && storedArgs[0] === args.amount; + + return { + abi: ConstantInitialVoiceCreditProxyFactory.abi, + bytecode: ConstantInitialVoiceCreditProxyFactory.bytecode, + alreadyDeployed: isAlreadyDeployed, + }; + default: + throw new Error(ErrorCodes.UNSUPPORTED_VOICE_CREDIT_PROXY); + } + } + + /** + * Add linked libraries to the bytecode + * + * @param bytecode - the bytecode + * @param libraries - the libraries + * @returns - the bytecode with the linked libraries + */ + addLinkedLibrariesToBytecode(bytecode: Hex, libraries: Record): Hex { + return Object.entries(libraries).reduce((linkedBytecode, [libName, libAddress]) => { + const placeholder = `__$${keccak256(toBytes(libName)).slice(2, 36)}$__`; + const regex = new RegExp(placeholder, "g"); + return linkedBytecode.replace(regex, libAddress.slice(2)) as Hex; + }, bytecode); + } + + /** + * Deploy a contract and get the address + * @param kernelClient - the kernel client + * @param abi - the abi + * @param bytecode - the bytecode + * @param args - the args + * @param publicClient - the public client + * @returns - the address + */ + async deployAndGetAddress( + kernelClient: KernelAccountClient< + ENTRYPOINT_ADDRESS_V07_TYPE, + Transport, + Chain, + KernelSmartAccount + >, + abi: Abi, + bytecode: Hex, + args: unknown[], + publicClient: PublicClient, + ): Promise { + const deployTx = await kernelClient.deployContract({ + abi, + args, + bytecode, + account: kernelClient.account.address, + }); + + const receipt = await publicClient.waitForTransactionReceipt({ + hash: deployTx, + }); + + const address = getDeployedContractAddress(receipt); + + return address; + } + + /** + * Deploy a contract and store the address + * + * @param contract - the contract to deploy + * @param args - the args + * @param abi - the abi + * @param bytecode - the bytecode + * @param kernelClient - the kernel client + * @param publicClient - the public client + * @param chain - the chain + * @returns - the address of the deployed contract + */ + async deployAndStore( + contract: EContracts, + args: unknown[], + abi: Abi, + bytecode: Hex, + kernelClient: KernelAccountClient< + ENTRYPOINT_ADDRESS_V07_TYPE, + Transport, + Chain, + KernelSmartAccount + >, + publicClient: PublicClient, + chain: ESupportedNetworks, + ): Promise { + let address = this.storage.getAddress(contract, chain); + + if (!address) { + address = await this.deployAndGetAddress(kernelClient, abi, bytecode, args, publicClient); + + if (!address) { + this.logger.error(`Failed to deploy contract: ${contract}`); + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} ${contract}`); + } + + await this.storage.register({ + id: contract, + contract: new BaseContract(address, abi as unknown as InterfaceAbi), + args: args.map((arg) => String(arg)), + network: chain, + }); + } + + return address; + } + + /** + * Deploy MACI contracts + * + * @param args - deploy maci arguments + * @param options - ws hooks + * @returns - deployed maci contract + * @returns the address of the deployed maci contract + */ + async deployMaci({ approval, sessionKeyAddress, chain, config }: IDeployMaciArgs): Promise { + const publicClient = getPublicClient(process.env.COORDINATOR_RPC_URL!, chain); + + const kernelClient = await this.sessionKeysService.generateClientFromSessionKey(sessionKeyAddress, approval, chain); + + let initialVoiceCreditProxyAddress = this.storage.getAddress( + config.initialVoiceCreditsProxy.type as unknown as EContracts, + chain, + ); + + const initialVoiceCreditProxyData = this.getVoiceCreditProxyData( + config.initialVoiceCreditsProxy.type, + chain, + config.initialVoiceCreditsProxy.args, + ); + + // if the initial voice credit proxy is not already deployed, we need to deploy it + if (!initialVoiceCreditProxyData.alreadyDeployed) { + initialVoiceCreditProxyAddress = await this.deployAndGetAddress( + kernelClient, + initialVoiceCreditProxyData.abi, + initialVoiceCreditProxyData.bytecode, + Object.values(config.initialVoiceCreditsProxy.args), + publicClient, + ); + + if (!initialVoiceCreditProxyAddress) { + this.logger.error(`Failed to deploy voice credit proxy: ${config.initialVoiceCreditsProxy.type}`); + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} ${config.initialVoiceCreditsProxy.type}`); + } + + await this.storage.register({ + id: config.initialVoiceCreditsProxy.type as unknown as EContracts, + contract: new BaseContract( + initialVoiceCreditProxyAddress, + initialVoiceCreditProxyData.abi as unknown as InterfaceAbi, + ), + args: Object.values(config.initialVoiceCreditsProxy.args), + network: chain, + }); + } + + let gatekeeperAddress = this.storage.getAddress(config.gatekeeper.type as unknown as EContracts, chain); + const gatekeeperData = this.getGatekeeperData(config.gatekeeper.type, chain, config.gatekeeper.args); + + // if the gatekeeper is not already deployed, we need to deploy it + if (!gatekeeperData.alreadyDeployed) { + gatekeeperAddress = await this.deployAndGetAddress( + kernelClient, + gatekeeperData.abi, + gatekeeperData.bytecode, + config.gatekeeper.args ? Object.values(config.gatekeeper.args) : [], + publicClient, + ); + + // if the gatekeeper address is not found, we need to throw an error + if (!gatekeeperAddress) { + this.logger.error(`Failed to deploy gatekeeper: ${config.gatekeeper.type}`); + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} ${config.gatekeeper.type}`); + } + + // store the gatekeeper address in the storage + await this.storage.register({ + id: config.gatekeeper.type as unknown as EContracts, + contract: new BaseContract(gatekeeperAddress, gatekeeperData.abi as InterfaceAbi), + args: config.gatekeeper.args ? Object.values(config.gatekeeper.args) : [], + network: chain, + }); + } + + // deploy all maci contracts + + // 1. verifier + await this.deployAndStore( + EContracts.Verifier, + [], + VerifierFactory.abi, + VerifierFactory.bytecode, + kernelClient, + publicClient, + chain, + ); + + // 2. poseidon + const poseidonT3Address = await this.deployAndStore( + EContracts.PoseidonT3, + [], + PoseidonT3Factory.abi, + PoseidonT3Factory.bytecode, + kernelClient, + publicClient, + chain, + ); + + const poseidonT4Address = await this.deployAndStore( + EContracts.PoseidonT4, + [], + PoseidonT4Factory.abi, + PoseidonT4Factory.bytecode, + kernelClient, + publicClient, + chain, + ); + + const poseidonT5Address = await this.deployAndStore( + EContracts.PoseidonT5, + [], + PoseidonT5Factory.abi, + PoseidonT5Factory.bytecode, + kernelClient, + publicClient, + chain, + ); + + const poseidonT6Address = await this.deployAndStore( + EContracts.PoseidonT6, + [], + PoseidonT6Factory.abi, + PoseidonT6Factory.bytecode, + kernelClient, + publicClient, + chain, + ); + + // 3. factories + const pollFactoryAddress = await this.deployAndStore( + EContracts.PollFactory, + [], + PollFactoryFactory.abi, + this.addLinkedLibrariesToBytecode(PollFactoryFactory.bytecode, { + PoseidonT3: poseidonT3Address, + PoseidonT4: poseidonT4Address, + PoseidonT5: poseidonT5Address, + PoseidonT6: poseidonT6Address, + }), + kernelClient, + publicClient, + chain, + ); + + const tallyFactoryAddress = await this.deployAndStore( + EContracts.TallyFactory, + [], + TallyFactoryFactory.abi, + this.addLinkedLibrariesToBytecode(TallyFactoryFactory.bytecode, { + PoseidonT3: poseidonT3Address, + PoseidonT4: poseidonT4Address, + PoseidonT5: poseidonT5Address, + PoseidonT6: poseidonT6Address, + }), + kernelClient, + publicClient, + chain, + ); + + const messageProcessorFactoryAddress = await this.deployAndStore( + EContracts.MessageProcessorFactory, + [], + MessageProcessorFactoryFactory.abi, + this.addLinkedLibrariesToBytecode(MessageProcessorFactoryFactory.bytecode, { + PoseidonT3: poseidonT3Address, + PoseidonT4: poseidonT4Address, + PoseidonT5: poseidonT5Address, + PoseidonT6: poseidonT6Address, + }), + kernelClient, + publicClient, + chain, + ); + + // 4. VkRegistry + const vkRegistryAddress = await this.deployAndStore( + EContracts.VkRegistry, + [], + VkRegistryFactory.abi, + VkRegistryFactory.bytecode, + kernelClient, + publicClient, + chain, + ); + + const processMessagesZkeyPathQv = this.fileService.getZkeyFilePaths("processMessages", true); + const tallyVotesZkeyPathQv = this.fileService.getZkeyFilePaths("tallyVotes", true); + const processMessagesZkeyPathNonQv = this.fileService.getZkeyFilePaths("processMessages", false); + const tallyVotesZkeyPathNonQv = this.fileService.getZkeyFilePaths("tallyVotes", false); + + const [qvProcessVk, qvTallyVk, nonQvProcessVk, nonQvTallyVk] = await Promise.all([ + extractVk(processMessagesZkeyPathQv.zkey), + extractVk(tallyVotesZkeyPathQv.zkey), + extractVk(processMessagesZkeyPathNonQv.zkey), + extractVk(tallyVotesZkeyPathNonQv.zkey), + ]).then((vks) => + vks.map( + (vk: IVkObjectParams | "" | undefined) => + vk && (VerifyingKey.fromObj(vk).asContractParam() as IVerifyingKeyStruct), + ), + ); + + const processZkeys = [qvProcessVk, nonQvProcessVk].filter(Boolean) as IVerifyingKeyStruct[]; + const tallyZkeys = [qvTallyVk, nonQvTallyVk].filter(Boolean) as IVerifyingKeyStruct[]; + + // set vKeys on the vk registry + const userOpHashVkRegistry = await kernelClient.sendUserOperation({ + userOperation: { + callData: await kernelClient.account.encodeCallData({ + to: vkRegistryAddress as Hex, + value: 0n, + data: encodeFunctionData({ + abi: VkRegistryFactory.abi, + functionName: "setVerifyingKeysBatch", + args: [ + config.VkRegistry.args.stateTreeDepth, + config.VkRegistry.args.intStateTreeDepth, + config.VkRegistry.args.messageTreeDepth, + config.VkRegistry.args.voteOptionTreeDepth, + MESSAGE_TREE_ARITY ** config.VkRegistry.args.messageBatchDepth, + [EMode.QV, EMode.NON_QV], + // @ts-expect-error - the abi has a more complex type for the processZkeys and tallyZkeys + processZkeys, + // @ts-expect-error - the abi has a more complex type for the processZkeys and tallyZkeys + tallyZkeys, + ], + }), + }), + }, + // @ts-expect-error zerodev/permissionless related issue + account: kernelClient.account, + }); + + const receiptVkRegistry = await publicClient.waitForTransactionReceipt({ + hash: userOpHashVkRegistry, + }); + + if (receiptVkRegistry.status !== "success") { + throw new Error(ErrorCodes.FAILED_TO_SET_VERIFYING_KEYS_ON_VK_REGISTRY); + } + + // 5. maci (here we don't check whether one is already deployed, we just deploy it) + const emptyBallotRoots = genEmptyBallotRoots(config.MACI.stateTreeDepth); + const maciAddress = await this.deployAndGetAddress( + kernelClient, + MACIFactory.abi, + this.addLinkedLibrariesToBytecode(MACIFactory.bytecode, { + PoseidonT3: poseidonT3Address, + PoseidonT4: poseidonT4Address, + PoseidonT5: poseidonT5Address, + PoseidonT6: poseidonT6Address, + }), + [ + pollFactoryAddress, + messageProcessorFactoryAddress, + tallyFactoryAddress, + gatekeeperAddress, + initialVoiceCreditProxyAddress, + config.MACI.stateTreeDepth, + emptyBallotRoots, + ], + publicClient, + ); + + if (!maciAddress) { + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} - ${EContracts.MACI}`); + } + + await this.storage.register({ + id: EContracts.MACI, + contract: new BaseContract(maciAddress, MACIFactory.abi as unknown as InterfaceAbi), + args: [ + pollFactoryAddress, + messageProcessorFactoryAddress, + tallyFactoryAddress, + gatekeeperAddress, + initialVoiceCreditProxyAddress, + config.MACI.stateTreeDepth, + emptyBallotRoots.map((root) => root.toString()), + ], + network: chain, + }); + + // set the gate on the gatekeeper + const userOpHash = await kernelClient.sendUserOperation({ + userOperation: { + callData: await kernelClient.account.encodeCallData({ + to: gatekeeperAddress! as Hex, + value: 0n, + data: encodeFunctionData({ + abi: gatekeeperData.abi, + functionName: "setMaciInstance", + args: [maciAddress], + }), + }), + }, + // @ts-expect-error zerodev/permissionless related issue + account: kernelClient.account, + }); + + const receipt = await publicClient.waitForTransactionReceipt({ + hash: userOpHash, + }); + + if (receipt.status !== "success") { + throw new Error(ErrorCodes.FAILED_TO_SET_MACI_INSTANCE_ON_GATEKEEPER); + } + + return maciAddress; + } + + /** + * Deploy a poll + * + * @param args - deploy poll dto + * @returns poll id + */ + async deployPoll({ approval, sessionKeyAddress, chain, config }: IDeployPollArgs): Promise { + // check if there is a maci contract deployed on this chain + const maciAddress = this.storage.getAddress(EContracts.MACI, chain); + if (!maciAddress) { + throw new Error(ErrorCodes.MACI_NOT_DEPLOYED); + } + + // check if there is a verifier deployed on this chain + const verifierAddress = this.storage.getAddress(EContracts.Verifier, chain); + if (!verifierAddress) { + throw new Error(ErrorCodes.VERIFIER_NOT_DEPLOYED); + } + + // check if there is a vk registry deployed on this chain + const vkRegistryAddress = this.storage.getAddress(EContracts.VkRegistry, chain); + if (!vkRegistryAddress) { + throw new Error(ErrorCodes.VK_REGISTRY_NOT_DEPLOYED); + } + + const mode = config.useQuadraticVoting ? EMode.QV : EMode.NON_QV; + + const kernelClient = await this.sessionKeysService.generateClientFromSessionKey(sessionKeyAddress, approval, chain); + + const publicClient = getPublicClient(process.env.COORDINATOR_RPC_URL!, chain); + + const pollId = await publicClient.readContract({ + address: maciAddress as Hex, + abi: MACIFactory.abi, + functionName: "nextPollId", + }); + + if (!pollId) { + throw new Error(ErrorCodes.FAILED_TO_GET_NEXT_POLL_ID); + } + + const userOpHash = await kernelClient.sendUserOperation({ + userOperation: { + callData: await kernelClient.account.encodeCallData({ + to: maciAddress as Hex, + value: 0n, + data: encodeFunctionData({ + abi: MACIFactory.abi, + functionName: "deployPoll", + args: [ + BigInt(config.pollDuration), + { + intStateTreeDepth: config.intStateTreeDepth, + messageTreeSubDepth: config.messageTreeSubDepth, + messageTreeDepth: config.messageTreeDepth, + voteOptionTreeDepth: config.voteOptionTreeDepth, + }, + PubKey.deserialize(config.coordinatorPubkey).asContractParam() as { x: bigint; y: bigint }, + verifierAddress as Hex, + vkRegistryAddress as Hex, + mode, + ], + }), + }), + }, + // @ts-expect-error zerodev/permissionless related issue + account: kernelClient.account, + }); + + const receipt = await publicClient.waitForTransactionReceipt({ + hash: userOpHash, + }); + + if (receipt.status !== "success") { + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} - ${EContracts.Poll}`); + } + + // get the addresses so we can store this information + const pollAddresses = await publicClient.readContract({ + address: maciAddress as Hex, + abi: MACIFactory.abi, + functionName: "getPoll", + args: [pollId], + }); + + // read the emptyBallotRoot + const emptyBallotRoot = await publicClient.readContract({ + address: pollAddresses.poll, + abi: PollFactory.abi, + functionName: "emptyBallotRoot", + }); + + const extContracts = await publicClient.readContract({ + address: pollAddresses.poll, + abi: PollFactory.abi, + functionName: "extContracts", + }); + + // store to storage + await Promise.all([ + this.storage.register({ + id: EContracts.Poll, + key: `poll-${pollId}`, + contract: new BaseContract(pollAddresses.poll, PollFactory.abi as unknown as InterfaceAbi), + args: [ + config.pollDuration, + { + intStateTreeDepth: config.intStateTreeDepth, + messageTreeSubDepth: config.messageTreeSubDepth, + messageTreeDepth: config.messageTreeDepth, + voteOptionTreeDepth: config.voteOptionTreeDepth, + }, + PubKey.deserialize(config.coordinatorPubkey).asContractParam() as { x: bigint; y: bigint }, + extContracts, + emptyBallotRoot.toString(), + ], + network: chain, + }), + this.storage.register({ + id: EContracts.MessageProcessor, + key: `poll-${pollId}`, + contract: new BaseContract(pollAddresses.messageProcessor, MessageProcessorFactory.abi), + args: [verifierAddress, vkRegistryAddress, pollAddresses.poll, mode], + network: chain, + }), + this.storage.register({ + id: EContracts.Tally, + key: `poll-${pollId}`, + contract: new BaseContract(pollAddresses.tally, TallyFactory.abi), + args: [verifierAddress, vkRegistryAddress, pollAddresses.poll, pollAddresses.messageProcessor, mode], + network: chain, + }), + this.storage.register({ + id: EContracts.AccQueueQuinaryMaci, + key: `poll-${pollId}`, + name: "contracts/trees/AccQueueQuinaryMaci.sol:AccQueueQuinaryMaci", + contract: new BaseContract(extContracts[1], AccQueueQuinaryMaciFactory.abi), + args: [config.messageTreeSubDepth], + network: chain, + }), + ]); + + return pollId.toString(); + } +} diff --git a/packages/coordinator/ts/deployer/dto.ts b/packages/coordinator/ts/deployer/dto.ts new file mode 100644 index 00000000..f2057f38 --- /dev/null +++ b/packages/coordinator/ts/deployer/dto.ts @@ -0,0 +1,100 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Transform } from "class-transformer"; +import { IsEnum, IsString } from "class-validator"; + +import type { IDeployMaciConfig, IDeployPollConfig } from "./types"; +import type { Hex } from "viem"; + +import { ESupportedNetworks } from "../common"; + +export const transformToString = ({ value }: { value: string }): string => value.toLowerCase(); + +/** + * Data transfer object for MACI contracts deployment + */ +export class DeployerServiceDeployMaciDto { + /** + * Session Key Approval string + */ + @ApiProperty({ + description: "Session Key Approval string", + type: String, + }) + @IsString() + approval!: string; + + /** + * Address of the session key + */ + @ApiProperty({ + description: "Address of the session key", + type: String, + }) + @IsString() + sessionKeyAddress!: Hex; + + /** + * Chain Name + */ + @ApiProperty({ + description: "Chain to which to deploy the contract(s)", + type: ESupportedNetworks, + }) + @IsEnum(ESupportedNetworks) + @Transform(transformToString) + chain!: ESupportedNetworks; + + /** + * Config + */ + @ApiProperty({ + description: "Deployment configuration", + type: Object, + }) + config!: IDeployMaciConfig; +} + +/** + * Data transfer object for Poll contract deployment + */ +export class DeployerServiceDeployPollDto { + /** + * Session Key Approval string + */ + @ApiProperty({ + description: "Session Key Approval string", + type: String, + }) + @IsString() + approval!: string; + + /** + * Address of the session key + */ + @ApiProperty({ + description: "Address of the session key", + type: String, + }) + @IsString() + sessionKeyAddress!: Hex; + + /** + * Chain Name + */ + @ApiProperty({ + description: "Chain to which to deploy the contract(s)", + type: ESupportedNetworks, + }) + @IsEnum(ESupportedNetworks) + @Transform(transformToString) + chain!: ESupportedNetworks; + + /** + * Config + */ + @ApiProperty({ + description: "Deployment configuration", + type: Object, + }) + config!: IDeployPollConfig; +} diff --git a/packages/coordinator/ts/deployer/types.ts b/packages/coordinator/ts/deployer/types.ts new file mode 100644 index 00000000..1704eea8 --- /dev/null +++ b/packages/coordinator/ts/deployer/types.ts @@ -0,0 +1,245 @@ +import { EGatekeepers, EInitialVoiceCreditProxies } from "maci-contracts"; + +import type { Hex } from "viem"; + +import { ESupportedNetworks } from "../common"; + +/** + * IDeployMACIArgs represents the arguments for deploying MACI + */ +export interface IDeployMaciArgs { + /** + * The address of the session key + */ + sessionKeyAddress: Hex; + /** + * The approval for the session key + */ + approval: string; + /** + * The chain name + */ + chain: ESupportedNetworks; + /** + * The configuration for deploying MACI + */ + config: IDeployMaciConfig; +} + +/** + * IDeployPollArgs represents the arguments for deploying a poll + */ +export interface IDeployPollArgs { + /** + * The address of the session key + */ + sessionKeyAddress: Hex; + /** + * The approval for the session key + */ + approval: string; + /** + * The chain name + */ + chain: ESupportedNetworks; + /** + * The configuration for deploying a poll + */ + config: IDeployPollConfig; +} + +/** + * IConstantInitialVoiceCreditProxyArgs represents the arguments for deploying a constant initial voice credit proxy + */ +export interface IConstantInitialVoiceCreditProxyArgs { + /** + * The amount of initial voice credits to deploy + */ + amount: string; +} + +/** + * IEASGatekeeperArgs represents the arguments for deploying an EAS gatekeeper + */ +export interface IEASGatekeeperArgs { + /** + * The address of the EAS contract + */ + easAddress: string; + /** + * The attestation schema to be used + */ + schema: string; + /** + * The trusted attester + */ + attester: string; +} + +/** + * IZupassGatekeeperArgs represents the arguments for deploying a Zupass gatekeeper + */ +export interface IZupassGatekeeperArgs { + /** + * The first signer + */ + signer1: string; + /** + * The second signer + */ + signer2: string; + /** + * The event ID + */ + eventId: string; + /** + * The Zupass verifier address + */ + zupassVerifier: string; +} + +/** + * IHatsGatekeeperArgs represents the arguments for deploying a Hats gatekeeper + */ +export interface IHatsGatekeeperArgs { + /** + * The hats protocol address + */ + hatsProtocolAddress: string; + /** + * The criterion hats + */ + critrionHats: string[]; +} + +/** + * ISemaphoreGatekeeperArgs represents the arguments for deploying a semaphore gatekeeper + */ +export interface ISemaphoreGatekeeperArgs { + /** + * The semaphore contract address + */ + semaphoreContract: string; + /** + * The group ID + */ + groupId: string; +} + +/** + * IGitcoinPassportGatekeeperArgs represents the arguments for deploying a gitcoin passport gatekeeper + */ +export interface IGitcoinPassportGatekeeperArgs { + /** + * The decoder address + */ + decoderAddress: string; + /** + * The passing score + */ + passingScore: string; +} + +/** + * IVkRegistryArgs represents the arguments for deploying a VkRegistry + */ +export interface IVkRegistryArgs { + /** + * The state tree depth + */ + stateTreeDepth: bigint; + /** + * The int state tree depth determines the tally batch size + */ + intStateTreeDepth: bigint; + /** + * The message tree depth + */ + messageTreeDepth: bigint; + /** + * The vote option tree depth + */ + voteOptionTreeDepth: bigint; + /** + * The message batch depth + */ + messageBatchDepth: bigint; +} + +/** + * IGatekeeperArgs represents the arguments for deploying a gatekeeper + */ +export type IGatekeeperArgs = + | IEASGatekeeperArgs + | IZupassGatekeeperArgs + | IHatsGatekeeperArgs + | ISemaphoreGatekeeperArgs + | IGitcoinPassportGatekeeperArgs; + +export type IInitialVoiceCreditProxyArgs = IConstantInitialVoiceCreditProxyArgs; +/** + * DeployMaciConfig is the configuration for deploying MACI + */ +export interface IDeployMaciConfig { + /** + * The gatekeeper configuration + */ + gatekeeper: { + type: EGatekeepers; + args?: IGatekeeperArgs; + }; + /** + * The initial voice credits proxy configuration + */ + initialVoiceCreditsProxy: { + type: EInitialVoiceCreditProxies; + args: IInitialVoiceCreditProxyArgs; + }; + /** + * The MACI configuration + */ + MACI: { + stateTreeDepth: number; + gatekeeper: EGatekeepers; + }; + /** + * The VkRegistry configuration + */ + VkRegistry: { + args: IVkRegistryArgs; + }; +} + +/** + * DeployPollConfig is the configuration for deploying a poll + */ +export interface IDeployPollConfig { + /** + * The poll duration + */ + pollDuration: number; + /** + * The coordinator pubkey + */ + coordinatorPubkey: string; + /** + * Whether to use quadratic voting + */ + useQuadraticVoting: boolean; + /** + * Determines the tally batch size + */ + intStateTreeDepth: number; + /** + * Determines the message batch size + */ + messageTreeSubDepth: number; + /** + * Message tree depth + */ + messageTreeDepth: number; + /** + * Vote option tree depth + */ + voteOptionTreeDepth: number; +} diff --git a/packages/coordinator/ts/file/file.service.ts b/packages/coordinator/ts/file/file.service.ts index e98e14ec..c9b93998 100644 --- a/packages/coordinator/ts/file/file.service.ts +++ b/packages/coordinator/ts/file/file.service.ts @@ -38,7 +38,7 @@ export class FileService { */ constructor() { this.logger = new Logger(FileService.name); - this.db = low(new FileSync(path.resolve(__dirname, "..", "..", "./session-keys.json"))); + this.db = low(new FileSync(path.resolve(process.cwd(), "./session-keys.json"))); } /** diff --git a/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts b/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts index 2795da94..7c1f3e4e 100644 --- a/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts +++ b/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts @@ -1,11 +1,10 @@ import dotenv from "dotenv"; import { ZeroAddress } from "ethers"; import { zeroAddress } from "viem"; -import { optimismSepolia } from "viem/chains"; import { KeyLike } from "crypto"; -import { ErrorCodes } from "../../common"; +import { ErrorCodes, ESupportedNetworks } from "../../common"; import { CryptoService } from "../../crypto/crypto.service"; import { FileService } from "../../file/file.service"; import { SessionKeysService } from "../sessionKeys.service"; @@ -60,7 +59,7 @@ describe("SessionKeysService", () => { sessionKeysService.generateClientFromSessionKey( sessionKeyAddress.sessionKeyAddress, encryptedApproval, - optimismSepolia, + ESupportedNetworks.OPTIMISM_SEPOLIA, ), ).rejects.toThrow(ErrorCodes.INVALID_APPROVAL); }); @@ -69,7 +68,11 @@ describe("SessionKeysService", () => { const approval = await mockSessionKeyApproval(zeroAddress); const encryptedApproval = cryptoService.encrypt(publicKey, approval); await expect( - sessionKeysService.generateClientFromSessionKey(zeroAddress, encryptedApproval, optimismSepolia), + sessionKeysService.generateClientFromSessionKey( + zeroAddress, + encryptedApproval, + ESupportedNetworks.OPTIMISM_SEPOLIA, + ), ).rejects.toThrow(ErrorCodes.SESSION_KEY_NOT_FOUND); }); @@ -91,12 +94,13 @@ describe("SessionKeysService", () => { const client = await sessionKeysService.generateClientFromSessionKey( sessionKeyAddress.sessionKeyAddress, encryptedApproval, - optimismSepolia, + ESupportedNetworks.OPTIMISM_SEPOLIA, ); + expect(mockGenerateClientFromSessionKey).toHaveBeenCalledWith( sessionKeyAddress.sessionKeyAddress, encryptedApproval, - optimismSepolia, + ESupportedNetworks.OPTIMISM_SEPOLIA, ); expect(client).toEqual({ mockedClient: true }); }); diff --git a/packages/coordinator/ts/sessionKeys/sessionKeys.module.ts b/packages/coordinator/ts/sessionKeys/sessionKeys.module.ts index fdc40cb3..a7f2cd4f 100644 --- a/packages/coordinator/ts/sessionKeys/sessionKeys.module.ts +++ b/packages/coordinator/ts/sessionKeys/sessionKeys.module.ts @@ -10,5 +10,6 @@ import { SessionKeysService } from "./sessionKeys.service"; imports: [FileModule, CryptoModule], controllers: [SessionKeysController], providers: [SessionKeysService], + exports: [SessionKeysService], }) export class SessionKeysModule {} diff --git a/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts b/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts index 2ecd67bd..bb9ee6ac 100644 --- a/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts +++ b/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts @@ -5,11 +5,12 @@ import { createKernelAccountClient, KernelAccountClient, KernelSmartAccount } fr import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; import { ENTRYPOINT_ADDRESS_V07 } from "permissionless"; import { ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types"; -import { type Chain, createPublicClient, type Hex, http, type HttpTransport, type Transport } from "viem"; +import { type Chain, type Hex, http, type HttpTransport, type Transport } from "viem"; import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"; -import { ErrorCodes } from "../common"; -import { genPimlicoRPCUrl } from "../common/accountAbstraction"; +import { ErrorCodes, ESupportedNetworks } from "../common"; +import { genPimlicoRPCUrl, getPublicClient } from "../common/accountAbstraction"; +import { viemChain } from "../common/networks"; import { CryptoService } from "../crypto/crypto.service"; import { FileService } from "../file/file.service"; @@ -66,18 +67,18 @@ export class SessionKeysService { * @param sessionKeyAddress - the address of the session key * @param encryptedApproval - the encrypted approval string * @param chain - the chain to use - * @returns + * @returns - the kernel client */ async generateClientFromSessionKey( sessionKeyAddress: Hex, encryptedApproval: string, - chain: Chain, + chain: ESupportedNetworks, ): Promise< KernelAccountClient< ENTRYPOINT_ADDRESS_V07_TYPE, Transport, - undefined, - KernelSmartAccount + Chain, + KernelSmartAccount > > { // the approval will have been encrypted so we need to decrypt it @@ -93,10 +94,8 @@ export class SessionKeysService { } // get the bundler url and create a public client - const bundlerUrl = genPimlicoRPCUrl(chain.name); - const publicClient = createPublicClient({ - transport: http(bundlerUrl), - }); + const bundlerUrl = genPimlicoRPCUrl(chain); + const publicClient = getPublicClient(bundlerUrl, chain); // Using a stored private key const sessionKeySigner = toECDSASigner({ @@ -117,9 +116,10 @@ export class SessionKeysService { bundlerTransport: http(bundlerUrl), entryPoint: ENTRYPOINT_ADDRESS_V07, account: sessionKeyAccount, + chain: viemChain(chain), }); } catch (error) { - this.logger.error("Error deserializing permission account", error); + this.logger.error(`Error: ${ErrorCodes.INVALID_APPROVAL}`, error); throw new Error(ErrorCodes.INVALID_APPROVAL); } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 187fe15b..3323a69e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -185,10 +185,10 @@ importers: version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)(@nestjs/platform-socket.io@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nomicfoundation/hardhat-ethers': specifier: ^3.0.6 - version: 3.0.6(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)) + version: 3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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': specifier: ^5.0.0 - version: 5.0.0(qc2x6fg2hvcwib2hi3ibxymkay) + version: 5.0.0(ghpydlnuczymvhitoqi7argaia) '@zerodev/ecdsa-validator': specifier: ^5.3.1 version: 5.3.1(@zerodev/sdk@5.3.9(permissionless@0.1.44(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.44(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) @@ -215,7 +215,7 @@ importers: version: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) hardhat: specifier: ^2.22.6 - version: 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) + version: 2.22.9(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) helmet: specifier: ^7.1.0 version: 7.1.0 @@ -223,20 +223,20 @@ importers: specifier: ^1.0.0 version: 1.0.0 maci-circuits: - specifier: ^2.1.0 - version: 2.1.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) + specifier: ^2.2.0 + version: 2.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-cli: - specifier: ^2.1.0 - version: 2.1.0(kp6mbe6ih3mck4yt24st7gzjpa) + specifier: ^2.2.0 + version: 2.2.1(5ndnp5vus77sksj5wp7h6axuom) maci-contracts: - specifier: ^2.1.0 - version: 2.1.0(7htv7pg2ka7ncyn53uwoaz3m6q) + specifier: ^2.2.1 + version: 2.2.1(n3k274vagrqph7hyx7gebacu6e) maci-domainobjs: - specifier: ^2.0.0 - version: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + specifier: ^2.2.0 + version: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-subgraph: - specifier: ^2.1.0 - version: 2.1.0(ux5q3l2djucetsca4fb5qxvpmu) + specifier: ^2.2.0 + version: 2.2.1(mg6phbsezkyassblb6tevovlzm) mustache: specifier: ^4.2.0 version: 4.2.0 @@ -7748,6 +7748,18 @@ packages: typescript: optional: true + hardhat@2.22.9: + resolution: {integrity: sha512-sWiuI/yRdFUPfndIvL+2H18Vs2Gav0XacCFYY5msT5dHOWkhLxESJySIk9j83mXL31aXL8+UMA9OgViFLexklg==} + hasBin: true + peerDependencies: + ts-node: '*' + typescript: '*' + peerDependenciesMeta: + ts-node: + optional: true + typescript: + optional: true + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} @@ -9263,16 +9275,9 @@ packages: maci-circuits@0.0.0-ci.17a38c9: resolution: {integrity: sha512-4D3dBdTHMF2q4Nqa2QNiz5xOLcYYobWCt9oHCI+GuZ50nZhsiNdOnjs55Xehdk2l1yy+Wn9k85+niWS0b7SuuQ==} - maci-circuits@2.1.0: - resolution: {integrity: sha512-nYk8OM8NigLPhod+hPRK2sNpfoKjsOakBflsYqOKTA6N/fWHsMsfQUS83Qg6v7y5s1m5Nr/cATj+eWea2esd2Q==} - maci-circuits@2.2.0: resolution: {integrity: sha512-KKAQaFGuIXXzxXZOcpVys9fFRI8GKsA9gdgnTjC/YRf0ieBF8LvsArTsRVxdtSNSHVi/m6jPITMqjn7PlimIbQ==} - maci-cli@2.1.0: - resolution: {integrity: sha512-nrbzEqwtRAoGtW1L34blmyEtXMRJsrZ0ovV2GY++yEZFiaYLLdPGzbDcrxBHMoViIrIjo/nNQn9CxqwzSI5+tw==} - hasBin: true - maci-cli@2.2.1: resolution: {integrity: sha512-byWUEeoZ7Z/QvMhe+/321sMNsLWwDAYEfYxTgyW4BJX0X4yX2SldcRIcQWs5U5tEqszlcwz4FH3oBMuYrjBZqQ==} hasBin: true @@ -9281,10 +9286,6 @@ packages: resolution: {integrity: sha512-XHh+TfOsLZ2uwZWsnHEh66NJ8qXi2AIipCqWlP5NYE2sEuBMXhcFZl5butVovmMlB66PtiDLNfyCfo2wartD+Q==} hasBin: true - maci-contracts@2.1.0: - resolution: {integrity: sha512-OwYSYPEmcyXlyrd9LauMsJwbXKftYcdWdcGvcl4o0W8EqlC1ujrav/nWLXDbl6EiHB7QL/mhhlNIP/RQSCstDQ==} - hasBin: true - maci-contracts@2.2.1: resolution: {integrity: sha512-X6NK47fDZ5Gt9K9V0MMFmkU4HwdwhX5bZ34pfme3oTnuagpOXhOWbnALfyKqPwikTjwPwj5190ahiwfTUCFnyw==} hasBin: true @@ -9298,23 +9299,17 @@ packages: maci-crypto@0.0.0-ci.17a38c9: resolution: {integrity: sha512-OzEujYBc8wwOA9XfNuvSp2ckIAWbr/i0XBwu1EEryJaJRExbDDNqHhodvor0w/uTPVXPpqK3NSAoQ4NeWrEdow==} - maci-crypto@2.0.0: - resolution: {integrity: sha512-bkgOoDA1ABG49MXDzzsQPsFVEijAkLk8ocJKGyeNQS7YpNhC3YEVVz/SE4g0td+N4xJhD3PbXsyHeaTM3ApIjw==} - maci-crypto@2.2.0: resolution: {integrity: sha512-kSbWfuAdDWOdtQsEyofvgDIdAE//+iRjFdYjluDpvXnk7//x4t+/U4VEQJlE0kJ3TbCVjmsAaGNcbkmwmU977Q==} maci-domainobjs@0.0.0-ci.17a38c9: resolution: {integrity: sha512-UbokT12pJCaTlsyIrV5bPF/9BfeU0G5UTgyQs9S8z9FvZEaiWg+4fDHZ2YU1NQdyLRacwCo0d3WtDhSWw8oczQ==} - maci-domainobjs@2.0.0: - resolution: {integrity: sha512-FmQdIC1omsWR/98wt8WvEJj0SDfnVTl9/2FMDp3N4WwUy1lzmmlVjUGKSFKj2+dj2Rx26DmBWsmKhbTIQeoPOQ==} - maci-domainobjs@2.2.0: resolution: {integrity: sha512-pPHqtdIHaPPvMGmWnmx7zXcXQM5+Q8ZGObeSEXgmH60ZKJTQgdoh5z+hs0TfLWEfoWmwWcetLl94EEfZQj0+vg==} - maci-subgraph@2.1.0: - resolution: {integrity: sha512-WRyuBoQLO6kf3ZXrrzwKH3IW0IWnQBSL8JL6OZzKYpcw6+ZUG6PO5eS/JSEg6zsSZk3sxdcCwALHMI862D1TOw==} + maci-subgraph@2.2.1: + resolution: {integrity: sha512-pO9Q0fvieFLrl0n6iy4p8dk8XNdlme/Wo/LIFNUBIjEOo2CfMq7P3pptuwt8RsTUQ7VIFqKhvNIX5g7Ee5k0rA==} magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} @@ -16129,6 +16124,17 @@ 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-chai-matchers@2.0.7(@nomicfoundation/hardhat-ethers@3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.9(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.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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) + deep-eql: 4.1.4 + ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + hardhat: 2.22.9(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) + ordinal: 1.0.3 + '@nomicfoundation/hardhat-ethers@3.0.6(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) @@ -16138,20 +16144,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@nomicfoundation/hardhat-ethers@3.0.6(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-ethers@3.0.6(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))': 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.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: 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) lodash.isequal: 4.5.0 transitivePeerDependencies: - supports-color - '@nomicfoundation/hardhat-ethers@3.0.6(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))': + '@nomicfoundation/hardhat-ethers@3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.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) + hardhat: 2.22.9(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 @@ -16172,6 +16178,14 @@ snapshots: 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) + '@nomicfoundation/hardhat-ignition-ethers@0.15.5(@nomicfoundation/hardhat-ethers@3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.9(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.9(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.9(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.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.9(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.9(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.9(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)': dependencies: '@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)) @@ -16202,6 +16216,21 @@ snapshots: - supports-color - utf-8-validate + '@nomicfoundation/hardhat-ignition@0.15.5(@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.9(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.9(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)': + dependencies: + '@nomicfoundation/hardhat-verify': 2.0.9(hardhat@2.22.9(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/ignition-core': 0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@nomicfoundation/ignition-ui': 0.15.5 + chalk: 4.1.2 + debug: 4.3.6(supports-color@8.1.1) + fs-extra: 10.1.0 + hardhat: 2.22.9(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) + prompts: 2.4.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + '@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))': dependencies: ethereumjs-util: 7.1.5 @@ -16212,26 +16241,10 @@ 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(73opvmet7kz3zbowkltdbofndi)': + '@nomicfoundation/hardhat-network-helpers@1.0.11(hardhat@2.22.9(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-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.6(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.6(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.6(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 + ethereumjs-util: 7.1.5 + hardhat: 2.22.9(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(croq2hzbpfcsi44fcj2jwym2jy)': dependencies: @@ -16254,7 +16267,28 @@ snapshots: typechain: 8.3.2(typescript@5.5.4) typescript: 5.5.4 - '@nomicfoundation/hardhat-toolbox@5.0.0(fxus7563hykhnskeghds65zmge)': + '@nomicfoundation/hardhat-toolbox@5.0.0(ghpydlnuczymvhitoqi7argaia)': + dependencies: + '@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.9(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.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.9(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.9(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.9(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.9(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.9(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.9(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.9(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.9(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.9(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(mykfjeexazgyrgti4xzsh3pu5u)': dependencies: '@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.6(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.6(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)) @@ -16268,17 +16302,17 @@ snapshots: '@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: 2.22.9(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(qc2x6fg2hvcwib2hi3ibxymkay)': + '@nomicfoundation/hardhat-toolbox@5.0.0(wutff4stwleov7fed6oyhfnbmq)': dependencies: '@nomicfoundation/hardhat-chai-matchers': 2.0.7(@nomicfoundation/hardhat-ethers@3.0.6(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.6(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.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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.6(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)) @@ -16289,7 +16323,7 @@ snapshots: '@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.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) + hardhat: 2.22.9(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) @@ -16326,6 +16360,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@nomicfoundation/hardhat-verify@2.0.9(hardhat@2.22.9(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 + '@ethersproject/address': 5.7.0 + cbor: 8.1.0 + chalk: 2.4.2 + debug: 4.3.6(supports-color@8.1.1) + hardhat: 2.22.9(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.clonedeep: 4.5.0 + semver: 6.3.1 + table: 6.8.2 + undici: 5.28.4 + transitivePeerDependencies: + - supports-color + '@nomicfoundation/ignition-core@0.15.5(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@ethersproject/address': 5.6.1 @@ -16580,7 +16629,7 @@ snapshots: chalk: 4.1.2 clean-stack: 3.0.1 cli-progress: 3.12.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) ejs: 3.1.10 get-package-type: 0.1.0 globby: 11.1.0 @@ -18067,6 +18116,14 @@ 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) typechain: 8.3.2(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.9(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))': + dependencies: + '@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) + fs-extra: 9.1.0 + hardhat: 2.22.9(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/aria-query@4.2.2': {} '@types/aria-query@5.0.4': {} @@ -21269,7 +21326,7 @@ snapshots: dns-over-http-resolver@1.2.3(node-fetch@2.7.0(encoding@0.1.13)): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) native-fetch: 3.0.0(node-fetch@2.7.0(encoding@0.1.13)) receptacle: 1.3.2 transitivePeerDependencies: @@ -23296,6 +23353,18 @@ snapshots: - debug - utf-8-validate + hardhat-gas-reporter@1.0.10(bufferutil@4.0.8)(hardhat@2.22.9(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): + dependencies: + array-uniq: 1.0.3 + eth-gas-reporter: 0.2.27(bufferutil@4.0.8)(utf-8-validate@5.0.10) + hardhat: 2.22.9(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) + sha1: 1.1.1 + transitivePeerDependencies: + - '@codechecks/client' + - bufferutil + - debug + - utf-8-validate + hardhat@2.22.1(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 @@ -23404,7 +23473,7 @@ snapshots: - supports-color - utf-8-validate - 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@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): dependencies: '@ethersproject/abi': 5.7.0 '@metamask/eth-sig-util': 4.0.1 @@ -23450,7 +23519,7 @@ snapshots: uuid: 8.3.2 ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: - ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - bufferutil @@ -23458,7 +23527,7 @@ snapshots: - supports-color - utf-8-validate - 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): + hardhat@2.22.9(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 '@metamask/eth-sig-util': 4.0.1 @@ -23504,7 +23573,7 @@ snapshots: uuid: 8.3.2 ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@5.0.10) optionalDependencies: - ts-node: 10.9.2(@types/node@22.2.0)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@20.14.14)(typescript@5.5.4) typescript: 5.5.4 transitivePeerDependencies: - bufferutil @@ -24010,7 +24079,7 @@ snapshots: any-signal: 2.1.2 blob-to-it: 1.0.4 browser-readablestream-to-it: 1.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.6(supports-color@8.1.1) err-code: 3.0.1 ipfs-core-types: 0.9.0(node-fetch@2.7.0(encoding@0.1.13)) ipfs-unixfs: 6.0.9 @@ -25455,20 +25524,6 @@ snapshots: - bufferutil - utf-8-validate - maci-circuits@2.1.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10): - dependencies: - '@zk-kit/circuits': 0.4.0 - circomkit: 0.2.1(@types/snarkjs@0.7.8)(snarkjs@0.7.4) - circomlib: 2.0.5 - maci-core: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - maci-crypto: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - maci-domainobjs: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - snarkjs: 0.7.4 - transitivePeerDependencies: - - '@types/snarkjs' - - bufferutil - - utf-8-validate - maci-circuits@2.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: '@zk-kit/circuits': 0.4.0 @@ -25483,18 +25538,18 @@ snapshots: - bufferutil - utf-8-validate - maci-cli@2.1.0(kp6mbe6ih3mck4yt24st7gzjpa): + maci-cli@2.2.1(5ndnp5vus77sksj5wp7h6axuom): dependencies: '@commander-js/extra-typings': 12.1.0(commander@12.1.0) - '@nomicfoundation/hardhat-toolbox': 5.0.0(qc2x6fg2hvcwib2hi3ibxymkay) + '@nomicfoundation/hardhat-toolbox': 5.0.0(ghpydlnuczymvhitoqi7argaia) 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.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) - maci-circuits: 2.1.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) - maci-contracts: 2.2.1(7htv7pg2ka7ncyn53uwoaz3m6q) + hardhat: 2.22.9(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.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) + maci-contracts: 2.2.1(n3k274vagrqph7hyx7gebacu6e) maci-core: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - maci-crypto: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + maci-crypto: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-domainobjs: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) prompt: 1.3.0 transitivePeerDependencies: @@ -25523,11 +25578,11 @@ snapshots: maci-cli@2.2.1(kp6mbe6ih3mck4yt24st7gzjpa): dependencies: '@commander-js/extra-typings': 12.1.0(commander@12.1.0) - '@nomicfoundation/hardhat-toolbox': 5.0.0(fxus7563hykhnskeghds65zmge) + '@nomicfoundation/hardhat-toolbox': 5.0.0(mykfjeexazgyrgti4xzsh3pu5u) 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) + hardhat: 2.22.9(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.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-contracts: 2.2.1(7htv7pg2ka7ncyn53uwoaz3m6q) maci-core: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -25594,20 +25649,20 @@ snapshots: - typescript - utf-8-validate - maci-contracts@2.1.0(7htv7pg2ka7ncyn53uwoaz3m6q): + maci-contracts@2.2.1(7htv7pg2ka7ncyn53uwoaz3m6q): dependencies: - '@nomicfoundation/hardhat-ethers': 3.0.6(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(qc2x6fg2hvcwib2hi3ibxymkay) + '@nomicfoundation/hardhat-ethers': 3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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(wutff4stwleov7fed6oyhfnbmq) '@openzeppelin/contracts': 5.0.2 circomlibjs: 0.1.7(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) + hardhat: 2.22.9(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) lowdb: 1.0.0 - maci-circuits: 2.1.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) + maci-circuits: 2.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-core: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - maci-crypto: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + maci-crypto: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-domainobjs: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - solidity-docgen: 0.6.0-beta.36(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)) + solidity-docgen: 0.6.0-beta.36(hardhat@2.22.9(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)) uuid: 10.0.0 transitivePeerDependencies: - '@nomicfoundation/hardhat-chai-matchers' @@ -25631,20 +25686,20 @@ snapshots: - typescript - utf-8-validate - maci-contracts@2.2.1(7htv7pg2ka7ncyn53uwoaz3m6q): + maci-contracts@2.2.1(n3k274vagrqph7hyx7gebacu6e): dependencies: - '@nomicfoundation/hardhat-ethers': 3.0.6(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(73opvmet7kz3zbowkltdbofndi) + '@nomicfoundation/hardhat-ethers': 3.0.6(ethers@6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10))(hardhat@2.22.9(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(ghpydlnuczymvhitoqi7argaia) '@openzeppelin/contracts': 5.0.2 circomlibjs: 0.1.7(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.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: 2.22.9(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) lowdb: 1.0.0 maci-circuits: 2.2.0(@types/snarkjs@0.7.8)(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-core: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-crypto: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-domainobjs: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - solidity-docgen: 0.6.0-beta.36(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)) + solidity-docgen: 0.6.0-beta.36(hardhat@2.22.9(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)) uuid: 10.0.0 transitivePeerDependencies: - '@nomicfoundation/hardhat-chai-matchers' @@ -25694,16 +25749,6 @@ snapshots: - bufferutil - utf-8-validate - maci-crypto@2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): - dependencies: - '@zk-kit/baby-jubjub': 1.0.1 - '@zk-kit/eddsa-poseidon': 1.0.2 - '@zk-kit/poseidon-cipher': 0.3.1 - ethers: 6.13.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - maci-crypto@2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: '@zk-kit/baby-jubjub': 1.0.1 @@ -25721,13 +25766,6 @@ snapshots: - bufferutil - utf-8-validate - maci-domainobjs@2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): - dependencies: - maci-crypto: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - utf-8-validate - maci-domainobjs@2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10): dependencies: maci-crypto: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -25735,11 +25773,11 @@ snapshots: - bufferutil - utf-8-validate - maci-subgraph@2.1.0(ux5q3l2djucetsca4fb5qxvpmu): + maci-subgraph@2.2.1(mg6phbsezkyassblb6tevovlzm): dependencies: '@graphprotocol/graph-cli': 0.80.0(@types/node@20.14.14)(bufferutil@4.0.8)(encoding@0.1.13)(node-fetch@2.7.0(encoding@0.1.13))(typescript@5.5.4)(utf-8-validate@5.0.10) '@graphprotocol/graph-ts': 0.35.1 - maci-contracts: 2.2.1(7htv7pg2ka7ncyn53uwoaz3m6q) + maci-contracts: 2.2.1(n3k274vagrqph7hyx7gebacu6e) transitivePeerDependencies: - '@nomicfoundation/hardhat-chai-matchers' - '@nomicfoundation/hardhat-ignition-ethers' @@ -28708,22 +28746,39 @@ snapshots: shelljs: 0.8.5 web3-utils: 1.10.4 - solidity-docgen@0.6.0-beta.36(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)): + solidity-coverage@0.8.12(hardhat@2.22.9(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: - handlebars: 4.7.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) - solidity-ast: 0.4.56 + '@ethersproject/abi': 5.7.0 + '@solidity-parser/parser': 0.18.0 + chalk: 2.4.2 + death: 1.1.0 + difflib: 0.2.4 + fs-extra: 8.1.0 + ghost-testrpc: 0.0.2 + global-modules: 2.0.0 + globby: 10.0.2 + hardhat: 2.22.9(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) + jsonschema: 1.4.1 + lodash: 4.17.21 + mocha: 10.7.0 + node-emoji: 1.11.0 + pify: 4.0.1 + recursive-readdir: 2.2.3 + sc-istanbul: 0.4.6 + semver: 7.6.3 + shelljs: 0.8.5 + web3-utils: 1.10.4 - solidity-docgen@0.6.0-beta.36(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)): + solidity-docgen@0.6.0-beta.36(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)): dependencies: handlebars: 4.7.8 - 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: 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) solidity-ast: 0.4.56 - solidity-docgen@0.6.0-beta.36(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)): + solidity-docgen@0.6.0-beta.36(hardhat@2.22.9(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: handlebars: 4.7.8 - 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) + hardhat: 2.22.9(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) solidity-ast: 0.4.56 sonic-boom@2.8.0: