Skip to main content

Summary

Base Verify Onchain lets your smart contract enforce “one real person, once” and gate on real-world traits, like an active Coinbase One membership. The check runs in your contract, so you don’t run a verification backend. Base Verify signs a short-lived verification your contract checks in the same transaction as a claim, deposit, or vote.
Live on Base Sepolia! Try the demo. Please reach out if you have use cases in mind!
Integration is three steps:
  1. Deploy or upgrade a contract to extend BaseVerifyConsumer and declare an immutable provider and conditions (your eligibility policy).
  2. Fetch a verification in your app: the user signs a SIWE message naming your contract, which you POST to POST /v1/onchain_verifications.
  3. Submit the returned { identityHash, expiration, signature } to your contract, which calls registry.verifyVerification(...) and dedupes on identityHash.
Two example consumers to copy from:

What is Base Verify Onchain?

Base Verify lets users prove ownership of verified accounts (X, Coinbase, Instagram, TikTok) without revealing their account details. It solves two problems that wallets alone cannot: Sybil resistance (one real identity counts once, no matter how many wallets it splits across) and policy gating (admit only users who meet a real-world bar, such as an active Coinbase One membership, even when a wallet has little onchain history). Base Verify Onchain enforces both directly in your contract. The Base Verify backend signs a short-lived EIP-712 verification that your contract checks in the same transaction as a claim, deposit, mint, or vote. No backend at claim time, and your contract never learns who the user is:
  • Sybil resistance comes from the identityHash. The same real-world identity always produces the same hash for your contract, regardless of which wallet it uses, so your contract counts each real person once.
  • Policy gating comes from your contract’s policy. You declare a provider and conditions (for example, X followers ≥ 10,000 or an active Coinbase One membership); Base Verify checks the user’s real credential against them and only signs when they pass.
A single check can do both at once: gate on your policy and dedupe on identity in the same transaction. If your app already enforces this offchain (your own backend and database), start with Verify Social Accounts instead. This guide is for enforcing it in a contract.

Core concepts

Verification

A short-lived object signed by the Base Verify backend that your contract checks through the SignerRegistry. It is signed as EIP-712 typed data and contains:
  • identityHash — a one-way hash of the user’s real-world identity (your dedupe key).
  • policyHash — binds the verification to your contract’s policy on a specific chain. The registry recomputes it onchain, so it never travels in the response.
  • expiration — unix seconds; verifications are short-lived (a few minutes).

identityHash

The dedupe key. It is deterministic per identity and per contract: the same real person always produces the same identityHash for your contract, across any wallet they verify from. You store each identityHash and reject repeats.
  • One-way — you cannot recover the user’s identity from it.
  • Per-contract — different for every contract, so identities cannot be correlated across apps.
  • Cross-wallet — a second wallet for the same person produces the same hash, so your contract blocks the duplicate.

Policy (provider + conditions)

Your contract declares who is eligible: one provider plus one or more conditions (for example, an active Coinbase One membership, or X followers greater than or equal to 1000). The Base Verify backend reads this policy directly from your contract and checks the user’s stored credential against it before signing.

How eligibility is enforced

“Base Verify” here means the Base Verify backend, the off-chain service that holds the signer key, not Base Chain. It reads your contract’s provider and conditions onchain (via eth_call), evaluates them against the user’s stored credential, and signs a verification only when they pass. Conditions come from your contract, never from the user, so a user cannot strip or fake one to obtain a verification they are not entitled to.

Architecture and flow

A claim moves through your app, the Base Verify API, and your contract:
  1. The user connects their wallet in your app.
  2. Your app builds a SIWE message that names your contract (in the Resources line) and has the user sign it.
  3. Your app posts the message and signature to the Base Verify API.
  4. Base recovers the wallet from the signature, reads your contract’s provider and conditions onchain, and checks the wallet’s stored credential against them.
  5. On success, Base returns a signed verification (identityHash, expiration, signature). If the user isn’t verified or doesn’t meet the conditions, it returns a 404 or 400 instead.
  6. Your app submits the verification to your contract’s enroll function (or your deposit, borrow, or claim path).
  7. Your contract calls registry.verifyVerification(...), which checks the signature and expiry and recomputes policyHash from your live policy. Your contract then dedupes on identityHash and lets the user participate.

