Integration
You build two things — a contract that declares your policy and checks verifications, and an app that fetches verifications — and point both at Base Verify.
What you'll need
| Input | Value (Base Sepolia) |
|---|---|
| SignerRegistry | 0x4f15593fbF7e3491d15080e1610E7AF8deBA1a02 |
| API base URL | https://verify.base.dev/v1 |
| Chain | Base Sepolia (84532) |
| Consumer base | BaseVerifyConsumer.sol (from Base Verify) |
Deployed example contracts (Base Sepolia)
Two working consumers you can inspect on Basescan, both extending BaseVerifyConsumer and calling the registry above:
ExampleCBOneAirdrop(gates on an active Coinbase One membership)ExampleAirdropXVerified(gates on a verified X account)
1. Write your contract
Extend the Base Verify consumer base so your policy is readable onchain, check verifications through the registry, and dedupe on identityHash.
import {BaseVerifyConsumer, Condition} from "@baseverify/BaseVerifyConsumer.sol";
contract MyAirdrop is BaseVerifyConsumer {
mapping(bytes32 => bool) public claimed;
error AlreadyClaimed();
// Base Verify registry address for your chain.
constructor(address registry_) BaseVerifyConsumer(registry_) {}
// Your eligibility policy — must be constant.
function provider() external pure override returns (string memory) {
return "coinbase";
}
function conditions() external pure override returns (Condition[] memory) {
Condition[] memory c = new Condition[](1);
c[0] = Condition("coinbase_one_active", "eq", "true");
return c;
}
function claim(bytes32 identityHash, uint40 expiration, bytes calldata signature) external {
if (claimed[identityHash]) revert AlreadyClaimed();
_verify(identityHash, expiration, signature); // binds msg.sender; reverts on a bad or expired verification
claimed[identityHash] = true;
// ... your reward logic ...
}
}provider() and conditions() must be immutable constants — a changing policy invalidates outstanding verifications and breaks dedupe.
Optionally override cutoffBlock() to require a credential no older than a given block; unlike provider/conditions it isn't part of policyHash, so you can change it without invalidating outstanding verifications. See Core concepts.
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. Pass the response to your contract's claim function.
import { createSiweMessage, generateSiweNonce } from "viem/siwe";
// The statement and Resources line below are required by the API.
const message = createSiweMessage({
domain: window.location.host,
address: userAddress,
statement: "Claim eligibility for a Base Verify onchain benefit.",
uri: window.location.origin,
version: "1",
chainId: 84532,
nonce: generateSiweNonce(),
resources: [`eip155:84532:${MY_CONTRACT_ADDRESS}`],
});
const signature = await signMessageAsync({ message });
const res = await fetch("https://verify.base.dev/v1/onchain_verifications", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message, signature }),
});
const { identityHash, expiration, signature } = await res.json();
await writeContract({
address: MY_CONTRACT_ADDRESS,
abi,
functionName: "claim",
args: [identityHash, expiration, signature],
});See the API reference for the full request, response, and errors.