You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/apps/guides/verify-onchain.mdx
+41-40Lines changed: 41 additions & 40 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,29 +1,30 @@
1
1
---
2
2
title: "Verify Users Onchain"
3
-
description: "Enforce Sybil resistance and policy gating inside any Base contract. Base signs a short-lived verification your contract checks in the same transaction as a claim, deposit, or vote, so one real identity counts once and only wallets that meet your bar can participate."
3
+
description: "Enforce Sybil resistance and policy gating inside any Base contract. Base Verify signs a short-lived verification your contract checks in the same transaction as a claim, deposit, or vote, so one real identity counts once and only wallets that meet your bar can participate."
4
4
---
5
5
6
-
## TL;DR
6
+
## Summary
7
7
8
-
**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 signs a short-lived verification your contract checks in the same transaction as a claim, deposit, or vote.
8
+
**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.
9
9
10
-
<Note>
11
-
Live on Base Sepolia! Try the [demo](https://base-verify-onchain-demo.vercel.app/). Please [reach out](https://forms.gle/WTcuWyKkvUV6gGik6) if you have use cases in mind!
out](https://forms.gle/WTcuWyKkvUV6gGik6) if you have use cases in mind!
14
+
</Tip>
13
15
14
16
Integration is three steps:
15
17
16
18
1.**Deploy or upgrade a contract** to extend `BaseVerifyConsumer` and declare an immutable `provider` and `conditions` (your eligibility policy).
17
19
2.**Fetch a verification** in your app: the user signs a SIWE message naming your contract, which you POST to `POST /v1/onchain_verifications`.
18
20
3.**Submit** the returned `{ identityHash, expiration, signature }` to your contract, which calls `registry.verifyVerification(...)` and dedupes on `identityHash`.
| Live demo |[base-verify-onchain-demo.vercel.app](https://base-verify-onchain-demo.vercel.app/)|
25
+
| API base URL |`https://verify.base.dev/v1`|
26
+
| Chain | Base Sepolia (`84532`) |
27
+
| Consumer base |`BaseVerifyConsumer.sol`|
27
28
28
29
Two example consumers to copy from:
29
30
@@ -37,24 +38,15 @@ Two example consumers to copy from:
37
38
**Base Verify Onchain enforces both directly in your contract.** The Base Verify backend signs a short-lived [EIP-712](https://eips.ethereum.org/EIPS/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:
38
39
39
40
-**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.
40
-
-**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 checks the user's real credential against them and only signs when they pass.
41
-
42
-
**Example use cases:**
43
-
44
-
-**Sybil-resistant incentives** — one enrollment per real person stops a farmer from multiplying liquidity-mining or points rewards across wallets
45
-
-**Gated or permissioned pools** — restrict deposits, borrows, or swaps to verified, policy-gated participants
46
-
-**Fair launches and sales** — a per-identity allocation cap keeps bots from sweeping a sale with fresh wallets
47
-
-**One-per-person claims, mints, or allowlists** in any app, not just DeFi
48
-
-**Gated content, membership, or access** unlocked once per identity
49
-
-**One-person-one-vote governance** enforced by the contract
41
+
-**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.
50
42
51
-
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.
43
+
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.
52
44
53
45
## Core concepts
54
46
55
47
### Verification
56
48
57
-
A short-lived object signed by Base that your contract checks through the `SignerRegistry`. It is signed as EIP-712 typed data and contains:
49
+
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:
58
50
59
51
-`identityHash` — a one-way hash of the user's real-world identity (your dedupe key).
60
52
-`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.
@@ -70,11 +62,11 @@ The dedupe key. It is deterministic per identity and per contract: the same real
70
62
71
63
### Policy (provider + conditions)
72
64
73
-
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). Base reads this policy directly from your contract and checks the user's stored credential against it before signing.
65
+
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.
74
66
75
-
### Eligibility is enforced by Base
67
+
### How eligibility is enforced
76
68
77
-
Conditions are read from your contract onchain, never taken from the user. Base evaluates them against the user's real stored credential and only signs when they pass, so a user cannot strip or fake a condition to get a verification they are not entitled to.
69
+
"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.
78
70
79
71
## Architecture and flow
80
72
@@ -138,6 +130,7 @@ A claim moves through your app, the Base Verify API, and your contract:
138
130
<Warning>
139
131
`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.
140
132
</Warning>
133
+
141
134
</Step>
142
135
143
136
<Steptitle="Fetch a verification in your app">
@@ -186,6 +179,7 @@ A claim moves through your app, the Base Verify API, and your contract:
186
179
<Note>
187
180
No API key: the request needs no `Authorization` header because the SIWE signature is the credential.
188
181
</Note>
182
+
189
183
</Step>
190
184
191
185
<Steptitle="Submit the verification to your contract">
@@ -205,22 +199,23 @@ A claim moves through your app, the Base Verify API, and your contract:
205
199
```
206
200
207
201
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.
202
+
208
203
</Step>
209
204
</Steps>
210
205
211
206
### Error handling
212
207
213
208
If the API does not return `200`, do not submit the transaction. Handle the response by status:
|**404**`contract_not_found`| The named contract isn't deployed on this chain or doesn't expose a policy. Check the address and chain.|
213
+
|**404**`verification_not_found`| The wallet has no credential for your contract's provider. Redirect the user to Base Verify to verify.|
214
+
|**404**`needs_reauth`| The credential is older than your contract's cutoff block. Send the user to Base Verify to re-authenticate. |
215
+
|**400**`conditions_not_satisfied`| The wallet is verified but does not meet your conditions. Show a message; do not redirect or retry.|
216
+
|**400**`invalid_policy`| Your contract's provider/condition/operator combination is unsupported. Fix the contract's policy.|
217
+
|**400**`invalid_argument`| Malformed or expired SIWE, wrong statement, or wrong chain. Rebuild the message.|
218
+
|**200**| Submit `identityHash`, `expiration`, and `signature` to your contract.|
224
219
225
220
To send a user to Base Verify to complete OAuth, redirect to `https://verify.base.dev` with your app URL and the provider:
226
221
@@ -248,7 +243,9 @@ Exchanges a SIWE signature for a signed, short-lived onchain verification. No AP
248
243
</ParamField>
249
244
250
245
<ParamFieldbody="signature"type="string"required>
251
-
The wallet's signature over the SIWE message. Base Account smart-wallet ([ERC-1271](https://eips.ethereum.org/EIPS/eip-1271) / [ERC-6492](https://eips.ethereum.org/EIPS/eip-6492)) signatures are supported.
246
+
The wallet's signature over the SIWE message. Base Account smart-wallet
`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).
337
+
`cutoffBlock()` is not part of `policyHash`, so you can change it without
338
+
invalidating outstanding verifications. Use it to require a fresh
339
+
authentication (for example, after a policy or security change).
0 commit comments