Skip to main content
Reputation discovery and proof generation require WASM. On-chain verification uses OpaqueReputationVerifier (bundled address per chain).

Discovery

discoverTraitsV2(rows, options)

Use this for schema-bound V2 attestations created by issueAttestation. V2 announcements use marker 0xB2, include 130 bytes of metadata, and are validated against the target chain’s schema registry before the SDK returns them.
const evmRows = await client.fetchAnnouncementRows("ethereum");
const evmTraits = await client.discoverTraitsV2(evmRows, { chain: "ethereum" });

const solRows = await client.fetchAnnouncementRows("solana");
const solTraits = await client.discoverTraitsV2(solRows, { chain: "solana" });

const traits = [
  ...evmTraits.map((t) => ({ ...t, chain: "ethereum" })),
  ...solTraits.map((t) => ({ ...t, chain: "solana" })),
];
Params
ParamTypeDescription
rowsIndexerAnnouncement[]Native announcement rows from fetchAnnouncementRows(chain) or your indexer
options.chain"ethereum" | "solana"Chain whose schema registry validates the marker
options.schemasSchemaV2[]Optional cached schema snapshot. The SDK fetches all schemas when omitted
options.currentSlotnumber | bigintOptional block or slot for expiry checks
options.trustedIssuersstring[]Optional issuer allowlist in chain-native identity format
Returns: Promise<DiscoveredTrait[]>. For V2, DiscoveredTrait includes schemaId, schemaName, issuer, attestationUid, dataHex, nonce, merkleLeafPreimage, isValid, and issuerAuthorized in addition to the shared stealth address, tx hash, block/slot, discovery time, and ephemeral pubkey. generateReputationProof consumes merkleLeafPreimage automatically. Attestation metadata never arrives over the cross-chain UAB because the Wormhole payload only carries a 24-byte metadata tail. Fetch trait rows natively on each chain.

discoverTraits(rows)

Legacy V1 discovery for 0xA7 metadata markers. New schema-bound attestations issued by issueAttestation use V2, so call discoverTraitsV2 for current app flows.
const traits = await client.discoverTraits(legacyRows);
Returns: Promise<DiscoveredTrait[]> with V1 fields: attestationId, stealthAddress, txHash, blockNumber, discoveredAt, and ephemeralPubkey.

getReputationTraitsFromAnnouncements(rows)

Legacy alias for discoverTraits.
const traits = await client.getReputationTraitsFromAnnouncements(rows);

announcementsJsonForReputationWitness(rows)

Serialize announcements for the prover witness.
const json = client.announcementsJsonForReputationWitness(rows);

getStealthSignerPrivateKeyForReputationTrait(trait)

Reconstruct signing key for a discovered trait.
const bytes = client.getStealthSignerPrivateKeyForReputationTrait(trait);

Proof generation

generateReputationProof(params)

Generate Groth16 proof bundle (requires snarkjs).
import { buildActionScope, externalNullifierFromScope } from "@opaquecash/opaque";

const scope = buildActionScope({ chainId: 11155111, module: "my-dapp", actionId: "gate-v1" });
const externalNullifier = externalNullifierFromScope(scope);  // bigint

const proofData = await client.generateReputationProof({
  trait,
  stealthPrivKeyBytes,
  externalNullifier: externalNullifier.toString(),
  artifacts: undefined,   // default hosted circuit paths; or { wasmPath, zkeyPath }
  onProgress: (stage, pct) => console.log(stage, pct),
});
For traits returned by discoverTraitsV2, the prover reads issuerPkX, traitDataHash, and nonce from trait.merkleLeafPreimage. You can still pass those fields manually to override the trait context. If they are absent, the prover fills deterministic dev-mode defaults. Returns: ProofData: { proof, publicSignals, nullifier, attestationId }. The public signals are [merkle_root, attestation_id, external_nullifier, nullifier_hash] and nullifier carries nullifier_hash.

Merkle roots

fetchLatestValidReputationRoot()

Latest non-expired root from verifier rootHistory.
const root = await client.fetchLatestValidReputationRoot();

isReputationRootValid(root)

Whether the verifier currently accepts this root.
const ok = await client.isReputationRootValid(root);

fetchReputationRootHistory()

Full root history with per-entry validity.
const history = await client.fetchReputationRootHistory();
// { index, root, valid }[]

Verification

verifyReputationProofView(args)

Read-only on-chain view; does not consume the nullifier.
const valid = await client.verifyReputationProofView({
  proofData,
  merkleRoot,
  externalNullifier,
});

simulateReputationVerification(wallet, args)

Simulate verifyReputation for gas / revert checks (Ethereum).
await client.simulateReputationVerification(walletClient, {
  proofData,
  merkleRoot,
  externalNullifier,
});

submitReputationVerification(chain, args)

Broadcast the proof to the verifier. Consumes the nullifier on success.
const { txHash } = await client.submitReputationVerification("ethereum", {
  proofData,
  merkleRoot,
  externalNullifier,
});
Signers: ethereumWalletClient (EVM) or solanaWallet (Solana).

Static scope helpers

const scope = OpaqueClient.buildReputationActionScope({
  chainId: 11155111,
  module: "module",
  actionId: "action",
});
const nullifier = OpaqueClient.reputationExternalNullifierFromScope(scope);  // bigint
Equivalent to buildActionScope / externalNullifierFromScope from @opaquecash/psr-core.

Full E2E

See Generate a reputation proof.