Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 52 additions & 5 deletions docs/apps/guides/verify-onchain.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ description: "Enforce Sybil resistance and policy gating inside any Base contrac

**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.

<Tip>
Live on Base Sepolia! Try the [demo](https://verify.base.dev/onchain-demo), or [reach
<Note>
Base Verify Onchain runs on Base Sepolia only. It is not deployed on Base mainnet, so build and test against it, but
do not put production value behind it. Try the [demo](https://verify.base.dev/onchain-demo), or [reach
out](https://forms.gle/WTcuWyKkvUV6gGik6) if you have use cases in mind.
</Tip>
</Note>

Integration is three steps:

Expand All @@ -22,7 +23,7 @@ Integration is three steps:
| :--------------- | :------------------------------------------------------------------------------------------------------------------------------ |
| `SignerRegistry` | [`0x4f15593fbF7e3491d15080e1610E7AF8deBA1a02`](https://sepolia.basescan.org/address/0x4f15593fbF7e3491d15080e1610E7AF8deBA1a02) |
| API base URL | `https://verify.base.dev/v1` |
| Chain | Base Sepolia (`84532`) |
| Chain | Base Sepolia (`84532`), testnet only |
| Consumer base | `BaseVerifyConsumer.sol` |

Two example consumers to copy from:
Expand All @@ -41,6 +42,8 @@ Two example consumers to copy from:

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](/base-account/guides/verify-social-accounts) instead. This guide is for enforcing it in a contract.

Neither the API response nor your contract carries the user's identity, but a claim is still a public transaction. See [What goes onchain](#what-goes-onchain) for exactly what it publishes.

## Core concepts

### Verification
Expand Down Expand Up @@ -79,6 +82,49 @@ A claim moves through your app, the Base Verify API, and your contract:
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.

## What goes onchain

A claim is an ordinary public transaction. Once it is mined, everything below is permanent, world-readable, and outside Base Verify's control. Weigh that when you design your policy and your contract.

### Written onchain

| Data | Where it appears | Notes |
| :------------------------------------------ | :---------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------- |
| `identityHash` | Transaction calldata, your contract's storage, and any event you emit with it | Permanently ties the claiming wallet to this identity inside your contract |
| Claiming wallet address | The transaction sender, and any event you emit with it | The wallet that signed the SIWE message and submitted the claim |
| `expiration` and the Base Verify `signature` | Transaction calldata | Public, but bound to your contract and that wallet, and it expires within minutes |
| Your `provider` and `conditions` | Public view functions on your deployed contract | Anyone can read your policy without sending a transaction |

Whatever your own contract stores or emits alongside a claim is public too. The two example consumers linked above keep a `mapping(bytes32 identityHash => bool)` and emit `Claimed(address indexed user, bytes32 indexed identityHash)`, which makes the wallet-to-hash pairing directly queryable from logs.

### Never onchain

- The provider account: no username, handle, account ID, or profile link.
- Trait values: no follower count, subscription state, or other credential data.
- OAuth tokens, or anything else Base Verify holds for the user.
- `policyHash`: the registry recomputes it in memory during verification and never stores or emits it.
- The user's real-world identity: `identityHash` is one-way and cannot be reversed.

### What an observer can infer

Your policy is public and your claims are public, so anyone can pair the two. The Coinbase One example consumer declares `provider = "coinbase"` and the condition `coinbase_one_active eq true`, so every wallet in its `Claimed` log is publicly known to have held an active Coinbase One membership at claim time. An observer cannot tell which Coinbase account, but does learn that the wallet had one.

A narrower policy leaks more. Gating on `followers gte 10000` marks each claiming wallet as a large X account, while gating on `verified eq true` says much less. Your contract name and any action naming leak the same way.

Because `identityHash` is scoped per contract, the same person claiming in two different contracts produces two unrelated hashes that observers cannot link. Within a single contract, every claim from that person shares one hash, which is exactly what makes the dedupe work.

### Where claim history lives

`verify.base.dev` surfaces a user's offchain credentials only: which providers they have verified, and the traits Base Verify stores for them. It has no view of onchain claims and cannot display, edit, or undo them.

Onchain claim history lives in your contract: your storage, your events, your chain. Users and observers read it through a block explorer or your contract's public getters. If you want your users to see their own claim history, you build that view.

<Warning>
Deleting a verification at `verify.base.dev` removes the user's offchain credential and stops Base Verify from
signing new verifications for them. It does not reach anything already written onchain. Claims your contract has
already recorded stay recorded, and the transactions stay in the chain's history.
</Warning>

## Implementation

<Steps>
Expand Down Expand Up @@ -238,7 +284,7 @@ Exchanges a SIWE signature for a signed, short-lived onchain verification. No AP
```

<ParamField body="message" type="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>`.
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`, the only supported chain), and its `Resources` must include `eip155:<chainId>:<yourContractAddress>`.
</ParamField>

<ParamField body="signature" type="string" required>
Expand Down Expand Up @@ -370,6 +416,7 @@ When you declare multiple conditions for a provider, **all** must be satisfied (
- 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.
- Tell your users what a claim publishes, and remember that you own any user-facing view of it. See [What goes onchain](#what-goes-onchain).

## Support

Expand Down
4 changes: 4 additions & 0 deletions docs/base-account/guides/verify-social-accounts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,10 @@ Users can delete their verifications at any time:
- Invalidates future token generation
- Your app's stored tokens become meaningless (user can't re-verify with the same account)

<Note>
Deletion covers the offchain credential that Base Verify holds. It cannot reach data an app has already written elsewhere, including anything a contract recorded through [Base Verify Onchain](/apps/guides/verify-onchain). `verify.base.dev` shows offchain credentials only, so onchain claim history is not visible or reversible there.
</Note>

### Caching

Cache verification results to reduce API calls:
Expand Down
Loading