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. Integration is three steps:- Deploy or upgrade a contract to extend
BaseVerifyConsumerand declare an immutableproviderandconditions(your eligibility policy). - Fetch a verification in your app: the user signs a SIWE message naming your contract, which you POST to
POST /v1/onchain_verifications. - Submit the returned
{ identityHash, expiration, signature }to your contract, which callsregistry.verifyVerification(...)and dedupes onidentityHash.
Two example consumers to copy from:
- Verified X account example — gates on a verified X account.
- Coinbase One example — gates on an active Coinbase One membership.
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
providerandconditions(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.
Core concepts
Verification
A short-lived object signed by the Base Verify backend that your contract checks through theSignerRegistry. 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 sameidentityHash 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: oneprovider 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’sprovider 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:- The user connects their wallet in your app.
- Your app builds a SIWE message that names your contract (in the
Resourcesline) and has the user sign it. - Your app posts the message and signature to the Base Verify API.
- Base recovers the wallet from the signature, reads your contract’s
providerandconditionsonchain, and checks the wallet’s stored credential against them. - On success, Base returns a signed verification (
identityHash,expiration,signature). If the user isn’t verified or doesn’t meet the conditions, it returns a404or400instead. - Your app submits the verification to your contract’s
enrollfunction (or your deposit, borrow, or claim path). - Your contract calls
registry.verifyVerification(...), which checks the signature and expiry and recomputespolicyHashfrom your live policy. Your contract then dedupes onidentityHashand 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
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 Your contract calls
enroll function (or your deposit, borrow, or claim path).enroll.ts
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 return200, 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
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.
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 ofidentityHash 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 oneprovider 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
identityHashyou 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
BaseVerifyConsumerand passmsg.sender).
Requirements
- Keep your policy (
providerandconditions) immutable. - Dedupe on
identityHashin your claim path. - Treat a successful check as proof of a unique verified identity, not of any specific account or personal data.