diff --git a/packages/actions/src/helpers/verification.ts b/packages/actions/src/helpers/verification.ts
index 6403d801..097faf6a 100644
--- a/packages/actions/src/helpers/verification.ts
+++ b/packages/actions/src/helpers/verification.ts
@@ -2,6 +2,7 @@ import { groth16, zKey } from "snarkjs"
 import fs from "fs"
 import { Firestore, where } from "firebase/firestore"
 import { Functions } from "firebase/functions"
+import { cwd } from "process"
 import { downloadCeremonyArtifact, getBucketName, getZkeyStorageFilePath } from "./storage"
 import { fromQueryToFirebaseDocumentInfo, getCeremonyCircuits, queryCollection } from "./database"
 import { commonTerms, finalContributionIndex } from "./constants"
@@ -133,6 +134,15 @@ export const exportVerifierAndVKey = async (
     fs.writeFileSync(vKeyLocalPath, JSON.stringify(verificationKeyJSONData))
 }
 
+interface CeremonyArtifacts {
+    circuitPrefix: string
+    directoryRoot: string
+    potLocalFilePath: string
+    r1csLocalFilePath: string
+    finalZkeyLocalFilePath: string
+    lastZkeyLocalFilePath: string
+}
+
 /**
  * Given a ceremony prefix, download all the ceremony artifacts
  * @param functions <Functions> firebase functions instance
@@ -145,7 +155,7 @@ export const downloadAllCeremonyArtifacts = async (
     firestore: Firestore,
     ceremonyPrefix: string,
     outputDirectory: string
-) => {
+): Promise<CeremonyArtifacts[]> => {
     // mkdir if not exists
     if (!fs.existsSync(outputDirectory)) {
         fs.mkdirSync(outputDirectory)
@@ -154,6 +164,8 @@ export const downloadAllCeremonyArtifacts = async (
     if (!process.env.CONFIG_CEREMONY_BUCKET_POSTFIX)
         throw new Error("CONFIG_CEREMONY_BUCKET_POSTFIX not set. Please review your env file and try again.")
 
+    const ceremonyArtifacts: CeremonyArtifacts[] = []
+
     // find the ceremony given the prefix
     const ceremonyQuery = await queryCollection(firestore, commonTerms.collections.ceremonies.name, [
         where(commonTerms.collections.ceremonies.fields.prefix, "==", ceremonyPrefix)
@@ -177,9 +189,9 @@ export const downloadAllCeremonyArtifacts = async (
         fs.mkdirSync(circuitDir, { recursive: true })
 
         // get all required file names in storage and for local storage
-        const {potStoragePath} = circuit.data.files
+        const { potStoragePath } = circuit.data.files
         const potLocalPath = `${circuitDir}/${circuit.data.files.potFilename}`
-        const {r1csStoragePath} = circuit.data.files
+        const { r1csStoragePath } = circuit.data.files
         const r1csLocalPath = `${circuitDir}/${circuit.data.files.r1csFilename}`
         const contributions = circuit.data.waitingQueue.completedContributions
         const zkeyIndex = formatZkeyIndex(contributions)
@@ -197,5 +209,97 @@ export const downloadAllCeremonyArtifacts = async (
         await downloadCeremonyArtifact(functions, bucketName, r1csStoragePath, r1csLocalPath)
         await downloadCeremonyArtifact(functions, bucketName, lastZKeyStoragePath, lastZKeyLocalPath)
         await downloadCeremonyArtifact(functions, bucketName, finalZkeyPath, finalZKeyLocalPath)
+
+        ceremonyArtifacts.push({
+            circuitPrefix: circuit.data.prefix,
+            directoryRoot: circuitDir,
+            potLocalFilePath: potLocalPath,
+            r1csLocalFilePath: r1csLocalPath,
+            finalZkeyLocalFilePath: finalZKeyLocalPath,
+            lastZkeyLocalFilePath: lastZKeyLocalPath
+        })
     }
+
+    return ceremonyArtifacts
+}
+
+/**
+ * Verify a ceremony validity
+ * 1. Download all artifacts
+ * 2. Verify that the zkeys are valid
+ * 3. Extract the verifier and the vKey
+ * 4. Generate a proof and verify it locally
+ * 5. Deploy Verifier contract and verify the proof on-chain
+ * @param functions <Functions> firebase functions instance
+ * @param firestore <Firestore> firebase firestore instance
+ * @param ceremonyPrefix <string> ceremony prefix
+ * @param outputDirectory <string> output directory where to store the ceremony artifacts
+ * @param solidityVersion <string> solidity version to use for the verifier contract
+ * @param wasmPath <string> path to the wasm file
+ * @param circuitInputs <object> circuit inputs
+ * @param logger <any> logger for printing snarkjs output
+ */
+export const verifyCeremony = async (
+    functions: Functions,
+    firestore: Firestore,
+    ceremonyPrefix: string,
+    outputDirectory: string,
+    solidityVersion: string,
+    wasmPath: string,
+    circuitInputs: object,
+    logger?: any
+): Promise<boolean> => {
+    // download all ceremony artifacts
+    const ceremonyArtifacts = await downloadAllCeremonyArtifacts(functions, firestore, ceremonyPrefix, outputDirectory)
+
+    if (ceremonyArtifacts.length === 0)
+        throw new Error(
+            "There was an error while downloading all ceremony artifacts. Please review your ceremony prefix and try again."
+        )
+
+    for (const ceremonyArtifact of ceremonyArtifacts) {
+        // 1. verify the zkeys
+        const isValid = await verifyZKey(
+            ceremonyArtifact.r1csLocalFilePath,
+            ceremonyArtifact.finalZkeyLocalFilePath,
+            ceremonyArtifact.potLocalFilePath,
+            logger
+        )
+        if (!isValid)
+            throw new Error(
+                `The zkey for Circuit ${ceremonyArtifact.circuitPrefix} is not valid. Please check that the artifact is correct. If not, you might have to re run the final contribution to compute a valid final zKey.`
+            )
+
+        // 2. extract the verifier and the vKey
+        const templatePath = `${cwd()}/node_modules/snarkjs/templates/verifier_groth16.sol.ejs`
+        const verifierLocalPath = `${cwd()}/packages/actions/contracts/Verifier_${ceremonyArtifact.circuitPrefix}.sol`
+        const vKeyLocalPath = `${ceremonyArtifact.directoryRoot}/${ceremonyArtifact.circuitPrefix}_vkey.json`
+        await exportVerifierAndVKey(
+            solidityVersion,
+            ceremonyArtifact.finalZkeyLocalFilePath,
+            verifierLocalPath,
+            vKeyLocalPath,
+            templatePath
+        )
+
+        // 3. generate a proof and verify it locally
+        const { proof, publicSignals } = await generateGROTH16Proof(
+            circuitInputs,
+            ceremonyArtifact.finalZkeyLocalFilePath,
+            wasmPath,
+            logger
+        )
+        const isProofValid = await verifyGROTH16Proof(vKeyLocalPath, publicSignals, proof)
+        if (!isProofValid)
+            throw new Error(
+                `Could not verify the proof for Circuit ${ceremonyArtifact.circuitPrefix}. Please check that the artifacts are correct as well as the inputs to the circuit, and try again.`
+            )
+
+        // 4. deploy Verifier contract and verify the proof on-chain
+        // const verifierContract = await deployVerifierContract(verifierLocalPath, ceremonyArtifact.circuitPrefix)
+        // const isProofValidOnChain = await verifyGROTH16ProofOnChain(verifierContract, publicSignals, proof)
+        // if (!isProofValidOnChain) throw new Error(`Could not verify the proof on-chain for Circuit ${ceremonyArtifact.circuitPrefix}. Please check that the artifacts are correct as well as the inputs to the circuit, and try again.`)
+    }
+
+    return true
 }