Create your ZK Badge in 15 Minutes
Mint a ZK badge for your app or community in 15 minutes

What’s inside?

This tutorial will walk you through the creation of your first ZK Badge for your app or your community! The tutorial is beginner-friendly. The aim of this tutorial is to show you how to create a ZK badge for an eligible group of users and your customized minting page that will be live on! Your users will love it! 😁

What are ZK Badges?

A Badge is a Non-Transferable Token (ERC-1155) that can only be minted by an eligible group of users for this ZK Badge (groups of addresses or web2 accounts).
A (ZK) Badge is related to two accounts: the source account (address or web2 account), from which the user will generate a proof of eligibility and a destination account (address) where the user will receive the badge. The (ZK) proof of eligibility is verified on-chain by a smart contract called a (ZK) Attester.
ZK Badges use ZK Proofs and are privacy preserving, from a badge held on a destination account, no one can infer which source account was used!

Tutorial use-case

This tutorial will show you how to easily define the group of eligible users for your ZK Badge and easily create your customized minting page. At the end of this tutorial, it will be up to you to do a pull request on the Sismo Hub to see it live on!
We will take the example of an app developer that wants to airdrop tokens to users that voted on at least one proposal from the Gitcoin Snapshot Space while guaranteeing them total privacy. Its strategy is to create a ZK Badge for all eligible users and then to airdrop tokens to user that minted the ZK Badge.
Take a look at your future minting page!
Your future custom minting page
This minting page is the page you want to redirect your users to see if they are in the eligible group of voters 💪
Note that the dev experience of this tutorial is designed to be the same as in the prod environment on polygon. You will be one pull request away of making your ZK badge available to mint for your users!

Setup of the local environment

Alright, let’s build this workflow in a local environment to give you a feeling of it 😄
Let's follow the user path of a developer who wants to do an airdrop to most active Gitcoin voters on Snapshot without doxxing them. We begin by setting up the local infrastructure for this tutorial.
All the things you want for this tutorial are located in this Sismo repository:
You can clone the repository and cd into it:
# using ssh
git clone [email protected]:sismo-core/sismo-hub.git
cd sismo-hub
You can now install all the dependencies with the yarn command:
# install dependencies
You can install yarn easily here.
Then, you will want to launch several services in your terminal (expand to see details) :
A ganache local chain
A local frontend of Sismo’s app
Several contracts on this local chain, deployed with Hardhat
A local relayer
You will only need to install Docker in order to launch all the services with the following command.
docker compose up
You will see the local chain starting and the contracts being deployed.
[+] Running 4/0
⠿ Container sismo-hub-frontend-1 Created 0.0s
⠿ Container sismo-hub-chain-1 Created 0.0s
⠿ Container sismo-hub-relayer-1 Created 0.0s
⠿ Container sismo-hub-deploy_contract-1 Created 0.0s
Attaching to sismo-hub-chain-1, sismo-hub-deploy_contract-1, sismo-hub-frontend-1, sismo-hub-relayer-1
sismo-hub-frontend-1 | / /docker-entrypoint.d/ is not empty, will attempt to perform configuration
sismo-hub-frontend-1 | / Looking for shell scripts in /docker-entrypoint.d/
sismo-hub-frontend-1 | / Launching /docker-entrypoint.d/
sismo-hub-frontend-1 | info: IPv6 listen already enabled
sismo-hub-frontend-1 | / Launching /docker-entrypoint.d/
sismo-hub-frontend-1 | / Configuration complete; ready for start up
sismo-hub-frontend-1 | 2022/09/01 09:36:32 [emerg] 50#50: io_setup() failed (38: Function not implemented)
sismo-hub-deploy_contract-1 | Waiting for chain to be up ...
sismo-hub-relayer-1 | Server listening on <http://localhost:8080>
sismo-hub-deploy_contract-1 | Waiting for chain to be up ...
sismo-hub-chain-1 | Ganache CLI v6.12.2 (ganache-core: 2.13.2)
sismo-hub-chain-1 | (Use `node --trace-deprecation ...` to show where the warning was created)
sismo-hub-chain-1 |
sismo-hub-chain-1 | Available Accounts
sismo-hub-chain-1 | ==================
sismo-hub-chain-1 | (0) 0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC (100 ETH)
The deployer address used for the local deployment is 0xb01ee322C4f028B8A6BFcD2a5d48107dc5bC99EC.
Here is the list of all the contracts with their addresses in the local infra:
  • AvailableRootsRegistry at 0x4CA636f37b577BfEEcE58eEc19053AC4490365BB
  • CommitmentMapperRegistry at 0xe52EB1f52349F98c09a5B11B0E3fA7f269268Add
  • HydraS1Verifier at 0xF7b494E07466759367BD71Eb4f545593eed4B854
  • HydraS1SimpleAttester at 0xa73a8094E303A823a8b64089fFD79913E76092cF
  • HydraS1SoulboundAttester at 0x7F098d94409110E8407BcE81D3648Ad6a8eDd44b
  • Front at 0x7f1624094ACe6cd9653A8c3C3D92F2fAbb241B07
  • Badges at 0xeF5b2Be9a6075a61bCA4384abc375485d5e196c3
