> ## 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.

# OpaqueClient overview

> Construction, configuration, and method index for the unified SDK client.

`OpaqueClient` in `@opaquecash/opaque` is the single integration surface for stealth payments, PSR admin, reputation proofs, and cross-chain announcements.

## Construction

### `OpaqueClient.fromWallet(params)` (recommended)

Pass the connected wallet(s) in the unified signer shape. The client prompts for the
one-time `SETUP_MESSAGE` signature (or reuses a cached one), derives the stealth keys,
and wires each wallet as that chain's write signer:

```ts theme={"dark"}
import { OpaqueClient } from "@opaquecash/opaque";

// Browser, Ethereum wallet
const client = await OpaqueClient.fromWallet({
  wallets: { chain: "ethereum", address: userAddress, provider: window.ethereum },
  chainId: 11155111,
  rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
  wasmModuleSpecifier: "/pkg/cryptography.js",
});

// All three chains at once (Solana wallet-adapter + viem WalletClient;
// Starknet scan + build methods enabled via the `starknet` option)
const client = await OpaqueClient.fromWallet({
  wallets: [
    { chain: "ethereum", address: userAddress, walletClient },
    { chain: "solana", publicKey, signMessage, signTransaction },
  ],
  chainId: 11155111,
  rpcUrl,
  solana: { cluster: "devnet" },
  starknet: {},
});
```

The FIRST wallet in the list signs `SETUP_MESSAGE` (key derivation) unless you pass a
cached `walletSignature`, in which case no prompt happens at all:

```ts theme={"dark"}
const restored = await OpaqueClient.fromWallet({
  wallets: [],                  // allowed when walletSignature is provided
  walletSignature: cachedSignature,
  chainId: 11155111,
  rpcUrl,
});
```

