Home

Awesome

npm License: MIT tests GitPOAP Badge hardhat

Hardhat ZKit

The ultimate TypeScript environment for Circom development.

What

This hardhat plugin is a zero-config, one-stop Circom development environment that streamlines circuits management and lets you focus on the important - code.

Installation

npm install --save-dev @solarity/hardhat-zkit

And add the following line to your hardhat.config:

import "@solarity/hardhat-zkit"; // TypeScript

require("@solarity/hardhat-zkit"); // JavaScript

[!TIP] There is no need to download the Circom compiler separately. The plugin automatically installs missing compilers under the hood.

Usage

The hardhat-zkit is a zero-config plugin, however, you may add the following to your hardhat.config file:

module.exports = {
  zkit: {
    compilerVersion: "2.1.8",
    circuitsDir: "circuits",
    compilationSettings: {
      artifactsDir: "zkit/artifacts",
      onlyFiles: [],
      skipFiles: [],
      c: false,
      json: false,
    },
    setupSettings: {
      contributionSettings: {
        provingSystem: "groth16",
        contributions: 1,
      },
      onlyFiles: [],
      skipFiles: [],
      ptauDir: undefined,
      ptauDownload: true,
    },
    verifiersSettings: {
      verifiersDir: "contracts/verifiers",
      verifiersType: "sol",  
    },
    typesDir: "generated-types/zkit",
    quiet: false,
  },
};

Where:

Tasks

There are several hardhat tasks in the zkit scope that the plugin provides:

To view the available options, run the help command:

npx hardhat help zkit <zkit task name>

Typization

The plugin provides full TypeScript typization of Circom circuits leveraging zktype library.

The following config may be added to tsconfig.json file to allow for a better development experience:

{
  "compilerOptions": {
    "paths": {
      "@zkit": ["./generated-types/zkit"]
    }
  }
}

Testing

In order to utilize user-friendly Chai assertions for witness and ZK proof testing, the chai-zkit package needs to be installed:

npm install --save-dev @solarity/chai-zkit

And add the following line to your hardhat.config:

import "@solarity/chai-zkit"; // TypeScript

require("@solarity/chai-zkit"); // JavaScript

The package extends expect chai assertion to recognize typed zktype objects for frictionless testing experience.

[!NOTE] Please note that for witness testing purposes it is sufficient to compile the circuit just with zkit compile task, without generating the keys.

Example

The plugin extends the hardhat environment with the zkit object that allows typed circuits to be used in scripts and tests:

<table style="width:100%"> <tr> <th>Circom circuit</th> <th>Usage example</th> </tr> <tr> <td>
// file location: ./circuits/multiplier.circom

pragma circom 2.0.0;

template Multiplier(){
   signal input in1;
   signal input in2;
   signal output out;

   out <== in1 * in2;
}

component main = Multiplier();
</td> <td>
import { zkit } from "hardhat"; // hardhat-zkit plugin
import { expect } from "chai"; // chai-zkit extension
import { Multiplier } from "@zkit"; // zktype circuit-object

async function main() {
  const circuit: Multiplier = await zkit.getCircuit("Multiplier");
  // or await zkit.getCircuit("circuits/multiplier.circom:Multiplier");

  // witness testing
  await expect(circuit)
    .with.witnessInputs({ in1: "3", in2: "7" })
    .to.have.witnessOutputs({ out: "21" });

  // proof testing
  const proof = await circuit.generateProof({ in1: "4", in2: "2" });

  await expect(circuit).to.verifyProof(proof);
}

main()
  .then()
  .catch((e) => console.log(e));
</td> </tr> </table>

To see the plugin in action, place the Multiplier circuit in the circuits directory and execute:

npx hardhat zkit make

This command will compile the circuit leveraging wasm-based Circom compiler, download the necessary ptau file regarding the number of circuit's constraints, build the required zkey and vkey files, and generate TypeScript object wrappers to enable full typization of signals and ZK proofs.

Afterward, you may run the provided hardhat script.

API reference


The method accepts the name of the main component of the circuit and returns the instantiated zkit object pointing to that circuit.

The method works regardless of how the circuit was compiled, however, if zkit compile task was used, the zkit methods that utilize proof generation or proof verification would throw an error by design.

In case there are conflicts between circuit file names and main component names, you should use the fullCircuitName, which has the following form: circuitSourceName:circuitName.

Where:

[!IMPORTANT] Please note that the method actually returns the zktype typed zkit wrapper objects which enable full TypeScript typization of signals and proofs. Also, check out the zkit documentation to understand zkit object capabilities and how to interact with circuits.

Known limitations