diff --git a/.gitignore b/.gitignore index 6f4ab1f2..e9cd3f3e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,8 @@ yarn-error.log* # do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables .env .env*.local -deploy-config.json -deployed-contracts.json +packages/coordinator/deploy-config.json +packages/coordinator/deployed-contracts.json # vercel .vercel diff --git a/packages/contracts/ts/index.ts b/packages/contracts/ts/index.ts index 98027a6c..5c74968e 100644 --- a/packages/contracts/ts/index.ts +++ b/packages/contracts/ts/index.ts @@ -1 +1,3 @@ export { ERegistryManagerRequestType, ERegistryManagerRequestStatus } from "./constants"; + +export * from "../typechain-types"; diff --git a/packages/coordinator/package.json b/packages/coordinator/package.json index 180bfb27..ade999ac 100644 --- a/packages/coordinator/package.json +++ b/packages/coordinator/package.json @@ -54,13 +54,13 @@ "maci-domainobjs": "^2.0.0", "maci-subgraph": "^2.3.0", "mustache": "^4.2.0", - "permissionless": "^0.1.44", + "permissionless": ">=0.1.18 <=0.1.29", "reflect-metadata": "^0.2.0", "rxjs": "^7.8.1", "socket.io": "^4.7.5", "tar": "^7.4.1", "ts-node": "^10.9.1", - "viem": "^2.7.15" + "viem": "^2.16.3" }, "devDependencies": { "@nestjs/cli": "^10.4.2", diff --git a/packages/coordinator/tests/aa.test.ts b/packages/coordinator/tests/aa.test.ts new file mode 100644 index 00000000..3fd3f8ae --- /dev/null +++ b/packages/coordinator/tests/aa.test.ts @@ -0,0 +1,141 @@ +import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator"; +import { serializePermissionAccount, toPermissionValidator } from "@zerodev/permissions"; +import { toSudoPolicy } from "@zerodev/permissions/policies"; +import { toECDSASigner } from "@zerodev/permissions/signers"; +import { addressToEmptyAccount, createKernelAccount } from "@zerodev/sdk"; +import { KERNEL_V3_1 } from "@zerodev/sdk/constants"; +import dotenv from "dotenv"; +import { ENTRYPOINT_ADDRESS_V07 } from "permissionless"; +import { Hex, zeroAddress } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; + +import { ErrorCodes, ESupportedNetworks } from "../ts/common"; +import { getPublicClient } from "../ts/common/accountAbstraction"; +import { testMaciDeploymentConfig, testPollDeploymentConfig } from "../ts/deployer/__tests__/utils"; +import { DeployerService } from "../ts/deployer/deployer.service"; +import { FileService } from "../ts/file/file.service"; +import { SessionKeysService } from "../ts/sessionKeys/sessionKeys.service"; + +dotenv.config(); + +const entryPoint = ENTRYPOINT_ADDRESS_V07; +const kernelVersion = KERNEL_V3_1; + +/** + * Generate an approval for a session key + * + * @param sessionKeyAddress - the session key address + * @returns - the approval + */ +export const generateApproval = async (sessionKeyAddress: Hex): Promise => { + const publicClient = getPublicClient(ESupportedNetworks.OPTIMISM_SEPOLIA); + + const sessionKeySigner = privateKeyToAccount(process.env.TEST_PRIVATE_KEY! as `0x${string}`); + const ecdsaValidator = await signerToEcdsaValidator(publicClient, { + signer: sessionKeySigner, + entryPoint, + kernelVersion, + }); + + const emptyAccount = addressToEmptyAccount(sessionKeyAddress); + const emptySessionKeySigner = toECDSASigner({ signer: emptyAccount }); + + const permissionPlugin = await toPermissionValidator(publicClient, { + entryPoint, + kernelVersion, + signer: emptySessionKeySigner, + policies: [toSudoPolicy({})], + }); + + const sessionKeyAccount = await createKernelAccount(publicClient, { + entryPoint, + kernelVersion, + plugins: { + sudo: ecdsaValidator, + regular: permissionPlugin, + }, + }); + + return serializePermissionAccount(sessionKeyAccount); +}; + +describe("Account Abstraction e2e", () => { + const fileService = new FileService(); + const sessionKeyService = new SessionKeysService(fileService); + const deployerService = new DeployerService(sessionKeyService, fileService); + + const { sessionKeyAddress } = sessionKeyService.generateSessionKey(); + + let approval: string; + + beforeAll(async () => { + approval = await generateApproval(sessionKeyAddress); + }); + + describe("deployMaci", () => { + it("should deploy all maci related contracts", async () => { + const maciAddress = await deployerService.deployMaci({ + approval, + sessionKeyAddress, + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + config: testMaciDeploymentConfig, + }); + + expect(maciAddress).not.toBe(zeroAddress); + }); + }); + + describe("deployPoll", () => { + it("should deploy a poll", async () => { + const poll = await deployerService.deployPoll({ + approval, + sessionKeyAddress, + chain: ESupportedNetworks.OPTIMISM_SEPOLIA, + config: testPollDeploymentConfig, + }); + + expect(poll).toBeDefined(); + }); + }); + + describe("sessionKeys", () => { + it("should create a client from a session key and an approval", async () => { + const client = await sessionKeyService.generateClientFromSessionKey( + sessionKeyAddress, + approval, + ESupportedNetworks.OPTIMISM_SEPOLIA, + ); + + expect(client).toBeDefined(); + expect(client.transport.key).toBe("http"); + expect(client.key).toBe("Account"); + expect(client.account.address).not.toBe(zeroAddress); + expect(client.account.kernelVersion).toBe(kernelVersion); + expect(client.account.entryPoint).toBe(entryPoint); + // this is an account with limited permissions so no sudo validator + expect(client.account.kernelPluginManager.address).toBe(zeroAddress); + expect(client.account.kernelPluginManager.sudoValidator).toBe(undefined); + + // send a transaction + const tx = await client.sendTransaction({ + to: zeroAddress, + value: 0n, + data: "0x", + }); + + expect(tx.length).toBeGreaterThan(0); + }); + + it("should not allow to create a client after the session key has been deactivated", async () => { + sessionKeyService.deactivateSessionKey(sessionKeyAddress); + + await expect( + sessionKeyService.generateClientFromSessionKey( + sessionKeyAddress, + approval, + ESupportedNetworks.OPTIMISM_SEPOLIA, + ), + ).rejects.toThrow(ErrorCodes.SESSION_KEY_NOT_FOUND); + }); + }); +}); 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 0621c6e0..c5c40501 100644 --- a/packages/coordinator/ts/common/accountAbstraction.ts +++ b/packages/coordinator/ts/common/accountAbstraction.ts @@ -1,26 +1,27 @@ +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 { type Chain, createPublicClient, http, type HttpTransport, type PublicClient } from "viem"; +import { BundlerClient, createBundlerClient, 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(); -/** - * Generate the RPCUrl for Pimlico based on the chain we need to interact with - * @param network - the network we want to interact with - * @returns the RPCUrl for the network - */ -export const genPimlicoRPCUrl = (network: string): string => { - const pimlicoAPIKey = process.env.PIMLICO_API_KEY; - - if (!pimlicoAPIKey) { - throw new Error(ErrorCodes.PIMLICO_API_KEY_NOT_SET); - } - - return `https://api.pimlico.io/v2/${network}/rpc?apikey=${pimlicoAPIKey}`; -}; - /** * Generate the RPCUrl for Alchemy based on the chain we need to interact with * @param network - the network we want to interact with @@ -53,3 +54,92 @@ export const getPublicClient = (chainName: ESupportedNetworks): PublicClient => + createBundlerClient({ + transport: http(genPimlicoRPCUrl(chainName)), + chain: viemChain(chainName), + entryPoint: ENTRYPOINT_ADDRESS_V07, + }); + +/** + * Generate the RPCUrl for Pimlico based on the chain we need to interact with + * @param network - the network we want to interact with + * @returns the RPCUrl for the network + */ +export const genPimlicoRPCUrl = (network: string): string => { + const pimlicoAPIKey = process.env.PIMLICO_API_KEY; + + if (!pimlicoAPIKey) { + throw new Error(ErrorCodes.PIMLICO_API_KEY_NOT_SET); + } + + return `https://api.pimlico.io/v2/${network}/rpc?apikey=${pimlicoAPIKey}`; +}; + +/** + * 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 publicClient = getPublicClient(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(process.env.ZERODEV_BUNDLER_RPC), + 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 27a5151a..eb288a0d 100644 --- a/packages/coordinator/ts/common/errors.ts +++ b/packages/coordinator/ts/common/errors.ts @@ -12,7 +12,18 @@ export enum ErrorCodes { SUBGRAPH_DEPLOY = "7", SESSION_KEY_NOT_FOUND = "8", PIMLICO_API_KEY_NOT_SET = "9", - INVALID_APPROVAL = "10", - UNSUPPORTED_NETWORK = "11", - RPC_API_KEY_NOT_SET = "12", + 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", + UNSUPPORTED_NETWORK = "20", + RPC_API_KEY_NOT_SET = "21", + FAILED_TO_DEPLOY_MACI = "22", + FAILED_TO_DEPLOY_POLL = "23", } 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/deployer/__tests__/deployer.controller.test.ts b/packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts new file mode 100644 index 00000000..d7486b48 --- /dev/null +++ b/packages/coordinator/ts/deployer/__tests__/deployer.controller.test.ts @@ -0,0 +1,96 @@ +import { Test } from "@nestjs/testing"; +import { zeroAddress } from "viem"; + +import { ErrorCodes, ESupportedNetworks } from "../../common"; +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 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.SESSION_KEY_NOT_FOUND); + }); + }); + + 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..26a05503 --- /dev/null +++ b/packages/coordinator/ts/deployer/__tests__/deployer.service.test.ts @@ -0,0 +1,518 @@ +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 { ErrorCodes, ESupportedNetworks } from "../../common"; +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 storageInstance = ContractStorage.getInstance(path.join(process.cwd(), "deployed-contracts.json")); + const sessionKeyService = new SessionKeysService(fileService); + const deployerService = new DeployerService(sessionKeyService, fileService); + const { sessionKeyAddress } = sessionKeyService.generateSessionKey(); + + 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 a non existent session key address", async () => { + await expect( + deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: "0x", + sessionKeyAddress: "0x5", + }), + ).rejects.toThrow(ErrorCodes.SESSION_KEY_NOT_FOUND); + }); + + test("should throw when the approval is not valid", async () => { + await expect( + deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval: "0x", + sessionKeyAddress, + }), + ).rejects.toThrow(ErrorCodes.INVALID_APPROVAL); + }); + + test("should deploy all new contracts", async () => { + const approval = await mockSessionKeyApproval(sessionKeyAddress); + + const mockDeployMaci = jest.fn().mockResolvedValue(zeroAddress); + jest.spyOn(DeployerService.prototype, "deployMaci").mockImplementation(mockDeployMaci); + + const maciAddress = await deployerService.deployMaci({ + config: testMaciDeploymentConfig, + chain, + approval, + 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 mockDeployPoll = jest.fn().mockResolvedValue("0"); + jest.spyOn(DeployerService.prototype, "deployPoll").mockImplementation(mockDeployPoll); + + const pollId = await deployerService.deployPoll({ + config: testPollDeploymentConfig, + chain, + approval, + 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..9d449720 --- /dev/null +++ b/packages/coordinator/ts/deployer/deployer.service.ts @@ -0,0 +1,956 @@ +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_V06_TYPE, ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types"; +import { Abi, Chain, encodeFunctionData, http, HttpTransport, PublicClient, Transport, type Hex } from "viem"; +import { KernelEcdsaSmartAccount, signerToEcdsaKernelSmartAccount } from "permissionless/accounts"; + +import path from "path"; + +import { ErrorCodes, ESupportedNetworks, MESSAGE_TREE_ARITY } from "../common"; +import { + genPimlicoRPCUrl, + getBundlerClient, + getDeployedContractAddress, + getPublicClient, +} from "../common/accountAbstraction"; +import { FileService } from "../file/file.service"; +import { SessionKeysService } from "../sessionKeys/sessionKeys.service"; +import { KernelClient } from "../sessionKeys/types"; + +import { + IDeployMaciArgs, + IDeployPollArgs, + IEASGatekeeperArgs, + IGatekeeperArgs, + IGitcoinPassportGatekeeperArgs, + IHatsGatekeeperArgs, + IInitialVoiceCreditProxyArgs, + ISemaphoreGatekeeperArgs, + IZupassGatekeeperArgs, +} from "./types"; +import { privateKeyToAccount } from "viem/accounts"; +import { BundlerClient, createBundlerClient, createSmartAccountClient, ENTRYPOINT_ADDRESS_V07 } from "permissionless"; +import { viemChain } from "../common/networks"; +import { prepareUserOperationRequest } from "permissionless/actions/smartAccount"; + +/** + * 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); + } + } + + /** + * 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[], + bundlerClient: BundlerClient, + publicClient: PublicClient, + ): Promise { + const deployCallData = await kernelClient.account.encodeDeployCallData({ + abi, + args, + bytecode, + }); + + const gasPrice = await kernelClient.getUserOperationGasPrice(); + + const opEstimate = await kernelClient.prepareUserOperationRequest({ + userOperation: { + callData: deployCallData, + sender: kernelClient.account.address, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + }, + }); + + const callGasLimitMultiplier = (opEstimate.callGasLimit / 100n) * 5n; + + const tx = await kernelClient.sendUserOperation({ + userOperation: { + callData: deployCallData, + sender: kernelClient.account.address, + maxFeePerGas: gasPrice.maxFeePerGas, + maxPriorityFeePerGas: gasPrice.maxPriorityFeePerGas, + callGasLimit: + opEstimate.callGasLimit + callGasLimitMultiplier < 28000000n + ? opEstimate.callGasLimit + callGasLimitMultiplier + : 28000000n, + }, + }); + + const receipt = await bundlerClient.waitForUserOperationReceipt({ + hash: tx, + }); + + const txReceipt = await publicClient.getTransactionReceipt({ + hash: receipt.receipt.transactionHash, + }); + + const address = getDeployedContractAddress(txReceipt); + + 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: KernelClient, + bundlerClient: BundlerClient, + publicClient: PublicClient, + chain: ESupportedNetworks, + ): Promise { + let address = this.storage.getAddress(contract, chain); + + if (!address) { + address = await this.deployAndGetAddress(kernelClient, abi, bytecode, args, bundlerClient, 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(chain); + const bundlerClient = getBundlerClient(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), + bundlerClient, + 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) : [], + bundlerClient, + 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, + bundlerClient, + publicClient, + chain, + ); + + // 2. poseidon + const poseidonT3Address = await this.deployAndStore( + EContracts.PoseidonT3, + [], + PoseidonT3Factory.abi, + PoseidonT3Factory.bytecode, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + const poseidonT4Address = await this.deployAndStore( + EContracts.PoseidonT4, + [], + PoseidonT4Factory.abi, + PoseidonT4Factory.bytecode, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + const poseidonT5Address = await this.deployAndStore( + EContracts.PoseidonT5, + [], + PoseidonT5Factory.abi, + PoseidonT5Factory.bytecode, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + const poseidonT6Address = await this.deployAndStore( + EContracts.PoseidonT6, + [], + PoseidonT6Factory.abi, + PoseidonT6Factory.bytecode, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + // 3. factories + const pollFactoryAddress = await this.deployAndStore( + EContracts.PollFactory, + [], + PollFactoryFactory.abi as unknown as Abi, + PollFactoryFactory.linkBytecode({ + "contracts/crypto/PoseidonT3.sol:PoseidonT3": poseidonT3Address, + "contracts/crypto/PoseidonT4.sol:PoseidonT4": poseidonT4Address, + "contracts/crypto/PoseidonT5.sol:PoseidonT5": poseidonT5Address, + "contracts/crypto/PoseidonT6.sol:PoseidonT6": poseidonT6Address, + }) as Hex, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + const tallyFactoryAddress = await this.deployAndStore( + EContracts.TallyFactory, + [], + TallyFactoryFactory.abi as unknown as Abi, + TallyFactoryFactory.linkBytecode({ + "contracts/crypto/PoseidonT3.sol:PoseidonT3": poseidonT3Address, + "contracts/crypto/PoseidonT4.sol:PoseidonT4": poseidonT4Address, + "contracts/crypto/PoseidonT5.sol:PoseidonT5": poseidonT5Address, + "contracts/crypto/PoseidonT6.sol:PoseidonT6": poseidonT6Address, + }) as Hex, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + const messageProcessorFactoryAddress = await this.deployAndStore( + EContracts.MessageProcessorFactory, + [], + MessageProcessorFactoryFactory.abi, + MessageProcessorFactoryFactory.linkBytecode({ + "contracts/crypto/PoseidonT3.sol:PoseidonT3": poseidonT3Address, + "contracts/crypto/PoseidonT4.sol:PoseidonT4": poseidonT4Address, + "contracts/crypto/PoseidonT5.sol:PoseidonT5": poseidonT5Address, + "contracts/crypto/PoseidonT6.sol:PoseidonT6": poseidonT6Address, + }) as Hex, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + // 4. VkRegistry + const vkRegistryAddress = await this.deployAndStore( + EContracts.VkRegistry, + [], + VkRegistryFactory.abi, + VkRegistryFactory.bytecode, + kernelClient, + bundlerClient, + publicClient, + chain, + ); + + try { + const processMessagesZkeyPathQv = this.fileService.getZkeyFilePaths( + process.env.COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME!, + true, + ); + const tallyVotesZkeyPathQv = this.fileService.getZkeyFilePaths(process.env.COORDINATOR_TALLY_ZKEY_NAME!, true); + const processMessagesZkeyPathNonQv = this.fileService.getZkeyFilePaths( + process.env.COORDINATOR_MESSAGE_PROCESS_ZKEY_NAME!, + false, + ); + const tallyVotesZkeyPathNonQv = this.fileService.getZkeyFilePaths( + process.env.COORDINATOR_TALLY_ZKEY_NAME!, + 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[]; + + // check if the keys are already set + const isProcessVkSet = await publicClient.readContract({ + address: vkRegistryAddress as Hex, + abi: VkRegistryFactory.abi, + functionName: "hasProcessVk", + args: [ + config.VkRegistry.args.stateTreeDepth, + config.VkRegistry.args.messageTreeDepth, + config.VkRegistry.args.voteOptionTreeDepth, + MESSAGE_TREE_ARITY ** config.VkRegistry.args.messageBatchDepth, + EMode.QV, + ], + }); + + const isProcessNonQvVkSet = await publicClient.readContract({ + address: vkRegistryAddress as Hex, + abi: VkRegistryFactory.abi, + functionName: "hasProcessVk", + args: [ + config.VkRegistry.args.stateTreeDepth, + config.VkRegistry.args.messageTreeDepth, + config.VkRegistry.args.voteOptionTreeDepth, + MESSAGE_TREE_ARITY ** config.VkRegistry.args.messageBatchDepth, + EMode.NON_QV, + ], + }); + + const isTallyVkSet = await publicClient.readContract({ + address: vkRegistryAddress as Hex, + abi: VkRegistryFactory.abi, + functionName: "hasTallyVk", + args: [ + config.VkRegistry.args.stateTreeDepth, + config.VkRegistry.args.intStateTreeDepth, + config.VkRegistry.args.voteOptionTreeDepth, + EMode.QV, + ], + }); + + const isTallyNonQvVkSet = await publicClient.readContract({ + address: vkRegistryAddress as Hex, + abi: VkRegistryFactory.abi, + functionName: "hasTallyVk", + args: [ + config.VkRegistry.args.stateTreeDepth, + config.VkRegistry.args.intStateTreeDepth, + config.VkRegistry.args.voteOptionTreeDepth, + EMode.QV, + ], + }); + + if (isProcessVkSet && isProcessNonQvVkSet && isTallyVkSet && isTallyNonQvVkSet) { + this.logger.debug("Verifying keys are already set on the vk registry"); + } else { + const gasEstimates = await kernelClient.getUserOperationGasPrice(); + const opEstimate = await kernelClient.prepareUserOperationRequest({ + userOperation: { + sender: kernelClient.account.address, + maxFeePerGas: gasEstimates.maxFeePerGas, + maxPriorityFeePerGas: gasEstimates.maxPriorityFeePerGas, + 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, + ], + }), + }), + }, + }); + // set vKeys on the vk registry + const callGasLimitMultiplier = (opEstimate.callGasLimit / 100n) * 5n; + + const userOpHashVkRegistry = await kernelClient.sendUserOperation({ + userOperation: { + callGasLimit: + opEstimate.callGasLimit + callGasLimitMultiplier < 28000000n + ? opEstimate.callGasLimit + callGasLimitMultiplier + : 28000000n, + sender: kernelClient.account.address, + maxFeePerGas: gasEstimates.maxFeePerGas, + maxPriorityFeePerGas: gasEstimates.maxPriorityFeePerGas, + 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, + ], + }), + }), + }, + }); + + const receiptVkRegistry = await bundlerClient.waitForUserOperationReceipt({ + hash: userOpHashVkRegistry, + }); + + if (!receiptVkRegistry.success) { + throw new Error(ErrorCodes.FAILED_TO_SET_VERIFYING_KEYS_ON_VK_REGISTRY); + } + } + } catch (error) { + this.logger.error(`Failed to set verifying keys on vk registry: ${error}`); + } + + // 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, + MACIFactory.linkBytecode({ + "contracts/crypto/PoseidonT3.sol:PoseidonT3": poseidonT3Address, + "contracts/crypto/PoseidonT4.sol:PoseidonT4": poseidonT4Address, + "contracts/crypto/PoseidonT5.sol:PoseidonT5": poseidonT5Address, + "contracts/crypto/PoseidonT6.sol:PoseidonT6": poseidonT6Address, + }) as Hex, + [ + pollFactoryAddress, + messageProcessorFactoryAddress, + tallyFactoryAddress, + gatekeeperAddress, + initialVoiceCreditProxyAddress, + config.MACI.stateTreeDepth, + emptyBallotRoots, + ], + bundlerClient, + 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], + }), + }), + }, + account: kernelClient.account, + }); + + const receipt = await bundlerClient.waitForUserOperationReceipt({ + hash: userOpHash, + }); + + if (!receipt.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(chain); + const bundlerClient = getBundlerClient(chain); + + const pollId = await publicClient.readContract({ + address: maciAddress as Hex, + abi: MACIFactory.abi, + functionName: "nextPollId", + }); + + if (pollId === undefined) { + throw new Error(ErrorCodes.FAILED_TO_GET_NEXT_POLL_ID); + } + + try { + const gasEstimates = await kernelClient.getUserOperationGasPrice(); + const opEstimate = await kernelClient.prepareUserOperationRequest({ + userOperation: { + sender: kernelClient.account.address, + maxFeePerGas: gasEstimates.maxFeePerGas, + maxPriorityFeePerGas: gasEstimates.maxPriorityFeePerGas, + 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, + ], + }), + }), + }, + }); + + const callGasLimitMultiplier = (opEstimate.callGasLimit / 100n) * 5n; + + const userOpHash = await kernelClient.sendUserOperation({ + userOperation: { + sender: kernelClient.account.address, + maxFeePerGas: gasEstimates.maxFeePerGas, + maxPriorityFeePerGas: gasEstimates.maxPriorityFeePerGas, + callGasLimit: + opEstimate.callGasLimit + callGasLimitMultiplier < 28000000n + ? opEstimate.callGasLimit + callGasLimitMultiplier + : 28000000n, + 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, + ], + }), + }), + }, + }); + + const receipt = await bundlerClient.waitForUserOperationReceipt({ + hash: userOpHash, + }); + + if (!receipt.success) { + throw new Error(`${ErrorCodes.FAILED_TO_DEPLOY_CONTRACT} - ${EContracts.Poll}`); + } + } catch (error) { + this.logger.error("error deploying poll", error); + throw new Error(ErrorCodes.FAILED_TO_DEPLOY_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..a9daa308 --- /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)", + enum: 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)", + enum: 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/sessionKeys/__tests__/sessionKeys.service.test.ts b/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts index 6ee18d9a..1c148c90 100644 --- a/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts +++ b/packages/coordinator/ts/sessionKeys/__tests__/sessionKeys.service.test.ts @@ -81,6 +81,7 @@ describe("SessionKeysService", () => { approval, ESupportedNetworks.OPTIMISM_SEPOLIA, ); + expect(mockGenerateClientFromSessionKey).toHaveBeenCalledWith( sessionKeyAddress.sessionKeyAddress, approval, diff --git a/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts b/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts index e76a725a..22546b0a 100644 --- a/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts +++ b/packages/coordinator/ts/sessionKeys/sessionKeys.service.ts @@ -106,7 +106,7 @@ export class SessionKeysService { ); return createKernelAccountClient({ - bundlerTransport: http(bundlerUrl), + bundlerTransport: http(process.env.ZERODEV_BUNDLER_RPC), entryPoint: ENTRYPOINT_ADDRESS_V07, account: sessionKeyAccount, chain: viemChain(chain), diff --git a/packages/coordinator/ts/sessionKeys/types.ts b/packages/coordinator/ts/sessionKeys/types.ts index ff4f93d7..72c95f42 100644 --- a/packages/coordinator/ts/sessionKeys/types.ts +++ b/packages/coordinator/ts/sessionKeys/types.ts @@ -1,4 +1,7 @@ -import type { Hex } from "viem"; +import { KernelAccountClient, KernelSmartAccount } from "@zerodev/sdk"; +import { ENTRYPOINT_ADDRESS_V07_TYPE } from "permissionless/types"; + +import type { Chain, Hex, HttpTransport, Transport } from "viem"; /** * Generate session key return type @@ -9,3 +12,10 @@ export interface IGenerateSessionKeyReturn { */ sessionKeyAddress: Hex; } + +export type KernelClient = KernelAccountClient< + ENTRYPOINT_ADDRESS_V07_TYPE, + Transport, + Chain, + KernelSmartAccount +>; diff --git a/packages/interface/package.json b/packages/interface/package.json index 54c568aa..3e68de5b 100644 --- a/packages/interface/package.json +++ b/packages/interface/package.json @@ -32,6 +32,11 @@ "@trpc/react-query": "11.0.0-next-beta.294", "@trpc/server": "11.0.0-next-beta.294", "@vercel/blob": "^0.19.0", + "@zerodev/ecdsa-validator": "^5.3.1", + "@zerodev/permissions": "^5.4.3", + "@zerodev/sdk": "^5.3.8", + "@zerodev/session-key": "^5.4.2", + "@zerodev/passkey-validator": "^5.4.1", "clsx": "^2.1.0", "date-fns": "^3.6.0", "dotenv": "^16.4.1", @@ -46,6 +51,7 @@ "next-themes": "^0.2.1", "node-fetch-cache": "^5.0.2", "nuqs": "^1.17.1", + "permissionless": ">=0.1.18 <=0.1.29", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.49.3", @@ -58,7 +64,7 @@ "tailwind-merge": "^2.2.1", "tailwind-variants": "^0.1.20", "tailwindcss": "^3.4.1", - "viem": "^2.7.15", + "viem": "2.16.3", "wagmi": "^2.9.8", "zod": "3.22.4" }, diff --git a/packages/interface/src/config.ts b/packages/interface/src/config.ts index ffe5dc80..27eaa89f 100644 --- a/packages/interface/src/config.ts +++ b/packages/interface/src/config.ts @@ -60,6 +60,7 @@ export const semaphoreEthersChain = (): string => { * @returns the alchemy RPC URL */ export const getRPCURL = (): string | undefined => { + console.log(process.env.NEXT_PUBLIC_CHAIN_NAME); switch (process.env.NEXT_PUBLIC_CHAIN_NAME) { case "optimismSepolia": return `https://opt-sepolia.g.alchemy.com/v2/${process.env.NEXT_PUBLIC_ALCHEMY_ID!}`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2f8f92ae..475119f3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -194,16 +194,16 @@ importers: version: 5.0.0(hke34ef7km4fdyz52h5gry5opq) '@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)) + version: 5.3.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(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.29(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)) '@zerodev/permissions': specifier: ^5.4.3 - version: 5.4.3(@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)))(@zerodev/webauthn-key@5.3.1(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)) + version: 5.4.3(@zerodev/sdk@5.3.9(permissionless@0.1.29(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)))(@zerodev/webauthn-key@5.3.1(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.29(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)) '@zerodev/sdk': specifier: ^5.3.8 - version: 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)) + version: 5.3.9(permissionless@0.1.29(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)) '@zerodev/session-key': specifier: ^5.4.2 - version: 5.4.2(@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)) + version: 5.4.2(@zerodev/sdk@5.3.9(permissionless@0.1.29(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.29(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)) class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -236,7 +236,7 @@ importers: version: 2.3.0(g3d4wepazdznljietc42e5anoy) maci-domainobjs: specifier: ^2.0.0 - version: 2.0.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + version: 2.2.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) maci-subgraph: specifier: ^2.3.0 version: 2.3.0(jqbxnb2fveugzmo4mmcj7czc2y) @@ -244,8 +244,8 @@ importers: specifier: ^4.2.0 version: 4.2.0 permissionless: - specifier: ^0.1.44 - version: 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)) + specifier: '>=0.1.18 <=0.1.29' + version: 0.1.29(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) reflect-metadata: specifier: ^0.2.0 version: 0.2.2 @@ -262,7 +262,7 @@ importers: specifier: ^10.9.1 version: 10.9.2(@types/node@20.14.14)(typescript@5.5.4) viem: - specifier: ^2.7.15 + specifier: ^2.16.3 version: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) devDependencies: '@nestjs/cli': @@ -315,7 +315,7 @@ importers: version: 1.6.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) '@hatsprotocol/sdk-v1-core': specifier: ^0.10.0 - version: 0.10.0(encoding@0.1.13)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + version: 0.10.0(encoding@0.1.13)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) '@hookform/resolvers': specifier: ^3.3.4 version: 3.9.0(react-hook-form@7.52.2(react@18.2.0)) @@ -330,7 +330,7 @@ importers: version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@rainbow-me/rainbowkit': specifier: ^2.0.1 - version: 2.1.4(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)) + version: 2.1.4(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)) '@semaphore-protocol/core': specifier: 4.0.3 version: 4.0.3(bufferutil@4.0.8)(utf-8-validate@5.0.10) @@ -358,6 +358,21 @@ importers: '@vercel/blob': specifier: ^0.19.0 version: 0.19.0 + '@zerodev/ecdsa-validator': + specifier: ^5.3.1 + version: 5.3.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/passkey-validator': + specifier: ^5.4.1 + version: 5.4.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(@zerodev/webauthn-key@5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/permissions': + specifier: ^5.4.3 + version: 5.4.3(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(@zerodev/webauthn-key@5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/sdk': + specifier: ^5.3.8 + version: 5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/session-key': + specifier: ^5.4.2 + version: 5.4.2(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) clsx: specifier: ^2.1.0 version: 2.1.1 @@ -400,6 +415,9 @@ importers: nuqs: specifier: ^1.17.1 version: 1.17.7(next@14.2.5(@babel/core@7.25.2)(@playwright/test@1.46.0)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)) + permissionless: + specifier: '>=0.1.18 <=0.1.29' + version: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) react: specifier: 18.2.0 version: 18.2.0 @@ -437,11 +455,11 @@ importers: specifier: ^3.4.1 version: 3.4.7(ts-node@10.9.2(@types/node@20.14.14)(typescript@5.5.4)) viem: - specifier: ^2.7.15 - version: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + specifier: 2.16.3 + version: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) wagmi: specifier: ^2.9.8 - version: 2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) + version: 2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) zod: specifier: 3.22.4 version: 3.22.4 @@ -4521,6 +4539,14 @@ packages: permissionless: ^0.1.18 viem: ^2.16.3 + '@zerodev/passkey-validator@5.4.1': + resolution: {integrity: sha512-50mUDueqQGj2hCv//f+cHJ5paJy3aB5N+XI1ndcc7jNDcSKHvmINaQ12QyeH+NlchMnnzB/Iqix9YpeeGtB5KQ==} + peerDependencies: + '@zerodev/sdk': ^5.2.1 + '@zerodev/webauthn-key': ^5.3.0 + permissionless: '>=0.1.18 <=0.1.29' + viem: ^2.16.3 + '@zerodev/permissions@5.4.3': resolution: {integrity: sha512-rC5npYPRQO8lwhMXO6h2MXO3DGgRSDqAEVh9E2pRnlYPhaMDHRVd+nPqLIsx1OubRT0jSXrPFTQJd2MqeKcJ6w==} peerDependencies: @@ -4601,6 +4627,17 @@ packages: zod: optional: true + abitype@1.0.4: + resolution: {integrity: sha512-UivtYZOGJGE8rsrM/N5vdRkUpqEZVmuTumfTuolm7m/6O09wprd958rx8kUBwVAAAhQDveGAgD0GJdBuR8s6tw==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + abitype@1.0.5: resolution: {integrity: sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==} peerDependencies: @@ -9458,15 +9495,9 @@ packages: maci-core@2.2.0: resolution: {integrity: sha512-jHS40/uGJZMYvslfDls3LUPXK8gAijVrc8L8o51SJQX44iocgR3aWpWycD8df9rBCGBxScZPbtn04CmtFT0lhQ==} - 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@2.0.0: - resolution: {integrity: sha512-FmQdIC1omsWR/98wt8WvEJj0SDfnVTl9/2FMDp3N4WwUy1lzmmlVjUGKSFKj2+dj2Rx26DmBWsmKhbTIQeoPOQ==} - maci-domainobjs@2.2.0: resolution: {integrity: sha512-pPHqtdIHaPPvMGmWnmx7zXcXQM5+Q8ZGObeSEXgmH60ZKJTQgdoh5z+hs0TfLWEfoWmwWcetLl94EEfZQj0+vg==} @@ -10671,10 +10702,10 @@ packages: performance-now@2.1.0: resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} - permissionless@0.1.44: - resolution: {integrity: sha512-NQBATmG4Fp3Zqy1IjjBihfp2huV6sTzUUzZzuSQ7xBnRNABOyIm8d+q76gy2B0LnFfu47RA/aW+fNfZjnbzl4Q==} + permissionless@0.1.29: + resolution: {integrity: sha512-jKkgl3G2b4hv5DvX2U8v0OI3JfX+nEzj9a6zsrcXuJaLZdS14td4tExnlmBY93nUN6lRC6NfPZjdLbi8Dkzz7w==} peerDependencies: - viem: '>=2.14.1 <2.18.0' + viem: ^2.9.17 picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -13014,6 +13045,14 @@ packages: typescript: optional: true + viem@2.16.3: + resolution: {integrity: sha512-6ExbIpi77C1HzGSjx6W+fn39Cz1ULlVV74XHxi1kYpOAqY7/iAoynK5X2aQp2LvxstTqS1GIKEdpCAZ8dKi2Ag==} + peerDependencies: + typescript: '>=5.0.4' + peerDependenciesMeta: + typescript: + optional: true + viem@2.19.1: resolution: {integrity: sha512-a0ca/ACEz3FRZB3OmiSfRUogWZGQh700wu7Pg3GmAWiGD+0PS9bVaWG67JQ+9azFZLq0BU/m0t2CeWd3xi8IzQ==} peerDependencies: @@ -15340,12 +15379,12 @@ snapshots: dependencies: '@hapi/hoek': 9.3.0 - '@hatsprotocol/sdk-v1-core@0.10.0(encoding@0.1.13)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + '@hatsprotocol/sdk-v1-core@0.10.0(encoding@0.1.13)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': dependencies: '@hatsprotocol/sdk-v1-subgraph': 1.0.0(encoding@0.1.13) graphql: 16.9.0 graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.9.0) - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - encoding @@ -17525,7 +17564,7 @@ snapshots: '@radix-ui/rect@1.1.0': {} - '@rainbow-me/rainbowkit@2.1.4(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))': + '@rainbow-me/rainbowkit@2.1.4(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4))': dependencies: '@tanstack/react-query': 5.51.21(react@18.2.0) '@vanilla-extract/css': 1.14.0 @@ -17537,8 +17576,8 @@ snapshots: react-dom: 18.2.0(react@18.2.0) react-remove-scroll: 2.5.7(@types/react@18.3.3)(react@18.2.0) ua-parser-js: 1.0.38 - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) - wagmi: 2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + wagmi: 2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) transitivePeerDependencies: - '@types/react' @@ -17940,7 +17979,7 @@ snapshots: '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.22.1 - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript @@ -19103,17 +19142,17 @@ snapshots: transitivePeerDependencies: - supports-color - '@wagmi/connectors@5.1.4(@types/react@18.3.3)(@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)': + '@wagmi/connectors@5.1.4(@types/react@18.3.3)(@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4)': dependencies: '@coinbase/wallet-sdk': 4.0.4 '@metamask/sdk': 0.27.0(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) - '@wagmi/core': 2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@wagmi/core': 2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) '@walletconnect/ethereum-provider': 2.14.0(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10) '@walletconnect/modal': 2.6.2(@types/react@18.3.3)(react@18.2.0) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: @@ -19142,11 +19181,11 @@ snapshots: - utf-8-validate - zod - '@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + '@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': dependencies: eventemitter3: 5.0.1 mipd: 0.0.7(typescript@5.5.4) - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) zustand: 4.4.1(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0) optionalDependencies: '@tanstack/query-core': 5.51.21 @@ -19581,34 +19620,77 @@ snapshots: js-yaml: 3.14.1 tslib: 2.6.3 - '@zerodev/ecdsa-validator@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))': + '@zerodev/ecdsa-validator@5.3.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + permissionless: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + '@zerodev/ecdsa-validator@5.3.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(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.29(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))': dependencies: - '@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)) + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(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.29(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) - '@zerodev/permissions@5.4.3(@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)))(@zerodev/webauthn-key@5.3.1(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))': + '@zerodev/passkey-validator@5.4.1(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(@zerodev/webauthn-key@5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + '@noble/curves': 1.4.2 + '@simplewebauthn/browser': 8.3.7 + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/webauthn-key': 5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + permissionless: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + '@zerodev/permissions@5.4.3(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(@zerodev/webauthn-key@5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + '@simplewebauthn/browser': 9.0.1 + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@zerodev/webauthn-key': 5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + merkletreejs: 0.3.11 + permissionless: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + '@zerodev/permissions@5.4.3(@zerodev/sdk@5.3.9(permissionless@0.1.29(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)))(@zerodev/webauthn-key@5.3.1(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.29(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))': dependencies: '@simplewebauthn/browser': 9.0.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)) + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(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)) '@zerodev/webauthn-key': 5.3.1(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) merkletreejs: 0.3.11 - 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)) + permissionless: 0.1.29(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) - '@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))': + '@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + permissionless: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + semver: 7.6.3 + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + '@zerodev/sdk@5.3.9(permissionless@0.1.29(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))': dependencies: - 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)) + permissionless: 0.1.29(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) semver: 7.6.3 viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) - '@zerodev/session-key@5.4.2(@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))': + '@zerodev/session-key@5.4.2(@zerodev/sdk@5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + merkletreejs: 0.3.11 + permissionless: 0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + '@zerodev/session-key@5.4.2(@zerodev/sdk@5.3.9(permissionless@0.1.29(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.29(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))': dependencies: - '@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)) + '@zerodev/sdk': 5.3.9(permissionless@0.1.29(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)) merkletreejs: 0.3.11 - 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)) + permissionless: 0.1.29(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) + '@zerodev/webauthn-key@5.3.1(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': + dependencies: + '@noble/curves': 1.4.2 + '@simplewebauthn/browser': 8.3.7 + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + '@zerodev/webauthn-key@5.3.1(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))': dependencies: '@noble/curves': 1.4.2 @@ -19671,6 +19753,11 @@ snapshots: typescript: 5.5.4 zod: 3.22.4 + abitype@1.0.4(typescript@5.5.4)(zod@3.22.4): + optionalDependencies: + typescript: 5.5.4 + zod: 3.22.4 + abitype@1.0.5(typescript@5.5.4)(zod@3.22.4): optionalDependencies: typescript: 5.5.4 @@ -26316,16 +26403,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 @@ -26336,13 +26413,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) @@ -27990,7 +28060,11 @@ snapshots: performance-now@2.1.0: {} - 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)): + permissionless@0.1.29(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)): + dependencies: + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + + permissionless@0.1.29(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)): dependencies: viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) @@ -30667,6 +30741,23 @@ snapshots: - utf-8-validate - zod + viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): + dependencies: + '@adraffy/ens-normalize': 1.10.0 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/bip32': 1.3.2 + '@scure/bip39': 1.2.1 + abitype: 1.0.4(typescript@5.5.4)(zod@3.22.4) + isows: 1.0.4(ws@8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + ws: 8.17.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.10.0 @@ -30721,14 +30812,14 @@ snapshots: wabt@1.0.36: {} - wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4): + wagmi@2.12.4(@tanstack/query-core@5.51.21)(@tanstack/react-query@5.51.21(react@18.2.0))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(immer@10.0.2)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4): dependencies: '@tanstack/react-query': 5.51.21(react@18.2.0) - '@wagmi/connectors': 5.1.4(@types/react@18.3.3)(@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) - '@wagmi/core': 2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) + '@wagmi/connectors': 5.1.4(@types/react@18.3.3)(@wagmi/core@2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)))(bufferutil@4.0.8)(encoding@0.1.13)(react-dom@18.2.0(react@18.2.0))(react-native@0.74.5(@babel/core@7.25.2)(@babel/preset-env@7.25.3(@babel/core@7.25.2))(@types/react@18.3.3)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.2.0)(utf-8-validate@5.0.10))(react@18.2.0)(rollup@4.20.0)(typescript@5.5.4)(utf-8-validate@5.0.10)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4))(zod@3.22.4) + '@wagmi/core': 2.13.3(@tanstack/query-core@5.51.21)(@types/react@18.3.3)(immer@10.0.2)(react@18.2.0)(typescript@5.5.4)(viem@2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4)) react: 18.2.0 use-sync-external-store: 1.2.0(react@18.2.0) - viem: 2.19.1(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.16.3(bufferutil@4.0.8)(typescript@5.5.4)(utf-8-validate@5.0.10)(zod@3.22.4) optionalDependencies: typescript: 5.5.4 transitivePeerDependencies: