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:
Challenge creation
The UnifiedID (e.g. alice) is embedded into the message:
Prove ownership of 0x... for UnifiedID alice at <timestamp>
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