> For the complete documentation index, see [llms.txt](https://zka-protocol.gitbook.io/zka-protocol-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://zka-protocol.gitbook.io/zka-protocol-docs/developer/building-zkap-frames.md).

# Building ZKAP Frames

A ZKAP Frame is the core object that replaces traditional blockchain transactions.\
Instead of sending a public, metadata-rich transaction, developers use the SDK to construct an **encrypted, zero-knowledge–validated frame** that can be submitted to the ZKAP Validator contract.

This file explains how to build a ZKAP Frame step-by-step:\
identity → payload → encryption → proof → frame assembly.

***

### 1. What is a ZKAP Frame?

A ZKAP Frame is a structured object containing:

* a zero-knowledge proof
* a commitment binding the payload
* encrypted payload (ciphertext)
* a nullifier (to prevent replay attacks)
* a session hash
* an expiry block

The final frame looks like this conceptually:

```
{
zk_proof: "0x...",
commitment_root: "0x...",
payload_ciphertext: "0x...",
nullifier: "0x...",
session_hash: "0x...",
expiry_block: 12345678
}
```

Every field is essential for validity and anonymity.

***

### 2. Prerequisites before building a frame

Before building a frame, you must have:

1. An ephemeral anonymous identity
2. A well-formed payload
3. A symmetric encryption key
4. Access to the ZK proof system
5. The expiry block for the request

These are typically created using the SDK.

***

### 3. Step 1 — Generate an Anonymous Identity

Identity is ephemeral and unlinkable.

Example (SDK function):

const id = generateIdentity();

This identity is used only inside the proof.

***

### 4. Step 2 — Create the Payload

A payload is a plain object representing the action.

Example:

```
const payload = {
action: "swap",
tokenIn: "USDC",
tokenOut: "ETH",
amount: "500",
minOut: "0.3",
expiry: 12345678
};
```

This data will be encrypted before being added to the frame.

***

### 5. Step 3 — Encrypt the Payload

Encryption ensures no one can read the payload except the destination application.

Example:

const ciphertext = await encryptPayload(payload, sessionKey);

The ciphertext hides all details, including:

* token types
* amounts
* action type
* routing logic
* strategy parameters

***

### 6. Step 4 — Generate the Zero-Knowledge Proof

The proof validates:

* payload structure
* identity correctness
* nullifier derivation
* expiry constraints
* commitment root consistency

Example (conceptual):

```
const proof = await generateProof({
identity: id,
payloadHash,
nonce,
expiry
});
```

The blockchain will verify this proof using the ZKAP Validator.

***

### 7. Step 5 — Build the Commitment Root

The commitment root binds the encrypted payload and expiry together.

commitment\_root = Hash(payload\_hash || expiry\_block)

This guarantees that the payload cannot be tampered with after proof generation.

***

### 8. Step 6 — Derive the Nullifier

The nullifier prevents replay attacks.

const nullifier = deriveNullifier(id, nonce);

A nullifier:

* reveals nothing about identity
* prevents replays
* is safe to publish

It’s the only persistent state stored by the validator.

***

### 9. Step 7 — Create the Session Hash

The session hash provides optional context for ephemeral flows.

const sessionHash = createSessionHash(id, payloadHash);

This makes correlation between actions impossible.

***

### 10. Step 8 — Assemble the ZKAP Frame

Now combine all components:

```
const frame = {
zk_proof: proof,
commitment_root,
payload_ciphertext: ciphertext,
nullifier,
session_hash: sessionHash,
expiry_block: payload.expiry
};
```

This frame is what gets submitted to the blockchain.

***

### 11. Step 9 — Submit the Frame

Once assembled, the frame can be submitted to the ZKAP Validator:

await submitFrame(frame);

If the proof verifies and nullifier is fresh,\
a **AnonymousAction** event is emitted.

***

### 12. Example Code (Putting It All Together)

Below is a simplified conceptual example:

```
import {
generateIdentity,
encryptPayload,
generateProof,
buildFrame,
submitFrame
} from "@zkap/sdk";

(async () => {
const id = generateIdentity();
const payload = { action: "swap", amount: 500, expiry: 12345678 };
const ciphertext = await encryptPayload(payload);
const proof = await generateProof({ identity: id, payload });
const frame = await buildFrame({ proof, ciphertext, id, payload });
await submitFrame(frame);
})();
```

The SDK abstracts many low-level details, making integration simple.
