> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.adid.dev/llms.txt.
> For full documentation content, see https://docs.adid.dev/llms-full.txt.

# Generate a ZK Proof

##### When to use

You want to share a "I am over 18" / "I'm a verified European resident" / "I have ≥ 5 BTC" proof without revealing the underlying credential. The portal generates the proof in your browser (or, optionally, server-side if your client cannot run the prover).

##### Before you begin

* A credential containing the relevant claim (e.g., a KYC credential with `dateOfBirth` for `age_gte`).
* The verifier-supplied challenge (or generate one from the API).

##### Steps

1. Open `/zk-identity`.
2. Click **Generate proof** and pick a predicate from the dropdown.
   3\. Fill predicate-specific parameters (e.g. for `age_gte`, the threshold = `18`).
3. Select the source credential.
4. Click **Generate**. The portal:
   * Calls `POST /api/v1/zkp/challenge` to obtain a fresh nonce.
   * Builds the witness from the credential's claims + your private signing share.
   * Runs the prover (in-browser via WASM, or server-side fallback).
   * Calls `POST /api/v1/zkp/proofs` to register the proof in the platform.
5. The portal displays the proof ID and a copy/share button.

##### API & SDK

<table>
  <tr>
    <th>cURL — challenge</th>

    <th>cURL — submit</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/zkp/challenge \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{ "predicate":"age_gte", "params":{"threshold":18} }'
      ```
    </td>

    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/zkp/proofs \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "predicate":"age_gte",
          "challenge":"f9c8b3e0...",
          "proof":"<hex>",
          "proverDid":"did:adi:0x9a2c...",
          "credentialId":"urn:uuid:c5b7e3..."
        }'
      ```
    </td>
  </tr>
</table>

```ts
const { challenge } = await client.generateZKChallenge();
const stored = await client.generateZKProof({
  proverDid:    myDid,
  credentialId: theCredential.id,
  predicates: [{ type: 'age_gte', field: 'birthDate', value: 18 }],
  challenge,
});
```

##### Verify

```bash
curl https://adid.dev/api/v1/zkp/proofs/<id> \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

shows `status: "verified"` and the predicate / params / credentialId.

##### Troubleshooting

| Code                          | Cause                                                                         | Fix                                                           |
| ----------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------- |
| `410 CHALLENGE_EXPIRED`       | More than 5 min between challenge and proof submit                            | Request a fresh challenge.                                    |
| `422 PREDICATE_NOT_SATISFIED` | The credential's claim does not satisfy the predicate (e.g. age \< threshold) | Use a different predicate, or accept that you do not satisfy. |
| `500 PROVER_ERROR`            | Browser ran out of memory                                                     | Use the server-side fallback (toggle in dev tools).           |

#### 3.3.4. Sharing & Anchoring Proofs On-Chain ##### When to use

The verifier requires a **public, immutable record** of your proof — typically for high-stakes use (regulatory KYC, on-chain DAO voting). On-chain anchoring is opt-in.

##### Before you begin

* A generated proof (§3.3.3).
* (Optional) wallet permission to sign the chain transaction.

##### Steps

1. From the proof's detail page, click **Anchor on chain**.
2. The wallet prompts for confirmation. Approve.
3. The wallet calls `submitProof(proofHash, proverDID, credentialId, predicateHash)` on the **ZKProofVerifier** contract (see [§11.6](#zk-proof-verifier)).
4. After receipt, the proof's record on the platform shows the `txHash` and `onChain: true`.

##### Sharing

To share a proof off-chain, click **Copy share link** — generates a URL like `https://adid.dev/proofs/abc123` that any party can visit (no auth needed) to verify.

##### API

<table>
  <tr>
    <th>cURL — verify</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/zkp/verify \
        -H "Content-Type: application/json" \
        -d '{
          "proof":"<hex>",
          "predicate":"age_gte",
          "params":{"threshold":18},
          "challenge":"f9c8b3e0...",
          "proverDid":"did:adi:0x9a2c..."
        }'
      ```
    </td>
  </tr>
</table>

(No auth — `/api/v1/zkp/verify` is public per `router.go:71`.)

##### Verify

After anchoring, query `verifyProofOnChain(proofHash)` on the contract via the API:

```bash
curl https://adid.dev/api/v1/zkp/proofs/<id>?includeOnChain=true \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

##### Troubleshooting

| Code                    | Cause                                 | Fix                                  |
| ----------------------- | ------------------------------------- | ------------------------------------ |
| `409 ALREADY_ANCHORED`  | This proof's hash is already on chain | No action needed.                    |
| `503 CHAIN_UNREACHABLE` | RPC down                              | Retry.                               |
| `403 NOT_PROVER`        | You are not the original prover       | Only the prover may anchor / revoke. |

> ⚠️ **Warning** — On-chain anchoring is permanent. Proof revocation is supported (call `revokeProof(proofHash)`) but the original anchor record remains visible (just with `revoked: true`). Do not anchor a proof unless you intend it to be auditable forever.