> ## Documentation Index
> Fetch the complete documentation index at: https://docs.opaque.cash/llms.txt
> Use this file to discover all available pages before exploring further.

# Reputation API

> Trait discovery, Groth16 proofs, and on-chain verification.

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.

```ts theme={"dark"}
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**

| Param                    | Type                     | Description                                                                  |
| ------------------------ | ------------------------ | ---------------------------------------------------------------------------- |
| `rows`                   | `IndexerAnnouncement[]`  | Native announcement rows from `fetchAnnouncementRows(chain)` or your indexer |
| `options.chain`          | `"ethereum" \| "solana"` | Chain whose schema registry validates the marker                             |
| `options.schemas`        | `SchemaV2[]`             | Optional cached schema snapshot. The SDK fetches all schemas when omitted    |
| `options.currentSlot`    | `number \| bigint`       | Optional block or slot for expiry checks                                     |
| `options.trustedIssuers` | `string[]`               | 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.

```ts theme={"dark"}
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`.

```ts theme={"dark"}
const traits = await client.getReputationTraitsFromAnnouncements(rows);
```

***

### `announcementsJsonForReputationWitness(rows)`

Serialize announcements for the prover witness.

```ts theme={"dark"}
const json = client.announcementsJsonForReputationWitness(rows);
```

***

### `getStealthSignerPrivateKeyForReputationTrait(trait)`

Reconstruct signing key for a discovered trait.

```ts theme={"dark"}
const bytes = client.getStealthSignerPrivateKeyForReputationTrait(trait);
```

***

## Proof generation

### `generateReputationProof(params)`

Generate Groth16 proof bundle (requires `snarkjs`).

```ts theme={"dark"}
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`.

```ts theme={"dark"}
const root = await client.fetchLatestValidReputationRoot();
```

***

### `isReputationRootValid(root)`

Whether the verifier currently accepts this root.

```ts theme={"dark"}
const ok = await client.isReputationRootValid(root);
```

***

### `fetchReputationRootHistory()`

Full root history with per-entry validity.

```ts theme={"dark"}
const history = await client.fetchReputationRootHistory();
// { index, root, valid }[]
```

***

## Verification

### `verifyReputationProofView(args)`

Read-only on-chain view; does not consume the nullifier.

```ts theme={"dark"}
const valid = await client.verifyReputationProofView({
  proofData,
  merkleRoot,
  externalNullifier,
});
```

***

### `simulateReputationVerification(wallet, args)`

Simulate `verifyReputation` for gas / revert checks (Ethereum).

```ts theme={"dark"}
await client.simulateReputationVerification(walletClient, {
  proofData,
  merkleRoot,
  externalNullifier,
});
```

***

### `submitReputationVerification(chain, args)`

Broadcast the proof to the verifier. **Consumes the nullifier** on success.

```ts theme={"dark"}
const { txHash } = await client.submitReputationVerification("ethereum", {
  proofData,
  merkleRoot,
  externalNullifier,
});
```

**Signers:** `ethereumWalletClient` (EVM) or `solanaWallet` (Solana). This facade covers
Ethereum and Solana only; for Starknet, see [Verify on Starknet](#verify-on-starknet) below.

***

### Verify on Starknet

`generateReputationProof` is chain-agnostic, and a bundle from the shared trusted setup
verifies on Ethereum, Solana, and Starknet. Starknet verification does **not** go through
`submitReputationVerification`; instead build an unsigned Starknet call with
`@opaquecash/psr-chain-starknet` and let the app's Starknet wallet broadcast it.

```ts theme={"dark"}
import {
  encodeFullProofWithHints,
  buildVerifyReputationCall,
} from "@opaquecash/psr-chain-starknet";

// Garaga-encoded `full_proof_with_hints` calldata for the Starknet verifier.
// `verificationKey` is the same V2 verification_key.json the on-chain verifier was built from.
const fullProofWithHints = await encodeFullProofWithHints(proofData, verificationKey);

// Unsigned call: `verify_reputation` (consumes the nullifier). Use
// buildVerifyReputationViewCall for a read-only check that spends nothing.
const call = buildVerifyReputationCall(fullProofWithHints);

// The app's Starknet account (starknet.js) signs and broadcasts.
const { transaction_hash } = await starknetAccount.execute(call);
```

<Note>
  Starknet is at testnet-preview maturity on Sepolia. The verifier stack
  (`OpaqueReputationVerifierV2` + Garaga Groth16 verifier, addresses in `STARKNET_SEPOLIA_PSR`)
  only **verifies and gates** proofs — schema registration and attestation issuance stay on
  Ethereum and Solana. See [Starknet](/concepts/starknet) for the full flow and caveats.
</Note>

***

## Static scope helpers

```ts theme={"dark"}
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](/guides/generate-proof).
