Skip to content
P

PragueExplorer

Privacy-first ZK geocaching on Scroll

Created on 9th May 2026

•

P

PragueExplorer

Privacy-first ZK geocaching on Scroll

The problem PragueExplorer solves

Prague Explorer is a privacy-first geocaching game for EthPrague attendees and Prague tourists. Players find physical QR codes hidden at six real Prague landmarks, scan them in person, answer a Czech history quiz, and claim an on-chain EAS attestation — without the contract ever learning who they are.

The privacy model is the core innovation. Presence is proved by combining a per-cache QR secret (only obtainable on-site) with a Semaphore v4 ZK proof generated entirely in the browser (~3 seconds). The contract sees only an opaque nullifier and a display name — never a wallet address, email, or any externally-linked identity. Nullifiers are scoped per-cache (scope = cacheId), so claiming two different caches produces cryptographically uncorrelated nullifiers that cannot be linked on-chain.

Zero friction for users. Privy embedded wallets let anyone sign in with email or Google — no MetaMask, no seed phrase. A Pimlico ERC-4337 paymaster on Scroll Sepolia covers gas entirely. A successful claim produces an EAS attestation the user owns permanently: a composable, portable credential, not a custodied badge.

Cultural layer. Each cache is tied to a real Prague landmark. Cache beneficiary addresses point to Czech cultural institutions that receive optional ETH micro-donations on claim. The app ships in Czech and English.

Challenges we ran into

In-browser ZK proving. Integrating Semaphore v4 into a live Next.js frontend was non-trivial: the WASM circuit and proving key total ~6 MB. We preload the key on page mount and display a progress indicator so the ~3-second proving time feels intentional rather than broken.

ERC-4337 on Scroll Sepolia. Wiring Pimlico's paymaster with permissionless v0.3.5 required careful chain-ID configuration (534351) and handling the case where the paymaster rejects sponsorship. We built a fail-open fallback: if the paymaster declines, the user's EOA pays gas directly — the claim flow never breaks.

Binding the display name to the proof. To prevent front-running (changing the displayed name after proof generation), we embed the display name in the Semaphore message field: message = uint256(keccak256(displayName)) >> 8. The >> 8 right-shift is required to fit within BN254's scalar field. Any post-generation name change produces an invalid proof, enforced at the circuit level.

Semaphore anonymity-set trade-off. The joinCache() call (group membership registration) is observable on-chain — an attacker can see that someone is joining cache X, but cannot determine who, because the identity commitment has no link to any external identifier. We mitigate this by funding user wallets from a separate deployer account so the gas-payer address is distinct from the Semaphore identity being registered.

Tracks Applied (3)

Ethereum Core

Built entirely on Ethereum's core stack: Scroll Sepolia — zkEVM L2, full EVM equivalence, EthPrague-native Semaphore v4...Read More

Network Economy

Prague Explorer puts user control first at the protocol level. Identity is derived entirely client-side from a QR secret...Read More

Future Society

Prague Explorer embeds Czech cultural heritage directly into Ethereum. Each of the six caches is tied to a real Prague l...Read More

Cheer Project

Cheering for a project means supporting a project you like with as little as 0.0025 ETH. Right now, you can Cheer using ETH on Arbitrum, Optimism and Base.

Discussion

Builders also viewed

See more projects on Devfolio