How zk proofs are generated

Binding UnifiedID into the challenge

We cryptographically bind the UnifiedID into a human-readable challenge string:

export function createChallenge(walletAddress: string, unifiedId: string, timestamp: number) {
  const timestampStr = new Date(timestamp).toISOString();
  return `Prove ownership of ${walletAddress} for UnifiedID ${unifiedId} at ${timestampStr}`;
}

Flow:

  1. Challenge creation

    The UnifiedID (e.g. alice) is embedded into the message:

    Prove ownership of 0x... for UnifiedID alice at <timestamp>

  2. User signs the challenge (off-chain ECDSA)

    The wallet signs this string using its private key, e.g.:

This ensures that the UnifiedID is cryptographically tied to the wallet’s signature, but the clear text never needs to be exposed in the proof.

Off-chain signature verification (pre-zk)

Before generating a zk proof, the SDK always verifies the signature off-chain.

In the browser SDK, proof generation aborts if verification fails.

Effect:

Only a user who can produce a valid ECDSA signature for the challenge can generate a zk proof.

The circuit itself operates only on bytes and hashes; the “this signature must be valid” guarantee is enforced by this off-chain policy.

Last updated