We use cookies to enhance your browsing experience and analyze our traffic. By clicking "Accept All", you consent to our use of cookies.. View our Privacy Policy for more information.
Your browser (Internet Explorer) is out of date. Please download one of these up-to-date, free and excellent browsers:
For more security speed and comfort.
The download is safe from the vendor's official website.


Proof-of-HUMANity on-chain: protect your smart contracts from bots

Crypto & Blockchain
Community Author
May 19, 2022

Proof-of-HUMANity on-chain: protect your smart contracts from bots

2 min read

Work by Alex Bakoushin, edited by HUMAN Protocol

This article will demonstrate how developers can use Proof of HUMANity to protect their DApps from bots.

The article will cover how smart contracts are vulnerable to bot activity, and offer a solution to developers: Proof-of-HUMANity.

Let’s consider the simplest example dApp — a counter stored on-chain.

Demo: basic-counter.bakoush.in

Note. All demos in this article are deployed on Polygon Mumbai Testnet. Consider getting some test MATIC via their faucet.

Humans can increase the counter by pressing the button in the UI. Bots, instead, could leverage a direct call to the smart contract. This is an example that holds true across many smart contracts.

const increment = async () => {
 console.log('🤖 Incrementing counter...');  // Just call the method...
 const tx = await counter.increment();
 await tx.wait();  const value = await counterContract.counter();
 console.log(`✅ Done. New value is ${value}`);

Full bot code on GitHub

If you want to play with the bot yourself, here is the address of the basic counter contract (Polygon Mumbai Testnet): 0x336c94E1F0F4D0103b012E78E6700959c89Ba8AD

Depending on your app design, you may want to prevent similar bots from interacting with your app.

This is where the problem of bot protection arises.

Given that everyone can interact with smart contracts’ public methods, it is not enough to integrate traditional CAPTCHA solutions into the UI or backend servers. We have to additionally check humanity on-chain, within the smart contract itself — which has no ability to interact with the outside world, and thus can not call any off-chain APIs.

We propose a simple, yet powerful mechanism to pass the Proof of HUMANity verification to on-chain infrastructure.

Proof-of-HUMANity concept

Proof-of-HUMANity is signed evidence of the caller being a human. Signed off-chain by a trusted party, PoH could be then verified on-chain.

PoH consists of a proof base 36 or 97 bytes long depending on the type of proof, and a 65-bytes validator signature over the proof base.

proof base     | validator
              | signature
36 or 97 bytes | 65 bytes

On-chain one can verify the validator signature and thus trust that the humanity of the caller is authentically confirmed.

Proof-of-HUMANity can be of two types: basic and sovereign.

Basic PoH

The basic proof has a base of 36 bytes and thus is 101 bytes long. It consists of a random challenge and a timestamp as a proof base, along with a validator signature over the proof base.

Basic PoH
random    | timestamp | validator
challenge |           | signature
32 bytes  | 4 bytes   | 65 bytes

Sovereign PoH

The sovereign proof has a base of 97 bytes and thus is 166 bytes long. In addition to basic PoH elements, its base includes the sender's signature.

This way, one can check on-chain that the proof is generated by the transaction sender itself, eliminating the possibility of generating proofs by those not in control of the sender’s address.

This provides more robustness, but requires that users sign the challenge with their wallet.

Sovereign PoH
random    | sender    | timestamp | validator
challenge | signature |           | signature
32 bytes  | 65 bytes  | 4 bytes   | 65 bytes

Using Proof-of-HUMANity in your DApp

When a user wants to send a transaction to your smart contract, your app UI could get a Proof-of-HUMANity verification using the hCaptcha system. This proof then could be used in the smart contract call.

The smart contract, in turn, verifies that the proof comes from the trusted source and has not been seen before. Otherwise, it reverts.

Integrating PoH into your app is fairly easy thanks to existing libraries. It takes 3 steps:

  1. Deploy the validator API (hCaptcha)
  2. Include PoH into the UI of your dApp
  3. Integrate PoH into your smart contracts

Validator API

The key element of the Proof-Of-HUMANity is the validator signature. You must trust that this validator is actually validating users’ humanity. This can be achieved in at least two ways:

  • you are in full control of the validator (hCaptcha API)
  • you are trusting an established third-party validator, like a major CAPTCHA provider

The latter option is not yet available (but we hope it will), so let’s focus on the former one.

Deploy the validator API

You can quickly deploy an API producing the Proof-of-HUMANity using a Docker image. It is an example of PoH validator API for the hCaptcha service.

docker run -it \
 -p 8080:8080 \
 --env PORT=8080 \
 --env VALIDATOR_KEY=<Validator private key> \
 --env HCAPTCHA_SECRET=<hCaptcha secret> \

You have to provide hCaptcha secret and the validator private key. This key will be used to sign proofs.

You can also create your own validator, as long as it produces the valid proofs adhering to the PoH format we discussed earlier.

Include PoH in the User Interface

To interact with the deployed API, you can use a set of React components designed to be quickly integrated into any app:

1) Wrap your app into the ProofOfHumanityProvider:

import { ProofOfHumanityProvider } from ‘poh-react’;<ProofOfHumanityProvider>
 <App />

2) Instantiate the PoH hCaptcha validator plugin:

import hCaptchaValidator from ‘poh-validator-hcaptcha’;const validator = (

3) Initialize the getProofOfHumanity method from the PoH hook using the instantiated validator:

import { useProofOfHumanity } from ‘poh-react’;const { getProofOfHumanity } = useProofOfHumanity(validator);

4) Obtain the Proof-Of-HUMANity prior to sending any sensitive transaction, and provide it as a parameter to the sensitive external method you are calling:

const handleClick = () => {
 try {
   const {
   } = await getProofOfHumanity();  
 if (!error) {
     const tx = await mySmartContract.someImportantMethod(proof);
 } catch(error) {
<button onClick={handleClick}>Send transaction</button>

That’s it for the UI!

Updating smart contracts

You can use poh-contracts library to easily integrate Proof-Of-HUMANity into your smart contracts.

Let’s look at an example. Here is our basic counter contract:

contract Counter {
 uint256 public counter;
 event Increment(uint256 currentCounter);
function increment() public {
 if (counter > 99) {
     counter = 1;
   emit Increment(counter);    

Here is how we can integrate it with Proof-Of-HUMANity:

  1. Inherit your contract from HumanOnly.
  2. Ensure the validator address is set (can be done in the constructor or by external call as well).
  3. Add one of -proof modifiers to your sensitive methods along with bytes calldata proof parameter.

import "poh-contracts/contracts/HumanOnly.sol";contract Counter is HumanOnly {
 uint256 public counter;
 event Increment(uint256 currentCounter);  constructor() {
 }  function increment(bytes calldata proof) public basicPoH(proof) {
   counter++;    if (counter > 99) {
     counter = 1;
   emit Increment(counter);    

That’s it for the smart contract!

Now, the method call will revert unless the proof has a valid signature and has not been seen before.

Demo: poh-counter.bakoush.in

If you are eager to try to bot this one, here is the address (Polygon Mumbai Testnet): 0x454C82492DF9E5582186c983D26Dda6Bf9861A50


dApp Example
Counter dApp
Source code

Validator API (Docker)
Proof-of-Humanity hCaptcha Validator API

UI Components
Proof-of-Humanity hCaptcha Validator React

Solidity Library
Proof-of-Humanity Solidity Contracts

Guest post