Decrypting Actions
After the ZKAP Validator verifies a private frame on-chain, it emits an AnonymousAction event containing the encrypted payload.
Applications must decrypt this payload off-chain to recover the original instruction and execute the requested logic.
This file explains everything developers need to know about decrypting anonymous actions.
1. Where decryption happens
Decryption always happens off-chain, typically in:
backend services
DEX engines
agent orchestrators
bridge routers
indexer-like consumers
secure enclaves
specialized off-chain workers
Nothing in the encrypted payload should ever be decrypted inside a smart contract.
2. Anatomy of the AnonymousAction event
The validator emits:
AnonymousAction(commitment_root, encrypted_payload)
Where:
commitment_rootis used to validate commitmentsencrypted_payloadis the ciphertext generated by the sender
Your application needs only the encrypted_payload to decrypt the action.
3. Requirements for decryption
To decrypt the payload, you must have:
The application’s symmetric decryption key
The ciphertext received from the event
The same encryption scheme used by the sender (AES-GCM or ChaCha20)
The correct IV/nonce (included in the ciphertext format)
The encryption key must be kept secure — it is the only piece of information that allows the payload to be read.
4. Using the SDK to decrypt payloads
The ZKAP SDK provides a simple helper function:
const payload = await decryptPayload(encrypted_payload, appKey);
This function:
validates ciphertext structure
extracts IV/nonce
performs authenticated decryption
returns the original JSON payload
If authentication fails, the payload will not decrypt.
5. Example: decrypting inside a backend listener
import { decryptPayload } from "@zkap/sdk";
contract.on("AnonymousAction", async (root, encrypted) => { try { const payload = await decryptPayload(encrypted, appKey); executeAction(payload); } catch (e) { console.error("Decryption failed:", e); } });
This pattern is the most common architecture for applications integrating ZKAP.
6. Example payload after decryption
After decrypting, the application sees the original instruction:
{ "action": "swap", "tokenIn": "USDC", "tokenOut": "ETH", "amount": "500", "minOut": "0.3", "expiry": 12345678 }
Only the application sees the content — nobody else can.
7. Encryption schemes supported
ZKAP supports:
AES-GCM
ChaCha20-Poly1305
Both provide:
confidentiality
authenticity (decryption fails on tampering)
integrity guarantees
Developers can choose the backend they prefer.
8. Handling invalid or corrupted ciphertext
The decryption function may throw errors when:
IV/nonce is missing
ciphertext was altered
wrong decryption key is used
payload is malformed
encryption backend mismatch
The SDK provides descriptive error messages.
9. Security model for decryption keys
Best practices:
Never embed keys in frontend code
Store keys in backend vaults (HashiCorp Vault, AWS KMS, GCP KMS)
Rotate keys periodically
Restrict who/what can decrypt payloads
Never expose keys in logs
Isolate decryption workers if possible
ZKAP relies entirely on off-chain decryption privacy, so key management is critical.
10. When to decrypt payloads
An application should decrypt payloads:
immediately after receiving the event
OR through a batch listener service
depending on business logic
For high-throughput applications (e.g., DEX/RFQ), parallel decryption workers may be used.
For agent runtimes, logic may require sequential processing.
11. Usage patterns across application types
Private DEX
Decrypt → read swap → execute routing logic.
Wallet backend
Decrypt → execute user operations or intents.
Agent system
Decrypt → trigger workflows, task execution, or multi-step logic.
RFQ engine
Decrypt → generate private quote → respond via ZKAP.
Bridge / Router
Decrypt → read cross-chain intent → dispatch to target chain.
Last updated