Implementation

1

Write your contract

Extend BaseVerifyConsumer so your policy is readable onchain, check verifications through the registry, and dedupe on identityHash. This example enrolls one verified, policy-gated identity per real person, so a single farmer can’t multiply rewards across wallets.
IncentiveProgram.sol
provider() and conditions() must be immutable (return constants). They are folded into policyHash; if they change, every outstanding verification stops verifying and one identity can re-enroll under a new hash, breaking your Sybil resistance.
2

Fetch a verification in your app

Have the user sign a SIWE message that names your contract, then POST it to the Base Verify API.
lib/fetch-verification.ts
No API key: the request needs no Authorization header because the SIWE signature is the credential.
3

Submit the verification to your contract

Pass the API response straight to your contract’s enroll function (or your deposit, borrow, or claim path).
enroll.ts
Your contract calls registry.verifyVerification(...); if the signature, expiry, and policy all check out, enroll() records the identityHash. A second wallet for the same person produces the same identityHash and is rejected.

Error handling

If the API does not return 200, do not submit the transaction. Handle the response by status: To send a user to Base Verify to complete OAuth, redirect to https://verify.base.dev with your app URL and the provider:
Base Verify redirect URL format

API reference

POST /v1/onchain_verifications

Exchanges a SIWE signature for a signed, short-lived onchain verification. No API key: the SIWE signature is the credential, and the verification is only usable by the signing wallet at the contract it names.

Request

POST /v1/onchain_verifications request body
string
required
The SIWE message. Its statement must be exactly Claim eligibility for a Base Verify onchain benefit., its chainId must be the chain you are claiming on (Base Sepolia 84532 during the test phase), and its Resources must include eip155:<chainId>:<yourContractAddress>.
string
required
The wallet’s signature over the SIWE message. Base Account smart-wallet (ERC-1271 / ERC-6492) signatures are supported.

Example request

POST /v1/onchain_verifications cURL example

Response 200

200 OK response
string (bytes32)
One-way hash of the user’s real-world identity. Deterministic per identity and per contract. Store it and dedupe on it.
integer
Unix seconds after which the verification is invalid. Verifications are short-lived (a few minutes), so submit the transaction promptly.
string
EIP-712 signature from a Base-operated signer. Pass it to your contract.
The response omits policyHash and the contract address: your contract recomputes policyHash from its own policy, and the verified wallet is the msg.sender your consumer passes to the registry. For error responses, see Error handling.

Contracts

SignerRegistry

A stateless verifier deployed by Base. It holds only a signer allowlist and answers one question: was this verification signed by a trusted signer, unexpired, and bound to the calling contract’s policy? Deduplication of identityHash is left to each consumer. Your contract calls verifyVerification, which reverts unless the verification is valid:
SignerRegistry.verifyVerification
msg.sender is the consumer contract, so a verification cannot be replayed at a different contract: the registry recomputes policyHash from the caller’s live policy, and another contract’s policy produces a different hash that fails signature recovery.

BaseVerifyConsumer

The base contract your consumer extends. It exposes your policy onchain and gives you a helper that binds the caller as the verified wallet.
BaseVerifyConsumer interface
cutoffBlock() is not part of policyHash, so you can change it without invalidating outstanding verifications. Use it to require a fresh authentication (for example, after a policy or security change).

Supported providers and conditions

A policy is one provider plus one or more conditions. Supported operators are eq, gt, gte, lt, lte, and in. When you declare multiple conditions for a provider, all must be satisfied (AND logic).

Security

What Base Verify enforces

  • Eligibility is checked against the user’s real credential, read from your contract’s policy, so users cannot fake conditions.
  • One identity, one action, via the deterministic identityHash you dedupe on.
  • Verifications are bound to your contract and chain and expire quickly (a few minutes).
  • Only the signing wallet can submit a verification, so it cannot be front-run out of the mempool (when you extend BaseVerifyConsumer and pass msg.sender).

Requirements

  • Keep your policy (provider and conditions) immutable.
  • Dedupe on identityHash in your claim path.
  • Treat a successful check as proof of a unique verified identity, not of any specific account or personal data.

Support

Want to integrate Base Verify Onchain? Fill out the interest form and the team will reach out.