See [Unified signer](#unified-signer) below for the wallet shapes.

### `OpaqueClient.create(config)` (manual)

Use `create` when you already hold the setup signature and want to wire signers yourself:

```ts theme={"dark"}
const client = await OpaqueClient.create(config);
```

`create` is async: it optionally loads WASM and derives viewing/spending keys from `walletSignature`.

### `OpaqueClient.createViewOnly(config, keys)`

Build a scan-only client from the viewing private key and spending public key (no
`walletSignature`). It scans and reads balances but cannot spend — `sweep` and key
reconstruction throw. This is the safe shape for a server-side scanner. See
[Server-side scanning](/guides/server-side-scanning).

```ts theme={"dark"}
const viewer = await OpaqueClient.createViewOnly(
  { chainId, rpcUrl, ethereumAddress, wasmModuleSpecifier },
  { viewingKey, spendPublicKey },
);
viewer.isViewOnly; // true
```

### OpaqueClientConfig

| Field                  | Type                                         | Required   | Description                                                                                                          |
| ---------------------- | -------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- |
| `chainId`              | `number`                                     | Yes        | EVM chain id (`11155111` Sepolia). Must be in `getSupportedChainIds()` unless contracts overridden.                  |
| `rpcUrl`               | `string`                                     | Yes        | HTTP RPC for EVM reads and sweep                                                                                     |
| `walletSignature`      | `Hex`                                        | Yes        | HKDF entropy from the `SETUP_MESSAGE` signature; never sent on-chain                                                 |
| `ethereumAddress`      | `Address`                                    | Yes        | Connected EOA (registrant context, PSR issuer on EVM)                                                                |
| `wasmModuleSpecifier`  | `string`                                     | No\*       | URL to `cryptography.js`. \*Required for scan/sweep/prove                                                            |
| `trackedTokens`        | `TrackedToken[]`                             | No         | Extra ERC-20s for balance aggregation                                                                                |
| `contracts`            | `Partial<{...}>`                             | No         | Override registry, announcer, verifier, UAB addresses                                                                |
| `solana`               | `SolanaAdapterConfig`                        | No\*\*     | \*\*Required when scanning/sending on Solana                                                                         |
| `starknet`             | `StarknetAdapterOptions`                     | No         | Required for Starknet scan + build methods (Sepolia, testnet preview); enable with `starknet: {}`                    |
| `ethereumProvider`     | `EIP1193Provider`                            | No\*\*\*   | \*\*\*For EVM writes if no `ethereumWalletClient`                                                                    |
| `ethereumWalletClient` | `WalletClient`                               | No\*\*\*   | Precedence over provider for EVM writes                                                                              |
| `solanaWallet`         | `{ publicKey, signTransaction }`             | No\*\*\*\* | \*\*\*\*Required for Solana writes                                                                                   |
| `ens`                  | `{ client?, getText? }`                      | No         | ENS read access for `resolveRecipient` of `*.eth` names (an ENS-capable viem client, or a custom text-record reader) |
| `ipfs`                 | `{ gateways?, fetch? }`                      | No         | Gateway list + fetch override for `resolveRecipient` of `ipfs://` DID documents                                      |
| `ons`                  | `{ parentName?, registry?, mirrorProgram? }` | No         | ONS overrides for `*.opq.eth`-style names; defaults from `@opaquecash/deployments` (testnet parent `opqtest.eth`)    |
| `sns`                  | `{ getRecord? }`                             | No         | Custom `.sol` record reader; defaults to the Records V2 TXT reader over the `solana` connection                      |

## Unified signer

`UnifiedSigner` is one shape over every supported wallet type. Pass it to `fromWallet`,
or use `requestSetupSignature` directly when you manage sessions yourself:

```ts theme={"dark"}
import { requestSetupSignature, type UnifiedSigner } from "@opaquecash/opaque";

// Ethereum: EIP-1193 provider or a viem WalletClient
const eth: UnifiedSigner = { chain: "ethereum", address, provider: window.ethereum };
const ethViem: UnifiedSigner = { chain: "ethereum", address, walletClient };

// Solana: wallet-adapter shape
const sol: UnifiedSigner = { chain: "solana", publicKey, signMessage, signTransaction };

const signature = await requestSetupSignature(eth);  // personal_sign over SETUP_MESSAGE
```

| Field               | Ethereum                                      | Solana                                     |
| ------------------- | --------------------------------------------- | ------------------------------------------ |
| Identity            | `address`                                     | `publicKey` (base58 string or `PublicKey`) |
| Setup signature     | `provider` (personal\_sign) or `walletClient` | `signMessage`                              |
| Transaction signing | same `provider` / `walletClient`              | `signTransaction`                          |

### Static helpers

| Method                                                                   | Returns                              | Description                         |
| ------------------------------------------------------------------------ | ------------------------------------ | ----------------------------------- |
| `OpaqueClient.supportedChainIds()`                                       | `number[]`                           | Bundled EVM chain ids               |
| `OpaqueClient.chainDeployment(chainId)`                                  | `OpaqueChainDeployment \| undefined` | Contract addresses + default tokens |
| `OpaqueClient.buildReputationActionScope({ chainId, module, actionId })` | `string`                             | Action scope string                 |
| `OpaqueClient.reputationExternalNullifierFromScope(scope)`               | `bigint`                             | Circuit external nullifier          |

## Method index

<CardGroup cols={2}>
  <Card title="Stealth API" icon="eye-slash" href="/sdk/stealth-api">
    Registry, recipient resolution, send, scan, sweep, anonymity utilities
  </Card>

  <Card title="PSR API" icon="certificate" href="/sdk/psr-api">
    Schemas, attestations, delegates
  </Card>

  <Card title="Reputation API" icon="shield" href="/sdk/reputation-api">
    Traits, proofs, verifier
  </Card>

  <Card title="Cross-chain API" icon="arrow-right-arrow-left" href="/sdk/cross-chain-api">
    UAB relay and cross-chain scan
  </Card>

  <Card title="React hooks" icon="atom" href="/sdk/react">
    OpaqueProvider, useScan, useStealthBalance
  </Card>

  <Card title="Utilities" icon="wrench" href="/sdk/utilities">
    Exported helpers, types, adapters
  </Card>
</CardGroup>

## Instance getters

```ts theme={"dark"}
client.getChainId()           // number
client.getEthereumAddress()   // Address
client.getMetaAddressHex()    // Hex, the 98-byte meta-address (V||S||S_ed)
client.getContracts()         // { stealthMetaAddressRegistry, stealthAddressAnnouncer, opaqueReputationVerifier? }
```

## Signer requirements summary

| Category          | Methods                                                                                                     | Ethereum signer  | Solana signer             | WASM |
| ----------------- | ----------------------------------------------------------------------------------------------------------- | ---------------- | ------------------------- | ---- |
| Read-only stealth | `resolveRecipient`, `resolveRecipientMetaAddress`, `isMetaAddressRegistered`                                | no               | `solana` for Solana check | no   |
| Stealth writes    | `registerMetaAddress`, `sendStealthPayment`                                                                 | yes              | yes                       | no   |
| Scan / sweep      | `scan`, `sweep`, `filterOwnedAnnouncements`, balances                                                       | no               | `solana`                  | yes  |
| PSR admin         | `createSchema`, `issueAttestation`, ...                                                                     | yes (EVM)        | yes (Solana)              | no   |
| Reputation        | `discoverTraitsV2`, `discoverTraits` (legacy V1), `generateReputationProof`, `submitReputationVerification` | yes (submit EVM) | yes (submit Solana)       | yes  |

<Note>
  **Starknet (Sepolia, testnet preview).** The chain-neutral keys and single meta-address
  span Ethereum, Solana, and Starknet, and `scan({ chains: ["ethereum", "solana", "starknet"] })`
  covers all three once the `starknet` option is set. Starknet sends and sweeps do **not** go
  through `sendStealthPayment`/`sweep` (those accept only `"ethereum"`/`"solana"`): use
  `buildStarknetStealthSend({ recipient, amount })` and
  `buildStarknetSweep({ output, destination, amount })`, which return unsigned calls for the
  app's Starknet wallet to broadcast. See [Starknet](/concepts/starknet) for the full flow and caveats.
</Note>

## Error: WASM not configured

If `wasmModuleSpecifier` is omitted and you call a WASM-backed method:

```
Opaque: this method requires the cryptography WASM module. Pass `wasmModuleSpecifier` to OpaqueClient.create.
```

PSR admin methods work without WASM.