You can read more about all the contracts here.

Launch your local API

The API will allow you to see the groups you will generate.
To launch the API, open a new terminal window and use the following command:
# in a new terminal
# launch local API
yarn api:watch
Experiment with the API
Congrats 🎉 You just launched your local infra 😄
Take a moment to see your already working frontend by going to http://localhost:3000/! Congrats!

Create the group for your ZK Badge

Now, let’s move on to the group generation. During this tutorial, you will be creating a brand new Group Generator for Gitcoin voters on Snapshot. It will be used to generate the group of eligible users for your ZK Badge before sending it on chain!
A group generator is a tool that allows to easily generate groups and store them in scalable infrastructure, you could learn more on the group generator here.

Generate your group

You can see all the group-generators here, this folder is where you can pick already known groups or add your owns.
Let’s create our group generator for Gitcoin DAO voters 🤘
Go to the folder called generators in a new terminal
# in a new terminal
# you are in sismo-hub
# cd into the folder
cd group-generators/generators
# create the group-generator folder for our new group
mkdir snapshot-gitcoindao-voters
cd snapshot-gitcoindao-voters
# create the file
touch index.ts
You will use the Snapshot Data Provider to retrieve the list of addresses that voted on the Gitcoin space.
// sismo-hub/group-generators/generators/snapshot-gitcoindao-voters/index.ts
import { dataProviders } from "@group-generators/helpers/providers";
import { Tags, ValueType, GroupWithData } from "topics/group";
import {
} from "topics/group-generator";
// Here you are hacker 😈
const generator: GroupGenerator = {
generationFrequency: GenerationFrequency.Once, // you generate the group only once
generate: async (
context: GenerationContext,
): Promise<GroupWithData[]> => {
// Instantiate your snapshot provider
const snapshotProvider = new dataProviders.SnapshotProvider();
// Query all voters from the space named "gitcoindao.eth"
const voters = await snapshotProvider.queryAllVoters({
space: "gitcoindao.eth",
return [
name: "snapshot-gitcoindao-voters", // give a name to your group
timestamp: context.timestamp,
data: voters, // we reference the queried data
valueType: ValueType.Score,
tags: [Tags.User],
export default generator;
You will then need to reference your newly created group generator in the generators folder.
// sismo-hub/group-generators/generators/index.ts
import ensVoters from "./ens-voters";
import ethereumDevelopers from "./ethereum-developers";
// you need to import your group generator
import snapshotGitcoinDaoVoters from "./snapshot-gitcoindao-voters";
import { GroupGeneratorsLibrary } from "topics/group-generator";
export const groupGenerators: GroupGeneratorsLibrary = {
"ens-voters": ensVoters,
"ethereum-developers": ethereumDevelopers,
// you need to reference your group generator
"snapshot-gitcoindao-voters": snapshotGitcoinDaoVoters
You can now generate the snapshot-gitcoindao-voters group!
Feel free to add your dev address to the group with the number of votes you want with the second command in order to experience the full user minting flow at the end of this tutorial.
# command to generate the group
yarn generate-group snapshot-gitcoindao-voters
# same command but your address is included with 4 votes in the group
yarn generate-group snapshot-gitcoindao-voters --additional-data 0x123...0bf=4
NB: You can add --help to know all the commands for generate-group
Make sure you also generate the local-group which is mandatory for the tutorial. The local-group is a made up group with no signification, it is available for you to experiment further by adding your own address to it and mint different badges quickly.
yarn generate-group local-group --additional-data 0x123...0bf=4
You can head over to the local API route that displays the latest groups you created at Here are your Gitcoin Voters group and the local group ! 💪
"items": [
"name": "local-group",
"timestamp": 1664458769,
"valueType": "Score",
"tags": [
"dataUrl": "/file-store/groups-data/local-group/1664458769.json"
"name": "snapshot-gitcoindao-voters",
"timestamp": 1664458754,
"valueType": "Score",
"tags": [
"dataUrl": "/file-store/groups-data/snapshot-gitcoindao-voters/1664458754.json"
You can use the browser extension JSON Viewer Extension for viewing JSON nicely
How to check if my address is in the group ? 🧐
Congrats 🎉 Your Gitcoin Voters group is now generated! Let's now send it on the local chain for your attester!

Send your groups to the Hydra-S1 ZK Attester

You just generated your groups but they are still stored off-chain.
Now, you need to send your groups to the attester. By doing so, the attester will have access to the data on-chain and will allow the badge minting (We need to send a transaction on-chain to send the Merkle Root of your group).
Let's send your groups to the hydra-s1 attester and make your ZK Badge available for minting!
Go to the attestations-collections folder. You will want to use the HydraS1 ZK Attester to preserve the privacy of the users that will mint your ZK badge.
Go the the local folder and reference your group and your badge metadata in the hydra-s1-local.ts file as follows:
// sismo-hub/attestations-collections/local/hydra-s1-local.ts
import { generateHydraS1Attester } from "@attestations-collections/base/hydra-s1";
import { Network } from "topics/attester";
import { BadgesCollection } from "topics/badge";
export const hydraS1LocalAttester = generateHydraS1Attester(
[Network.Local]: {
attesterAddress: "0xa73a8094E303A823a8b64089fFD79913E76092cF",
rootsRegistryAddress: "0x4CA636f37b577BfEEcE58eEc19053AC4490365BB",
name: "hydra-s1-local",
networks: [Network.Local],
attestationsCollections: [
// Sismo contributors
internalCollectionId: 0,
groupFetcher: async (groupStore) => [
await groupStore.latest("local-group"),
// Snapshot Gitcoin voters
// notice the collection id and choose one that is available
internalCollectionId: 3,
groupFetcher: async (groupStore) => [
// you fetch the last group generated with the name "snapshot-gitcoindao-voters"
await groupStore.latest("snapshot-gitcoindao-voters"),
export const hydraS1LocalBadges: BadgesCollection = {
collectionIdFirsts: {
[Network.Local]: 10000001,
badges: [
internalCollectionId: 0,
name: "Sismo Contributor ZK Badge",
description: "ZK Badge received by early contributors of Sismo",
image: "sismo_digger.svg",
groupGeneratorName: "local-group",
publicContacts: [{
type: "github",
contact: "leosayous21"
eligibility: {
shortDescription: "",
specification: "",
links: []
internalCollectionId: 3, // input the same collection id than before
name: "Gitcoin Voter on Snapshot ZK Badge", // add a name to your badge
description: "ZK Badge received by Gitcoin voters on Snapshot", // describe it !
image: "sismo_citizen.svg", // give it a wonderful artwork ;)
groupGeneratorName: "snapshot-gitcoindao-voters", // input the name of your GroupGenerator
publicContacts: [{ // give us a way to join you :)
type: "github", // github | twitter | lens ...
contact: "leosayous21" // your username
eligibility: {
// provide a short description of your eligibility criterias
shortDescription: "Vote in the GitcoinDAO space on Snapshot",
// provide a technical description of your eligibility criterias
specification: "You should have voted at least once in the GitcoinDAO space on Snapshot",
links: [
logoUrl: "", // a nice logo to have next to the url
label: "Gitcoin Space", // label for your url
url: "" // url of your project (here the Gitcoin space)
You can submit your badges artwork in the static/badges folder in a svg format. You can use the following svg template, follow the badge creation guide style and easily add your logo on top of it with this free website:
Badge Template to download
Don't forget to register your badge in order to have a working frontend at the end of the tutorial 😄
You can notice the other groups “Sismo Diggers”, “Fake Masquerade”, “Fake Ethereum power users” are fetching the latest created group called local-group.
Let's use the next command to send your generated groups to the hydra-s1 attester on your local chain. 😊 The groups are now available on the blockchain for the attester.
yarn send-to-attester hydra-s1-local local --send-on-chain
If the command outputs an error, make sure to generate all the groups that are in attestations-collections/local/index.ts. You can see the CLI documentation here.
You can see the latest groups sent on chain for your local HydraS1 here.
"items": [
"attesterName": "hydra-s1-local",
"timestamp": 1663418720,
"identifier": "0x16f896a987d86d4b219aadb2a6e77516781756dbceea813e429fc932efff1909",
"isOnChain": true,
"transactionHash": "0x70b64e71ad20fc77bbff2024c73d2540e2f70c4565e55cb25e6c978275531eac",
"url": "/file-store/available-groups/0x16f896a987d86d4b219aadb2a6e77516781756dbceea813e429fc932efff1909",
"network": "local"
Understand how your group is stored on-chain 🤓
Congrats again 🎉 ! You just sent your generated groups to the Hydra-S1 attester. Now, anyone in your group can mint a badge and have its attestation registered on-chain!

Create your custom minting page

Now, that you have generated your group and sent it on chain with the badge metadata associated to it, it is time to create a customized minting page for your users!
If you go to http://localhost:3000/ you can see that your badge is missing 🧐
UI without your flow
You don’t have your “flow” yet! The flow is the customizable minting experience you are offering to your users. Here we want to make the snapshot-gitcoindao-voters flow. So let’s get to it 🤘
The flow can be customized by changing the local.ts file in the flows folder because all our contracts are deployed locally.
// sismo-hub/flows/local.ts
import {
} from "@attestations-collections/local";
import { Network } from "topics/attester";
import { Flow } from "topics/flow";
export const localFlows: Flow[] = [
path: "contributors",
network: Network.Local,
attesterType: "hydra-s1",
badgesCollection: hydraS1LocalBadges,
badgesInternalCollectionsIds: [0],
title: "Contributors",
logoUrl: null,
subtitle: "Show that you are an early contributor to Sismo.",
onboardingDescription: "Mint this badge to show that you are an early Sismo contributor",
ctaLabel: "Local",
ctaUrl: "<>",
congratulationTexts: ["1. Congratulation"],
path: "snapshot-gitcoindao-voters", // choose your frontend path
network: Network.Local,
attesterType: "hydra-s1", // choose your attester
badgesCollection: hydraS1LocalBadges,
badgesInternalCollectionsIds: [3], // choose your badge id here
title: "Gitcoin Voters", // choose your title
logoUrl: null,
subtitle: "Attest your vote on Snapshot's Gitcoin space", // choose your subtitle
onboardingDescription: "Access gated Gitcoin channels and become an active member of the Gitcoin DAO", // hype your users !
ctaLabel: "Access gated channels",
ctaUrl: "<>", // provide a link to your users. If you does not have a link you can add "" this will redirect to the sismo explorer.
congratulationTexts: ["Join Snapshot space !"],
You want to pay attention to the frontend path! Here you input the path that will be used to redirect your users from your app to your custom minting page! Feel free to customize your minting experience with the other fields 😁
By saving this file and refreshing the frontend, it displays your flow ! 😇 You can see your flow among the others by going to http://localhost:3000/ or directly to the path you chose http://localhost:3000/snapshot-gitcoindao-voters.
UI with your flow at http://localhost:3000/
Congrats! You just nailed the tutorial 💪 Let's recap all the steps:
  • You launched your local infra
  • You generated your group (all the users eligible to mint your future badge)
  • You sent your group to the HydraS1 attester, the attester can now issue badges and attestations for your group
  • You created your custom minting page for your users at http://localhost:3000/snapshot-gitcoindao-voters
If you want to make your PR to see your contribution used in the future, the next section is for you !

See your work live

You have to contribute to see your flow integrated on By doing this our infrastructure will take care of generating your group of eligible users, send it to our Polygon Playground Hydra-S1 Attester and display your minting page on
You don't have to store anything or launch anything on your side, just add a pull request to the Sismo Hub to see your minting page live! 😁
Great news: you have done the hardest part, so let's see how to make a good PR !
Let’s first of all create a new branch.
git checkout -b feature/snapshot-gitcoindao-voters
You will then need to change only two things:
  • Move your attestation collection and your badge from attestations-collections/local/index.ts to attestations-collections/playground/polygon/hydra-s1-simple.ts. By doing it, you allow your group of users to be available for our Hydra S1 playground attester on Polygon.
  • Then, add your custom flow to flows/playground.ts instead of flows/local.ts. You want to pay attention to network, badgesCollection and badgesInternalCollectionsIds.
Don't forget to remove your changes in local files after putting them in playground files. The PR will require changes otherwise. 😣
Don't hesitate to make a PR, we are quickly reviewing it to give you a feedback or accept it if it's all good 😇
Huge congrats for your contribution 💪 🎉

Next steps for your community

You are now able to redirect your users to the path you chose for your minting page on, if they are eligible they will mint your badge on the Sismo UI easily.
You will then be able to use token-gated tools such as to engage your community or Snapshot for privacy preserving votes. You can also gate access to features of your dapp by checking if your users hold your badge with this quick tutorial.
Don’t hesitate to go on our discord if you have any questions or if you liked your first experience and you want to follow our next moves!
Copy link
On this page
What’s inside?
What are ZK Badges?
Tutorial use-case
Setup of the local environment
Create the group for your ZK Badge
Send your groups to the Hydra-S1 ZK Attester
Create your custom minting page
See your work live
Next steps for your community