Integrate Validator
The ZKAP Validator contract is the on-chain component responsible for verifying ZK proofs, enforcing replay protection, checking expiry windows, and emitting encrypted events for applications to consume. This file explains how to integrate the validator into your backend, smart contracts, or application infrastructure.
1. What the Validator Does
The ZKAP Validator performs three critical checks:
Verifies the zero-knowledge proof
Ensures the nullifier has not been used
Confirms the request has not expired
If all checks pass, it emits:
AnonymousAction(commitment_root, encrypted_payload)
Applications listen to these events and decrypt payloads off-chain.
The validator never learns:
who sent the action
what the action contains
any metadata about the user or agent
2. Deploying the Validator Contract
You can install the validator contract package using:
npm install @zkap/contracts
The contract can be deployed via:
Hardhat
Foundry (Forge)
Remix
Any EVM deployment tool
Example (using Hardhat):
const Validator = await ethers.getContractFactory("ZKAPValidator"); const validator = await Validator.deploy(); await validator.waitForDeployment(); console.log("Validator deployed at:", validator.target);
Once deployed, you can interact with it via any RPC provider.
3. Validator ABI and Interface
The validator exposes the following core functions:
submitFrame(frame) verifyProof(proof, commitment_root) (internal)
And the key event:
event AnonymousAction(bytes32 commitment_root, bytes encrypted_payload);
The frame object contains:
{ zk_proof, commitment_root, payload_ciphertext, nullifier, session_hash, expiry_block }
4. Submitting a Frame On-Chain
After generating a frame using the SDK, developers submit it like this:
import { ethers } from "ethers";
const contract = new ethers.Contract(validatorAddress, ValidatorABI, signer); await contract.submitFrame(frame);
The validator:
verifies the proof
checks nullifier reuse
checks expiry
emits AnonymousAction
If something fails, it reverts with a descriptive error.
5. Listening for AnonymousAction Events
Applications must listen for the encrypted event emitted by the validator.
Example:
contract.on("AnonymousAction", async (root, encrypted) => { const payload = await decryptPayload(encrypted, appKey); handleAction(payload); });
This event is the entry point to application logic.
Important: No sender address is included in the event. The action comes through anonymously.
6. Integrating With Backend Services
Most applications integrate the validator with a backend listener:
Subscribe to AnonymousAction
Receive encrypted payload
Decrypt payload
Run application logic
Execute internal or cross-chain actions
This pattern supports:
DEX swaps
RFQ matching
agent orchestration
bridge routing
governance actions
The backend acts as the “consumer” of private instructions.
7. Multi-Chain Integration
Each chain has its own validator contract instance. Applications can:
deploy multiple validators across chains
listen to events from all chains
decrypt and route actions appropriately
ZKAP maintains privacy even across L1/L2/L3 boundaries.
8. Common Integration Patterns
8.1 Private DEX
Listen → decrypt → execute swap internally.
8.2 Wallet backend
Listen → decrypt → execute user operation.
8.3 Agent runtime
Listen → decrypt → run task or instruction.
8.4 Cross-chain router
Listen → decrypt → forward encrypted intent to another chain.
Each integration only needs to consume decrypted payloads.
9. Error Handling
The validator throws clear errors when:
nullifier is reused
proof is invalid
expiry exceeded
malformed frame
Backend services should watch for revert reasons when submitting frames.
10. Security Considerations
Always check nullifier status before acting
Never decrypt payloads on-chain
Keep decryption keys private
Enforce rate limiting if needed
Validate payload structure after decryption
These ensure the system remains both private and secure.
Last updated