A decentralized royalty distribution system where authors prove content authorship via ZK email verification and claim payouts on-chain. Reviewers vote anonymously using Semaphore, and all user operations are gasless via ERC-4337 account abstraction.
This is the v2 implementation. For v1, see the v1 tag.
v2 is deployed on Base, which is where the DKIM Registry (maintained by ZK Email team) and Semaphore are available.
| Version | Network | Address |
|---|---|---|
| v2 | Base | 0x3991CB2b0744AEDb8F985E0d1C74d8dAe6a30433 |
| v2 | Base Sepolia | 0xEb6cD8eac109FDD4cD69AB43AAfFa50eD885FF65 |
| v1 | Ethereum | 0xf50b818138e3848C314783FA593fb39653FB0178 |
| v1 | Sepolia | 0x66ECf28b049f8b917C58B6e81a999CDF309283eA |
| Tool | Version | Purpose |
|---|---|---|
| Foundry | latest | Smart contract toolchain |
| Bun | latest | Frontend and script runner |
| Noir | 1.0.0-beta.5 | ZK circuit compiler |
| @aztec/bb.js | 0.84.0 | HONK proving backend |
Run the frontend locally (defaults to Base Sepolia in dev mode, but works with any supported network as long as you provide the proxy contract address and required API keys):
# 1. Clone and install
git clone --recurse-submodules https://github.com/EtherTW/RoyaltyAutoClaim.git
cd RoyaltyAutoClaim
# 2. Build contracts and generate TypeScript bindings
make build
# 3. Set up frontend environment
cp frontend/.env.example frontend/.env
# Fill in API keys (see frontend/.env.example for descriptions):
# VITE_ALCHEMY_API_KEY — RPC provider
# VITE_PIMLICO_API_KEY — ERC-4337 bundler
# VITE_TENDERLY_*_API_KEY — event fetching (optional)
# 4. Deploy contracts to Base Sepolia (requires VITE_TEST_PRIVATE_KEY in frontend/.env)
make deploy
# 5. Update frontend/.env with the deployed proxy address
# VITE_ROYALTY_AUTO_CLAIM_PROXY_ADDRESS_BASE_SEPOLIA=<proxy-address>
# 6. Prepare circuit artifacts for the frontend
make prepare-circuit
# 7. Start the dev server
make devSee docs/deployment.md for the full guide covering:
- Deploying contracts to Base mainnet
- Contract verification on Basescan
- DKIM public key registration
- Frontend configuration
When the circuit code changes, recompile and regenerate artifacts:
cd circuits/title_hash
nargo compile
cd ../..
cd circuits
bun run script/genProofTitleHash.ts ../emails/test.eml
bun run script/genVerifier.ts title_hashThe frontend currently only implements the title_hash circuit.
forge build
forge test
forge fmtIf the contract has been updated, regenerate TypeScript bindings:
make build # runs forge build + bun run gen-typesSee Contract Development for more commands.
cd frontend
bun i
bun run devUseful scripts (run from the frontend/ directory):
bun run scripts/gen-proof.ts <EMAIL_FILENAME> <EMAIL_VERIFIER_ADDRESS>
bun run scripts/register.ts <EMAIL_FILENAME> <RAC_ADDRESS>
bun run scripts/update-recipient.ts <EMAIL_FILENAME> <RECIPIENT_ADDRESS>Notes:
- Don't use the
@alias in.tsfiles — scripts that run outside Vite won't resolve it. - Run
forge buildbeforebun run gen-typeswhen contracts change.
forge test # run tests
forge test --gas-report # with gas report
forge coverage --report lcov # coverage (use with vscode-coverage-gutters)Ensure the storage layout is empty to avoid storage collision during future upgrades:
forge inspect ./src/RoyaltyAutoClaim.sol:RoyaltyAutoClaim storageIf Error: failed to read artifact source file for... appears, clean and recompile:
forge cleanThe UserOp signature is a ZK proof, but the bundler's estimateGas runs with a dummy proof, so verificationGasLimit must be pinned as a constant in frontend/src/config.ts.
Re-estimate whenever the circuit changes:
make estimate-vgl-base-sepolia EMAIL=test RAC=0xfDDbc7f5D726B20C0F89Aa44C5B03FC71cC035e8
make estimate-vgl-base EMAIL=test_prod RAC=0x3991CB2b0744AEDb8F985E0d1C74d8dAe6a30433For a detailed explanation of how the DKIMRegistry works and how our EmailVerifier interacts with it, see docs/dkim-registry.md.
The EMAIL parameter refers to a .eml file (without extension) in the emails/ directory. Download raw .eml files from your email client and place them there.
Check whether an email's DKIM public key hash is registered:
make check-dkim EMAIL=test4Set a DKIM public key hash. The PRIVATE_KEY in .env must belong to the owner of the EmailVerifier contract:
make set-dkim EMAIL=test_prod
make set-dkim EMAIL=test_prod CHAIN=baseCHAIN defaults to base-sepolia if not provided. Valid values: base-sepolia, base.
- UserOverrideableDKIMRegistry — ZK Email Deployed Contracts
- Circuits use Noir + UltraHonk proving system (see Prerequisites for required versions)
- Inspired by mintmarks.fun
- For the legacy Circom implementation, see the zkemail-circom tag
- Semaphore — Semaphore Deployed Contracts
- Used for anonymous reviewer voting via zero-knowledge group membership proofs
- Alchemy — primary RPC for all networks
- Tenderly — fetching on-chain contract events (fewer limitations than standard RPC)
- ERC-4337 bundler: v1 uses Alchemy (Entrypoint v0.7), v2 uses Pimlico (Entrypoint v0.8)