| title | Quickstart |
|---|---|
| description | Install @opaquecash/opaque, derive stealth keys, and run a minimal send/receive flow. |
This guide takes you from zero to a working integration: install the SDK, create an OpaqueClient from a connected wallet, register your meta-address, and run a stealth send.
- Node.js 20.17+
- An Ethereum wallet (Sepolia testnet ETH for writes)
- Optional: a Solana (devnet) or Starknet (Sepolia) wallet for multichain flows
- For scan/sweep/prove: host or load the cryptography WASM module
npm install @opaquecash/opaque viemOpaqueClient.fromWallet does the whole session setup in one call: it prompts the
wallet once for a signature over the canonical SETUP_MESSAGE, derives the viewing and
spending keys from it (your private key is never touched), and wires the wallet as the
chain's transaction signer.
import { OpaqueClient } from "@opaquecash/opaque";
const client = await OpaqueClient.fromWallet({
wallets: { chain: "ethereum", address: userAddress, provider: window.ethereum },
chainId: 11155111, // Sepolia
rpcUrl: "https://ethereum-sepolia-rpc.publicnode.com",
wasmModuleSpecifier: "/pkg/cryptography.js", // required for scan/sweep/prove
});Solana wallets use the same shape with wallet-adapter fields:
wallets: { chain: "solana", publicKey, signMessage, signTransaction }If you prefer to manage the signature yourself, sign SETUP_MESSAGE manually and use
OpaqueClient.create.
const metaAddress = client.getMetaAddressHex();
// Share this 98-byte hex so senders and issuers can pay or attest to you.Register on-chain so others can resolve your normal address:
// High-level: submits the tx for you
const { txHash } = await client.registerMetaAddress("ethereum");
// Or build calldata and submit via your own wallet UI
const reg = client.buildRegisterMetaAddressTransaction();
await walletClient.sendTransaction({ to: reg.to, data: reg.data, chain: sepolia });sendStealthPayment resolves the recipient, derives a one-time stealth destination,
transfers the native asset, and publishes the discovery announcement:
import { parseEther } from "viem";
const result = await client.sendStealthPayment({
chain: "ethereum",
recipient: "0xRecipientEoaAddress...",
amount: parseEther("0.01"),
});The recipient can be an EVM address, a meta-address, a Solana pubkey, an
ipfs:// DID document, or a *.eth name with a com.opaque.meta record. To resolve
without sending (for example to show a confirmation screen):
const { metaAddressHex, source } = await client.resolveRecipient(recipientInput);For full control (custom gas, batching, your own wallet UI), use the low-level pair:
const send = client.prepareStealthSend(metaAddressHex);
const announce = client.buildAnnounceTransactionRequest(send);
await walletClient.sendTransaction({ to: send.stealthAddress, value: parseEther("0.01"), chain: sepolia });
await walletClient.sendTransaction({ to: announce.to, data: announce.data, chain: sepolia });const inbox = await client.scan({ chains: ["ethereum"] });
const balances = await client.getBalancesForOutputs(inbox);
if (inbox.length > 0) {
const { tx } = await client.sweep({
output: inbox[0],
chain: inbox[0].chain,
destination: freshAddress,
});
}Building a React app? @opaquecash/react packages the read side as hooks; see
React hooks.
The SDK repo ships a runnable example per flow in
sdk/examples/: from-wallet,
scan, send (including a delayed announcement), psr-prove, and uab-readonly.