Skip to main content
This guide covers trait discovery, Groth16 proof generation, simulation, and submission to OpaqueReputationVerifier.

Prerequisites

  • WASM module (wasmModuleSpecifier)
  • Native announcement rows with V2 PSR attestation metadata
  • snarkjs (pulled in by @opaquecash/psr-prover)
  • Circuit artifacts (default: hosted on opaque.cash /circuits/...)

Step 1: Discover traits

discoverTraitsV2 validates each 0xB2 marker against the chain’s schema registry and returns only traits issued by an authorized schema authority or delegate. Fetch rows from the native chain, not UAB, because relayed payloads cannot carry the full V2 metadata.

Step 2: Reconstruct the stealth signing key

Step 3: Build the action scope

Or use the equivalent static helpers on the client (OpaqueClient.buildReputationActionScope / reputationExternalNullifierFromScope).

Step 4: Generate the proof

When trait comes from discoverTraitsV2, the SDK uses the trait’s V2 Merkle leaf preimage fields automatically. You only need to pass issuerPkX, traitDataHash, or nonce yourself for custom witness construction.

Step 5: Fetch a valid Merkle root

Step 6: Simulate (optional)

Step 7: Verify view (read-only)

Step 8: Submit on-chain

Consumes the nullifier on success:
Solana:
The same Groth16 proof — from the same trusted setup — verifies on Ethereum, Solana, and Starknet. On Starknet (Sepolia, testnet preview) PSR is verification and gating only: the OpaqueReputationVerifierV2 Groth16 verifier plus the PsrGate credential-gated-entry contract, via @opaquecash/psr-chain-starknet (getStarknetDeployment("sepolia")). Schema registration and attestation issuance stay on Ethereum and Solana. See Starknet for the full flow and caveats.

Full flow diagram

Each (trait, externalNullifier) pair can only be verified once. Choose scopes carefully per action you gate.