Request form
WorldCoin ID Starter
  • Identity
  • Protocol

Building the world’s largest identity and financial network as a public utility, giving ownership to everyone.





WorldCoin ID Starter
  • Identity
  • Protocol

Get Cookin'



World ID Starter Kit (Smart Contracts w/ Hardhat)

Easiest way to get started with World ID on-chain. This repository contains the minimum requirements to build web3 dApps with World ID, allowing you to easily add sybil-resistance and uniqueness features to your dApp.

This repository contains the smart contract code, and is built with the Hardhat toolkit. We also have a Foundry version in the world-id-starter repository.

🏃 Getting started

Start with the verifyAndExecute function on the Contract.sol file, which contains the basic World ID logic. You can rename this function as you choose (for example, we use claim on our airdrop example).

Setting your Action ID

The action ID (also called "external nullifier") makes sure that the proof your contract receives was generated for it. We recommend generating Action IDs on the Developer Portal (more on Action IDs).

Note: Make sure you're passing the correct Action ID when initializing the JS widget! The generated proof will be invalid otherwise.

Setting your signal

The signal adds an additional layer of protection to the World ID ZKP, it makes sure that the input provided to the contract is the one the person who generated the proof intended (more on signals). By default this contract expects an address (receiver), but you can update it to be any arbitrary string.

To update the signal, you should change the input on the abi.encodePacked(input).hashToField() line. You should provide the exact same string when initializing the JS widget, to make sure the proof includes them.

Note: The hashToField part is really important, as validation will fail otherwise even with the right parameters. Make sure to include it!

About nullifiers

Nullifiers are what enforces uniqueness in World ID. You can generate multiple proofs for a given signal and action ID, but they will all have the same nullifier. Note how, in the verifyAndExecute function we first check if the given nullifier has already been used (and revert if so), then mark it as used after the proof is verified.

If your use-case doesn't require uniqueness, you can use them as "anonymous identifiers", linking users between different signals (for example, allowing them to change which address they've verified in a social network). To do this, update the nullifierHashes mapping to point to some sort of identifier instead of a boolean. See this project as an example.

Advanced: Supporting multiple actions

The external nullifier makes sure that the proof your contract receives was generated for it, and not for a different contract using the same signal. Unless your use case requires users to perform more than one World ID action (claiming multiple airdrops that live in the same contract, for example, instead of a single one), you should use one Action ID, which you can obtain from the Developer Portal.

For advanced use cases, you can add additional arguments to the abi.encodePacked call to differentiate between actions, like so:

function claimAirdrop(
    uint256 airdropId,
    address receiver,
    uint256 root,
    uint256 nullifierHash,
    uint256[8] calldata proof
) public {
    // ...

        abi.encodePacked(actionId, airdropId).hashToField(),

    // ...

🗝 Usage instructions

  1. End users will need a verified identity, which can be obtained through our Simulator (see docs for more info). In production, this would be obtained by verifying with an orb.

  2. Use the JS widget to prompt the user with verification (make sure you're providing the correct signal and action ID). Upon acceptance, you'll get a merkle_root, nullifier_hash and proof.

  3. The ZKP (attribute proof) is a uint256[8] array and your smart contract expects it that way. For easier handling, the JS widget will return the proof encoded. Unpack your proof before sending it to your smart contract.

import { defaultAbiCoder as abi } from '@ethers/utils'
const unpackedProof = abi.decode(['uint256[8]'], proof)[0]
// You can now pass your unpackedProof to your smart contract
  1. Use the obtained parameters, along with any inputs your contract needs (which should be included in the signal), to call your smart contract!

🧑‍💻 Development & testing

This repository uses the Hardhat smart contract toolkit. You'll also need Node.js v14 in order to build, and deploy your contract.

Once you have node installed, you can run pnpm install from the base directory to install all dependencies.

About World ID

World ID is the privacy-first identity protocol that brings global proof of personhood to the internet. More on World ID in the announcement blog post.

World ID lets you seamlessly integrate authentication into your app that verifies accounts belong to real persons through Sign in with Worldcoin. For additional flexibility and cases where you need extreme privacy, Anonymous Actions lets you verify users in a way that cannot be tracked across verifications.

Follow the Quick Start guide for the easiest way to get started.

📄 Documentation

All the technical docs for the Wordcoin SDK, World ID Protocol, examples, guides can be found at

Building the world’s largest identity and financial network as a public utility, giving ownership to everyone.