Skip to main content

Install

npm install @opaquecash/opaque
Peer dependencies you typically also need:
PackageWhen
viemEthereum reads/writes, wallet clients
@solana/web3.jsSolana reads/writes (re-exported transitively)
@opaquecash/reactReact apps: provider + hooks over the client (React hooks)
@opaquecash/deploymentsRaw generated addresses/ABIs/program ids (Deployments)

WASM module

Scanning, sweeping, trait discovery, key reconstruction, and Groth16 witness generation run in a Rust→WASM module (opaque-scanner). Pass the JS glue URL as wasmModuleSpecifier:
await OpaqueClient.create({
  // ...
  wasmModuleSpecifier: "/pkg/cryptography.js",
});
Build from the scanner crate:
wasm-pack build --target web --out-dir pkg --out-name cryptography
Serve pkg/cryptography.js and pkg/cryptography_bg.wasm from your app’s public/ directory. The glue file loads the .wasm sibling automatically.
Do not hotlink https://www.opaque.cash/pkg/cryptography.js in production. Self-host the artifact so deploys are not blocked by CORS or third-party availability.

PSR admin without WASM

Schema and attestation management uses pure-JS DKSAP. You can omit wasmModuleSpecifier for issuer backends:
const issuer = await OpaqueClient.create({
  chainId: 11155111,
  rpcUrl,
  walletSignature,
  ethereumAddress,
  ethereumWalletClient,  // backend issuer
  // no wasmModuleSpecifier
});

await issuer.createSchema("ethereum", { /* … */ });
Calling scan, sweep, or generateReputationProof without WASM throws a clear error.

Signer matrix

OperationEthereumSolana
Reads (getMySchemas, scan, balances)rpcUrlsolana: { connection | rpcUrl | cluster }
Writes (registerMetaAddress, sendStealthPayment, PSR)ethereumProvider or ethereumWalletClientsolanaWallet: { publicKey, signTransaction }
Scan / sweep / provewasmModuleSpecifierwasmModuleSpecifier + solana config

Backend issuer (Ethereum)

import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";

const account = privateKeyToAccount(process.env.ISSUER_PRIVATE_KEY as `0x${string}`);
const walletClient = createWalletClient({ account, chain: sepolia, transport: http(rpcUrl) });

const client = await OpaqueClient.create({
  chainId: sepolia.id,
  rpcUrl,
  walletSignature: testEntropySignature,
  ethereumAddress: account.address,
  ethereumWalletClient: walletClient,
});

Backend issuer (Solana)

import { Keypair, Transaction } from "@solana/web3.js";

const keypair = Keypair.fromSecretKey(/* … */);

const client = await OpaqueClient.create({
  chainId: 11155111,  // EVM chainId required by config; unused on Solana-only path
  rpcUrl: "https://ethereum-sepolia.publicnode.com",
  walletSignature,
  ethereumAddress: "0x0000000000000000000000000000000000000001",
  solana: { rpcUrl: solanaRpc, cluster: "devnet" },
  solanaWallet: {
    publicKey: keypair.publicKey,
    signTransaction: async (tx: Transaction) => {
      tx.partialSign(keypair);
      return tx;
    },
  },
});

Monorepo / workspace

Link the local package during development:
{
  "dependencies": {
    "@opaquecash/opaque": "workspace:*"
  }
}
Build the SDK first: cd sdk && npm install && npm run build.

Runnable examples

sdk/examples/ ships one runnable script per flow:
ExampleFlow
from-wallet.tsBuild a client from a unified signer (offline)
scan.tsUnified cross-chain inbox scan + balances (read-only)
send.tsStealth send with optional delayed announcement (spends testnet ETH)
psr-prove.tsGenerate and verify a Groth16 reputation proof (offline)
uab-readonly.tsBuild a cross-chain announce request + read UAB inbox (read-only)
cd sdk && npm run build
npx tsx examples/from-wallet.ts