Human Verification for Starknet dApps
Prove you're human once through World ID. Any dApp checks your status instantly. Provider-agnostic middleware that makes sybil resistance a one-liner.
import { useHumanGate, WorldIdVerify } from "@humangate/react";
const { signedAttestation, submitVerification } = useHumanGate();
// 1. Verify → 2. Sign → 3. Claim on-chain (~$0.01)Everything you need for human verification
A complete toolkit for sybil-resistant Starknet applications.
Provider Agnostic
World ID today. KYC, eID, social attestations tomorrow. Same API for dApps regardless of provider.
Verify Once, Use Everywhere
Users verify once on HumanGate Portal. Every dApp on Starknet can check their status.
On-Chain Attestations
Verification stored on Starknet. Fully decentralized, auditable, and composable.
Anti-Sybil by Design
Nullifier hashes ensure one human = one use per action, without revealing identity.
Two Verification Levels
Device (phone) for wide reach. Orb (biometric) for high-stakes like airdrops and voting.
Developer-First SDK
React hooks, TypeScript SDK, and Cairo interfaces. Integrate in 5 minutes.
How it works
Three simple steps to sybil-resistant dApps.
Verify Once
User connects their Starknet wallet and verifies through World ID. The backend signs an attestation linked to their address.
Register On-Chain
User submits the signed attestation to Starknet with their wallet (~$0.01 gas). The contract verifies the signature and stores it.
Use Everywhere
Any dApp reads the attestation on-chain for free. Scoped nullifiers prevent double-claiming per action without revealing identity.
Connect your Starknet wallet and verify via World ID. Takes less than a minute.
Integrate in minutes
Whether you're building a React frontend, reading on-chain data, or writing a Cairo smart contract.
React Frontend
import { useHumanGate, WorldIdVerify } from "@humangate/react";
function VerifyButton({ address }) {
const [open, setOpen] = useState(false);
const { signedAttestation, submitVerification } = useHumanGate();
return (
<>
<button onClick={() => setOpen(true)}>
{signedAttestation ? "Verified" : "Verify as Human"}
</button>
<WorldIdVerify
action="verify-human"
address={address}
open={open}
onOpenChange={setOpen}
onSuccess={async (proof) => {
setOpen(false);
await submitVerification(proof, "verify-human", address);
}}
/>
</>
);
}On-Chain Reads
import { HumanGateChainReader } from "@humangate/sdk";
const reader = new HumanGateChainReader({
rpcUrl: "https://rpc.starknet.lava.build",
registryAddress: "0x07f668...7d26",
});
// Check if user is a verified human (free, no gas)
const isHuman = await reader.isHuman(userAddress);
// Check eligibility for scoped action (airdrop, vote)
const eligible = await reader.isEligible(
userAddress, scopeHash, "worldcoin"
);
// Check if action already consumed
const consumed = await reader.hasConsumed(userAddress, scopeHash);Cairo Contract
use humangate::interface::IHumanGateDispatcherTrait;
use humangate::interface::IHumanGateDispatcher;
#[starknet::contract]
mod Airdrop {
use super::{IHumanGateDispatcher, IHumanGateDispatcherTrait};
use starknet::ContractAddress;
#[storage]
struct Storage {
humangate: IHumanGateDispatcher,
}
#[external(v0)]
fn claim(ref self: ContractState) {
let caller = starknet::get_caller_address();
// Check human verification on-chain (free)
let eligible = self.humangate.read().is_eligible(
caller, scope_hash, provider_id
);
assert(eligible, 'Not verified as human');
// Consume action (prevents double-claim)
self.humangate.read().consume_action(
caller, scope_hash, provider_id
);
// ... distribute tokens
}
}Live on Starknet
Deployed and verified. Start building today.
Registry Contract
0x07f66804a1556e31993afb4eddfb2c90b78f67785c92a5860a076edc38b17d26Ready to make your dApp sybil-resistant?
Start integrating HumanGate in minutes. Free for development.