A real bank card stamped a wallet
0xd7e0…bc07
Made under our first card list, which also allowed prepaid cards.
Your users tap their bank card on their phone once, and their wallet gets a stamp. Bots can make endless wallets, but they can't make bank cards, so they never get in. No KYC, and no gas fees for your users.
Who gets inExample
Only wallets with a stamp get through.
What it stops
A bot can make a thousand wallets in a minute, but it can't make a thousand bank cards. That one fact protects all of these.
Behind all of them is the same one-line check: isStamped(wallet, appId)
Three steps
Add it
Use Solidity, TypeScript or your AI agent. There's no API key and nothing to sign up for.
Add one modifier to your contract, and wallets without a stamp get turned away.
function claim()external onlyStamped { … }
Paste once, from docs/INTEGRATE.md
interface IWatermarkRegistry {
function isStamped(address wallet, uint256 appId) external view returns (bool ok, uint64 expiresAt);
}
modifier onlyStamped() {
(bool ok, uint64 expiresAt) = watermark.isStamped(msg.sender, watermarkAppId);
require(ok && expiresAt > block.timestamp, "Watermark: no live stamp");
_;
}Check one wallet or 200 at once. It reads straight from the chain.
The SDK lives in the repo (sdk/). It isn't on npm yet.
import { check, checkMany } from "@watermark/sdk";
const registry = "0x998934Efa31a6bE75945fDfAa126d3D6401F8Fd6";
const appId = "393344891601835728760966937624776276735512086066329273346256164761263100630";
const { stamped, expiresAt } = await check(userAddress, appId, { registry });
if (!stamped) showGetStampedButton(); // links to the /verify page (verifyLink)
// Airdrop or allowlist export: up to 200 wallets per call, one multicall
const results = await checkMany(addresses, appId, { registry });Your agent can check wallets before it pays anyone. It holds no keys and never sees card data.
{
"mcpServers": {
"watermark": {
"command": "node",
"args": ["/absolute/path/to/watermark/mcp/src/index.ts"],
"env": { "WHITECHAIN_RPC": "https://rpc.ankr.com/whitechain_testnet" }
}
}
}12 tools, each named watermark_…
watermark_check_walletwatermark_check_walletswatermark_stamp_historywatermark_app_infowatermark_get_policywatermark_explain_policywatermark_list_appswatermark_supported_cardswatermark_draft_policywatermark_prepare_register_appwatermark_create_verify_linkwatermark_integration_snippetRead the full guide (opens in a new tab)MCP setup (opens in a new tab)
For AI agents
Copy this prompt into Claude Code, Cursor or any coding agent. It reads our docs and adds the bot check to your app.
Add Watermark anti-bot gating to this project. A Watermark stamp means a wallet was linked to one real bank card. Only stamped wallets may get what this app gives away or protects: airdrops, mints, rewards, faucet tokens, sponsored gas, votes or access.
1. Read https://usewatermark.xyz/watermark.txt first. Use only the addresses, functions and tool names it gives. Don't invent any.
2. Find every place this repo gives something away or lets a wallet in: airdrop claims, mints, rewards, faucets, sponsored gas, votes, allowlists. Check whether they live in Solidity contracts, a TypeScript/JavaScript backend, or both.
3. App id: <YOUR_APP_ID> below is a placeholder; keep it in one config constant. For real RuPay bank cards, set it to the live app id: 393344891601835728760966937624776276735512086066329273346256164761263100630. Register our own app only if we want our own card list: draft it with watermark_draft_policy, then watermark_prepare_register_app (unsigned; I send it, and we must run our own session signer). Tell me which you chose and why.
4. Solidity: contracts on Whitechain Sepolia (chain 1874) can gate directly. Add the IWatermarkRegistry interface and the onlyStamped modifier from the docs, with the registry 0x998934Efa31a6bE75945fDfAa126d3D6401F8Fd6 and <YOUR_APP_ID>, and put onlyStamped on each claim function. A contract on another chain can't read the registry: check in the backend instead.
5. Backend: before paying out, call check() from @watermark/sdk, or the plain viem isStamped read in the docs (the SDK isn't on npm yet). Refuse when stamped is false. Never trust a stamp flag sent by the browser.
6. For users who aren't stamped, show a "Get verified with Watermark" link to https://usewatermark.xyz/verify?app=<YOUR_APP_ID> (what watermark_create_verify_link returns). Tell them to verify the same wallet they claim with.
7. Never ask for, store or log card numbers or any card data. We only need the wallet address.
8. Keep every existing test passing. Add a test showing an unstamped wallet is refused (mock the registry or the check). Add no keys or secrets; reading a stamp needs none.
Finish with a short summary: files changed, where each check sits, the app id and why, how to run the new test, and anything left for me to do.Try to break it
Press the button and we'll send 5 bot wallets at our own drop. You'll watch every one of them get turned away, and these are real requests.
You stay in control
You can use our card list or bring your own. Either way, the list is public.
Visa and MastercardComing soon
Public records · Test networks
Everything below happened on a public chain. Tap any card to check it yourself.
Privacy and limits